LCOV - code coverage report
Current view: top level - src/rpc - request.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 112 124 90.3 %
Date: 2026-06-25 07:23:43 Functions: 11 11 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2021 The Bitcoin Core developers
       3             : // Copyright (c) 2014-2025 The Dash Core developers
       4             : // Distributed under the MIT software license, see the accompanying
       5             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       6             : 
       7             : #include <rpc/request.h>
       8             : 
       9             : #include <fs.h>
      10             : #include <random.h>
      11             : #include <rpc/protocol.h>
      12             : #include <util/system.h>
      13             : #include <util/strencodings.h>
      14             : 
      15             : #include <fstream>
      16             : #include <stdexcept>
      17             : #include <string>
      18             : #include <vector>
      19             : 
      20             : /**
      21             :  * JSON-RPC protocol.  Bitcoin speaks version 1.0 for maximum compatibility,
      22             :  * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
      23             :  * unspecified (HTTP errors and contents of 'error').
      24             :  *
      25             :  * 1.0 spec: http://json-rpc.org/wiki/specification
      26             :  * 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html
      27             :  */
      28             : 
      29        1457 : UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id)
      30             : {
      31        1457 :     UniValue request(UniValue::VOBJ);
      32        1457 :     request.pushKV("method", strMethod);
      33        1457 :     request.pushKV("params", params);
      34        1457 :     request.pushKV("id", id);
      35        1457 :     return request;
      36        1457 : }
      37             : 
      38      500491 : UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id)
      39             : {
      40      500491 :     UniValue reply(UniValue::VOBJ);
      41      500491 :     if (!error.isNull())
      42        9648 :         reply.pushKV("result", NullUniValue);
      43             :     else
      44      490843 :         reply.pushKV("result", result);
      45      500491 :     reply.pushKV("error", error);
      46      500491 :     reply.pushKV("id", id);
      47      500491 :     return reply;
      48      500491 : }
      49             : 
      50      499967 : std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id)
      51             : {
      52      499967 :     UniValue reply = JSONRPCReplyObj(result, error, id);
      53      499967 :     return reply.write() + "\n";
      54      499967 : }
      55             : 
      56       10609 : UniValue JSONRPCError(int code, const std::string& message)
      57             : {
      58       10609 :     UniValue error(UniValue::VOBJ);
      59       10609 :     error.pushKV("code", code);
      60       10609 :     error.pushKV("message", message);
      61       10609 :     return error;
      62       10609 : }
      63             : 
      64             : /** Username used when cookie authentication is in use (arbitrary, only for
      65             :  * recognizability in debugging/logging purposes)
      66             :  */
      67             : static const std::string COOKIEAUTH_USER = "__cookie__";
      68             : /** Default name for auth cookie file */
      69             : static const char* const COOKIEAUTH_FILE = ".cookie";
      70             : 
      71             : /** Get name of RPC authentication cookie file */
      72       10332 : static fs::path GetAuthCookieFile(bool temp=false)
      73             : {
      74       10332 :     fs::path arg = gArgs.GetPathArg("-rpccookiefile", COOKIEAUTH_FILE);
      75       10332 :     if (temp) {
      76        3017 :         arg += ".tmp";
      77        3017 :     }
      78       10332 :     return AbsPathForConfigVal(arg);
      79       10332 : }
      80             : 
      81             : static bool g_generated_cookie = false;
      82             : 
      83        3017 : bool GenerateAuthCookie(std::string *cookie_out)
      84             : {
      85        3017 :     const size_t COOKIE_SIZE = 32;
      86             :     unsigned char rand_pwd[COOKIE_SIZE];
      87        3017 :     GetRandBytes(rand_pwd);
      88        3017 :     std::string cookie = COOKIEAUTH_USER + ":" + HexStr(rand_pwd);
      89             : 
      90             :     /** the umask determines what permissions are used to create this file -
      91             :      * these are set to 0077 in util/system.cpp.
      92             :      */
      93        3017 :     std::ofstream file;
      94        3017 :     fs::path filepath_tmp = GetAuthCookieFile(true);
      95        3017 :     file.open(filepath_tmp);
      96        3017 :     if (!file.is_open()) {
      97           2 :         LogPrintf("Unable to open cookie authentication file %s for writing\n", fs::PathToString(filepath_tmp));
      98           2 :         return false;
      99             :     }
     100        3015 :     file << cookie;
     101        3015 :     file.close();
     102             : 
     103        3015 :     fs::path filepath = GetAuthCookieFile(false);
     104        3015 :     if (!RenameOver(filepath_tmp, filepath)) {
     105           0 :         LogPrintf("Unable to rename cookie authentication file %s to %s\n", fs::PathToString(filepath_tmp), fs::PathToString(filepath));
     106           0 :         return false;
     107             :     }
     108        3015 :     g_generated_cookie = true;
     109        3015 :     LogPrintf("Generated RPC authentication cookie %s\n", fs::PathToString(filepath));
     110             : 
     111        3015 :     if (cookie_out)
     112        3015 :         *cookie_out = cookie;
     113        3015 :     return true;
     114        3017 : }
     115             : 
     116        1285 : bool GetAuthCookie(std::string *cookie_out)
     117             : {
     118        1285 :     std::ifstream file;
     119        1285 :     std::string cookie;
     120        1285 :     fs::path filepath = GetAuthCookieFile();
     121        1285 :     file.open(filepath);
     122        1285 :     if (!file.is_open())
     123          28 :         return false;
     124        1257 :     std::getline(file, cookie);
     125        1257 :     file.close();
     126             : 
     127        1257 :     if (cookie_out)
     128        1257 :         *cookie_out = cookie;
     129        1257 :     return true;
     130        1285 : }
     131             : 
     132        3030 : void DeleteAuthCookie()
     133             : {
     134             :     try {
     135        3030 :         if (g_generated_cookie) {
     136             :             // Delete the cookie file if it was generated by this process
     137        3015 :             fs::remove(GetAuthCookieFile());
     138        3015 :         }
     139        3030 :     } catch (const fs::filesystem_error& e) {
     140           0 :         LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
     141           0 :     }
     142        3030 : }
     143             : 
     144          56 : std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in)
     145             : {
     146          56 :     if (!in.isArray()) {
     147           0 :         throw std::runtime_error("Batch must be an array");
     148             :     }
     149          56 :     const size_t num {in.size()};
     150          56 :     std::vector<UniValue> batch(num);
     151         280 :     for (const UniValue& rec : in.getValues()) {
     152         224 :         if (!rec.isObject()) {
     153           0 :             throw std::runtime_error("Batch member must be an object");
     154             :         }
     155         224 :         size_t id = rec["id"].getInt<int>();
     156         224 :         if (id >= num) {
     157           0 :             throw std::runtime_error("Batch member id is larger than batch size");
     158             :         }
     159         224 :         batch[id] = rec;
     160             :     }
     161          56 :     return batch;
     162          56 : }
     163             : 
     164      500429 : void JSONRPCRequest::parse(const UniValue& valRequest)
     165             : {
     166             :     // Parse request
     167      500429 :     if (!valRequest.isObject())
     168           0 :         throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
     169      500429 :     const UniValue& request = valRequest.get_obj();
     170             : 
     171             :     // Parse id now so errors from here on will have the id
     172      500429 :     id = request.find_value("id");
     173             : 
     174             :     // Parse method
     175      500429 :     const UniValue& valMethod{request.find_value("method")};
     176      500429 :     if (valMethod.isNull())
     177           0 :         throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
     178      500429 :     if (!valMethod.isStr())
     179           0 :         throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
     180      500429 :     strMethod = valMethod.get_str();
     181      500429 :     if (strMethod != "getblocktemplate") {
     182      499854 :         if (fLogIPs)
     183          54 :             LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s peeraddr=%s\n", SanitizeString(strMethod),
     184             :                 this->authUser, this->peerAddr);
     185             :         else
     186      499800 :             LogPrint(BCLog::RPC, "ThreadRPCServer method=%s user=%s\n", SanitizeString(strMethod), this->authUser);
     187      499854 :     }
     188             : 
     189             :     // Parse params
     190      500429 :     const UniValue& valParams{request.find_value("params")};
     191      500429 :     if (valParams.isArray() || valParams.isObject())
     192      500165 :         params = valParams;
     193         264 :     else if (valParams.isNull())
     194         264 :         params = UniValue(UniValue::VARR);
     195             :     else
     196           0 :         throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
     197      500429 : }
     198             : 
     199       35734 : JSONRPCRequest JSONRPCRequest::squashed() const
     200             : {
     201       35734 :    if (params.empty()) {
     202           0 :         return *this;
     203             :     }
     204       35734 :     JSONRPCRequest new_request{*this};
     205       35734 :     new_request.strMethod = strMethod + params[0].get_str();
     206       35734 :     new_request.params.setArray();
     207       82777 :     for (unsigned int i = 1; i < params.size(); ++i) {
     208       47043 :         new_request.params.push_back(params[i]);
     209       47043 :     }
     210       35734 :     return new_request;
     211       71468 : }

Generated by: LCOV version 1.16