bitcoinrpc.cpp 49.6 KB
Newer Older
1
// Copyright (c) 2010 Satoshi Nakamoto
super3's avatar
super3 committed
2
// Copyright (c) 2009-2013 The Bitcoin developers
3
// Distributed under the MIT/X11 software license, see the accompanying
Fordy's avatar
Fordy committed
4
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5

6
#include "chainparams.h"
7
#include "init.h"
8
9
#include "util.h"
#include "sync.h"
Pieter Wuille's avatar
Pieter Wuille committed
10
#include "ui_interface.h"
11
#include "base58.h"
12
#include "bitcoinrpc.h"
13
#include "db.h"
Pieter Wuille's avatar
Pieter Wuille committed
14

15
#include <boost/algorithm/string.hpp>
16
#include <boost/asio.hpp>
17
#include <boost/asio/ip/v6_only.hpp>
18
#include <boost/asio/ssl.hpp>
19
#include <boost/bind.hpp>
Pieter Wuille's avatar
Pieter Wuille committed
20
#include <boost/filesystem.hpp>
21
#include <boost/filesystem/fstream.hpp>
22
#include <boost/foreach.hpp>
23
24
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream.hpp>
25
#include <boost/lexical_cast.hpp>
26
#include <boost/shared_ptr.hpp>
27
#include <list>
Gavin Andresen's avatar
Gavin Andresen committed
28

29
30
31
32
33
using namespace std;
using namespace boost;
using namespace boost::asio;
using namespace json_spirit;

constantined's avatar
constantined committed
34
35
static const char* Value_type_name[]={"obj", "array", "str", "bool", "int", "real", "null"};

36
37
static std::string strRPCUserColonPass;

38
39
// These are created by StartRPCThreads, destroyed in StopRPCThreads
static asio::io_service* rpc_io_service = NULL;
40
static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
41
42
static ssl::context* rpc_ssl_context = NULL;
static boost::thread_group* rpc_worker_group = NULL;
43

44
45
46
47
48
49
50
51
Object JSONRPCError(int code, const string& message)
{
    Object error;
    error.push_back(Pair("code", code));
    error.push_back(Pair("message", message));
    return error;
}

52
void RPCTypeCheck(const Array& params,
53
54
                  const list<Value_type>& typesExpected,
                  bool fAllowNull)
55
{
56
    unsigned int i = 0;
57
58
59
60
61
    BOOST_FOREACH(Value_type t, typesExpected)
    {
        if (params.size() <= i)
            break;

62
63
        const Value& v = params[i];
        if (!((v.type() == t) || (fAllowNull && (v.type() == null_type))))
64
65
66
        {
            string err = strprintf("Expected type %s, got %s",
                                   Value_type_name[t], Value_type_name[v.type()]);
67
            throw JSONRPCError(RPC_TYPE_ERROR, err);
68
69
70
71
72
73
        }
        i++;
    }
}

void RPCTypeCheck(const Object& o,
74
75
                  const map<string, Value_type>& typesExpected,
                  bool fAllowNull)
76
77
78
79
{
    BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected)
    {
        const Value& v = find_value(o, t.first);
80
        if (!fAllowNull && v.type() == null_type)
81
            throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first.c_str()));
82
83

        if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type))))
84
85
86
        {
            string err = strprintf("Expected type %s for %s, got %s",
                                   Value_type_name[t.second], t.first.c_str(), Value_type_name[v.type()]);
87
            throw JSONRPCError(RPC_TYPE_ERROR, err);
88
89
90
91
        }
    }
}

92
int64 AmountFromValue(const Value& value)
93
94
95
{
    double dAmount = value.get_real();
    if (dAmount <= 0.0 || dAmount > 21000000.0)
96
        throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
97
    int64 nAmount = roundint64(dAmount * COIN);
98
    if (!MoneyRange(nAmount))
99
        throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
100
101
102
    return nAmount;
}

103
Value ValueFromAmount(int64 amount)
104
105
106
107
{
    return (double)amount / (double)COIN;
}

108
std::string HexBits(unsigned int nBits)
109
110
111
112
113
114
115
116
117
{
    union {
        int32_t nBits;
        char cBits[4];
    } uBits;
    uBits.nBits = htonl((int32_t)nBits);
    return HexStr(BEGIN(uBits.cBits), END(uBits.cBits));
}

118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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);
}
146

147

148
149
150
151
///
/// Note: This interface may still be subject to change.
///

152
string CRPCTable::help(string strCommand) const
153
154
155
{
    string strRet;
    set<rpcfn_type> setDone;
156
    for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
157
    {
158
        const CRPCCommand *pcmd = mi->second;
159
        string strMethod = mi->first;
160
        // We already filter duplicates, but these deprecated screw up the sort order
161
        if (strMethod.find("label") != string::npos)
162
163
164
            continue;
        if (strCommand != "" && strMethod != strCommand)
            continue;
165
166
167
        if (pcmd->reqWallet && !pwalletMain)
            continue;

168
169
170
        try
        {
            Array params;
171
            rpcfn_type pfn = pcmd->actor;
172
173
174
175
176
177
178
179
            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 == "")
180
                if (strHelp.find('\n') != string::npos)
181
182
183
184
185
186
187
188
189
190
                    strHelp = strHelp.substr(0, strHelp.find('\n'));
            strRet += strHelp + "\n";
        }
    }
    if (strRet == "")
        strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
    strRet = strRet.substr(0,strRet.size()-1);
    return strRet;
}

191
192
193
194
195
196
197
198
199
200
201
202
203
204
Value help(const Array& params, bool fHelp)
{
    if (fHelp || params.size() > 1)
        throw runtime_error(
            "help [command]\n"
            "List commands, or get help for a command.");

    string strCommand;
    if (params.size() > 0)
        strCommand = params[0].get_str();

    return tableRPC.help(strCommand);
}

205
206
207

Value stop(const Array& params, bool fHelp)
{
208
    // Accept the deprecated and ignored 'detach' boolean argument
209
    if (fHelp || params.size() > 1)
210
        throw runtime_error(
211
212
            "stop\n"
            "Stop Bitcoin server.");
213
    // Shutdown will take long enough that the response should get back
214
    StartShutdown();
215
    return "Bitcoin server stopping";
216
217
218
219
220
221
222
223
}



//
// Call Table
//

224

225
static const CRPCCommand vRPCCommands[] =
226
227
228
229
230
231
232
233
{ //  name                      actor (function)         okSafeMode threadSafe reqWallet
  //  ------------------------  -----------------------  ---------- ---------- ---------
    { "help",                   &help,                   true,      true,       false },
    { "stop",                   &stop,                   true,      true,       false },
    { "getblockcount",          &getblockcount,          true,      false,      false },
    { "getbestblockhash",       &getbestblockhash,       true,      false,      false },
    { "getconnectioncount",     &getconnectioncount,     true,      false,      false },
    { "getpeerinfo",            &getpeerinfo,            true,      false,      false },
Josh Lehan's avatar
Josh Lehan committed
234
    { "ping",                   &ping,                   true,      false,      false },
235
236
    { "addnode",                &addnode,                true,      true,       false },
    { "getaddednodeinfo",       &getaddednodeinfo,       true,      true,       false },
Scott Ellis's avatar
Scott Ellis committed
237
    { "getnettotals",           &getnettotals,           true,      true,       false },
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
    { "getdifficulty",          &getdifficulty,          true,      false,      false },
    { "getnetworkhashps",       &getnetworkhashps,       true,      false,      false },
    { "getgenerate",            &getgenerate,            true,      false,      false },
    { "setgenerate",            &setgenerate,            true,      false,      true },
    { "gethashespersec",        &gethashespersec,        true,      false,      false },
    { "getinfo",                &getinfo,                true,      false,      false },
    { "getmininginfo",          &getmininginfo,          true,      false,      false },
    { "getnewaddress",          &getnewaddress,          true,      false,      true },
    { "getaccountaddress",      &getaccountaddress,      true,      false,      true },
    { "getrawchangeaddress",    &getrawchangeaddress,    true,      false,      true },
    { "setaccount",             &setaccount,             true,      false,      true },
    { "getaccount",             &getaccount,             false,     false,      true },
    { "getaddressesbyaccount",  &getaddressesbyaccount,  true,      false,      true },
    { "sendtoaddress",          &sendtoaddress,          false,     false,      true },
    { "getreceivedbyaddress",   &getreceivedbyaddress,   false,     false,      true },
    { "getreceivedbyaccount",   &getreceivedbyaccount,   false,     false,      true },
    { "listreceivedbyaddress",  &listreceivedbyaddress,  false,     false,      true },
    { "listreceivedbyaccount",  &listreceivedbyaccount,  false,     false,      true },
    { "backupwallet",           &backupwallet,           true,      false,      true },
    { "keypoolrefill",          &keypoolrefill,          true,      false,      true },
    { "walletpassphrase",       &walletpassphrase,       true,      false,      true },
    { "walletpassphrasechange", &walletpassphrasechange, false,     false,      true },
    { "walletlock",             &walletlock,             true,      false,      true },
    { "encryptwallet",          &encryptwallet,          false,     false,      true },
    { "validateaddress",        &validateaddress,        true,      false,      false },
    { "getbalance",             &getbalance,             false,     false,      true },
    { "move",                   &movecmd,                false,     false,      true },
    { "sendfrom",               &sendfrom,               false,     false,      true },
    { "sendmany",               &sendmany,               false,     false,      true },
    { "addmultisigaddress",     &addmultisigaddress,     false,     false,      true },
    { "createmultisig",         &createmultisig,         true,      true ,      false },
    { "getrawmempool",          &getrawmempool,          true,      false,      false },
    { "getblock",               &getblock,               false,     false,      false },
    { "getblockhash",           &getblockhash,           false,     false,      false },
    { "gettransaction",         &gettransaction,         false,     false,      true },
    { "listtransactions",       &listtransactions,       false,     false,      true },
    { "listaddressgroupings",   &listaddressgroupings,   false,     false,      true },
    { "signmessage",            &signmessage,            false,     false,      true },
    { "verifymessage",          &verifymessage,          false,     false,      false },
    { "getwork",                &getwork,                true,      false,      true },
    { "listaccounts",           &listaccounts,           false,     false,      true },
    { "settxfee",               &settxfee,               false,     false,      true },
    { "getblocktemplate",       &getblocktemplate,       true,      false,      false },
    { "submitblock",            &submitblock,            false,     false,      false },
    { "listsinceblock",         &listsinceblock,         false,     false,      true },
    { "dumpprivkey",            &dumpprivkey,            true,      false,      true },
    { "dumpwallet",             &dumpwallet,             true,      false,      true },
    { "importprivkey",          &importprivkey,          false,     false,      true },
    { "importwallet",           &importwallet,           false,     false,      true },
    { "listunspent",            &listunspent,            false,     false,      true },
    { "getrawtransaction",      &getrawtransaction,      false,     false,      false },
    { "createrawtransaction",   &createrawtransaction,   false,     false,      false },
    { "decoderawtransaction",   &decoderawtransaction,   false,     false,      false },
    { "decodescript",           &decodescript,           false,     false,      false },
    { "signrawtransaction",     &signrawtransaction,     false,     false,      false },
    { "sendrawtransaction",     &sendrawtransaction,     false,     false,      false },
    { "gettxoutsetinfo",        &gettxoutsetinfo,        true,      false,      false },
    { "gettxout",               &gettxout,               true,      false,      false },
    { "lockunspent",            &lockunspent,            false,     false,      true },
    { "listlockunspent",        &listlockunspent,        false,     false,      true },
    { "verifychain",            &verifychain,            true,      false,      false },
299
300
};

301
CRPCTable::CRPCTable()
302
303
304
305
{
    unsigned int vcidx;
    for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
    {
306
        const CRPCCommand *pcmd;
307

308
309
310
311
        pcmd = &vRPCCommands[vcidx];
        mapCommands[pcmd->name] = pcmd;
    }
}
312

313
314
315
316
317
318
319
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;
}
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

//
// HTTP protocol
//
// This ain't Apache.  We're just using HTTP header for the length field
// and to be compatible with other JSON-RPC implementations.
//

string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
{
    ostringstream s;
    s << "POST / HTTP/1.1\r\n"
      << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
      << "Host: 127.0.0.1\r\n"
      << "Content-Type: application/json\r\n"
      << "Content-Length: " << strMsg.size() << "\r\n"
336
      << "Connection: close\r\n"
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
      << "Accept: application/json\r\n";
    BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
        s << item.first << ": " << item.second << "\r\n";
    s << "\r\n" << strMsg;

    return s.str();
}

string rfc1123Time()
{
    char buffer[64];
    time_t now;
    time(&now);
    struct tm* now_gmt = gmtime(&now);
    string locale(setlocale(LC_TIME, NULL));
352
    setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings
353
354
355
356
357
    strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
    setlocale(LC_TIME, locale.c_str());
    return string(buffer);
}

358
static string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
359
{
360
    if (nStatus == HTTP_UNAUTHORIZED)
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
        return strprintf("HTTP/1.0 401 Authorization Required\r\n"
            "Date: %s\r\n"
            "Server: bitcoin-json-rpc/%s\r\n"
            "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
            "Content-Type: text/html\r\n"
            "Content-Length: 296\r\n"
            "\r\n"
            "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
            "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
            "<HTML>\r\n"
            "<HEAD>\r\n"
            "<TITLE>Error</TITLE>\r\n"
            "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
            "</HEAD>\r\n"
            "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
            "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
377
    const char *cStatus;
378
379
380
381
382
         if (nStatus == HTTP_OK) cStatus = "OK";
    else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request";
    else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden";
    else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found";
    else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error";
383
    else cStatus = "";
384
385
386
    return strprintf(
            "HTTP/1.1 %d %s\r\n"
            "Date: %s\r\n"
387
            "Connection: %s\r\n"
388
            "Content-Length: %"PRIszu"\r\n"
389
390
391
392
393
            "Content-Type: application/json\r\n"
            "Server: bitcoin-json-rpc/%s\r\n"
            "\r\n"
            "%s",
        nStatus,
394
        cStatus,
395
        rfc1123Time().c_str(),
396
        keepalive ? "keep-alive" : "close",
397
398
399
400
401
        strMsg.size(),
        FormatFullVersion().c_str(),
        strMsg.c_str());
}

402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
                         string& http_method, string& http_uri)
{
    string str;
    getline(stream, str);

    // HTTP request line is space-delimited
    vector<string> vWords;
    boost::split(vWords, str, boost::is_any_of(" "));
    if (vWords.size() < 2)
        return false;

    // HTTP methods permitted: GET, POST
    http_method = vWords[0];
    if (http_method != "GET" && http_method != "POST")
        return false;

    // HTTP URI must be an absolute path, relative to current host
    http_uri = vWords[1];
    if (http_uri.size() == 0 || http_uri[0] != '/')
        return false;

    // parse proto, if present
    string strProto = "";
    if (vWords.size() > 2)
        strProto = vWords[2];

    proto = 0;
    const char *ver = strstr(strProto.c_str(), "HTTP/1.");
    if (ver != NULL)
        proto = atoi(ver+7);

    return true;
}

437
int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
438
439
440
441
442
443
{
    string str;
    getline(stream, str);
    vector<string> vWords;
    boost::split(vWords, str, boost::is_any_of(" "));
    if (vWords.size() < 2)
444
        return HTTP_INTERNAL_SERVER_ERROR;
445
446
447
448
    proto = 0;
    const char *ver = strstr(str.c_str(), "HTTP/1.");
    if (ver != NULL)
        proto = atoi(ver+7);
449
450
451
    return atoi(vWords[1].c_str());
}

452
int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
453
454
{
    int nLen = 0;
455
    while (true)
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
    {
        string str;
        std::getline(stream, str);
        if (str.empty() || str == "\r")
            break;
        string::size_type nColon = str.find(":");
        if (nColon != string::npos)
        {
            string strHeader = str.substr(0, nColon);
            boost::trim(strHeader);
            boost::to_lower(strHeader);
            string strValue = str.substr(nColon+1);
            boost::trim(strValue);
            mapHeadersRet[strHeader] = strValue;
            if (strHeader == "content-length")
                nLen = atoi(strValue.c_str());
        }
    }
    return nLen;
}

477
478
479
int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
                    string>& mapHeadersRet, string& strMessageRet,
                    int nProto)
480
481
482
483
484
{
    mapHeadersRet.clear();
    strMessageRet = "";

    // Read header
485
    int nLen = ReadHTTPHeaders(stream, mapHeadersRet);
486
    if (nLen < 0 || nLen > (int)MAX_SIZE)
487
        return HTTP_INTERNAL_SERVER_ERROR;
488
489
490
491
492
493
494
495
496

    // Read message
    if (nLen > 0)
    {
        vector<char> vch(nLen);
        stream.read(&vch[0], nLen);
        strMessageRet = string(vch.begin(), vch.end());
    }

497
498
499
500
501
502
503
504
505
506
    string sConHdr = mapHeadersRet["connection"];

    if ((sConHdr != "close") && (sConHdr != "keep-alive"))
    {
        if (nProto >= 1)
            mapHeadersRet["connection"] = "keep-alive";
        else
            mapHeadersRet["connection"] = "close";
    }

507
    return HTTP_OK;
508
509
510
511
512
513
514
515
516
}

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);
517
    return TimingResistantEqual(strUserPass, strRPCUserColonPass);
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
}

//
// JSON-RPC protocol.  Bitcoin speaks version 1.0 for maximum compatibility,
// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
// unspecified (HTTP errors and contents of 'error').
//
// 1.0 spec: http://json-rpc.org/wiki/specification
// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
//

string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
{
    Object request;
    request.push_back(Pair("method", strMethod));
    request.push_back(Pair("params", params));
    request.push_back(Pair("id", id));
536
    return write_string(Value(request), false) + "\n";
537
538
}

539
Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
540
541
542
543
544
545
546
547
{
    Object reply;
    if (error.type() != null_type)
        reply.push_back(Pair("result", Value::null));
    else
        reply.push_back(Pair("result", result));
    reply.push_back(Pair("error", error));
    reply.push_back(Pair("id", id));
548
549
550
551
552
553
    return reply;
}

string JSONRPCReply(const Value& result, const Value& error, const Value& id)
{
    Object reply = JSONRPCReplyObj(result, error, id);
554
    return write_string(Value(reply), false) + "\n";
555
556
557
558
559
}

void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
{
    // Send error reply from json-rpc error object
560
    int nStatus = HTTP_INTERNAL_SERVER_ERROR;
561
    int code = find_value(objError, "code").get_int();
562
563
    if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST;
    else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND;
564
    string strReply = JSONRPCReply(Value::null, objError, id);
565
    stream << HTTPReply(nStatus, strReply, false) << std::flush;
566
567
}

568
bool ClientAllowed(const boost::asio::ip::address& address)
569
{
570
571
572
573
574
575
576
    // 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()))
        return ClientAllowed(address.to_v6().to_v4());

    if (address == asio::ip::address_v4::loopback()
577
578
     || address == asio::ip::address_v6::loopback()
     || (address.is_v4()
579
         // Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet)
580
      && (address.to_v4().to_ulong() & 0xff000000) == 0x7f000000))
581
        return true;
582
583

    const string strAddress = address.to_string();
584
585
586
587
588
589
590
591
592
593
    const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
    BOOST_FOREACH(string strAllow, vAllow)
        if (WildcardMatch(strAddress, strAllow))
            return true;
    return false;
}

//
// IOStream device that speaks SSL but can also speak non-SSL
//
594
template <typename Protocol>
595
596
class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
public:
597
    SSLIOStreamDevice(asio::ssl::stream<typename Protocol::socket> &streamIn, bool fUseSSLIn) : stream(streamIn)
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
    {
        fUseSSL = fUseSSLIn;
        fNeedHandshake = fUseSSLIn;
    }

    void handshake(ssl::stream_base::handshake_type role)
    {
        if (!fNeedHandshake) return;
        fNeedHandshake = false;
        stream.handshake(role);
    }
    std::streamsize read(char* s, std::streamsize n)
    {
        handshake(ssl::stream_base::server); // HTTPS servers read first
        if (fUseSSL) return stream.read_some(asio::buffer(s, n));
        return stream.next_layer().read_some(asio::buffer(s, n));
    }
    std::streamsize write(const char* s, std::streamsize n)
    {
        handshake(ssl::stream_base::client); // HTTPS clients write first
        if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
        return asio::write(stream.next_layer(), asio::buffer(s, n));
    }
    bool connect(const std::string& server, const std::string& port)
    {
        ip::tcp::resolver resolver(stream.get_io_service());
        ip::tcp::resolver::query query(server.c_str(), port.c_str());
        ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
        ip::tcp::resolver::iterator end;
        boost::system::error_code error = asio::error::host_not_found;
        while (error && endpoint_iterator != end)
        {
            stream.lowest_layer().close();
            stream.lowest_layer().connect(*endpoint_iterator++, error);
        }
        if (error)
            return false;
        return true;
    }

private:
    bool fNeedHandshake;
    bool fUseSSL;
641
    asio::ssl::stream<typename Protocol::socket>& stream;
642
643
};

644
645
class AcceptedConnection
{
646
647
648
649
650
651
652
public:
    virtual ~AcceptedConnection() {}

    virtual std::iostream& stream() = 0;
    virtual std::string peer_address_to_string() const = 0;
    virtual void close() = 0;
};
653

654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
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();
    }
677

678
679
680
681
682
683
684
685
686
687
688
    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;
689
690
};

691
void ServiceConnection(AcceptedConnection *conn);
692

693
// Forward declaration required for RPCListen
694
695
template <typename Protocol, typename SocketAcceptorService>
static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
696
697
698
699
700
701
702
703
                             ssl::context& context,
                             bool fUseSSL,
                             AcceptedConnection* conn,
                             const boost::system::error_code& error);

/**
 * Sets up I/O resources to accept and handle a new connection.
 */
704
705
template <typename Protocol, typename SocketAcceptorService>
static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
706
707
708
709
                   ssl::context& context,
                   const bool fUseSSL)
{
    // Accept connection
710
    AcceptedConnectionImpl<Protocol>* conn = new AcceptedConnectionImpl<Protocol>(acceptor->get_io_service(), context, fUseSSL);
711
712
713
714

    acceptor->async_accept(
            conn->sslStream.lowest_layer(),
            conn->peer,
715
            boost::bind(&RPCAcceptHandler<Protocol, SocketAcceptorService>,
716
717
718
719
720
721
722
723
724
725
                acceptor,
                boost::ref(context),
                fUseSSL,
                conn,
                boost::asio::placeholders::error));
}

/**
 * Accept and handle incoming connection.
 */
726
727
template <typename Protocol, typename SocketAcceptorService>
static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
728
729
730
731
732
                             ssl::context& context,
                             const bool fUseSSL,
                             AcceptedConnection* conn,
                             const boost::system::error_code& error)
{
733
    // Immediately start accepting new connections, except when we're cancelled or our socket is closed.
734
    if (error != asio::error::operation_aborted && acceptor->is_open())
735
        RPCListen(acceptor, context, fUseSSL);
736

737
738
    AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn);

739
740
741
742
743
744
745
746
747
    // TODO: Actually handle errors
    if (error)
    {
        delete conn;
    }

    // Restrict callers by IP.  It is important to
    // do this before starting client thread, to filter out
    // certain DoS and misbehaving clients.
748
    else if (tcp_conn && !ClientAllowed(tcp_conn->peer.address()))
749
750
751
    {
        // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
        if (!fUseSSL)
752
            conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush;
753
754
        delete conn;
    }
755
756
757
    else {
        ServiceConnection(conn);
        conn->close();
758
759
760
761
        delete conn;
    }
}

762
void StartRPCThreads()
763
{
764
    strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
765
766
    if (((mapArgs["-rpcpassword"] == "") ||
         (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) && Params().RequireRPCPassword())
767
    {
768
769
        unsigned char rand_pwd[32];
        RAND_bytes(rand_pwd, 32);
770
771
772
773
774
        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\"");
775
        uiInterface.ThreadSafeMessageBox(strprintf(
776
777
            _("%s, you must set a rpcpassword in the configuration file:\n"
              "%s\n"
778
779
780
781
              "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"
782
              "The username and password MUST NOT be the same.\n"
Gavin Andresen's avatar
Gavin Andresen committed
783
784
785
              "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"),
786
                strWhatAmI.c_str(),
787
                GetConfigFile().string().c_str(),
788
                EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
789
                "", CClientUIInterface::MSG_ERROR);
790
        StartShutdown();
791
792
793
        return;
    }

794
795
796
    assert(rpc_io_service == NULL);
    rpc_io_service = new asio::io_service();
    rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23);
797

798
    const bool fUseSSL = GetBoolArg("-rpcssl", false);
799

800
801
    if (fUseSSL)
    {
802
        rpc_ssl_context->set_options(ssl::context::no_sslv2);
803
804
805

        filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
        if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile;
806
        if (filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string());
807
        else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str());
808
809
810

        filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
        if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile;
811
        if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem);
812
        else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str());
813

814
        string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH");
815
        SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str());
816
817
    }

818
819
820
    // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets
    const bool loopback = !mapArgs.count("-rpcallowip");
    asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any();
821
    ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", Params().RPCPort()));
822
    boost::system::error_code v6_only_error;
823
    boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service));
824

825
826
    bool fListening = false;
    std::string strerr;
827
828
    try
    {
829
830
        acceptor->open(endpoint.protocol());
        acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
831
832

        // Try making the socket dual IPv6/IPv4 (if listening on the "any" address)
833
        acceptor->set_option(boost::asio::ip::v6_only(loopback), v6_only_error);
834

835
836
        acceptor->bind(endpoint);
        acceptor->listen(socket_base::max_connections);
837

838
        RPCListen(acceptor, *rpc_ssl_context, fUseSSL);
839

840
841
842
843
        fListening = true;
    }
    catch(boost::system::system_error &e)
    {
844
        strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what());
845
846
847
    }

    try {
848
        // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately
849
        if (!fListening || loopback || v6_only_error)
850
851
852
853
        {
            bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any();
            endpoint.address(bindAddress);

854
            acceptor.reset(new ip::tcp::acceptor(*rpc_io_service));
855
856
857
858
859
            acceptor->open(endpoint.protocol());
            acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
            acceptor->bind(endpoint);
            acceptor->listen(socket_base::max_connections);

860
            RPCListen(acceptor, *rpc_ssl_context, fUseSSL);
861
862

            fListening = true;
863
864
865
866
        }
    }
    catch(boost::system::system_error &e)
    {
867
        strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv4: %s"), endpoint.port(), e.what());
868
869
870
    }

    if (!fListening) {
871
        uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR);
