Changeset View
Changeset View
Standalone View
Standalone View
src/httprpc.cpp
Show All 18 Lines | |||||
#include <boost/algorithm/string.hpp> // boost::trim | #include <boost/algorithm/string.hpp> // boost::trim | ||||
#include <cstdio> | #include <cstdio> | ||||
/** WWW-Authenticate to present with 401 Unauthorized response */ | /** WWW-Authenticate to present with 401 Unauthorized response */ | ||||
static const char *WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\""; | static const char *WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\""; | ||||
/** Simple one-shot callback timer to be used by the RPC mechanism to e.g. | /** RPC auth failure delay to make brute-forcing expensive */ | ||||
static const int64_t RPC_AUTH_BRUTE_FORCE_DELAY = 250; | |||||
/** | |||||
* Simple one-shot callback timer to be used by the RPC mechanism to e.g. | |||||
* re-lock the wallet. | * re-lock the wallet. | ||||
*/ | */ | ||||
class HTTPRPCTimer : public RPCTimerBase { | class HTTPRPCTimer : public RPCTimerBase { | ||||
public: | public: | ||||
HTTPRPCTimer(struct event_base *eventBase, std::function<void(void)> &func, | HTTPRPCTimer(struct event_base *eventBase, std::function<void(void)> &func, | ||||
int64_t millis) | int64_t millis) | ||||
: ev(eventBase, false, func) { | : ev(eventBase, false, func) { | ||||
struct timeval tv; | struct timeval tv; | ||||
tv.tv_sec = millis / 1000; | tv.tv_sec = millis / 1000; | ||||
tv.tv_usec = (millis % 1000) * 1000; | tv.tv_usec = (millis % 1000) * 1000; | ||||
ev.trigger(&tv); | ev.trigger(&tv); | ||||
} | } | ||||
private: | private: | ||||
HTTPEvent ev; | HTTPEvent ev; | ||||
}; | }; | ||||
class HTTPRPCTimerInterface : public RPCTimerInterface { | class HTTPRPCTimerInterface : public RPCTimerInterface { | ||||
public: | public: | ||||
HTTPRPCTimerInterface(struct event_base *_base) : base(_base) {} | HTTPRPCTimerInterface(struct event_base *_base) : base(_base) {} | ||||
const char *Name() override { return "HTTP"; } | const char *Name() override { return "HTTP"; } | ||||
RPCTimerBase *NewTimer(std::function<void(void)> &func, | RPCTimerBase *NewTimer(std::function<void(void)> &func, | ||||
int64_t millis) override { | int64_t millis) override { | ||||
return new HTTPRPCTimer(base, func, millis); | return new HTTPRPCTimer(base, func, millis); | ||||
} | } | ||||
private: | private: | ||||
struct event_base *base; | struct event_base *base; | ||||
}; | }; | ||||
Show All 13 Lines | else if (code == RPC_METHOD_NOT_FOUND) | ||||
nStatus = HTTP_NOT_FOUND; | nStatus = HTTP_NOT_FOUND; | ||||
std::string strReply = JSONRPCReply(NullUniValue, objError, id); | std::string strReply = JSONRPCReply(NullUniValue, objError, id); | ||||
req->WriteHeader("Content-Type", "application/json"); | req->WriteHeader("Content-Type", "application/json"); | ||||
req->WriteReply(nStatus, strReply); | req->WriteReply(nStatus, strReply); | ||||
} | } | ||||
// This function checks username and password against -rpcauth entries from | /* | ||||
// config file. | * This function checks username and password against -rpcauth entries from | ||||
* config file. | |||||
*/ | |||||
static bool multiUserAuthorized(std::string strUserPass) { | static bool multiUserAuthorized(std::string strUserPass) { | ||||
if (strUserPass.find(":") == std::string::npos) { | if (strUserPass.find(":") == std::string::npos) { | ||||
return false; | return false; | ||||
} | } | ||||
std::string strUser = strUserPass.substr(0, strUserPass.find(":")); | std::string strUser = strUserPass.substr(0, strUserPass.find(":")); | ||||
std::string strPass = strUserPass.substr(strUserPass.find(":") + 1); | std::string strPass = strUserPass.substr(strUserPass.find(":") + 1); | ||||
for (const std::string &strRPCAuth : gArgs.GetArgs("-rpcauth")) { | for (const std::string &strRPCAuth : gArgs.GetArgs("-rpcauth")) { | ||||
▲ Show 20 Lines • Show All 198 Lines • ▼ Show 20 Lines | if (!authHeader.first) { | ||||
return false; | return false; | ||||
} | } | ||||
JSONRPCRequest jreq; | JSONRPCRequest jreq; | ||||
if (!RPCAuthorized(config, authHeader.second, jreq.authUser)) { | if (!RPCAuthorized(config, authHeader.second, jreq.authUser)) { | ||||
LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", | LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", | ||||
req->GetPeer().ToString()); | req->GetPeer().ToString()); | ||||
/* Deter brute-forcing. | /** | ||||
* Deter brute-forcing. | |||||
* If this results in a DoS the user really shouldn't have their RPC | * If this results in a DoS the user really shouldn't have their RPC | ||||
* port exposed. */ | * port exposed. | ||||
MilliSleep(250); | */ | ||||
MilliSleep(RPC_AUTH_BRUTE_FORCE_DELAY); | |||||
req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA); | req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA); | ||||
req->WriteReply(HTTP_UNAUTHORIZED); | req->WriteReply(HTTP_UNAUTHORIZED); | ||||
return false; | return false; | ||||
} | } | ||||
try { | try { | ||||
// Parse request | // Parse request | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | static bool InitRPCAuthentication(Config &config) { | ||||
config.SetRPCCORSDomain(gArgs.GetArg("-rpccorsdomain", "")); | config.SetRPCCORSDomain(gArgs.GetArg("-rpccorsdomain", "")); | ||||
return true; | return true; | ||||
} | } | ||||
bool StartHTTPRPC(Config &config, | bool StartHTTPRPC(Config &config, | ||||
HTTPRPCRequestProcessor &httpRPCRequestProcessor) { | HTTPRPCRequestProcessor &httpRPCRequestProcessor) { | ||||
LogPrint(BCLog::RPC, "Starting HTTP RPC server\n"); | LogPrint(BCLog::RPC, "Starting HTTP RPC server\n"); | ||||
if (!InitRPCAuthentication(config)) return false; | if (!InitRPCAuthentication(config)) { | ||||
return false; | |||||
} | |||||
const std::function<bool(Config &, HTTPRequest *, const std::string &)> | const std::function<bool(Config &, HTTPRequest *, const std::string &)> | ||||
&rpcFunction = | &rpcFunction = | ||||
std::bind(&HTTPRPCRequestProcessor::DelegateHTTPRequest, | std::bind(&HTTPRPCRequestProcessor::DelegateHTTPRequest, | ||||
&httpRPCRequestProcessor, std::placeholders::_2); | &httpRPCRequestProcessor, std::placeholders::_2); | ||||
RegisterHTTPHandler("/", true, rpcFunction); | RegisterHTTPHandler("/", true, rpcFunction); | ||||
#ifdef ENABLE_WALLET | #ifdef ENABLE_WALLET | ||||
// ifdef can be removed once we switch to better endpoint support and API | // ifdef can be removed once we switch to better endpoint support and API | ||||
Show All 22 Lines |