Skip to content

Commit 4702532

Browse files
authored
Merge pull request #925 from evoskuil/master
Integrate options and origin support for bitcoind.
2 parents 972fae6 + 074e875 commit 4702532

File tree

10 files changed

+141
-44
lines changed

10 files changed

+141
-44
lines changed

include/bitcoin/node/interfaces/bitcoind.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ struct bitcoind_methods
3232
{
3333
/// Blockchain methods.
3434
method<"getbestblockhash">{},
35-
method<"getblock", string_t, optional<0>>{ "blockhash", "verbosity" },
35+
method<"getblock", string_t, optional<1>>{ "blockhash", "verbosity" },
3636
method<"getblockchaininfo">{},
3737
method<"getblockcount">{},
3838
method<"getblockfilter", string_t, optional<"basic"_t>>{ "blockhash", "filtertype" },

include/bitcoin/node/protocols/protocol_bitcoind.hpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,20 @@ class BCN_API protocol_bitcoind
4949
void start() NOEXCEPT override;
5050

5151
protected:
52+
using post = network::http::method::post;
53+
using options = network::http::method::options;
54+
5255
template <class Derived, typename Method, typename... Args>
5356
inline void subscribe(Method&& method, Args&&... args) NOEXCEPT
5457
{
5558
dispatcher_.subscribe(BIND_SHARED(method, args));
5659
}
5760

5861
/// Dispatch.
62+
void handle_receive_options(const code& ec,
63+
const network::http::method::options::cptr& options) NOEXCEPT override;
5964
void handle_receive_post(const code& ec,
60-
const network::http::method::post::cptr& post) NOEXCEPT override;
65+
const post::cptr& post) NOEXCEPT override;
6166

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

101106
private:
107+
// Provide the request for serialization, keeping it out of dispatch.
108+
void set_post(const post::cptr& post) NOEXCEPT;
109+
const post& get_post() const NOEXCEPT;
110+
111+
// Send the response.
112+
void send_json(boost::json::value&& model, size_t size_hint) NOEXCEPT;
113+
102114
// This is thread safe.
103115
////const options_t& options_;
104116

105-
// This is protected by strand.
117+
// These are protected by strand.
106118
dispatcher dispatcher_{};
119+
post::cptr post_{};
107120
};
108121

109122
} // namespace node

include/bitcoin/node/protocols/protocol_html.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,6 @@ class BCN_API protocol_html
8181
const network::http::request& request={}) NOEXCEPT;
8282

8383
/// Utilities.
84-
bool is_allowed_origin(const network::http::fields& fields,
85-
size_t version) const NOEXCEPT;
8684
std::filesystem::path to_path(
8785
const std::string& target = "/") const NOEXCEPT;
8886
std::filesystem::path to_local_path(

include/bitcoin/node/settings.hpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,6 @@ class BCN_API settings
147147
/// Default page for default URL (recommended).
148148
std::string default_{ "index.html" };
149149

150-
/// Validated against origins if configured (recommended).
151-
network::config::endpoints origins{};
152-
153-
/// Normalized origins.
154-
virtual system::string_list origin_names() const NOEXCEPT;
155-
156150
/// !path.empty() && http_server::enabled() [hidden, not virtual]
157151
virtual bool enabled() const NOEXCEPT;
158152
};

src/parser.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,12 @@ options_metadata parser::load_settings() THROWS
800800
(
801801
"web.origin",
802802
value<network::config::endpoints>(&configured.server.web.origins),
803-
"The allowed origin (http verification), multiple allowed, defaults to empty (disabled)."
803+
"The allowed origin (see CORS), multiple allowed, defaults to empty (disabled)."
804+
)
805+
(
806+
"web.allow_opaque_origin",
807+
value<bool>(&configured.server.web.allow_opaque_origin),
808+
"Allow requests from opaue origin (see CORS), multiple allowed, defaults to false."
804809
)
805810
(
806811
"web.path",
@@ -852,7 +857,12 @@ options_metadata parser::load_settings() THROWS
852857
(
853858
"explore.origin",
854859
value<network::config::endpoints>(&configured.server.explore.origins),
855-
"The allowed origin (http verification), multiple allowed, defaults to empty (disabled)."
860+
"The allowed origin (see CORS), multiple allowed, defaults to empty (disabled)."
861+
)
862+
(
863+
"explore.allow_opaque_origin",
864+
value<bool>(&configured.server.explore.allow_opaque_origin),
865+
"Allow requests from opaue origin (see CORS), multiple allowed, defaults to false."
856866
)
857867
(
858868
"explore.path",
@@ -906,6 +916,16 @@ options_metadata parser::load_settings() THROWS
906916
value<network::config::endpoints>(&configured.server.bitcoind.hosts),
907917
"The host name (http verification), multiple allowed, defaults to empty (disabled)."
908918
)
919+
(
920+
"bitcoind.origin",
921+
value<network::config::endpoints>(&configured.server.bitcoind.origins),
922+
"The allowed origin (see CORS), multiple allowed, defaults to empty (disabled)."
923+
)
924+
(
925+
"bitcoind.allow_opaque_origin",
926+
value<bool>(&configured.server.bitcoind.allow_opaque_origin),
927+
"Allow requests from opaue origin (see CORS), multiple allowed, defaults to false."
928+
)
909929

910930
/* [electrum] */
911931
////(

src/protocols/protocol_bitcoind.cpp

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ using namespace network::http;
3434
using namespace std::placeholders;
3535
using namespace boost::json;
3636

37-
using json_t = json_body::value_type;
38-
37+
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
3938
BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
4039
BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR)
4140