872
        StartShutdown();
873
874
        return;
    }
875

876
877
878
879
880
881
882
883
884
    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));
}

void StopRPCThreads()
{
    if (rpc_io_service == NULL) return;

885
    deadlineTimers.clear();
886
    rpc_io_service->stop();
887
888
    if (rpc_worker_group != NULL)
        rpc_worker_group->join_all();
889
890
891
    delete rpc_worker_group; rpc_worker_group = NULL;
    delete rpc_ssl_context; rpc_ssl_context = NULL;
    delete rpc_io_service; rpc_io_service = NULL;
892
893
}

894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func)
{
    if (!err)
        func();
}

void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64 nSeconds)
{
    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));
}


914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
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)
929
        throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
930
931
932
933
934
935
936
937
    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)
938
        throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
939
    if (valMethod.type() != str_type)
940
        throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
941
    strMethod = valMethod.get_str();
942
    if (strMethod != "getwork" && strMethod != "getblocktemplate")
943
        LogPrint("rpc", "ThreadRPCServer method=%s\n", strMethod.c_str());
944
945
946
947
948
949
950
951

    // 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
952
        throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
953
954
}

955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
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,
973
                                     JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
974
975
976
977
978
979
980
981
982
983
984
    }

    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]));

985
    return write_string(Value(ret), false) + "\n";
986
987
}

988
void ServiceConnection(AcceptedConnection *conn)
989
{
990
    bool fRun = true;
991
992
    while (fRun)
    {
993
        int nProto = 0;
994
        map<string, string> mapHeaders;
995
996
997
998
999
        string strRequest, strMethod, strURI;

        // Read HTTP request line
        if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI))
            break;
1000

1001
1002
        // Read HTTP message headers and body
        ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto);
1003

1004
1005
1006
1007
1008
        if (strURI != "/") {
            conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush;
            break;
        }

1009
1010
1011
        // Check authorization
        if (mapHeaders.count("authorization") == 0)
        {
1012
            conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush;
1013
            break;
1014
1015
1016
        }
        if (!HTTPAuthorized(mapHeaders))
        {
1017
            LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str());
1018
            /* Deter brute-forcing short passwords.
1019
1020
               If this results in a DoS the user really
               shouldn't have their RPC port exposed. */
1021
            if (mapArgs["-rpcpassword"].size() < 20)
1022
                MilliSleep(250);
1023

1024
            conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush;
1025
            break;
1026
        }
1027
1028
        if (mapHeaders["connection"] == "close")
            fRun = false;
1029

1030
        JSONRequest jreq;
1031
1032
1033
1034
        try
        {
            // Parse request
            Value valRequest;
1035
            if (!read_string(strRequest, valRequest))
1036
                throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
1037

1038
1039
1040
1041
1042
            string strReply;

            // singleton request
            if (valRequest.type() == obj_type) {
                jreq.parse(valRequest);
1043

1044
                Value result = tableRPC.execute(jreq.strMethod, jreq.params);
1045

1046
1047
1048
1049
1050
1051
1052
                // Send reply
                strReply = JSONRPCReply(result, Value::null, jreq.id);

            // array of requests
            } else if (valRequest.type() == array_type)
                strReply = JSONRPCExecBatch(valRequest.get_array());
            else
1053
                throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
1054

1055
            conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush;
1056
1057
1058
        }
        catch (Object& objError)
        {
1059
            ErrorReply(conn->stream(), objError, jreq.id);
1060
            break;
1061
1062
1063
        }
        catch (std::exception& e)
        {
1064
            ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
1065
            break;
1066
1067
1068
1069
        }
    }
}

1070
1071
1072
1073
1074
json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array &params) const
{
    // Find method
    const CRPCCommand *pcmd = tableRPC[strMethod];
    if (!pcmd)
1075
        throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
1076
1077
    if (pcmd->reqWallet && !pwalletMain)
        throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)");
1078

1079
1080
    // Observe safe mode
    string strWarning = GetWarnings("rpc");
1081
    if (strWarning != "" && !GetBoolArg("-disablesafemode", false) &&
1082
        !pcmd->okSafeMode)
1083
        throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning);
1084
1085
1086
1087
1088
1089

    try
    {
        // Execute
        Value result;
        {
1090
            if (pcmd->threadSafe)
1091
                result = pcmd->actor(params, false);
1092
1093
1094
1095
            else if (!pwalletMain) {
                LOCK(cs_main);
                result = pcmd->actor(params, false);
            } else {
1096
1097
1098
                LOCK2(cs_main, pwalletMain->cs_wallet);
                result = pcmd->actor(params, false);
            }
1099
1100
1101
1102
1103
        }
        return result;
    }
    catch (std::exception& e)
    {
1104
        throw JSONRPCError(RPC_MISC_ERROR, e.what());
1105
1106
    }
}
1107
1108
1109
1110
1111
1112
1113
1114


Object CallRPC(const string& strMethod, const Array& params)
{
    if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
        throw runtime_error(strprintf(
            _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
              "If the file does not exist, create it with owner-readable-only file permissions."),
1115
                GetConfigFile().string().c_str()));
