Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update websocket/rpc mappings. #502

Merged
merged 5 commits into from
Jan 23, 2019
Merged

Conversation

thecodefactory
Copy link
Member

Add support for both core and native websocket/rpc mappings.

Pass through when an rpc request is incoming so that the output can be
formatted appropriately.

@thecodefactory
Copy link
Member Author

Depends on libbitcoin/libbitcoin-protocol#199

@evoskuil
Copy link
Member

evoskuil commented Dec 30, 2018

This looks unsafe. Client input can crash debug server and is not handled in a non-debug server.

@@ -63,6 +72,17 @@ query_socket::query_socket(zmq::context& context, server_node& node,
request.enqueue(to_chunk(hash));
};

const auto encode_hash_or_height = [&](zmq::message& request,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid the conditionality here, create independent handlers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method calls the independent handlers, the issue is that arguments can contain either a hash or a height. I agree that the decode isn't the safest way to determine which it is though. A lexical cast for height's type should work (which should throw if given a hash as input). UPDATE: That last part was sort of a question ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean that there is a single RPC method call (same name) on the wire that allows either parameter type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there is, but there doesn't have to be. We can support some calls by either (e.g. getblock/getblockheader). Not required, but I thought it would be nice :-) I see why it's dangerous too though, so I'll separate these properly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

encode_hash_or_height is not yet removed in this revision. It's marginally safer (and can properly error now), but can still be removed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks entirely safe (the domain of a base 10 encoded uint32_t is less than required for a valid base 16 hash encoding).

@evoskuil
Copy link
Member

Use .enqueue() here.

{
request.enqueue(command);
request.enqueue_little_endian(id);
const uint32_t height = std::strtoul(arguments.c_str(), nullptr, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the multi-argument encoding within the string here. But std::strtoul should be avoided here. Passing the c string directly is Unicode unsafe.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an implicit cast here from unsigned long to uint32_t. These aren't the same size, making this a bug unless the argument is first validated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't appear that there's any way using these handlers to return an error for invalid parameterization.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't appear that there's any way using these handlers to return an error for invalid parameterization.

I'm adding that, there's no reason there shouldn't be error handing on failed encodings.

const std::string& command, const std::string& arguments, uint32_t id)
{
data_chunk decoded;
if (decode_base16(decoded, arguments) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use bool decode_hash() here and handle the error (false).

@thecodefactory
Copy link
Member Author

This looks unsafe. Client input can crash debug server and is not handled in a non-debug server.

Agreed, will handle properly.

request.enqueue_little_endian(height);
try
{
request.enqueue_little_endian(boost::lexical_cast<uint32_t>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include boost header

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never like to see try/catch. This should use system::deserialize(...). [In locating the proper deserializer in libbitcoin-system I found and fixed an issue.]

uint32_t value;
if (!deserialize(value, arguments, false))
    return false;

request.enqueue_little_endian(value);
return true;

else
encode_height(request, command, arguments, id);
return encode_hash(request, command, arguments, id) ?
true : encode_height(request, command, arguments, id);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Boolean literal should never be included in a conditional expression...

return encode_hash(request, command, arguments, id) || 
    encode_height(request, command, arguments, id);

@@ -63,6 +72,17 @@ query_socket::query_socket(zmq::context& context, server_node& node,
request.enqueue(to_chunk(hash));
};

const auto encode_hash_or_height = [&](zmq::message& request,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks entirely safe (the domain of a base 10 encoded uint32_t is less than required for a valid base 16 hash encoding).

@thecodefactory
Copy link
Member Author

Updated (as well as protocol). Also just noticed our HTTP(s) origins were incorrect in the config, so fixed that here (though not particularly related).

const auto height = source.read_4_bytes_little_endian();
decode_send(connection, http::to_json(height, id));
const auto height = static_cast<uint64_t>(
source.read_4_bytes_little_endian());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for an explicit upcast for an integer (just downcast).

const auto decode_block = [this, &node, decode_send](
const data_chunk& data, const uint32_t id, connection_ptr connection)
const auto decode_block_raw = [this, &node, decode_send](
const data_chunk& data, const uint32_t id, connection_ptr connection,
Copy link
Member

@evoskuil evoskuil Jan 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use const for non-ref parameters (see other places too).

Pass through when an rpc request is incoming so that the output can be
formatted appropriately.
Updates based on libbitcoin-protocol changes.
@thecodefactory thecodefactory merged commit 99874f5 into libbitcoin:master Jan 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants