static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) { // JSONRPC handles only POST if (req->GetRequestMethod() != HTTPRequest::POST) { req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests"); return false; } // Check authorization std::pair<bool, std::string> authHeader = req->GetHeader("authorization"); if (!authHeader.first) { req->WriteReply(HTTP_UNAUTHORIZED); return false; } if (!RPCAuthorized(authHeader.second)) { LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString()); /* Deter brute-forcing If this results in a DoS the user really shouldn't have their RPC port exposed. */ MilliSleep(250); req->WriteReply(HTTP_UNAUTHORIZED); return false; } JSONRequest jreq; try { // Parse request UniValue valRequest; if (!valRequest.read(req->ReadBody())) throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); std::string strReply; // singleton request if (valRequest.isObject()) { jreq.parse(valRequest); UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); // Send reply strReply = JSONRPCReply(result, NullUniValue, jreq.id); // array of requests } else if (valRequest.isArray()) strReply = JSONRPCExecBatch(valRequest.get_array()); else throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strReply); } catch (const UniValue& objError) { JSONErrorReply(req, objError, jreq.id); return false; } catch (const std::exception& e) { JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); return false; } return true; }
void ErrorReply (std::ostream& stream, Json::Value const& objError, Json::Value const& id) { // Send error reply from json-rpc error object int nStatus = 500; int code = objError[jss::code].asInt (); if (code == -32600) nStatus = 400; else if (code == -32601) nStatus = 404; std::string strReply = JSONRPCReply (Json::Value (), objError, id); stream << HTTPReply (nStatus, strReply) << std::flush; }
static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id) { // Send error reply from json-rpc error object int nStatus = HTTP_INTERNAL_SERVER_ERROR; int code = find_value(objError, "code").get_int(); if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST; else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND; std::string strReply = JSONRPCReply(NullUniValue, objError, id); req->WriteHeader("Content-Type", "application/json"); req->WriteReply(nStatus, strReply); }
void ThreadCli() { // - Simple persistent cli // TODO: // unbuffered terminal input on linux // format help text char buffer[4096]; size_t n; fd_set rfds; struct timeval tv; printf("vTorrent CLI ready:\n> "); fflush(stdout); for (;;) { boost::this_thread::interruption_point(); // - must be set every iteration FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); tv.tv_sec = 0; tv.tv_usec = 200000; if (select(1, &rfds, NULL, NULL, &tv) < 1) // read blocks thread from interrupt continue; if ((n = read(STDIN_FILENO, buffer, sizeof(buffer))) < 1) continue; buffer[n] = '\0'; for ( ; n > 0 && (buffer[n-1] == '\n' || buffer[n-1] == '\r'); --n) buffer[n-1] = '\0'; if (strcmp(buffer, "stop") == 0 || strcmp(buffer, "exit") == 0 || strcmp(buffer, "quit") == 0 || strcmp(buffer, "q") == 0) { puts("Exiting..."); break; }; std::string strMethod; std::vector<std::string> strParams; char *p; if ((p = strchr(buffer, ' '))) { strMethod = std::string(buffer, p); char *pPS = p+1; char *pPE = p+1; while (*pPS && ((pPE = strchr(pPS, ' ')) || (pPE = strchr(pPS, '\0')))) { strParams.push_back(std::string(pPS, pPE)); pPS = pPE+1; }; } else { strMethod = std::string(buffer); }; std::string strReply; JSONRequest jreq; try { json_spirit::Array params = RPCConvertValues(strMethod, strParams); json_spirit::Value result = tableRPC.execute(strMethod, params); strReply = json_spirit::write_string(result, true); ReplaceStrInPlace(strReply, "\\n", "\n"); // format help msg if (write(STDOUT_FILENO, strReply.data(), strReply.length()) != (uint32_t) strReply.length()) throw std::runtime_error("write failed."); printf("\n> "); fflush(stdout); } catch (json_spirit::Object& objError) { std::string strReply = JSONRPCReply(json_spirit::Value::null, objError, 0); printf("Error: %s\n> ", strReply.c_str()); fflush(stdout); } catch (std::exception& e) { printf("Error: %s\n> ", e.what()); fflush(stdout); }; fflush(stdout); }; StartShutdown(); };
// VFALCO ARGH! returning a single std::string for the entire response? std::string ServerHandlerImp::processRequest (std::string const& request, beast::IP::Endpoint const& remoteIPAddress) { Json::Value jvRequest; { Json::Reader reader; if ((request.size () > 1000000) || ! reader.parse (request, jvRequest) || jvRequest.isNull () || ! jvRequest.isObject ()) { return createResponse (400, "Unable to parse request"); } } auto const role = getConfig ().getAdminRole (jvRequest, remoteIPAddress); Resource::Consumer usage; if (role == Config::ADMIN) usage = m_resourceManager.newAdminEndpoint (remoteIPAddress.to_string()); else usage = m_resourceManager.newInboundEndpoint(remoteIPAddress); if (usage.disconnect ()) return createResponse (503, "Server is overloaded"); // Parse id now so errors from here on will have the id // // VFALCO NOTE Except that "id" isn't included in the following errors. // Json::Value const id = jvRequest ["id"]; Json::Value const method = jvRequest ["method"]; if (method.isNull ()) return createResponse (400, "Null method"); if (! method.isString ()) return createResponse (400, "method is not string"); std::string strMethod = method.asString (); if (strMethod.empty()) return createResponse (400, "method is empty"); // Parse params Json::Value params = jvRequest ["params"]; if (params.isNull ()) params = Json::Value (Json::arrayValue); else if (!params.isArray ()) return HTTPReply (400, "params unparseable"); // VFALCO TODO Shouldn't we handle this earlier? // if (role == Config::FORBID) { // VFALCO TODO Needs implementing // FIXME Needs implementing // XXX This needs rate limiting to prevent brute forcing password. return HTTPReply (403, "Forbidden"); } std::string response; RPCHandler rpcHandler (m_networkOPs); Resource::Charge loadType = Resource::feeReferenceRPC; m_journal.debug << "Query: " << strMethod << params; Json::Value const result (rpcHandler.doRpcCommand ( strMethod, params, role, loadType)); m_journal.debug << "Reply: " << result; usage.charge (loadType); response = JSONRPCReply (result, Json::Value (), id); return createResponse (200, response); }
// Stolen directly from RPCServerHandler std::string processRequest (std::string const& request, std::string const& remoteAddress) { Json::Value jvRequest; { Json::Reader reader; if (! reader.parse (request, jvRequest) || jvRequest.isNull () || ! jvRequest.isObject ()) { return createResponse (400, "Unable to parse request"); } } Config::Role const role (getConfig ().getAdminRole (jvRequest, remoteAddress)); // Parse id now so errors from here on will have the id // // VFALCO NOTE Except that "id" isn't included in the following errors... // Json::Value const id = jvRequest ["id"]; Json::Value const method = jvRequest ["method"]; if (method.isNull ()) { return createResponse (400, "Null method"); } else if (! method.isString ()) { return createResponse (400, "method is not string"); } std::string strMethod = method.asString (); // Parse params Json::Value params = jvRequest ["params"]; if (params.isNull ()) { params = Json::Value (Json::arrayValue); } else if (!params.isArray ()) { return HTTPReply (400, "params unparseable"); } // VFALCO TODO Shouldn't we handle this earlier? // if (role == Config::FORBID) { // VFALCO TODO Needs implementing // FIXME Needs implementing // XXX This needs rate limiting to prevent brute forcing password. return HTTPReply (403, "Forbidden"); } // This code does all the work on the io_service thread and // has no rate-limiting based on source IP or anything. // This is a temporary safety if ((role != Config::ADMIN) && (getApp().getFeeTrack().isLoadedLocal())) { return HTTPReply (503, "Unable to service at this time"); } std::string response; m_journal.debug << "Query: " << strMethod << params; RPCHandler rpcHandler (&m_networkOPs); LoadType loadType = LT_RPCReference; Json::Value const result (rpcHandler.doRpcCommand ( strMethod, params, role, &loadType)); // VFALCO NOTE We discard loadType since there is no endpoint to punish m_journal.debug << "Reply: " << result; response = JSONRPCReply (result, Json::Value (), id); return createResponse (200, response); }