Skip to content
2 changes: 1 addition & 1 deletion include/bitcoin/node/interfaces/bitcoind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct bitcoind_methods
{
/// Blockchain methods.
method<"getbestblockhash">{},
method<"getblock", string_t, optional<0>>{ "blockhash", "verbosity" },
method<"getblock", string_t, optional<1>>{ "blockhash", "verbosity" },
method<"getblockchaininfo">{},
method<"getblockcount">{},
method<"getblockfilter", string_t, optional<"basic"_t>>{ "blockhash", "filtertype" },
Expand Down
17 changes: 15 additions & 2 deletions include/bitcoin/node/protocols/protocol_bitcoind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,20 @@ class BCN_API protocol_bitcoind
void start() NOEXCEPT override;

protected:
using post = network::http::method::post;
using options = network::http::method::options;

template <class Derived, typename Method, typename... Args>
inline void subscribe(Method&& method, Args&&... args) NOEXCEPT
{
dispatcher_.subscribe(BIND_SHARED(method, args));
}

/// Dispatch.
void handle_receive_options(const code& ec,
const network::http::method::options::cptr& options) NOEXCEPT override;
void handle_receive_post(const code& ec,
const network::http::method::post::cptr& post) NOEXCEPT override;
const post::cptr& post) NOEXCEPT override;

/// Handlers.
bool handle_get_best_block_hash(const code& ec,
Expand Down Expand Up @@ -99,11 +104,19 @@ class BCN_API protocol_bitcoind
interface::verify_tx_out_set, const std::string&) NOEXCEPT;

private:
// Provide the request for serialization, keeping it out of dispatch.
void set_post(const post::cptr& post) NOEXCEPT;
const post& get_post() const NOEXCEPT;

// Send the response.
void send_json(boost::json::value&& model, size_t size_hint) NOEXCEPT;

// This is thread safe.
////const options_t& options_;

// This is protected by strand.
// These are protected by strand.
dispatcher dispatcher_{};
post::cptr post_{};
};

} // namespace node
Expand Down
2 changes: 0 additions & 2 deletions include/bitcoin/node/protocols/protocol_html.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ class BCN_API protocol_html
const network::http::request& request={}) NOEXCEPT;

/// Utilities.
bool is_allowed_origin(const network::http::fields& fields,
size_t version) const NOEXCEPT;
std::filesystem::path to_path(
const std::string& target = "/") const NOEXCEPT;
std::filesystem::path to_local_path(
Expand Down
6 changes: 0 additions & 6 deletions include/bitcoin/node/settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,6 @@ class BCN_API settings
/// Default page for default URL (recommended).
std::string default_{ "index.html" };

/// Validated against origins if configured (recommended).
network::config::endpoints origins{};

/// Normalized origins.
virtual system::string_list origin_names() const NOEXCEPT;

/// !path.empty() && http_server::enabled() [hidden, not virtual]
virtual bool enabled() const NOEXCEPT;
};
Expand Down
24 changes: 22 additions & 2 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,12 @@ options_metadata parser::load_settings() THROWS
(
"web.origin",
value<network::config::endpoints>(&configured.server.web.origins),
"The allowed origin (http verification), multiple allowed, defaults to empty (disabled)."
"The allowed origin (see CORS), multiple allowed, defaults to empty (disabled)."
)
(
"web.allow_opaque_origin",
value<bool>(&configured.server.web.allow_opaque_origin),
"Allow requests from opaue origin (see CORS), multiple allowed, defaults to false."
)
(
"web.path",
Expand Down Expand Up @@ -852,7 +857,12 @@ options_metadata parser::load_settings() THROWS
(
"explore.origin",
value<network::config::endpoints>(&configured.server.explore.origins),
"The allowed origin (http verification), multiple allowed, defaults to empty (disabled)."
"The allowed origin (see CORS), multiple allowed, defaults to empty (disabled)."
)
(
"explore.allow_opaque_origin",
value<bool>(&configured.server.explore.allow_opaque_origin),
"Allow requests from opaue origin (see CORS), multiple allowed, defaults to false."
)
(
"explore.path",
Expand Down Expand Up @@ -906,6 +916,16 @@ options_metadata parser::load_settings() THROWS
value<network::config::endpoints>(&configured.server.bitcoind.hosts),
"The host name (http verification), multiple allowed, defaults to empty (disabled)."
)
(
"bitcoind.origin",
value<network::config::endpoints>(&configured.server.bitcoind.origins),
"The allowed origin (see CORS), multiple allowed, defaults to empty (disabled)."
)
(
"bitcoind.allow_opaque_origin",
value<bool>(&configured.server.bitcoind.allow_opaque_origin),
"Allow requests from opaue origin (see CORS), multiple allowed, defaults to false."
)

/* [electrum] */
////(
Expand Down
94 changes: 89 additions & 5 deletions src/protocols/protocol_bitcoind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ using namespace network::http;
using namespace std::placeholders;
using namespace boost::json;

using json_t = json_body::value_type;

BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR)

