2011-05-14 20:10:21 +02:00
// Copyright (c) 2010 Satoshi Nakamoto
2014-02-08 22:50:24 +01:00
// Copyright (c) 2009-2014 The Bitcoin developers
2011-05-14 20:10:21 +02:00
// Distributed under the MIT/X11 software license, see the accompanying
2012-05-18 16:02:28 +02:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2011-05-14 20:10:21 +02:00
2013-11-20 14:18:57 +01:00
# include "rpcserver.h"
2013-04-13 07:13:08 +02:00
# include "base58.h"
2011-06-18 18:46:01 +02:00
# include "init.h"
2013-04-13 07:13:08 +02:00
# include "main.h"
2013-11-29 16:04:29 +01:00
# include "ui_interface.h"
2014-01-11 18:14:29 +01:00
# include "util.h"
2013-11-29 16:04:29 +01:00
# ifdef ENABLE_WALLET
2013-04-13 07:13:08 +02:00
# include "wallet.h"
2013-11-29 16:04:29 +01:00
# endif
2013-04-13 07:13:08 +02:00
2013-05-07 16:47:00 +02:00
# include <boost/algorithm/string.hpp>
2011-05-14 20:10:21 +02:00
# include <boost/asio.hpp>
2013-05-07 16:47:00 +02:00
# include <boost/asio/ssl.hpp>
2011-08-10 13:53:13 +02:00
# include <boost/bind.hpp>
2011-07-13 11:56:38 +02:00
# include <boost/filesystem.hpp>
2011-08-10 15:07:46 +02:00
# include <boost/foreach.hpp>
2011-05-14 20:10:21 +02:00
# include <boost/iostreams/concepts.hpp>
# include <boost/iostreams/stream.hpp>
2011-08-10 13:53:13 +02:00
# include <boost/shared_ptr.hpp>
2013-04-13 07:13:08 +02:00
# include "json/json_spirit_writer_template.h"
2012-04-05 03:19:27 +02:00
2011-05-14 20:10:21 +02:00
using namespace boost ;
using namespace boost : : asio ;
using namespace json_spirit ;
2014-06-27 11:13:25 +02:00
using namespace std ;
2011-05-14 20:10:21 +02:00
2011-12-01 15:07:02 +01:00
static std : : string strRPCUserColonPass ;
2012-05-13 06:43:24 +02:00
static bool fRPCRunning = false ;
2013-03-07 04:31:26 +01:00
// These are created by StartRPCThreads, destroyed in StopRPCThreads
static asio : : io_service * rpc_io_service = NULL ;
2013-05-07 16:47:00 +02:00
static map < string , boost : : shared_ptr < deadline_timer > > deadlineTimers ;
2013-03-07 04:31:26 +01:00
static ssl : : context * rpc_ssl_context = NULL ;
static boost : : thread_group * rpc_worker_group = NULL ;
2014-01-17 16:32:35 +01:00
static boost : : asio : : io_service : : work * rpc_dummy_work = NULL ;
2014-04-28 13:48:26 +02:00
static std : : vector < CSubNet > rpc_allow_subnets ; //!< List of subnets to allow RPC connections from
2014-05-09 10:01:50 +02:00
static std : : vector < boost : : shared_ptr < ip : : tcp : : acceptor > > rpc_acceptors ;
2012-04-15 02:35:58 +02:00
2012-06-23 00:36:42 +02:00
void RPCTypeCheck ( const Array & params ,
2012-08-20 22:18:17 +02:00
const list < Value_type > & typesExpected ,
bool fAllowNull )
2012-06-23 00:36:42 +02:00
{
2012-07-05 19:25:52 +02:00
unsigned int i = 0 ;
2012-06-23 00:36:42 +02:00
BOOST_FOREACH ( Value_type t , typesExpected )
{
if ( params . size ( ) < = i )
break ;
2012-08-20 22:18:17 +02:00
const Value & v = params [ i ] ;
if ( ! ( ( v . type ( ) = = t ) | | ( fAllowNull & & ( v . type ( ) = = null_type ) ) ) )
2012-06-23 00:36:42 +02:00
{
string err = strprintf ( " Expected type %s, got %s " ,
Value_type_name [ t ] , Value_type_name [ v . type ( ) ] ) ;
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_TYPE_ERROR , err ) ;
2012-06-23 00:36:42 +02:00
}
i + + ;
}
}
void RPCTypeCheck ( const Object & o ,
2012-08-20 22:18:17 +02:00
const map < string , Value_type > & typesExpected ,
bool fAllowNull )
2012-06-23 00:36:42 +02:00
{
BOOST_FOREACH ( const PAIRTYPE ( string , Value_type ) & t , typesExpected )
{
const Value & v = find_value ( o , t . first ) ;
2012-08-20 22:18:17 +02:00
if ( ! fAllowNull & & v . type ( ) = = null_type )
2014-01-16 16:15:27 +01:00
throw JSONRPCError ( RPC_TYPE_ERROR , strprintf ( " Missing %s " , t . first ) ) ;
2012-08-20 22:18:17 +02:00
if ( ! ( ( v . type ( ) = = t . second ) | | ( fAllowNull & & ( v . type ( ) = = null_type ) ) ) )
2012-06-23 00:36:42 +02:00
{
string err = strprintf ( " Expected type %s for %s, got %s " ,
2014-01-16 16:15:27 +01:00
Value_type_name [ t . second ] , t . first , Value_type_name [ v . type ( ) ] ) ;
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_TYPE_ERROR , err ) ;
2012-06-23 00:36:42 +02:00
}
}
}
2013-04-13 07:13:08 +02:00
int64_t AmountFromValue ( const Value & value )
2011-05-14 20:10:21 +02:00
{
double dAmount = value . get_real ( ) ;
if ( dAmount < = 0.0 | | dAmount > 21000000.0 )
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_TYPE_ERROR , " Invalid amount " ) ;
2013-04-13 07:13:08 +02:00
int64_t nAmount = roundint64 ( dAmount * COIN ) ;
2011-05-14 20:10:21 +02:00
if ( ! MoneyRange ( nAmount ) )
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_TYPE_ERROR , " Invalid amount " ) ;
2011-05-14 20:10:21 +02:00
return nAmount ;
}
2013-04-13 07:13:08 +02:00
Value ValueFromAmount ( int64_t amount )
2011-05-14 20:10:21 +02:00
{
return ( double ) amount / ( double ) COIN ;
}
2013-07-15 08:24:33 +02:00
uint256 ParseHashV ( const Value & v , string strName )
{
string strHex ;
if ( v . type ( ) = = str_type )
strHex = v . get_str ( ) ;
if ( ! IsHex ( strHex ) ) // Note: IsHex("") is false
throw JSONRPCError ( RPC_INVALID_PARAMETER , strName + " must be hexadecimal string (not ' " + strHex + " ') " ) ;
uint256 result ;
result . SetHex ( strHex ) ;
return result ;
}
uint256 ParseHashO ( const Object & o , string strKey )
{
return ParseHashV ( find_value ( o , strKey ) , strKey ) ;
}
vector < unsigned char > ParseHexV ( const Value & v , string strName )
{
string strHex ;
if ( v . type ( ) = = str_type )
strHex = v . get_str ( ) ;
if ( ! IsHex ( strHex ) )
throw JSONRPCError ( RPC_INVALID_PARAMETER , strName + " must be hexadecimal string (not ' " + strHex + " ') " ) ;
return ParseHex ( strHex ) ;
}
vector < unsigned char > ParseHexO ( const Object & o , string strKey )
{
return ParseHexV ( find_value ( o , strKey ) , strKey ) ;
}
2011-05-14 20:10:21 +02:00
2012-02-22 23:44:09 +01:00
2011-05-14 20:10:21 +02:00
///
/// Note: This interface may still be subject to change.
///
2012-04-18 22:42:17 +02:00
string CRPCTable : : help ( string strCommand ) const
2011-05-14 20:10:21 +02:00
{
string strRet ;
2014-07-15 21:38:52 +02:00
string category ;
2011-05-14 20:10:21 +02:00
set < rpcfn_type > setDone ;
2014-07-15 21:38:52 +02:00
vector < pair < string , const CRPCCommand * > > vCommands ;
2012-04-18 22:42:17 +02:00
for ( map < string , const CRPCCommand * > : : const_iterator mi = mapCommands . begin ( ) ; mi ! = mapCommands . end ( ) ; + + mi )
2014-07-15 21:38:52 +02:00
vCommands . push_back ( make_pair ( mi - > second - > category + mi - > first , mi - > second ) ) ;
sort ( vCommands . begin ( ) , vCommands . end ( ) ) ;
BOOST_FOREACH ( const PAIRTYPE ( string , const CRPCCommand * ) & command , vCommands )
2011-05-14 20:10:21 +02:00
{
2014-07-15 21:38:52 +02:00
const CRPCCommand * pcmd = command . second ;
string strMethod = pcmd - > name ;
2011-05-14 20:10:21 +02:00
// We already filter duplicates, but these deprecated screw up the sort order
2012-05-18 05:43:00 +02:00
if ( strMethod . find ( " label " ) ! = string : : npos )
2011-05-14 20:10:21 +02:00
continue ;
if ( strCommand ! = " " & & strMethod ! = strCommand )
continue ;
2013-11-29 16:04:29 +01:00
# ifdef ENABLE_WALLET
2013-08-25 06:00:02 +02:00
if ( pcmd - > reqWallet & & ! pwalletMain )
continue ;
2013-11-29 16:04:29 +01:00
# endif
2013-08-25 06:00:02 +02:00
2011-05-14 20:10:21 +02:00
try
{
Array params ;
2012-04-15 05:55:05 +02:00
rpcfn_type pfn = pcmd - > actor ;
2011-05-14 20:10:21 +02:00
if ( setDone . insert ( pfn ) . second )
( * pfn ) ( params , true ) ;
}
catch ( std : : exception & e )
{
// Help text is returned in an exception
string strHelp = string ( e . what ( ) ) ;
if ( strCommand = = " " )
2014-07-15 21:38:52 +02:00
{
2012-04-15 22:47:24 +02:00
if ( strHelp . find ( ' \n ' ) ! = string : : npos )
2011-05-14 20:10:21 +02:00
strHelp = strHelp . substr ( 0 , strHelp . find ( ' \n ' ) ) ;
2014-07-15 21:38:52 +02:00
if ( category ! = pcmd - > category )
{
if ( ! category . empty ( ) )
strRet + = " \n " ;
category = pcmd - > category ;
string firstLetter = category . substr ( 0 , 1 ) ;
boost : : to_upper ( firstLetter ) ;
strRet + = " == " + firstLetter + category . substr ( 1 ) + " == \n " ;
}
}
2011-05-14 20:10:21 +02:00
strRet + = strHelp + " \n " ;
}
}
if ( strRet = = " " )
2014-01-16 16:15:27 +01:00
strRet = strprintf ( " help: unknown command: %s \n " , strCommand ) ;
2011-05-14 20:10:21 +02:00
strRet = strRet . substr ( 0 , strRet . size ( ) - 1 ) ;
return strRet ;
}
2012-04-18 22:42:17 +02:00
Value help ( const Array & params , bool fHelp )
{
if ( fHelp | | params . size ( ) > 1 )
throw runtime_error (
2013-10-29 12:29:44 +01:00
" help ( \" command \" ) \n "
" \n List all commands, or get help for a specified command. \n "
" \n Arguments: \n "
" 1. \" command \" (string, optional) The command to get help on \n "
" \n Result: \n "
" \" text \" (string) The help text \n "
) ;
2012-04-18 22:42:17 +02:00
string strCommand ;
if ( params . size ( ) > 0 )
strCommand = params [ 0 ] . get_str ( ) ;
return tableRPC . help ( strCommand ) ;
}
2011-05-14 20:10:21 +02:00
Value stop ( const Array & params , bool fHelp )
{
2013-01-20 18:50:30 +01:00
// Accept the deprecated and ignored 'detach' boolean argument
2012-09-19 04:54:43 +02:00
if ( fHelp | | params . size ( ) > 1 )
2011-05-14 20:10:21 +02:00
throw runtime_error (
2012-11-04 12:48:45 +01:00
" stop \n "
2013-10-29 12:29:44 +01:00
" \n Stop Bitcoin server. " ) ;
2011-05-14 20:10:21 +02:00
// Shutdown will take long enough that the response should get back
2012-06-11 07:40:14 +02:00
StartShutdown ( ) ;
2012-05-13 16:09:14 +02:00
return " Bitcoin server stopping " ;
2011-05-14 20:10:21 +02:00
}
//
// Call Table
//
2012-04-15 05:55:05 +02:00
2012-04-21 01:37:34 +02:00
static const CRPCCommand vRPCCommands [ ] =
2014-07-15 21:38:52 +02:00
{ // category name actor (function) okSafeMode threadSafe reqWallet
// --------------------- ------------------------ ----------------------- ---------- ---------- ---------
2014-03-26 12:26:43 +01:00
/* Overall control/query calls */
2014-07-15 21:38:52 +02:00
{ " control " , " getinfo " , & getinfo , true , false , false } , /* uses wallet if enabled */
{ " control " , " help " , & help , true , true , false } ,
{ " control " , " stop " , & stop , true , true , false } ,
2014-03-26 12:26:43 +01:00
/* P2P networking */
2014-07-15 21:38:52 +02:00
{ " network " , " getnetworkinfo " , & getnetworkinfo , true , false , false } ,
{ " network " , " addnode " , & addnode , true , true , false } ,
{ " network " , " getaddednodeinfo " , & getaddednodeinfo , true , true , false } ,
{ " network " , " getconnectioncount " , & getconnectioncount , true , false , false } ,
{ " network " , " getnettotals " , & getnettotals , true , true , false } ,
{ " network " , " getpeerinfo " , & getpeerinfo , true , false , false } ,
{ " network " , " ping " , & ping , true , false , false } ,
2014-03-26 12:26:43 +01:00
/* Block chain and UTXO */
2014-07-15 21:38:52 +02:00
{ " blockchain " , " getblockchaininfo " , & getblockchaininfo , true , false , false } ,
{ " blockchain " , " getbestblockhash " , & getbestblockhash , true , false , false } ,
{ " blockchain " , " getblockcount " , & getblockcount , true , false , false } ,
{ " blockchain " , " getblock " , & getblock , true , false , false } ,
{ " blockchain " , " getblockhash " , & getblockhash , true , false , false } ,
{ " blockchain " , " getchaintips " , & getchaintips , true , false , false } ,
{ " blockchain " , " getdifficulty " , & getdifficulty , true , false , false } ,
{ " blockchain " , " getrawmempool " , & getrawmempool , true , false , false } ,
{ " blockchain " , " gettxout " , & gettxout , true , false , false } ,
{ " blockchain " , " gettxoutsetinfo " , & gettxoutsetinfo , true , false , false } ,
{ " blockchain " , " verifychain " , & verifychain , true , false , false } ,
2013-11-29 16:04:29 +01:00
2013-12-08 15:26:08 +01:00
/* Mining */
2014-07-15 21:38:52 +02:00
{ " mining " , " getblocktemplate " , & getblocktemplate , true , false , false } ,
{ " mining " , " getmininginfo " , & getmininginfo , true , false , false } ,
{ " mining " , " getnetworkhashps " , & getnetworkhashps , true , false , false } ,
{ " mining " , " prioritisetransaction " , & prioritisetransaction , true , false , false } ,
{ " mining " , " submitblock " , & submitblock , true , true , false } ,
# ifdef ENABLE_WALLET
/* Coin generation */
{ " generating " , " getgenerate " , & getgenerate , true , false , false } ,
{ " generating " , " gethashespersec " , & gethashespersec , true , false , false } ,
{ " generating " , " setgenerate " , & setgenerate , true , true , false } ,
# endif
2014-03-26 12:26:43 +01:00
/* Raw transactions */
2014-07-15 21:38:52 +02:00
{ " rawtransactions " , " createrawtransaction " , & createrawtransaction , true , false , false } ,
{ " rawtransactions " , " decoderawtransaction " , & decoderawtransaction , true , false , false } ,
{ " rawtransactions " , " decodescript " , & decodescript , true , false , false } ,
{ " rawtransactions " , " getrawtransaction " , & getrawtransaction , true , false , false } ,
{ " rawtransactions " , " sendrawtransaction " , & sendrawtransaction , false , false , false } ,
{ " rawtransactions " , " signrawtransaction " , & signrawtransaction , false , false , false } , /* uses wallet if enabled */
2014-03-26 12:26:43 +01:00
/* Utility functions */
2014-07-15 21:38:52 +02:00
{ " util " , " createmultisig " , & createmultisig , true , true , false } ,
{ " util " , " validateaddress " , & validateaddress , true , false , false } , /* uses wallet if enabled */
{ " util " , " verifymessage " , & verifymessage , true , false , false } ,
{ " util " , " estimatefee " , & estimatefee , true , true , false } ,
{ " util " , " estimatepriority " , & estimatepriority , true , true , false } ,
2013-12-08 15:26:08 +01:00
# ifdef ENABLE_WALLET
/* Wallet */
2014-07-15 21:38:52 +02:00
{ " wallet " , " addmultisigaddress " , & addmultisigaddress , true , false , true } ,
{ " wallet " , " backupwallet " , & backupwallet , true , false , true } ,
{ " wallet " , " dumpprivkey " , & dumpprivkey , true , false , true } ,
{ " wallet " , " dumpwallet " , & dumpwallet , true , false , true } ,
{ " wallet " , " encryptwallet " , & encryptwallet , true , false , true } ,
{ " wallet " , " getaccountaddress " , & getaccountaddress , true , false , true } ,
{ " wallet " , " getaccount " , & getaccount , true , false , true } ,
{ " wallet " , " getaddressesbyaccount " , & getaddressesbyaccount , true , false , true } ,
{ " wallet " , " getbalance " , & getbalance , false , false , true } ,
{ " wallet " , " getnewaddress " , & getnewaddress , true , false , true } ,
{ " wallet " , " getrawchangeaddress " , & getrawchangeaddress , true , false , true } ,
{ " wallet " , " getreceivedbyaccount " , & getreceivedbyaccount , false , false , true } ,
{ " wallet " , " getreceivedbyaddress " , & getreceivedbyaddress , false , false , true } ,
{ " wallet " , " gettransaction " , & gettransaction , false , false , true } ,
{ " wallet " , " getunconfirmedbalance " , & getunconfirmedbalance , false , false , true } ,
{ " wallet " , " getwalletinfo " , & getwalletinfo , false , false , true } ,
{ " wallet " , " importprivkey " , & importprivkey , true , false , true } ,
{ " wallet " , " importwallet " , & importwallet , true , false , true } ,
{ " wallet " , " importaddress " , & importaddress , true , false , true } ,
{ " wallet " , " keypoolrefill " , & keypoolrefill , true , false , true } ,
{ " wallet " , " listaccounts " , & listaccounts , false , false , true } ,
{ " wallet " , " listaddressgroupings " , & listaddressgroupings , false , false , true } ,
{ " wallet " , " listlockunspent " , & listlockunspent , false , false , true } ,
{ " wallet " , " listreceivedbyaccount " , & listreceivedbyaccount , false , false , true } ,
{ " wallet " , " listreceivedbyaddress " , & listreceivedbyaddress , false , false , true } ,
{ " wallet " , " listsinceblock " , & listsinceblock , false , false , true } ,
{ " wallet " , " listtransactions " , & listtransactions , false , false , true } ,
{ " wallet " , " listunspent " , & listunspent , false , false , true } ,
{ " wallet " , " lockunspent " , & lockunspent , true , false , true } ,
{ " wallet " , " move " , & movecmd , false , false , true } ,
{ " wallet " , " sendfrom " , & sendfrom , false , false , true } ,
{ " wallet " , " sendmany " , & sendmany , false , false , true } ,
{ " wallet " , " sendtoaddress " , & sendtoaddress , false , false , true } ,
{ " wallet " , " setaccount " , & setaccount , true , false , true } ,
{ " wallet " , " settxfee " , & settxfee , true , false , true } ,
{ " wallet " , " signmessage " , & signmessage , true , false , true } ,
{ " wallet " , " walletlock " , & walletlock , true , false , true } ,
{ " wallet " , " walletpassphrasechange " , & walletpassphrasechange , true , false , true } ,
{ " wallet " , " walletpassphrase " , & walletpassphrase , true , false , true } ,
2013-11-29 16:04:29 +01:00
# endif // ENABLE_WALLET
2011-05-14 20:10:21 +02:00
} ;
2012-04-18 22:42:17 +02:00
CRPCTable : : CRPCTable ( )
2012-04-15 05:55:05 +02:00
{
unsigned int vcidx ;
for ( vcidx = 0 ; vcidx < ( sizeof ( vRPCCommands ) / sizeof ( vRPCCommands [ 0 ] ) ) ; vcidx + + )
{
2012-04-21 01:37:34 +02:00
const CRPCCommand * pcmd ;
2011-05-14 20:10:21 +02:00
2012-04-15 05:55:05 +02:00
pcmd = & vRPCCommands [ vcidx ] ;
mapCommands [ pcmd - > name ] = pcmd ;
}
}
2011-05-14 20:10:21 +02:00
2012-04-18 22:42:17 +02:00
const CRPCCommand * CRPCTable : : operator [ ] ( string name ) const
{
map < string , const CRPCCommand * > : : const_iterator it = mapCommands . find ( name ) ;
if ( it = = mapCommands . end ( ) )
return NULL ;
return ( * it ) . second ;
}
2011-05-14 20:10:21 +02:00
bool HTTPAuthorized ( map < string , string > & mapHeaders )
{
string strAuth = mapHeaders [ " authorization " ] ;
if ( strAuth . substr ( 0 , 6 ) ! = " Basic " )
return false ;
string strUserPass64 = strAuth . substr ( 6 ) ; boost : : trim ( strUserPass64 ) ;
string strUserPass = DecodeBase64 ( strUserPass64 ) ;
2013-08-08 11:58:57 +02:00
return TimingResistantEqual ( strUserPass , strRPCUserColonPass ) ;
2011-05-14 20:10:21 +02:00
}
void ErrorReply ( std : : ostream & stream , const Object & objError , const Value & id )
{
// Send error reply from json-rpc error object
2012-10-04 10:43:40 +02:00
int nStatus = HTTP_INTERNAL_SERVER_ERROR ;
2011-05-14 20:10:21 +02:00
int code = find_value ( objError , " code " ) . get_int ( ) ;
2012-10-04 10:43:40 +02:00
if ( code = = RPC_INVALID_REQUEST ) nStatus = HTTP_BAD_REQUEST ;
else if ( code = = RPC_METHOD_NOT_FOUND ) nStatus = HTTP_NOT_FOUND ;
2011-05-14 20:10:21 +02:00
string strReply = JSONRPCReply ( Value : : null , objError , id ) ;
2012-04-24 07:10:02 +02:00
stream < < HTTPReply ( nStatus , strReply , false ) < < std : : flush ;
2011-05-14 20:10:21 +02:00
}
2014-04-28 15:23:29 +02:00
CNetAddr BoostAsioToCNetAddr ( boost : : asio : : ip : : address address )
2011-05-14 20:10:21 +02:00
{
2014-04-28 13:48:26 +02:00
CNetAddr netaddr ;
2011-08-10 14:21:43 +02:00
// Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses
if ( address . is_v6 ( )
& & ( address . to_v6 ( ) . is_v4_compatible ( )
| | address . to_v6 ( ) . is_v4_mapped ( ) ) )
2014-04-28 13:48:26 +02:00
address = address . to_v6 ( ) . to_v4 ( ) ;
if ( address . is_v4 ( ) )
{
boost : : asio : : ip : : address_v4 : : bytes_type bytes = address . to_v4 ( ) . to_bytes ( ) ;
netaddr . SetRaw ( NET_IPV4 , & bytes [ 0 ] ) ;
}
else
{
boost : : asio : : ip : : address_v6 : : bytes_type bytes = address . to_v6 ( ) . to_bytes ( ) ;
netaddr . SetRaw ( NET_IPV6 , & bytes [ 0 ] ) ;
}
return netaddr ;
}
bool ClientAllowed ( const boost : : asio : : ip : : address & address )
{
CNetAddr netaddr = BoostAsioToCNetAddr ( address ) ;
BOOST_FOREACH ( const CSubNet & subnet , rpc_allow_subnets )
if ( subnet . Match ( netaddr ) )
2011-05-14 20:10:21 +02:00
return true ;
return false ;
}
2011-08-10 15:07:46 +02:00
template < typename Protocol >
class AcceptedConnectionImpl : public AcceptedConnection
{
public :
AcceptedConnectionImpl (
asio : : io_service & io_service ,
ssl : : context & context ,
bool fUseSSL ) :
sslStream ( io_service , context ) ,
_d ( sslStream , fUseSSL ) ,
_stream ( _d )
{
}
virtual std : : iostream & stream ( )
{
return _stream ;
}
virtual std : : string peer_address_to_string ( ) const
{
return peer . address ( ) . to_string ( ) ;
}
2012-04-15 02:35:58 +02:00
2011-08-10 15:07:46 +02:00
virtual void close ( )
{
_stream . close ( ) ;
}
typename Protocol : : endpoint peer ;
asio : : ssl : : stream < typename Protocol : : socket > sslStream ;
private :
SSLIOStreamDevice < Protocol > _d ;
iostreams : : stream < SSLIOStreamDevice < Protocol > > _stream ;
2012-04-15 02:35:58 +02:00
} ;
2013-03-07 04:31:26 +01:00
void ServiceConnection ( AcceptedConnection * conn ) ;
2011-05-14 20:10:21 +02:00
2011-08-10 13:53:13 +02:00
// Forward declaration required for RPCListen
2011-08-10 15:07:46 +02:00
template < typename Protocol , typename SocketAcceptorService >
static void RPCAcceptHandler ( boost : : shared_ptr < basic_socket_acceptor < Protocol , SocketAcceptorService > > acceptor ,
2011-08-10 13:53:13 +02:00
ssl : : context & context ,
bool fUseSSL ,
2014-05-07 09:24:44 +02:00
boost : : shared_ptr < AcceptedConnection > conn ,
2011-08-10 13:53:13 +02:00
const boost : : system : : error_code & error ) ;
/**
* Sets up I / O resources to accept and handle a new connection .
*/
2011-08-10 15:07:46 +02:00
template < typename Protocol , typename SocketAcceptorService >
static void RPCListen ( boost : : shared_ptr < basic_socket_acceptor < Protocol , SocketAcceptorService > > acceptor ,
2011-08-10 13:53:13 +02:00
ssl : : context & context ,
const bool fUseSSL )
{
// Accept connection
2014-05-07 09:24:44 +02:00
boost : : shared_ptr < AcceptedConnectionImpl < Protocol > > conn ( new AcceptedConnectionImpl < Protocol > ( acceptor - > get_io_service ( ) , context , fUseSSL ) ) ;
2011-08-10 13:53:13 +02:00
acceptor - > async_accept (
conn - > sslStream . lowest_layer ( ) ,
conn - > peer ,
2011-08-10 15:07:46 +02:00
boost : : bind ( & RPCAcceptHandler < Protocol , SocketAcceptorService > ,
2011-08-10 13:53:13 +02:00
acceptor ,
boost : : ref ( context ) ,
fUseSSL ,
conn ,
2014-05-07 09:09:13 +02:00
_1 ) ) ;
2011-08-10 13:53:13 +02:00
}
2013-11-20 14:18:57 +01:00
2011-08-10 13:53:13 +02:00
/**
* Accept and handle incoming connection .
*/
2011-08-10 15:07:46 +02:00
template < typename Protocol , typename SocketAcceptorService >
static void RPCAcceptHandler ( boost : : shared_ptr < basic_socket_acceptor < Protocol , SocketAcceptorService > > acceptor ,
2011-08-10 13:53:13 +02:00
ssl : : context & context ,
const bool fUseSSL ,
2014-05-07 09:24:44 +02:00
boost : : shared_ptr < AcceptedConnection > conn ,
2011-08-10 13:53:13 +02:00
const boost : : system : : error_code & error )
{
2012-07-26 02:48:39 +02:00
// Immediately start accepting new connections, except when we're cancelled or our socket is closed.
2013-03-07 04:31:26 +01:00
if ( error ! = asio : : error : : operation_aborted & & acceptor - > is_open ( ) )
2012-06-24 13:20:17 +02:00
RPCListen ( acceptor , context , fUseSSL ) ;
2011-08-10 13:53:13 +02:00
2014-05-07 09:24:44 +02:00
AcceptedConnectionImpl < ip : : tcp > * tcp_conn = dynamic_cast < AcceptedConnectionImpl < ip : : tcp > * > ( conn . get ( ) ) ;
2011-08-10 15:07:46 +02:00
2011-08-10 13:53:13 +02:00
if ( error )
{
2014-05-07 09:09:13 +02:00
// TODO: Actually handle errors
LogPrintf ( " %s: Error: %s \n " , __func__ , error . message ( ) ) ;
2011-08-10 13:53:13 +02:00
}
// Restrict callers by IP. It is important to
// do this before starting client thread, to filter out
// certain DoS and misbehaving clients.
2013-03-07 04:31:26 +01:00
else if ( tcp_conn & & ! ClientAllowed ( tcp_conn - > peer . address ( ) ) )
2011-08-10 13:53:13 +02:00
{
// Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
if ( ! fUseSSL )
2014-06-29 03:14:36 +02:00
conn - > stream ( ) < < HTTPError ( HTTP_FORBIDDEN , false ) < < std : : flush ;
2014-05-07 09:24:44 +02:00
conn - > close ( ) ;
2011-08-10 13:53:13 +02:00
}
2013-03-07 04:31:26 +01:00
else {
2014-05-07 09:24:44 +02:00
ServiceConnection ( conn . get ( ) ) ;
2013-03-07 04:31:26 +01:00
conn - > close ( ) ;
2011-08-10 13:53:13 +02:00
}
}
2014-02-17 17:35:40 +01:00
static ip : : tcp : : endpoint ParseEndpoint ( const std : : string & strEndpoint , int defaultPort )
{
std : : string addr ;
int port = defaultPort ;
SplitHostPort ( strEndpoint , port , addr ) ;
return ip : : tcp : : endpoint ( asio : : ip : : address : : from_string ( addr ) , port ) ;
}
2013-03-07 04:31:26 +01:00
void StartRPCThreads ( )
2011-05-14 20:10:21 +02:00
{
2014-04-28 13:48:26 +02:00
rpc_allow_subnets . clear ( ) ;
rpc_allow_subnets . push_back ( CSubNet ( " 127.0.0.0/8 " ) ) ; // always allow IPv4 local subnet
rpc_allow_subnets . push_back ( CSubNet ( " ::1 " ) ) ; // always allow IPv6 localhost
if ( mapMultiArgs . count ( " -rpcallowip " ) )
{
const vector < string > & vAllow = mapMultiArgs [ " -rpcallowip " ] ;
BOOST_FOREACH ( string strAllow , vAllow )
{
CSubNet subnet ( strAllow ) ;
if ( ! subnet . IsValid ( ) )
{
uiInterface . ThreadSafeMessageBox (
2014-07-03 06:26:03 +02:00
strprintf ( " Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). " , strAllow ) ,
2014-04-28 13:48:26 +02:00
" " , CClientUIInterface : : MSG_ERROR ) ;
StartShutdown ( ) ;
return ;
}
rpc_allow_subnets . push_back ( subnet ) ;
}
}
std : : string strAllowed ;
BOOST_FOREACH ( const CSubNet & subnet , rpc_allow_subnets )
strAllowed + = subnet . ToString ( ) + " " ;
LogPrint ( " rpc " , " Allowing RPC connections from: %s \n " , strAllowed ) ;
2011-12-01 15:07:02 +01:00
strRPCUserColonPass = mapArgs [ " -rpcuser " ] + " : " + mapArgs [ " -rpcpassword " ] ;
2013-05-07 15:16:25 +02:00
if ( ( ( mapArgs [ " -rpcpassword " ] = = " " ) | |
( mapArgs [ " -rpcuser " ] = = mapArgs [ " -rpcpassword " ] ) ) & & Params ( ) . RequireRPCPassword ( ) )
2011-05-14 20:10:21 +02:00
{
2012-02-05 08:30:43 +01:00
unsigned char rand_pwd [ 32 ] ;
2014-06-24 14:27:32 +02:00
GetRandBytes ( rand_pwd , 32 ) ;
2011-05-14 20:10:21 +02:00
string strWhatAmI = " To use bitcoind " ;
if ( mapArgs . count ( " -server " ) )
strWhatAmI = strprintf ( _ ( " To use the %s option " ) , " \" -server \" " ) ;
else if ( mapArgs . count ( " -daemon " ) )
strWhatAmI = strprintf ( _ ( " To use the %s option " ) , " \" -daemon \" " ) ;
2012-05-06 19:40:58 +02:00
uiInterface . ThreadSafeMessageBox ( strprintf (
2013-04-02 15:45:08 +02:00
_ ( " %s, you must set a rpcpassword in the configuration file: \n "
" %s \n "
2012-03-31 15:08:25 +02:00
" It is recommended you use the following random password: \n "
" rpcuser=bitcoinrpc \n "
" rpcpassword=%s \n "
" (you do not need to remember this password) \n "
2012-11-05 07:41:53 +01:00
" The username and password MUST NOT be the same. \n "
2013-03-21 15:08:21 +01:00
" If the file does not exist, create it with owner-readable-only file permissions. \n "
" It is also recommended to set alertnotify so you are notified of problems; \n "
" for example: alertnotify=echo %%s | mail -s \" Bitcoin Alert \" admin@foo.com \n " ) ,
2014-01-16 16:15:27 +01:00
strWhatAmI ,
GetConfigFile ( ) . string ( ) ,
EncodeBase58 ( & rand_pwd [ 0 ] , & rand_pwd [ 0 ] + 32 ) ) ,
2012-11-05 08:04:21 +01:00
" " , CClientUIInterface : : MSG_ERROR ) ;
2012-06-11 07:40:14 +02:00
StartShutdown ( ) ;
2011-05-14 20:10:21 +02:00
return ;
}
2013-03-07 04:31:26 +01:00
assert ( rpc_io_service = = NULL ) ;
rpc_io_service = new asio : : io_service ( ) ;
rpc_ssl_context = new ssl : : context ( * rpc_io_service , ssl : : context : : sslv23 ) ;
2011-05-14 20:10:21 +02:00
2013-04-28 17:37:50 +02:00
const bool fUseSSL = GetBoolArg ( " -rpcssl " , false ) ;
2012-05-20 20:27:53 +02:00
2011-05-14 20:10:21 +02:00
if ( fUseSSL )
{
2013-03-07 04:31:26 +01:00
rpc_ssl_context - > set_options ( ssl : : context : : no_sslv2 ) ;
2012-03-31 15:05:55 +02:00
filesystem : : path pathCertFile ( GetArg ( " -rpcsslcertificatechainfile " , " server.cert " ) ) ;
if ( ! pathCertFile . is_complete ( ) ) pathCertFile = filesystem : : path ( GetDataDir ( ) ) / pathCertFile ;
2013-03-07 04:31:26 +01:00
if ( filesystem : : exists ( pathCertFile ) ) rpc_ssl_context - > use_certificate_chain_file ( pathCertFile . string ( ) ) ;
2014-01-16 16:15:27 +01:00
else LogPrintf ( " ThreadRPCServer ERROR: missing server certificate file %s \n " , pathCertFile . string ( ) ) ;
2012-03-31 15:05:55 +02:00
filesystem : : path pathPKFile ( GetArg ( " -rpcsslprivatekeyfile " , " server.pem " ) ) ;
if ( ! pathPKFile . is_complete ( ) ) pathPKFile = filesystem : : path ( GetDataDir ( ) ) / pathPKFile ;
2013-03-07 04:31:26 +01:00
if ( filesystem : : exists ( pathPKFile ) ) rpc_ssl_context - > use_private_key_file ( pathPKFile . string ( ) , ssl : : context : : pem ) ;
2014-01-16 16:15:27 +01:00
else LogPrintf ( " ThreadRPCServer ERROR: missing server private key file %s \n " , pathPKFile . string ( ) ) ;
2012-03-31 15:05:55 +02:00
2013-10-17 16:11:25 +02:00
string strCiphers = GetArg ( " -rpcsslciphers " , " TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH " ) ;
2013-03-07 04:31:26 +01:00
SSL_CTX_set_cipher_list ( rpc_ssl_context - > impl ( ) , strCiphers . c_str ( ) ) ;
2011-05-14 20:10:21 +02:00
}
2014-02-17 17:35:40 +01:00
std : : vector < ip : : tcp : : endpoint > vEndpoints ;
bool bBindAny = false ;
2014-06-19 15:10:04 +02:00
int defaultPort = GetArg ( " -rpcport " , BaseParams ( ) . RPCPort ( ) ) ;
2014-02-17 17:35:40 +01:00
if ( ! mapArgs . count ( " -rpcallowip " ) ) // Default to loopback if not allowing external IPs
{
vEndpoints . push_back ( ip : : tcp : : endpoint ( asio : : ip : : address_v6 : : loopback ( ) , defaultPort ) ) ;
vEndpoints . push_back ( ip : : tcp : : endpoint ( asio : : ip : : address_v4 : : loopback ( ) , defaultPort ) ) ;
if ( mapArgs . count ( " -rpcbind " ) )
{
LogPrintf ( " WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect \n " ) ;
}
} else if ( mapArgs . count ( " -rpcbind " ) ) // Specific bind address
{
BOOST_FOREACH ( const std : : string & addr , mapMultiArgs [ " -rpcbind " ] )
{
try {
vEndpoints . push_back ( ParseEndpoint ( addr , defaultPort ) ) ;
}
catch ( boost : : system : : system_error & e )
{
uiInterface . ThreadSafeMessageBox (
strprintf ( _ ( " Could not parse -rpcbind value %s as network address " ) , addr ) ,
" " , CClientUIInterface : : MSG_ERROR ) ;
StartShutdown ( ) ;
return ;
}
}
} else { // No specific bind address specified, bind to any
vEndpoints . push_back ( ip : : tcp : : endpoint ( asio : : ip : : address_v6 : : any ( ) , defaultPort ) ) ;
vEndpoints . push_back ( ip : : tcp : : endpoint ( asio : : ip : : address_v4 : : any ( ) , defaultPort ) ) ;
// Prefer making the socket dual IPv6/IPv4 instead of binding
// to both addresses seperately.
bBindAny = true ;
}
2012-06-28 18:32:32 +02:00
2012-09-12 16:45:01 +02:00
bool fListening = false ;
std : : string strerr ;
2014-02-17 17:35:40 +01:00
BOOST_FOREACH ( const ip : : tcp : : endpoint & endpoint , vEndpoints )
2011-08-10 14:17:02 +02:00
{
2014-02-17 17:35:40 +01:00
asio : : ip : : address bindAddress = endpoint . address ( ) ;
LogPrintf ( " Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i) \n " , bindAddress . to_string ( ) , endpoint . port ( ) , bBindAny ) ;
boost : : system : : error_code v6_only_error ;
2014-05-09 10:01:50 +02:00
boost : : shared_ptr < ip : : tcp : : acceptor > acceptor ( new ip : : tcp : : acceptor ( * rpc_io_service ) ) ;
2011-08-10 14:17:02 +02:00
2014-02-17 17:35:40 +01:00
try {
2012-06-24 13:20:17 +02:00
acceptor - > open ( endpoint . protocol ( ) ) ;
acceptor - > set_option ( boost : : asio : : ip : : tcp : : acceptor : : reuse_address ( true ) ) ;
2014-02-17 17:35:40 +01:00
// Try making the socket dual IPv6/IPv4 when listening on the IPv6 "any" address
acceptor - > set_option ( boost : : asio : : ip : : v6_only (
! bBindAny | | bindAddress ! = asio : : ip : : address_v6 : : any ( ) ) , v6_only_error ) ;
2012-06-24 13:20:17 +02:00
acceptor - > bind ( endpoint ) ;
acceptor - > listen ( socket_base : : max_connections ) ;
2013-03-07 04:31:26 +01:00
RPCListen ( acceptor , * rpc_ssl_context , fUseSSL ) ;
2012-09-12 16:45:01 +02:00
fListening = true ;
2014-06-19 08:19:07 +02:00
rpc_acceptors . push_back ( acceptor ) ;
2014-02-17 17:35:40 +01:00
// If dual IPv6/IPv4 bind succesful, skip binding to IPv4 separately
if ( bBindAny & & bindAddress = = asio : : ip : : address_v6 : : any ( ) & & ! v6_only_error )
break ;
}
catch ( boost : : system : : system_error & e )
{
LogPrintf ( " ERROR: Binding RPC on address %s port %i failed: %s \n " , bindAddress . to_string ( ) , endpoint . port ( ) , e . what ( ) ) ;
strerr = strprintf ( _ ( " An error occurred while setting up the RPC address %s port %u for listening: %s " ) , bindAddress . to_string ( ) , endpoint . port ( ) , e . what ( ) ) ;
2011-08-10 14:17:02 +02:00
}
2012-09-12 16:45:01 +02:00
}
if ( ! fListening ) {
2012-11-05 08:04:21 +01:00
uiInterface . ThreadSafeMessageBox ( strerr , " " , CClientUIInterface : : MSG_ERROR ) ;
2012-06-17 14:30:37 +02:00
StartShutdown ( ) ;
2011-08-10 14:17:02 +02:00
return ;
}
2011-05-14 20:10:21 +02:00
2013-03-07 04:31:26 +01:00
rpc_worker_group = new boost : : thread_group ( ) ;
for ( int i = 0 ; i < GetArg ( " -rpcthreads " , 4 ) ; i + + )
rpc_worker_group - > create_thread ( boost : : bind ( & asio : : io_service : : run , rpc_io_service ) ) ;
2012-05-13 06:43:24 +02:00
fRPCRunning = true ;
2013-03-07 04:31:26 +01:00
}
2014-01-17 16:32:35 +01:00
void StartDummyRPCThread ( )
{
if ( rpc_io_service = = NULL )
{
rpc_io_service = new asio : : io_service ( ) ;
/* Create dummy "work" to keep the thread from exiting when no timeouts active,
* see http : //www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.stopping_the_io_service_from_running_out_of_work */
rpc_dummy_work = new asio : : io_service : : work ( * rpc_io_service ) ;
rpc_worker_group = new boost : : thread_group ( ) ;
rpc_worker_group - > create_thread ( boost : : bind ( & asio : : io_service : : run , rpc_io_service ) ) ;
2012-05-13 06:43:24 +02:00
fRPCRunning = true ;
2014-01-17 16:32:35 +01:00
}
}
2013-03-07 04:31:26 +01:00
void StopRPCThreads ( )
{
if ( rpc_io_service = = NULL ) return ;
2012-05-13 06:43:24 +02:00
// Set this to false first, so that longpolling loops will exit when woken up
fRPCRunning = false ;
2013-03-07 04:31:26 +01:00
2014-05-09 10:01:50 +02:00
// First, cancel all timers and acceptors
// This is not done automatically by ->stop(), and in some cases the destructor of
// asio::io_service can hang if this is skipped.
2014-06-17 09:09:12 +02:00
boost : : system : : error_code ec ;
2014-05-09 10:01:50 +02:00
BOOST_FOREACH ( const boost : : shared_ptr < ip : : tcp : : acceptor > & acceptor , rpc_acceptors )
2014-06-17 09:09:12 +02:00
{
acceptor - > cancel ( ec ) ;
if ( ec )
LogPrintf ( " %s: Warning: %s when cancelling acceptor " , __func__ , ec . message ( ) ) ;
}
2014-05-09 10:01:50 +02:00
rpc_acceptors . clear ( ) ;
BOOST_FOREACH ( const PAIRTYPE ( std : : string , boost : : shared_ptr < deadline_timer > ) & timer , deadlineTimers )
2014-06-17 09:09:12 +02:00
{
timer . second - > cancel ( ec ) ;
if ( ec )
LogPrintf ( " %s: Warning: %s when cancelling timer " , __func__ , ec . message ( ) ) ;
}
2013-05-07 16:47:00 +02:00
deadlineTimers . clear ( ) ;
2014-05-09 10:01:50 +02:00
2013-03-07 04:31:26 +01:00
rpc_io_service - > stop ( ) ;
2012-05-13 06:43:24 +02:00
cvBlockChange . notify_all ( ) ;
2013-10-15 07:30:20 +02:00
if ( rpc_worker_group ! = NULL )
rpc_worker_group - > join_all ( ) ;
2014-01-17 16:32:35 +01:00
delete rpc_dummy_work ; rpc_dummy_work = NULL ;
2013-03-07 04:31:26 +01:00
delete rpc_worker_group ; rpc_worker_group = NULL ;
delete rpc_ssl_context ; rpc_ssl_context = NULL ;
delete rpc_io_service ; rpc_io_service = NULL ;
2012-04-15 02:35:58 +02:00
}
2012-05-13 06:43:24 +02:00
bool IsRPCRunning ( )
{
return fRPCRunning ;
}
2013-05-07 16:47:00 +02:00
void RPCRunHandler ( const boost : : system : : error_code & err , boost : : function < void ( void ) > func )
{
if ( ! err )
func ( ) ;
}
2013-04-13 07:13:08 +02:00
void RPCRunLater ( const std : : string & name , boost : : function < void ( void ) > func , int64_t nSeconds )
2013-05-07 16:47:00 +02:00
{
assert ( rpc_io_service ! = NULL ) ;
if ( deadlineTimers . count ( name ) = = 0 )
{
deadlineTimers . insert ( make_pair ( name ,
boost : : shared_ptr < deadline_timer > ( new deadline_timer ( * rpc_io_service ) ) ) ) ;
}
deadlineTimers [ name ] - > expires_from_now ( posix_time : : seconds ( nSeconds ) ) ;
deadlineTimers [ name ] - > async_wait ( boost : : bind ( RPCRunHandler , _1 , func ) ) ;
}
2012-06-27 19:47:02 +02:00
class JSONRequest
{
public :
Value id ;
string strMethod ;
Array params ;
JSONRequest ( ) { id = Value : : null ; }
void parse ( const Value & valRequest ) ;
} ;
void JSONRequest : : parse ( const Value & valRequest )
{
// Parse request
if ( valRequest . type ( ) ! = obj_type )
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_INVALID_REQUEST , " Invalid Request object " ) ;
2012-06-27 19:47:02 +02:00
const Object & request = valRequest . get_obj ( ) ;
// Parse id now so errors from here on will have the id
id = find_value ( request , " id " ) ;
// Parse method
Value valMethod = find_value ( request , " method " ) ;
if ( valMethod . type ( ) = = null_type )
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_INVALID_REQUEST , " Missing method " ) ;
2012-06-27 19:47:02 +02:00
if ( valMethod . type ( ) ! = str_type )
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_INVALID_REQUEST , " Method must be a string " ) ;
2012-06-27 19:47:02 +02:00
strMethod = valMethod . get_str ( ) ;
2014-04-28 12:52:32 +02:00
if ( strMethod ! = " getblocktemplate " )
2014-01-16 16:15:27 +01:00
LogPrint ( " rpc " , " ThreadRPCServer method=%s \n " , strMethod ) ;
2012-06-27 19:47:02 +02:00
// Parse params
Value valParams = find_value ( request , " params " ) ;
if ( valParams . type ( ) = = array_type )
params = valParams . get_array ( ) ;
else if ( valParams . type ( ) = = null_type )
params = Array ( ) ;
else
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_INVALID_REQUEST , " Params must be an array " ) ;
2012-06-27 19:47:02 +02:00
}
2013-11-20 14:18:57 +01:00
2012-06-24 08:01:28 +02:00
static Object JSONRPCExecOne ( const Value & req )
{
Object rpc_result ;
JSONRequest jreq ;
try {
jreq . parse ( req ) ;
Value result = tableRPC . execute ( jreq . strMethod , jreq . params ) ;
rpc_result = JSONRPCReplyObj ( result , Value : : null , jreq . id ) ;
}
catch ( Object & objError )
{
rpc_result = JSONRPCReplyObj ( Value : : null , objError , jreq . id ) ;
}
catch ( std : : exception & e )
{
rpc_result = JSONRPCReplyObj ( Value : : null ,
2012-10-04 09:34:44 +02:00
JSONRPCError ( RPC_PARSE_ERROR , e . what ( ) ) , jreq . id ) ;
2012-06-24 08:01:28 +02:00
}
return rpc_result ;
}
static string JSONRPCExecBatch ( const Array & vReq )
{
Array ret ;
for ( unsigned int reqIdx = 0 ; reqIdx < vReq . size ( ) ; reqIdx + + )
ret . push_back ( JSONRPCExecOne ( vReq [ reqIdx ] ) ) ;
2013-10-22 11:43:38 +02:00
return write_string ( Value ( ret ) , false ) + " \n " ;
2012-06-24 08:01:28 +02:00
}
2014-06-04 17:38:33 +02:00
static bool HTTPReq_JSONRPC ( AcceptedConnection * conn ,
string & strRequest ,
map < string , string > & mapHeaders ,
bool fRun )
{
// Check authorization
if ( mapHeaders . count ( " authorization " ) = = 0 )
{
2014-06-29 03:14:36 +02:00
conn - > stream ( ) < < HTTPError ( HTTP_UNAUTHORIZED , false ) < < std : : flush ;
2014-06-04 17:38:33 +02:00
return false ;
}
if ( ! HTTPAuthorized ( mapHeaders ) )
{
LogPrintf ( " ThreadRPCServer incorrect password attempt from %s \n " , conn - > peer_address_to_string ( ) ) ;
/* Deter brute-forcing short passwords.
If this results in a DoS the user really
shouldn ' t have their RPC port exposed . */
if ( mapArgs [ " -rpcpassword " ] . size ( ) < 20 )
MilliSleep ( 250 ) ;
2014-06-29 03:14:36 +02:00
conn - > stream ( ) < < HTTPError ( HTTP_UNAUTHORIZED , false ) < < std : : flush ;
2014-06-04 17:38:33 +02:00
return false ;
}
JSONRequest jreq ;
try
{
// Parse request
Value valRequest ;
if ( ! read_string ( strRequest , valRequest ) )
throw JSONRPCError ( RPC_PARSE_ERROR , " Parse error " ) ;
string strReply ;
// singleton request
if ( valRequest . type ( ) = = obj_type ) {
jreq . parse ( valRequest ) ;
Value result = tableRPC . execute ( jreq . strMethod , jreq . params ) ;
// Send reply
strReply = JSONRPCReply ( result , Value : : null , jreq . id ) ;
// array of requests
} else if ( valRequest . type ( ) = = array_type )
strReply = JSONRPCExecBatch ( valRequest . get_array ( ) ) ;
else
throw JSONRPCError ( RPC_PARSE_ERROR , " Top-level object parse error " ) ;
2014-08-06 13:01:49 +02:00
conn - > stream ( ) < < HTTPReplyHeader ( HTTP_OK , fRun , strReply . size ( ) ) < < strReply < < std : : flush ;
2014-06-04 17:38:33 +02:00
}
catch ( Object & objError )
{
ErrorReply ( conn - > stream ( ) , objError , jreq . id ) ;
return false ;
}
catch ( std : : exception & e )
{
ErrorReply ( conn - > stream ( ) , JSONRPCError ( RPC_PARSE_ERROR , e . what ( ) ) , jreq . id ) ;
return false ;
}
return true ;
}
2013-03-07 04:31:26 +01:00
void ServiceConnection ( AcceptedConnection * conn )
2012-04-15 02:35:58 +02:00
{
2012-04-24 07:10:02 +02:00
bool fRun = true ;
2014-03-13 22:51:05 +01:00
while ( fRun & & ! ShutdownRequested ( ) )
2013-03-07 04:31:26 +01:00
{
2012-11-04 22:06:38 +01:00
int nProto = 0 ;
2011-05-14 20:10:21 +02:00
map < string , string > mapHeaders ;
2012-11-04 23:16:46 +01:00
string strRequest , strMethod , strURI ;
// Read HTTP request line
if ( ! ReadHTTPRequestLine ( conn - > stream ( ) , nProto , strMethod , strURI ) )
break ;
2011-05-14 20:10:21 +02:00
2012-11-04 22:06:38 +01:00
// Read HTTP message headers and body
2014-08-06 13:03:58 +02:00
ReadHTTPMessage ( conn - > stream ( ) , mapHeaders , strRequest , nProto , MAX_SIZE ) ;
2011-05-14 20:10:21 +02:00
2014-06-04 17:38:33 +02:00
// HTTP Keep-Alive is false; close connection immediately
2012-04-24 07:10:02 +02:00
if ( mapHeaders [ " connection " ] = = " close " )
fRun = false ;
2011-05-14 20:10:21 +02:00
2014-06-04 17:38:33 +02:00
if ( strURI = = " / " ) {
if ( ! HTTPReq_JSONRPC ( conn , strRequest , mapHeaders , fRun ) )
break ;
2014-06-27 11:13:25 +02:00
} else {
2014-06-29 03:14:36 +02:00
conn - > stream ( ) < < HTTPError ( HTTP_NOT_FOUND , false ) < < std : : flush ;
2012-04-15 02:35:58 +02:00
break ;
2011-05-14 20:10:21 +02:00
}
}
}
2012-04-09 21:07:25 +02:00
json_spirit : : Value CRPCTable : : execute ( const std : : string & strMethod , const json_spirit : : Array & params ) const
{
// Find method
const CRPCCommand * pcmd = tableRPC [ strMethod ] ;
if ( ! pcmd )
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_METHOD_NOT_FOUND , " Method not found " ) ;
2013-11-29 16:04:29 +01:00
# ifdef ENABLE_WALLET
2013-08-25 06:00:02 +02:00
if ( pcmd - > reqWallet & & ! pwalletMain )
throw JSONRPCError ( RPC_METHOD_NOT_FOUND , " Method not found (disabled) " ) ;
2013-11-29 16:04:29 +01:00
# endif
2011-05-14 20:10:21 +02:00
2012-04-09 21:07:25 +02:00
// Observe safe mode
string strWarning = GetWarnings ( " rpc " ) ;
2013-04-28 17:37:50 +02:00
if ( strWarning ! = " " & & ! GetBoolArg ( " -disablesafemode " , false ) & &
2012-04-09 21:07:25 +02:00
! pcmd - > okSafeMode )
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_FORBIDDEN_BY_SAFE_MODE , string ( " Safe mode: " ) + strWarning ) ;
2012-04-09 21:07:25 +02:00
try
{
// Execute
Value result ;
{
2013-03-07 12:18:55 +01:00
if ( pcmd - > threadSafe )
2012-08-21 18:03:52 +02:00
result = pcmd - > actor ( params , false ) ;
2013-11-29 16:04:29 +01:00
# ifdef ENABLE_WALLET
2013-08-25 06:00:02 +02:00
else if ( ! pwalletMain ) {
LOCK ( cs_main ) ;
result = pcmd - > actor ( params , false ) ;
} else {
2012-08-21 18:03:52 +02:00
LOCK2 ( cs_main , pwalletMain - > cs_wallet ) ;
result = pcmd - > actor ( params , false ) ;
}
2013-11-29 16:04:29 +01:00
# else // ENABLE_WALLET
else {
LOCK ( cs_main ) ;
result = pcmd - > actor ( params , false ) ;
}
# endif // !ENABLE_WALLET
2012-04-09 21:07:25 +02:00
}
return result ;
}
catch ( std : : exception & e )
{
2012-10-04 09:34:44 +02:00
throw JSONRPCError ( RPC_MISC_ERROR , e . what ( ) ) ;
2012-04-09 21:07:25 +02:00
}
}
2011-05-14 20:10:21 +02:00
2013-11-29 15:48:07 +01:00
std : : string HelpExampleCli ( string methodname , string args ) {
return " > bitcoin-cli " + methodname + " " + args + " \n " ;
}
std : : string HelpExampleRpc ( string methodname , string args ) {
return " > curl --user myusername --data-binary '{ \" jsonrpc \" : \" 1.0 \" , \" id \" : \" curltest \" , "
" \" method \" : \" " + methodname + " \" , \" params \" : [ " + args + " ] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/ \n " ;
}
2012-04-21 01:37:34 +02:00
const CRPCTable tableRPC ;