@@ -74,14 +73,55 @@ void protocol_bitcoind::start() NOEXCEPT
7473
// Dispatch.
7574
// ----------------------------------------------------------------------------
7675

76+
void protocol_bitcoind::handle_receive_options(const code& ec,
77+
const options::cptr& options) NOEXCEPT
78+
{
79+
BC_ASSERT(stranded());
80+
81+
if (stopped(ec))
82+
return;
83+
84+
// Enforce http host header (if any hosts are configured).
85+
if (!is_allowed_host(*options, options->version()))
86+
{
87+
send_bad_host(*options);
88+
return;
89+
}
90+
91+
// Enforce http origin policy (if any origins are configured).
92+
if (!is_allowed_origin(*options, options->version()))
93+
{
94+
send_forbidden(*options);
95+
return;
96+
}
97+
98+
send_ok(*options);
99+
}
100+
101+
// TODO: also handle_receive_get and dispatch based on URL parse.
77102
void protocol_bitcoind::handle_receive_post(const code& ec,
78-
const network::http::method::post::cptr& post) NOEXCEPT
103+
const post::cptr& post) NOEXCEPT
79104
{
80105
BC_ASSERT(stranded());
81106

82107
if (stopped(ec))
83108
return;
84109

110+
// Enforce http host header (if any hosts are configured).
111+
if (!is_allowed_host(*post, post->version()))
112+
{
113+
send_bad_host(*post);
114+
return;
115+
}
116+
117+
// Enforce http origin policy (if any origins are configured).
118+
if (!is_allowed_origin(*post, post->version()))
119+
{
120+
send_forbidden(*post);
121+
return;
122+
}
123+
124+
using json_t = json_body::value_type;
85125
const auto& body = post->body();
86126
if (!body.contains<json_t>())
87127
{
@@ -105,18 +145,30 @@ void protocol_bitcoind::handle_receive_post(const code& ec,
105145
return;
106146
}
107147

108-
// TODO: post-process request.
148+
set_post(post);
109149
if (const auto code = dispatcher_.notify(request))
110150
stop(code);
111151
}
112152

113153
// Handlers.
114154
// ----------------------------------------------------------------------------
155+
// github.com/bitcoin/bitcoin/blob/master/doc/JSON-RPC-interface.md
156+
// TODO: precompute size for buffer hints.
115157

158+
// {"jsonrpc": "1.0", "id": "curltest", "method": "getbestblockhash", "params": []}
116159
bool protocol_bitcoind::handle_get_best_block_hash(const code& ec,
117160
interface::get_best_block_hash) NOEXCEPT
118161
{
119-
return !ec;
162+
if (stopped(ec))
163+
return false;
164+
165+
const auto& query = archive();
166+
const auto hash = query.get_header_key(query.to_confirmed(
167+
query.get_top_confirmed()));
168+
169+
const response_t model{ .result = encode_hash(hash) };
170+
send_json(value_from(model), two * system::hash_size);
171+
return true;
120172
}
121173

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

283+
// private
284+
// ----------------------------------------------------------------------------
285+
286+
void protocol_bitcoind::set_post(const post::cptr& post) NOEXCEPT
287+
{
288+
BC_ASSERT(post);
289+
post_ = post;
290+
}
291+
292+
const protocol_bitcoind::post& protocol_bitcoind::get_post() const NOEXCEPT
293+
{
294+
BC_ASSERT(post_);
295+
return *post_;
296+
}
297+
298+
// TODO: post-process response for json-rpc version.
299+
void protocol_bitcoind::send_json(boost::json::value&& model,
300+
size_t size_hint) NOEXCEPT
301+
{
302+
BC_ASSERT(stranded());
303+
const auto& post = get_post();
304+
constexpr auto json = media_type::application_json;
305+
response response{ status::ok, post.version() };
306+
add_common_headers(response, post);
307+
add_access_control_headers(response, post);
308+
response.set(field::content_type, from_media_type(json));
309+
response.body() = { std::move(model), size_hint };
310+
response.prepare_payload();
311+
SEND(std::move(response), handle_complete, _1, error::success);
312+
}
313+
314+
BC_POP_WARNING()
231315
BC_POP_WARNING()
232316
BC_POP_WARNING()
233317

src/protocols/protocol_explore.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,8 @@ bool protocol_explore::handle_get_output_spenders(const code& ec,
934934
bool protocol_explore::handle_get_address(const code& ec, interface::address,
935935
uint8_t, uint8_t media, const hash_cptr& hash, bool turbo) NOEXCEPT
936936
{
937+
BC_ASSERT(stranded());
938+
937939
if (stopped(ec))
938940
return false;
939941

@@ -1010,6 +1012,8 @@ bool protocol_explore::handle_get_address_confirmed(const code& ec,
10101012
interface::address_confirmed, uint8_t, uint8_t media,
10111013
const hash_cptr& hash, bool turbo) NOEXCEPT
10121014
{
1015+
BC_ASSERT(stranded());
1016+
10131017
if (stopped(ec))
10141018
return false;
10151019

@@ -1045,6 +1049,8 @@ bool protocol_explore::handle_get_address_unconfirmed(const code& ec,
10451049
interface::address_unconfirmed, uint8_t, uint8_t,
10461050
const hash_cptr&, bool) NOEXCEPT
10471051
{
1052+
BC_ASSERT(stranded());
1053+
10481054
if (stopped(ec))
10491055
return false;
10501056

@@ -1060,6 +1066,8 @@ bool protocol_explore::handle_get_address_balance(const code& ec,
10601066
interface::address_balance, uint8_t, uint8_t media,
10611067
const hash_cptr& hash, bool turbo) NOEXCEPT
10621068
{
1069+
BC_ASSERT(stranded());
1070+
10631071
if (stopped(ec))
10641072
return false;
10651073

src/protocols/protocol_html.cpp

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ void protocol_html::send_json(boost::json::value&& model, size_t size_hint,
168168
BC_ASSERT(stranded());
169169
response response{ status::ok, request.version() };
170170
add_common_headers(response, request);
171+
add_access_control_headers(response, request);
171172
response.set(field::content_type, from_media_type(json));
172173
response.body() = { std::move(model), size_hint };
173174
response.prepare_payload();
@@ -180,6 +181,7 @@ void protocol_html::send_text(std::string&& hexidecimal,
180181
BC_ASSERT(stranded());
181182
response response{ status::ok, request.version() };
182183
add_common_headers(response, request);
184+
add_access_control_headers(response, request);
183185
response.set(field::content_type, from_media_type(text));
184186
response.body() = std::move(hexidecimal);
185187
response.prepare_payload();
@@ -192,6 +194,7 @@ void protocol_html::send_chunk(system::data_chunk&& bytes,
192194
BC_ASSERT(stranded());
193195
response response{ status::ok, request.version() };
194196
add_common_headers(response, request);
197+
add_access_control_headers(response, request);
195198
response.set(field::content_type, from_media_type(data));
196199
response.body() = std::move(bytes);
197200
response.prepare_payload();
@@ -205,6 +208,7 @@ void protocol_html::send_file(file&& file, media_type type,
205208
BC_ASSERT_MSG(file.is_open(), "sending closed file handle");
206209
response response{ status::ok, request.version() };
207210
add_common_headers(response, request);
211+
add_access_control_headers(response, request);
208212
response.set(field::content_type, from_media_type(type));
209213
response.body() = std::move(file);
210214
response.prepare_payload();
@@ -217,6 +221,7 @@ void protocol_html::send_span(span_body::value_type&& span,
217221
BC_ASSERT(stranded());
218222
response response{ status::ok, request.version() };
219223
add_common_headers(response, request);
224+
add_access_control_headers(response, request);
220225
response.set(field::content_type, from_media_type(type));
221226
response.body() = std::move(span);
222227
response.prepare_payload();
@@ -229,6 +234,7 @@ void protocol_html::send_buffer(buffer_body::value_type&& buffer,
229234
BC_ASSERT(stranded());
230235
response response{ status::ok, request.version() };
231236
add_common_headers(response, request);
237+
add_access_control_headers(response, request);
232238
response.set(field::content_type, from_media_type(type));
233239
response.body() = std::move(buffer);
234240
response.prepare_payload();
@@ -238,19 +244,6 @@ void protocol_html::send_buffer(buffer_body::value_type&& buffer,
238244
// Utilities.
239245
// ----------------------------------------------------------------------------
240246

241-
bool protocol_html::is_allowed_origin(const fields& fields,
242-
size_t version) const NOEXCEPT
243-
{
244-
// Allow same-origin and no-origin requests.
245-
// Origin header field is not available until http 1.1.
246-
const auto origin = fields[field::origin];
247-
if (origin.empty() || version < version_1_1)
248-
return true;
249-
250-
return options_.origins.empty() || system::contains(options_.origins,
251-
network::config::to_normal_host(origin, default_port()));
252-
}
253-
254247
std::filesystem::path protocol_html::to_path(
255248
const std::string& target) const NOEXCEPT
256249
{

src/settings.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,6 @@ settings::html_server::html_server(const std::string_view& logging_name,
180180
{
181181
}
182182

183-
system::string_list settings::html_server::origin_names() const NOEXCEPT
184-
{
185-
// secure changes default port from 80 to 443.
186-
const auto port = secure ? http::default_tls : http::default_http;
187-
return network::config::to_host_names(hosts, port);
188-
}
189-
190183
bool settings::html_server::enabled() const NOEXCEPT
191184
{
192185
return (!path.empty() || pages.enabled()) && http_server::enabled();

0 commit comments

Comments
 (0)