Expand Down Expand Up @@ -74,14 +73,55 @@ void protocol_bitcoind::start() NOEXCEPT
// Dispatch.
// ----------------------------------------------------------------------------

void protocol_bitcoind::handle_receive_options(const code& ec,
const options::cptr& options) NOEXCEPT
{
BC_ASSERT(stranded());

if (stopped(ec))
return;

// Enforce http host header (if any hosts are configured).
if (!is_allowed_host(*options, options->version()))
{
send_bad_host(*options);
return;
}

// Enforce http origin policy (if any origins are configured).
if (!is_allowed_origin(*options, options->version()))
{
send_forbidden(*options);
return;
}

send_ok(*options);
}

// TODO: also handle_receive_get and dispatch based on URL parse.
void protocol_bitcoind::handle_receive_post(const code& ec,
const network::http::method::post::cptr& post) NOEXCEPT
const post::cptr& post) NOEXCEPT
{
BC_ASSERT(stranded());

if (stopped(ec))
return;

// Enforce http host header (if any hosts are configured).
if (!is_allowed_host(*post, post->version()))
{
send_bad_host(*post);
return;
}

// Enforce http origin policy (if any origins are configured).
if (!is_allowed_origin(*post, post->version()))
{
send_forbidden(*post);
return;
}

using json_t = json_body::value_type;
const auto& body = post->body();
if (!body.contains<json_t>())
{
Expand All @@ -105,18 +145,30 @@ void protocol_bitcoind::handle_receive_post(const code& ec,
return;
}

// TODO: post-process request.
set_post(post);
if (const auto code = dispatcher_.notify(request))
stop(code);
}

// Handlers.
// ----------------------------------------------------------------------------
// github.com/bitcoin/bitcoin/blob/master/doc/JSON-RPC-interface.md
// TODO: precompute size for buffer hints.

// {"jsonrpc": "1.0", "id": "curltest", "method": "getbestblockhash", "params": []}
bool protocol_bitcoind::handle_get_best_block_hash(const code& ec,
interface::get_best_block_hash) NOEXCEPT
{
return !ec;
if (stopped(ec))
return false;

const auto& query = archive();
const auto hash = query.get_header_key(query.to_confirmed(
query.get_top_confirmed()));

const response_t model{ .result = encode_hash(hash) };
send_json(value_from(model), two * system::hash_size);
return true;
}

// method<"getblock", string_t, optional<0_u32>>{ "blockhash", "verbosity" },
Expand Down Expand Up @@ -228,6 +280,38 @@ bool protocol_bitcoind::handle_verify_tx_out_set(const code& ec,
return !ec;
}

// private
// ----------------------------------------------------------------------------

void protocol_bitcoind::set_post(const post::cptr& post) NOEXCEPT
{
BC_ASSERT(post);
post_ = post;
}

const protocol_bitcoind::post& protocol_bitcoind::get_post() const NOEXCEPT
{
BC_ASSERT(post_);
return *post_;
}

// TODO: post-process response for json-rpc version.
void protocol_bitcoind::send_json(boost::json::value&& model,
size_t size_hint) NOEXCEPT
{
BC_ASSERT(stranded());
const auto& post = get_post();
constexpr auto json = media_type::application_json;
response response{ status::ok, post.version() };
add_common_headers(response, post);
add_access_control_headers(response, post);
response.set(field::content_type, from_media_type(json));
response.body() = { std::move(model), size_hint };
response.prepare_payload();
SEND(std::move(response), handle_complete, _1, error::success);
}

BC_POP_WARNING()
BC_POP_WARNING()
BC_POP_WARNING()

Expand Down
8 changes: 8 additions & 0 deletions src/protocols/protocol_explore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,8 @@ bool protocol_explore::handle_get_output_spenders(const code& ec,
bool protocol_explore::handle_get_address(const code& ec, interface::address,
uint8_t, uint8_t media, const hash_cptr& hash, bool turbo) NOEXCEPT
{
BC_ASSERT(stranded());

if (stopped(ec))
return false;

Expand Down Expand Up @@ -1010,6 +1012,8 @@ bool protocol_explore::handle_get_address_confirmed(const code& ec,
interface::address_confirmed, uint8_t, uint8_t media,
const hash_cptr& hash, bool turbo) NOEXCEPT
{
BC_ASSERT(stranded());

if (stopped(ec))
return false;

Expand Down Expand Up @@ -1045,6 +1049,8 @@ bool protocol_explore::handle_get_address_unconfirmed(const code& ec,
interface::address_unconfirmed, uint8_t, uint8_t,
const hash_cptr&, bool) NOEXCEPT
{
BC_ASSERT(stranded());

if (stopped(ec))
return false;

Expand All @@ -1060,6 +1066,8 @@ bool protocol_explore::handle_get_address_balance(const code& ec,
interface::address_balance, uint8_t, uint8_t media,
const hash_cptr& hash, bool turbo) NOEXCEPT
{
BC_ASSERT(stranded());

if (stopped(ec))
return false;

Expand Down
19 changes: 6 additions & 13 deletions src/protocols/protocol_html.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ void protocol_html::send_json(boost::json::value&& model, size_t size_hint,
BC_ASSERT(stranded());
response response{ status::ok, request.version() };
add_common_headers(response, request);
add_access_control_headers(response, request);
response.set(field::content_type, from_media_type(json));
response.body() = { std::move(model), size_hint };
response.prepare_payload();
Expand All @@ -180,6 +181,7 @@ void protocol_html::send_text(std::string&& hexidecimal,
BC_ASSERT(stranded());
response response{ status::ok, request.version() };
add_common_headers(response, request);
add_access_control_headers(response, request);
response.set(field::content_type, from_media_type(text));
response.body() = std::move(hexidecimal);
response.prepare_payload();
Expand All @@ -192,6 +194,7 @@ void protocol_html::send_chunk(system::data_chunk&& bytes,
BC_ASSERT(stranded());
response response{ status::ok, request.version() };
add_common_headers(response, request);
add_access_control_headers(response, request);
response.set(field::content_type, from_media_type(data));
response.body() = std::move(bytes);
response.prepare_payload();
Expand All @@ -205,6 +208,7 @@ void protocol_html::send_file(file&& file, media_type type,
BC_ASSERT_MSG(file.is_open(), "sending closed file handle");
response response{ status::ok, request.version() };
add_common_headers(response, request);
add_access_control_headers(response, request);
response.set(field::content_type, from_media_type(type));
response.body() = std::move(file);
response.prepare_payload();
Expand All @@ -217,6 +221,7 @@ void protocol_html::send_span(span_body::value_type&& span,
BC_ASSERT(stranded());
response response{ status::ok, request.version() };
add_common_headers(response, request);
add_access_control_headers(response, request);
response.set(field::content_type, from_media_type(type));
response.body() = std::move(span);
response.prepare_payload();
Expand All @@ -229,6 +234,7 @@ void protocol_html::send_buffer(buffer_body::value_type&& buffer,
BC_ASSERT(stranded());
response response{ status::ok, request.version() };
add_common_headers(response, request);
add_access_control_headers(response, request);
response.set(field::content_type, from_media_type(type));
response.body() = std::move(buffer);
response.prepare_payload();
Expand All @@ -238,19 +244,6 @@ void protocol_html::send_buffer(buffer_body::value_type&& buffer,
// Utilities.
// ----------------------------------------------------------------------------

bool protocol_html::is_allowed_origin(const fields& fields,
size_t version) const NOEXCEPT
{
// Allow same-origin and no-origin requests.
// Origin header field is not available until http 1.1.
const auto origin = fields[field::origin];
if (origin.empty() || version < version_1_1)
return true;

return options_.origins.empty() || system::contains(options_.origins,
network::config::to_normal_host(origin, default_port()));
}

std::filesystem::path protocol_html::to_path(
const std::string& target) const NOEXCEPT
{
Expand Down
7 changes: 0 additions & 7 deletions src/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,6 @@ settings::html_server::html_server(const std::string_view& logging_name,
{
}

system::string_list settings::html_server::origin_names() const NOEXCEPT
{
// secure changes default port from 80 to 443.
const auto port = secure ? http::default_tls : http::default_http;
return network::config::to_host_names(hosts, port);
}

bool settings::html_server::enabled() const NOEXCEPT
{
return (!path.empty() || pages.enabled()) && http_server::enabled();
Expand Down
Loading
Loading