1116
1117

    // Connect to localhost
1118
    bool fUseSSL = GetBoolArg("-rpcssl", false);
1119
1120
1121
    asio::io_service io_service;
    ssl::context context(io_service, ssl::context::sslv23);
    context.set_options(ssl::context::no_sslv2);
1122
1123
1124
    asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context);
    SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL);
    iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
1125
    if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(Params().RPCPort()))))
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
        throw runtime_error("couldn't connect to server");

    // HTTP basic authentication
    string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
    map<string, string> mapRequestHeaders;
    mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;

    // Send request
    string strRequest = JSONRPCRequest(strMethod, params, 1);
    string strPost = HTTPPost(strRequest, mapRequestHeaders);
    stream << strPost << std::flush;

1138
1139
1140
1141
1142
    // Receive HTTP reply status
    int nProto = 0;
    int nStatus = ReadHTTPStatus(stream, nProto);

    // Receive HTTP reply message headers and body
1143
1144
    map<string, string> mapHeaders;
    string strReply;
1145
1146
    ReadHTTPMessage(stream, mapHeaders, strReply, nProto);

1147
    if (nStatus == HTTP_UNAUTHORIZED)
1148
        throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
1149
    else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
        throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
    else if (strReply.empty())
        throw runtime_error("no response from server");

    // Parse reply
    Value valReply;
    if (!read_string(strReply, valReply))
        throw runtime_error("couldn't parse reply from server");
    const Object& reply = valReply.get_obj();
    if (reply.empty())
        throw runtime_error("expected reply to have result, error and id properties");

    return reply;
}




template<typename T>
1169
void ConvertTo(Value& value, bool fAllowNull=false)
1170
{
1171
1172
    if (fAllowNull && value.type() == null_type)
        return;
1173
1174
1175
1176
    if (value.type() == str_type)
    {
        // reinterpret string as unquoted json value
        Value value2;
1177
1178
1179
        string strJSON = value.get_str();
        if (!read_string(strJSON, value2))
            throw runtime_error(string("Error parsing JSON:")+strJSON);
1180
1181
        ConvertTo<T>(value2, fAllowNull);
        value = value2;
1182
1183
1184
1185
1186
1187
1188
    }
    else
    {
        value = value.get_value<T>();
    }
}

1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
// Convert strings to command-specific RPC representation
Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
{
    Array params;
    BOOST_FOREACH(const std::string &param, strParams)
        params.push_back(param);

    int n = params.size();

    //
    // Special case non-string parameter types
    //
1201
    if (strMethod == "stop"                   && n > 0) ConvertTo<bool>(params[0]);
Matt Corallo's avatar
Matt Corallo committed
1202
    if (strMethod == "getaddednodeinfo"       && n > 0) ConvertTo<bool>(params[0]);
1203
1204
    if (strMethod == "setgenerate"            && n > 0) ConvertTo<bool>(params[0]);
    if (strMethod == "setgenerate"            && n > 1) ConvertTo<boost::int64_t>(params[1]);
1205
1206
    if (strMethod == "getnetworkhashps"       && n > 0) ConvertTo<boost::int64_t>(params[0]);
    if (strMethod == "getnetworkhashps"       && n > 1) ConvertTo<boost::int64_t>(params[1]);
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
    if (strMethod == "sendtoaddress"          && n > 1) ConvertTo<double>(params[1]);
    if (strMethod == "settxfee"               && n > 0) ConvertTo<double>(params[0]);
    if (strMethod == "getreceivedbyaddress"   && n > 1) ConvertTo<boost::int64_t>(params[1]);
    if (strMethod == "getreceivedbyaccount"   && n > 1) ConvertTo<boost::int64_t>(params[1]);
    if (strMethod == "listreceivedbyaddress"  && n > 0) ConvertTo<boost::int64_t>(params[0]);
    if (strMethod == "listreceivedbyaddress"  && n > 1) ConvertTo<bool>(params[1]);
    if (strMethod == "listreceivedbyaccount"  && n > 0) ConvertTo<boost::int64_t>(params[0]);
    if (strMethod == "listreceivedbyaccount"  && n > 1) ConvertTo<bool>(params[1]);
    if (strMethod == "getbalance"             && n > 1) ConvertTo<boost::int64_t>(params[1]);
    if (strMethod == "getblockhash"           && n > 0) ConvertTo<boost::int64_t>(params[0]);
    if (strMethod == "move"                   && n > 2) ConvertTo<double>(params[2]);
    if (strMethod == "move"                   && n > 3) ConvertTo<boost::int64_t>(params[3]);
    if (strMethod == "sendfrom"               && n > 2) ConvertTo<double>(params[2]);
    if (strMethod == "sendfrom"               && n > 3) ConvertTo<boost::int64_t>(params[3]);
    if (strMethod == "listtransactions"       && n > 1) ConvertTo<boost::int64_t>(params[1]);
    if (strMethod == "listtransactions"       && n > 2) ConvertTo<boost::int64_t>(params[2]);
    if (strMethod == "listaccounts"           && n > 0) ConvertTo<boost::int64_t>(params[0]);
    if (strMethod == "walletpassphrase"       && n > 1) ConvertTo<boost::int64_t>(params[1]);
1225
    if (strMethod == "getblocktemplate"       && n > 0) ConvertTo<Object>(params[0]);
1226
    if (strMethod == "listsinceblock"         && n > 1) ConvertTo<boost::int64_t>(params[1]);
1227
1228
1229
1230
    if (strMethod == "sendmany"               && n > 1) ConvertTo<Object>(params[1]);
    if (strMethod == "sendmany"               && n > 2) ConvertTo<boost::int64_t>(params[2]);
    if (strMethod == "addmultisigaddress"     && n > 0) ConvertTo<boost::int64_t>(params[0]);
    if (strMethod == "addmultisigaddress"     && n > 1) ConvertTo<Array>(params[1]);
1231
1232
    if (strMethod == "createmultisig"         && n > 0) ConvertTo<boost::int64_t>(params[0]);
    if (strMethod == "createmultisig"         && n > 1) ConvertTo<Array>(params[1]);
1233
1234
    if (strMethod == "listunspent"            && n > 0) ConvertTo<boost::int64_t>(params[0]);
    if (strMethod == "listunspent"            && n > 1) ConvertTo<boost::int64_t>(params[1]);
1235
    if (strMethod == "listunspent"            && n > 2) ConvertTo<Array>(params[2]);
1236
    if (strMethod == "getblock"               && n > 1) ConvertTo<bool>(params[1]);
1237
1238
1239
    if (strMethod == "getrawtransaction"      && n > 1) ConvertTo<boost::int64_t>(params[1]);
    if (strMethod == "createrawtransaction"   && n > 0) ConvertTo<Array>(params[0]);
    if (strMethod == "createrawtransaction"   && n > 1) ConvertTo<Object>(params[1]);
1240
1241
    if (strMethod == "signrawtransaction"     && n > 1) ConvertTo<Array>(params[1], true);
    if (strMethod == "signrawtransaction"     && n > 2) ConvertTo<Array>(params[2], true);
1242
    if (strMethod == "sendrawtransaction"     && n > 1) ConvertTo<bool>(params[1], true);
1243
1244
    if (strMethod == "gettxout"               && n > 1) ConvertTo<boost::int64_t>(params[1]);
    if (strMethod == "gettxout"               && n > 2) ConvertTo<bool>(params[2]);
1245
1246
    if (strMethod == "lockunspent"            && n > 0) ConvertTo<bool>(params[0]);
    if (strMethod == "lockunspent"            && n > 1) ConvertTo<Array>(params[1]);
1247
    if (strMethod == "importprivkey"          && n > 2) ConvertTo<bool>(params[2]);
1248
1249
    if (strMethod == "verifychain"            && n > 0) ConvertTo<boost::int64_t>(params[0]);
    if (strMethod == "verifychain"            && n > 1) ConvertTo<boost::int64_t>(params[1]);
1250
    if (strMethod == "keypoolrefill"          && n > 0) ConvertTo<boost::int64_t>(params[0]);
1251

1252
1253
1254
    return params;
}

1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
int CommandLineRPC(int argc, char *argv[])
{
    string strPrint;
    int nRet = 0;
    try
    {
        // Skip switches
        while (argc > 1 && IsSwitchChar(argv[1][0]))
        {
            argc--;
            argv++;
        }

        // Method
        if (argc < 2)
            throw runtime_error("too few parameters");
        string strMethod = argv[1];

        // Parameters default to strings
1274
1275
        std::vector<std::string> strParams(&argv[2], &argv[argc]);
        Array params = RPCConvertValues(strMethod, strParams);
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286

        // Execute
        Object reply = CallRPC(strMethod, params);

        // Parse reply
        const Value& result = find_value(reply, "result");
        const Value& error  = find_value(reply, "error");

        if (error.type() != null_type)
        {
            // Error
1287
            strPrint = "error: " + write_string(error, false);
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
            int code = find_value(error.get_obj(), "code").get_int();
            nRet = abs(code);
        }
        else
        {
            // Result
            if (result.type() == null_type)
                strPrint = "";
            else if (result.type() == str_type)
                strPrint = result.get_str();
            else
1299
                strPrint = write_string(result, true);
1300
1301
        }
    }
Gavin Andresen's avatar
Gavin Andresen committed
1302
1303
1304
1305
    catch (boost::thread_interrupted) {
        throw;
    }
    catch (std::exception& e) {
1306
1307
1308
        strPrint = string("error: ") + e.what();
        nRet = 87;
    }
Gavin Andresen's avatar
Gavin Andresen committed
1309
    catch (...) {
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
        PrintException(NULL, "CommandLineRPC()");
    }

    if (strPrint != "")
    {
        fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
    }
    return nRet;
}




#ifdef TEST
int main(int argc, char *argv[])
{
#ifdef _MSC_VER
1327
    // Turn off Microsoft heap dump noise
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
#endif
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    try
    {
        if (argc >= 2 && string(argv[1]) == "-server")
        {
1339
            LogPrintf("server ready\n");
1340
1341
1342
1343
1344
1345
1346
            ThreadRPCServer(NULL);
        }
        else
        {
            return CommandLineRPC(argc, argv);
        }
    }
Gavin Andresen's avatar
Gavin Andresen committed
1347
1348
1349
    catch (boost::thread_interrupted) {
        throw;
    }
1350
1351
1352
1353
1354
1355
1356
1357
    catch (std::exception& e) {
        PrintException(&e, "main()");
    } catch (...) {
        PrintException(NULL, "main()");
    }
    return 0;
}
#endif
1358
1359

const CRPCTable tableRPC;