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
UNIX sockets support for RPC #9919
Conversation
@@ -377,6 +381,9 @@ bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPN | |||
|
|||
# bitcoin-cli binary # | |||
bitcoin_cli_SOURCES = bitcoin-cli.cpp | |||
if BUILD_EVUNIX |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@theuni This is pretty ugly, but I don't quite know how to do this otherwise. My first intuition was to add the file to libbitcoin_util_a_SOURCES
, however these can't depend on libevent as that is shared between all executables. There is no library that is shared between just bitcoind
and bitcoin-cli
and adding one just for this seems overkill.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is OK. I think we'll end up with a few wrappers for libevent, so moving those to their own internal helper lib will make sense.
d374f11
to
836d2d5
Compare
Neat. |
This does not compile with
|
@paveljanik Yes, that needs a version
|
For merge we should probably just not have the client version, then?
Still, this is cool - lots of better auth controls can be applied to
sockets than IPs :)
…On 03/04/17 21:05, Wladimir J. van der Laan wrote:
@paveljanik <https://github.com/paveljanik> Yes, that needs a version
|#ifdef|, that call doesn't exist in older versions of libevent, and on
newer ones (until my patch lands) it ignores the current connection and
tries to open a new one so that trick doesn't work. I'd recommend
testing with my patched libevent. Alternatively comment out the
bitcoin-cli change and test connecting with something else:
* |nc -U <socket>| works, for example
* curl also has a |--unix-socket| parameter, but haven't tried it yet
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#9919 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAnoHrbUK-rDkbYbVKJSmWq4tqSJyg64ks5ridILgaJpZM4MTHvN>.
|
Would prefer putting it behind |
Indeed. Default permissions of the socket will be
|
This is great! |
3bf02a9
to
cd3d513
Compare
Added a commit that makes the tests' RPC run over UNIX sockets on platforms that support it, except for a few tests that make explicit assumptions about running over TCP/IP. |
6f9286c
to
971af66
Compare
I've been trying to test this locally by running the qa tests. Something that I do quite often is to run the tests with --noshutdown, wait for the test to fail, and use bitcoin-cli to interactively run RPCs on the test nodes:
With this PR, I'm not able to do that anymore because the conf file doesn't contain the username/password, and even when I add them back in, I get the error:
Do you have a better way of connecting to a test node during qa testing? |
Right, I could change it to add the options (including
The above should work, though. That's exactly how I've been testing it. The connection failure is strange. Did you patch libevent so that
I'll add an environment variable to force running the RPC tests over TCP. |
@jnewbery Okay, added two commits:
|
5e20ec3
to
681d73c
Compare
Needed rebase, so rebased and squashed the squasme: commits. |
F.I.Y: However the CLI responded with: Maybe we should bump libevent in the dependencies directory? |
Yes, that's expected. Without, you should still be able to use the socket with other clients, for example from Python or with curl. The server-side support is not dependent on that patch. This could be done as part of the depends system, but rather not in this PR as it is a wholly separate discussion. |
8394db2
to
2dd6914
Compare
Rebased (RPC tests logging changes #9768) |
Add functionality for RPC over UNIX sockets, on platforms that support UNIX sockets. This is specified with `:unix:/path/to/socket` to `-rpcbind` and `-rpcconnect`. By default the socket path is relative to the data directory.
2dd6914
to
6b2997e
Compare
evhttp has no direct support for UNIX sockets thus is not able to recognize UNIX peers. Add custom code in HTTPRequest::GetPeer and evunix to recognize these connections as coming from localhost. The previous solution did not work on some libevent versions.
6b2997e
to
695e854
Compare
Oops, I accidentally did my review of this code on the other PR. See here #9979 (review) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 695e854, though code is pretty out of date
if (!sockaddr_from_path(&addr, path)) { | ||
return -1; | ||
} | ||
int fd = socket(AF_UNIX, SOCK_STREAM, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "rpc: UNIX sockets support"
Maybe add a sock_from_path function to dedup code between evunix_bind_fd & evunix_connect_fd a little more.
addr.sun_family = AF_UNIX; ]])], | ||
[ AC_MSG_RESULT(yes); | ||
AC_DEFINE(HAVE_SOCKADDR_UN, 1,[Define this symbol if the sockaddr_un is available]) | ||
AM_CONDITIONAL([BUILD_EVUNIX],[true]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "rpc: UNIX sockets support"
Naming of BUILD_EVUNIX seems a little unusual. Would expect something more like ENABLE_UNIX_SOCKETS (to be similar to ENABLE_WALLET, ENABLE_QT, ENABLE_ZMQ, etc).
Maybe more to the point though, since BUILD_EVUNIX is only used to control building of a single source file, maybe consider not defining it as build variable at all and just using #if HAVE_SOCKADDR_UN
in the c++ source. I think this makes sense since you are already using HAVE_SOCKADDR_UN to avoid calling evunix functions, so you might as well use the same variable to avoid defining those functions.
|
||
// If path is not complete, it is interpreted relative to the data directory. | ||
if (!name.is_complete()) { | ||
name = GetDataDir(true) / name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "rpc: UNIX sockets support"
Maybe consider deduping the path manipulation code with the same code in bitcoin-cli
src/httpserver.cpp
Outdated
/* Special: will get here for UNIX sockets. As we have no way to indicate that, | ||
* just pass localhost IPv6. | ||
*/ | ||
address = "::1"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "rpc: UNIX sockets support"
Seems like this instead of emulating an NET_IPV6 address, this should have it's own network type in netaddress.h
similar to NET_TOR
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1. This seems like another good use-case for NET_INTERNAL from #10446 (or something similar). As a bonus, that would allow us to use associative containers for unix socket addresses, since each path would have its own hash.
{ | ||
std::string name = path.string(); | ||
if (name.size() >= sizeof(addr->sun_path)) { | ||
/* Name too long */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "rpc: UNIX sockets support"
Would it be possible to throw an exception here? Since the path comes from the user's configuration it seems like it would be better to provide a specific error about the path than a generic connect or bind error.
{ | ||
struct sockaddr_un peer_unix; | ||
socklen_t peer_unix_len = sizeof(peer_unix); | ||
if (getpeername(fd, (sockaddr*)&peer_unix, &peer_unix_len) == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "rpc: More robust localhost-UNIX detection"
Would it be better to use getsockname than getpeername for this? Seems like maybe that would work even on a socket that isn't connected.
''' | ||
raise NotImplementedError | ||
|
||
class RPCConnectInfoTCP: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "tests: Run RPC tests optionally over UNIX socket"
Probably should inherit from RPCConnectInfo. Same with RPCConnectInfoUNIX class below. Doesn't really affect anything, but might make generated documentation / help messages a little better.
""" | ||
Args: | ||
url (str): URL of the RPC server to call | ||
node_number (int): the node number (or id) that this calls to | ||
urlinfo (RPCConnectInfo): Connection info of the RPC server to call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "tests: Run RPC tests optionally over UNIX socket"
s/urlinfo/conninfo/
I've had a quick read through the changes to the functional tests, but I'll defer doing a full review until this is rebased. A few general comments:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK. I added a few comments, and @ryanofsky beat me to several others.
@@ -377,6 +381,9 @@ bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPN | |||
|
|||
# bitcoin-cli binary # | |||
bitcoin_cli_SOURCES = bitcoin-cli.cpp | |||
if BUILD_EVUNIX |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is OK. I think we'll end up with a few wrappers for libevent, so moving those to their own internal helper lib will make sense.
src/httpserver.cpp
Outdated
/* Special: will get here for UNIX sockets. As we have no way to indicate that, | ||
* just pass localhost IPv6. | ||
*/ | ||
address = "::1"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1. This seems like another good use-case for NET_INTERNAL from #10446 (or something similar). As a bonus, that would allow us to use associative containers for unix socket addresses, since each path would have its own hash.
close(fd); | ||
return -1; | ||
} | ||
evutil_make_socket_nonblocking(fd); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to disable SIGPIPE here too?
#ifdef HAVE_SOCKADDR_UN | ||
//! Clean up UNIX sockets on shutdown | ||
for (const auto& path: cleanupUNIXSockets) { | ||
evunix_remove_socket(path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should probably .clear() in case this gets called twice.
Closing this for now. I'll probably pick it up again for 0.16. |
Concept ACK. c-lightning does this too FWIW. This was briefly discussed during the meeting today. Tag as Up for Grabs? |
Marked as "up for grabs" |
The server side should be easy to pick up. |
Does it have to be HTTP? Would it be easier to implement if it was pure (line delimited) JSON-RPC over UNIX domain sockets? |
This was discussed today on IRC. In summary, this is what c-lightning does for its UNIX socket support and what Core could potentially do in future for UNIX socket support as an alternative approach to getting a PR merged in libevent. Any non HTTP based UNIX socket support would need to be in addition to the existing HTTP based JSON-RPC rather than as a replacement. |
This seems ok, but without URLs, you would have to decide how to handle wallet endpoints. Maybe create different sockets for different wallets like |
contestable, i think it would be great to deprecate then remove http support & leave it up to external proxy services. |
This patch adds functionality to pass a pre-existing connection as a bufferevent to `evhttp_connection_base_bufferevent_new`. When the bufferevent has an existing fd, the evcon starts in state `EVCON_IDLE` so that requests can be made immediately. I am using this in Bitcoin Core for doing HTTP requests over a UNIX socket (bitcoin/bitcoin#9919). It could also be useful in sandboxed environments such as cloudabi that cannot directly make connections but can pass around fds.
Add functionality for RPC over UNIX sockets, on platforms that support UNIX sockets. This is specified with
:unix:/path/to/socket
to-rpcbind
and-rpcconnect
. By default the socket path is relative to the data directory.For the server side this works as-is.
For the client side (bitcoin-cli) this requires a small patch to libevent to be able to pass in a file descriptor of an existing connection (libevent/libevent#479).
Even without the client change this could be useful though. For example the Python RPC tests could connect through UNIX socket to avoid port collisions
(TODO: make a Python example). This has been implemented.Also the overhead of using UNIX sockets should be lower than using local TCP (see https://stackoverflow.com/questions/14973942/performance-tcp-loopback-connection-vs-unix-domain-socket).
Example
Server:
Client: