diff options
Diffstat (limited to 'src/httpserver.cpp')
| -rw-r--r-- | src/httpserver.cpp | 71 |
1 files changed, 34 insertions, 37 deletions
diff --git a/src/httpserver.cpp b/src/httpserver.cpp index c29f7a437..b9ca037c9 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -6,10 +6,11 @@ #include <chainparamsbase.h> #include <compat.h> -#include <util.h> -#include <utilstrencodings.h> +#include <util/system.h> +#include <util/strencodings.h> #include <netbase.h> #include <rpc/protocol.h> // For HTTP status codes +#include <shutdown.h> #include <sync.h> #include <ui_interface.h> @@ -21,7 +22,6 @@ #include <sys/types.h> #include <sys/stat.h> #include <signal.h> -#include <future> #include <event2/thread.h> #include <event2/buffer.h> @@ -124,7 +124,6 @@ public: struct HTTPPathHandler { - HTTPPathHandler() {} HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler): prefix(_prefix), exactMatch(_exactMatch), handler(_handler) { @@ -224,21 +223,25 @@ static void http_request_cb(struct evhttp_request* req, void* arg) } std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req)); - LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n", - RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString()); - // Early address-based allow check if (!ClientAllowed(hreq->GetPeer())) { + LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Client network is not allowed RPC access\n", + hreq->GetPeer().ToString()); hreq->WriteReply(HTTP_FORBIDDEN); return; } // Early reject unknown HTTP methods if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) { + LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n", + hreq->GetPeer().ToString()); hreq->WriteReply(HTTP_BADMETHOD); return; } + LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n", + RequestMethodString(hreq->GetRequestMethod()), SanitizeString(hreq->GetURI(), SAFE_CHARS_URI).substr(0, 100), hreq->GetPeer().ToString()); + // Find registered handler for prefix std::string strURI = hreq->GetURI(); std::string path; @@ -292,26 +295,26 @@ static bool ThreadHTTP(struct event_base* base) /** Bind HTTP server to specified addresses */ static bool HTTPBindAddresses(struct evhttp* http) { - int defaultPort = gArgs.GetArg("-rpcport", BaseParams().RPCPort()); + int http_port = gArgs.GetArg("-rpcport", BaseParams().RPCPort()); std::vector<std::pair<std::string, uint16_t> > endpoints; // Determine what addresses to bind to - if (!gArgs.IsArgSet("-rpcallowip")) { // Default to loopback if not allowing external IPs - endpoints.push_back(std::make_pair("::1", defaultPort)); - endpoints.push_back(std::make_pair("127.0.0.1", defaultPort)); + if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs + endpoints.push_back(std::make_pair("::1", http_port)); + endpoints.push_back(std::make_pair("127.0.0.1", http_port)); + if (gArgs.IsArgSet("-rpcallowip")) { + LogPrintf("WARNING: option -rpcallowip was specified without -rpcbind; this doesn't usually make sense\n"); + } if (gArgs.IsArgSet("-rpcbind")) { LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); } } else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) { - int port = defaultPort; + int port = http_port; std::string host; SplitHostPort(strRPCBind, port, host); endpoints.push_back(std::make_pair(host, port)); } - } else { // No specific bind address specified, bind to any - endpoints.push_back(std::make_pair("::", defaultPort)); - endpoints.push_back(std::make_pair("0.0.0.0", defaultPort)); } // Bind addresses @@ -319,6 +322,10 @@ static bool HTTPBindAddresses(struct evhttp* http) LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first, i->second); evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second); if (bind_handle) { + CNetAddr addr; + if (i->first.empty() || (LookupHost(i->first.c_str(), addr, false) && addr.IsBindAny())) { + LogPrintf("WARNING: the RPC server is not safe to expose to untrusted networks such as the public internet\n"); + } boundSockets.push_back(bind_handle); } else { LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second); @@ -357,8 +364,8 @@ bool InitHTTPServer() // Update libevent's log handling. Returns false if our version of // libevent doesn't support debug logging, in which case we should // clear the BCLog::LIBEVENT flag. - if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) { - g_logger->DisableCategory(BCLog::LIBEVENT); + if (!UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT))) { + LogInstance().DisableCategory(BCLog::LIBEVENT); } #ifdef WIN32 @@ -413,7 +420,6 @@ bool UpdateHTTPServerLogging(bool enable) { } std::thread threadHTTP; -std::future<bool> threadResult; static std::vector<std::thread> g_thread_http_workers; void StartHTTPServer() @@ -421,9 +427,7 @@ void StartHTTPServer() LogPrint(BCLog::HTTP, "Starting HTTP server\n"); int rpcThreads = std::max((long)gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); LogPrintf("HTTP: starting %d worker threads\n", rpcThreads); - std::packaged_task<bool(event_base*)> task(ThreadHTTP); - threadResult = task.get_future(); - threadHTTP = std::thread(std::move(task), eventBase); + threadHTTP = std::thread(ThreadHTTP, eventBase); for (int i = 0; i < rpcThreads; i++) { g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue); @@ -434,10 +438,6 @@ void InterruptHTTPServer() { LogPrint(BCLog::HTTP, "Interrupting HTTP server\n"); if (eventHTTP) { - // Unlisten sockets - for (evhttp_bound_socket *socket : boundSockets) { - evhttp_del_accept_socket(eventHTTP, socket); - } // Reject requests on current connections evhttp_set_gencb(eventHTTP, http_reject_request_cb, nullptr); } @@ -457,20 +457,14 @@ void StopHTTPServer() delete workQueue; workQueue = nullptr; } + // Unlisten sockets, these are what make the event loop running, which means + // that after this and all connections are closed the event loop will quit. + for (evhttp_bound_socket *socket : boundSockets) { + evhttp_del_accept_socket(eventHTTP, socket); + } + boundSockets.clear(); if (eventBase) { LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n"); - // Exit the event loop as soon as there are no active events. - event_base_loopexit(eventBase, nullptr); - // Give event loop a few seconds to exit (to send back last RPC responses), then break it - // Before this was solved with event_base_loopexit, but that didn't work as expected in - // at least libevent 2.0.21 and always introduced a delay. In libevent - // master that appears to be solved, so in the future that solution - // could be used again (if desirable). - // (see discussion in https://github.com/bitcoin/bitcoin/pull/6990) - if (threadResult.valid() && threadResult.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) { - LogPrintf("HTTP event loop did not exit within allotted time, sending loopbreak\n"); - event_base_loopbreak(eventBase); - } threadHTTP.join(); } if (eventHTTP) { @@ -575,6 +569,9 @@ void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value) void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) { assert(!replySent && req); + if (ShutdownRequested()) { + WriteHeader("Connection", "close"); + } // Send event to main http thread to send reply message struct evbuffer* evb = evhttp_request_get_output_buffer(req); assert(evb); |