#include "net.h"
#include "init.h"
#include "ui_interface.h"
+ #include "base58.h"
#include "bitcoinrpc.h"
#undef printf
#include <boost/asio.hpp>
+#include <boost/asio/ip/v6_only.hpp>
+#include <boost/bind.hpp>
#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
- #include <boost/asio/ssl.hpp>
+ #include <boost/asio/ssl.hpp>
#include <boost/filesystem/fstream.hpp>
-typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLStream;
+#include <boost/shared_ptr.hpp>
+#include <list>
#define printf OutputDebugStringF
// MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are
return;
txnouttype type;
- vector<CBitcoinAddress> addresses;
+ vector<CTxDestination> addresses;
int nRequired;
- if (!ExtractAddresses(txprev.vout[txin.prevout.n].scriptPubKey, type,
+ if (!ExtractDestinations(txprev.vout[txin.prevout.n].scriptPubKey, type,
addresses, nRequired))
{
out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD)));
}
Array a;
- BOOST_FOREACH(const CBitcoinAddress& addr, addresses)
- a.push_back(addr.ToString());
+ BOOST_FOREACH(const CTxDestination& addr, addresses)
+ a.push_back(CBitcoinAddress(addr).ToString());
out.push_back(Pair("addresses", a));
}
ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out)
{
txnouttype type;
- vector<CBitcoinAddress> addresses;
+ vector<CTxDestination> addresses;
int nRequired;
out.push_back(Pair("asm", scriptPubKey.ToString()));
out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
- if (!ExtractAddresses(scriptPubKey, type, addresses, nRequired))
+ if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired))
{
out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD)));
return;
out.push_back(Pair("type", GetTxnOutputType(type)));
Array a;
- BOOST_FOREACH(const CBitcoinAddress& addr, addresses)
- a.push_back(addr.ToString());
+ BOOST_FOREACH(const CTxDestination& addr, addresses)
+ a.push_back(CBitcoinAddress(addr).ToString());
out.push_back(Pair("addresses", a));
}
"stop\n"
"Stop Bitcoin server.");
// Shutdown will take long enough that the response should get back
- uiInterface.QueueShutdown();
+ StartShutdown();
return "Bitcoin server stopping";
}
"getinfo\n"
"Returns an object containing various state info.");
+ CService addrProxy;
+ GetProxy(NET_IPV4, addrProxy);
+
Object obj;
obj.push_back(Pair("version", (int)CLIENT_VERSION));
obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
obj.push_back(Pair("blocks", (int)nBestHeight));
obj.push_back(Pair("connections", (int)vNodes.size()));
- obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
+ obj.push_back(Pair("proxy", (addrProxy.IsValid() ? addrProxy.ToStringIPPort() : string())));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("testnet", fTestNet));
obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
pwalletMain->TopUpKeyPool();
// Generate a new key that is added to wallet
- std::vector<unsigned char> newKey;
+ CPubKey newKey;
if (!pwalletMain->GetKeyFromPool(newKey, false))
throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
- CBitcoinAddress address(newKey);
+ CKeyID keyID = newKey.GetID();
- pwalletMain->SetAddressBookName(address, strAccount);
+ pwalletMain->SetAddressBookName(keyID, strAccount);
- return address.ToString();
+ return CBitcoinAddress(keyID).ToString();
}
bool bKeyUsed = false;
// Check if the current key has been used
- if (!account.vchPubKey.empty())
+ if (account.vchPubKey.IsValid())
{
CScript scriptPubKey;
- scriptPubKey.SetBitcoinAddress(account.vchPubKey);
+ scriptPubKey.SetDestination(account.vchPubKey.GetID());
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
- it != pwalletMain->mapWallet.end() && !account.vchPubKey.empty();
+ it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
++it)
{
const CWalletTx& wtx = (*it).second;
}
// Generate a new key
- if (account.vchPubKey.empty() || bForceNew || bKeyUsed)
+ if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
{
if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first");
- pwalletMain->SetAddressBookName(CBitcoinAddress(account.vchPubKey), strAccount);
+ pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount);
walletdb.WriteAccount(strAccount, account);
}
- return CBitcoinAddress(account.vchPubKey);
+ return CBitcoinAddress(account.vchPubKey.GetID());
}
Value getaccountaddress(const Array& params, bool fHelp)
strAccount = AccountFromValue(params[1]);
// Detect when changing the account of an address that is the 'unused current key' of another account:
- if (pwalletMain->mapAddressBook.count(address))
+ if (pwalletMain->mapAddressBook.count(address.Get()))
{
- string strOldAccount = pwalletMain->mapAddressBook[address];
+ string strOldAccount = pwalletMain->mapAddressBook[address.Get()];
if (address == GetAccountAddress(strOldAccount))
GetAccountAddress(strOldAccount, true);
}
- pwalletMain->SetAddressBookName(address, strAccount);
+ pwalletMain->SetAddressBookName(address.Get(), strAccount);
return Value::null;
}
throw JSONRPCError(-5, "Invalid Bitcoin address");
string strAccount;
- map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
+ map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.empty())
strAccount = (*mi).second;
return strAccount;
if (pwalletMain->IsLocked())
throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
- string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
+ string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
if (strError != "")
throw JSONRPCError(-4, strError);
if (!addr.IsValid())
throw JSONRPCError(-3, "Invalid address");
+ CKeyID keyID;
+ if (!addr.GetKeyID(keyID))
+ throw JSONRPCError(-3, "Address does not refer to key");
+
CKey key;
- if (!pwalletMain->GetKey(addr, key))
+ if (!pwalletMain->GetKey(keyID, key))
throw JSONRPCError(-4, "Private key not available");
CDataStream ss(SER_GETHASH, 0);
if (!addr.IsValid())
throw JSONRPCError(-3, "Invalid address");
+ CKeyID keyID;
+ if (!addr.GetKeyID(keyID))
+ throw JSONRPCError(-3, "Address does not refer to key");
+
bool fInvalid = false;
vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig))
return false;
- return (CBitcoinAddress(key.GetPubKey()) == addr);
+ return (key.GetPubKey().GetID() == keyID);
}
CScript scriptPubKey;
if (!address.IsValid())
throw JSONRPCError(-5, "Invalid Bitcoin address");
- scriptPubKey.SetBitcoinAddress(address);
+ scriptPubKey.SetDestination(address.Get());
if (!IsMine(*pwalletMain,scriptPubKey))
return (double)0.0;
}
- void GetAccountAddresses(string strAccount, set<CBitcoinAddress>& setAddress)
+ void GetAccountAddresses(string strAccount, set<CTxDestination>& setAddress)
{
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, pwalletMain->mapAddressBook)
+ BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& item, pwalletMain->mapAddressBook)
{
- const CBitcoinAddress& address = item.first;
+ const CTxDestination& address = item.first;
const string& strName = item.second;
if (strName == strAccount)
setAddress.insert(address);
}
}
-
Value getreceivedbyaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
// Get the set of pub keys assigned to account
string strAccount = AccountFromValue(params[0]);
- set<CBitcoinAddress> setAddress;
+ set<CTxDestination> setAddress;
GetAccountAddresses(strAccount, setAddress);
// Tally
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
- CBitcoinAddress address;
- if (ExtractAddress(txout.scriptPubKey, address) && pwalletMain->HaveKey(address) && setAddress.count(address))
+ CTxDestination address;
+ if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
if (wtx.GetDepthInMainChain() >= nMinDepth)
nAmount += txout.nValue;
}
int64 allGeneratedImmature, allGeneratedMature, allFee;
allGeneratedImmature = allGeneratedMature = allFee = 0;
string strSentAccount;
- list<pair<CBitcoinAddress, int64> > listReceived;
- list<pair<CBitcoinAddress, int64> > listSent;
+ list<pair<CTxDestination, int64> > listReceived;
+ list<pair<CTxDestination, int64> > listSent;
wtx.GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
if (wtx.GetDepthInMainChain() >= nMinDepth)
{
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
+ BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived)
nBalance += r.second;
}
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listSent)
+ BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent)
nBalance -= r.second;
nBalance -= allFee;
nBalance += allGeneratedMature;
throw JSONRPCError(-6, "Account has insufficient funds");
// Send
- string strError = pwalletMain->SendMoneyToBitcoinAddress(address, nAmount, wtx);
+ string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
if (strError != "")
throw JSONRPCError(-4, strError);
setAddress.insert(address);
CScript scriptPubKey;
- scriptPubKey.SetBitcoinAddress(address);
- int64 nAmount = AmountFromValue(s.value_);
+ scriptPubKey.SetDestination(address.Get());
+ int64 nAmount = AmountFromValue(s.value_);
totalAmount += nAmount;
vecSend.push_back(make_pair(scriptPubKey, nAmount));
CBitcoinAddress address(ks);
if (address.IsValid())
{
- if (address.IsScript())
+ CKeyID keyID;
+ if (!address.GetKeyID(keyID))
throw runtime_error(
- strprintf("%s is a pay-to-script address",ks.c_str()));
- std::vector<unsigned char> vchPubKey;
- if (!pwalletMain->GetPubKey(address, vchPubKey))
+ strprintf("%s does not refer to a key",ks.c_str()));
+ CPubKey vchPubKey;
+ if (!pwalletMain->GetPubKey(keyID, vchPubKey))
throw runtime_error(
strprintf("no full public key for address %s",ks.c_str()));
- if (vchPubKey.empty() || !pubkeys[i].SetPubKey(vchPubKey))
+ if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
throw runtime_error(" Invalid public key: "+ks);
}
// Case 2: hex public key
else if (IsHex(ks))
{
- vector<unsigned char> vchPubKey = ParseHex(ks);
- if (vchPubKey.empty() || !pubkeys[i].SetPubKey(vchPubKey))
+ CPubKey vchPubKey(ParseHex(ks));
+ if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
throw runtime_error(" Invalid public key: "+ks);
}
else
// Construct using pay-to-script-hash:
CScript inner;
inner.SetMultisig(nRequired, pubkeys);
-
- uint160 scriptHash = Hash160(inner);
- CScript scriptPubKey;
- scriptPubKey.SetPayToScriptHash(inner);
+ CScriptID innerID = inner.GetID();
pwalletMain->AddCScript(inner);
- CBitcoinAddress address;
- address.SetScriptHash160(scriptHash);
- pwalletMain->SetAddressBookName(address, strAccount);
- return address.ToString();
+ pwalletMain->SetAddressBookName(innerID, strAccount);
+ return CBitcoinAddress(innerID).ToString();
}
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
- CBitcoinAddress address;
- if (!ExtractAddress(txout.scriptPubKey, address) || !pwalletMain->HaveKey(address) || !address.IsValid())
+ CTxDestination address;
+ if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
continue;
tallyitem& item = mapTally[address];
{
int64 nGeneratedImmature, nGeneratedMature, nFee;
string strSentAccount;
- list<pair<CBitcoinAddress, int64> > listReceived;
- list<pair<CBitcoinAddress, int64> > listSent;
+ list<pair<CTxDestination, int64> > listReceived;
+ list<pair<CTxDestination, int64> > listSent;
wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
// Sent
if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
{
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
+ BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
{
Object entry;
entry.push_back(Pair("account", strSentAccount));
- entry.push_back(Pair("address", s.first.ToString()));
+ entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString()));
entry.push_back(Pair("category", "send"));
entry.push_back(Pair("amount", ValueFromAmount(-s.second)));
entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
// Received
if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
{
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
+ BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
{
string account;
if (pwalletMain->mapAddressBook.count(r.first))
{
Object entry;
entry.push_back(Pair("account", account));
- entry.push_back(Pair("address", r.first.ToString()));
+ entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString()));
entry.push_back(Pair("category", "receive"));
entry.push_back(Pair("amount", ValueFromAmount(r.second)));
if (fLong)
if ((int)ret.size() >= (nCount+nFrom)) break;
}
// ret is newest to oldest
-
+
if (nFrom > (int)ret.size())
nFrom = ret.size();
if ((nFrom + nCount) > (int)ret.size())
nMinDepth = params[0].get_int();
map<string, int64> mapAccountBalances;
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& entry, pwalletMain->mapAddressBook) {
- if (pwalletMain->HaveKey(entry.first)) // This address belongs to me
+ BOOST_FOREACH(const PAIRTYPE(CTxDestination, string)& entry, pwalletMain->mapAddressBook) {
+ if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
mapAccountBalances[entry.second] = 0;
}
const CWalletTx& wtx = (*it).second;
int64 nGeneratedImmature, nGeneratedMature, nFee;
string strSentAccount;
- list<pair<CBitcoinAddress, int64> > listReceived;
- list<pair<CBitcoinAddress, int64> > listSent;
+ list<pair<CTxDestination, int64> > listReceived;
+ list<pair<CTxDestination, int64> > listSent;
wtx.GetAmounts(nGeneratedImmature, nGeneratedMature, listReceived, listSent, nFee, strSentAccount);
mapAccountBalances[strSentAccount] -= nFee;
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& s, listSent)
+ BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent)
mapAccountBalances[strSentAccount] -= s.second;
if (wtx.GetDepthInMainChain() >= nMinDepth)
{
mapAccountBalances[""] += nGeneratedMature;
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, int64)& r, listReceived)
+ BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived)
if (pwalletMain->mapAddressBook.count(r.first))
mapAccountBalances[pwalletMain->mapAddressBook[r.first]] += r.second;
else
// BDB seems to have a bad habit of writing old data into
// slack space in .dat files; that is bad if the old data is
// unencrypted private keys. So:
- uiInterface.QueueShutdown();
+ StartShutdown();
return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet";
}
+ class DescribeAddressVisitor : public boost::static_visitor<Object>
+ {
+ public:
+ Object operator()(const CNoDestination &dest) const { return Object(); }
+
+ Object operator()(const CKeyID &keyID) const {
+ Object obj;
+ CPubKey vchPubKey;
+ pwalletMain->GetPubKey(keyID, vchPubKey);
+ obj.push_back(Pair("isscript", false));
+ obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
+ obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
+ return obj;
+ }
+
+ Object operator()(const CScriptID &scriptID) const {
+ Object obj;
+ obj.push_back(Pair("isscript", true));
+ CScript subscript;
+ pwalletMain->GetCScript(scriptID, subscript);
+ std::vector<CTxDestination> addresses;
+ txnouttype whichType;
+ int nRequired;
+ ExtractDestinations(subscript, whichType, addresses, nRequired);
+ obj.push_back(Pair("script", GetTxnOutputType(whichType)));
+ Array a;
+ BOOST_FOREACH(const CTxDestination& addr, addresses)
+ a.push_back(CBitcoinAddress(addr).ToString());
+ obj.push_back(Pair("addresses", a));
+ if (whichType == TX_MULTISIG)
+ obj.push_back(Pair("sigsrequired", nRequired));
+ return obj;
+ }
+ };
Value validateaddress(const Array& params, bool fHelp)
{
ret.push_back(Pair("isvalid", isValid));
if (isValid)
{
- // Call Hash160ToAddress() so we always return current ADDRESSVERSION
- // version of the address:
+ CTxDestination dest = address.Get();
string currentAddress = address.ToString();
ret.push_back(Pair("address", currentAddress));
- if (pwalletMain->HaveKey(address))
- {
- ret.push_back(Pair("ismine", true));
- std::vector<unsigned char> vchPubKey;
- pwalletMain->GetPubKey(address, vchPubKey);
- ret.push_back(Pair("pubkey", HexStr(vchPubKey)));
- CKey key;
- key.SetPubKey(vchPubKey);
- ret.push_back(Pair("iscompressed", key.IsCompressed()));
- }
- else if (pwalletMain->HaveCScript(address.GetHash160()))
- {
- ret.push_back(Pair("isscript", true));
- CScript subscript;
- pwalletMain->GetCScript(address.GetHash160(), subscript);
- ret.push_back(Pair("ismine", ::IsMine(*pwalletMain, subscript)));
- std::vector<CBitcoinAddress> addresses;
- txnouttype whichType;
- int nRequired;
- ExtractAddresses(subscript, whichType, addresses, nRequired);
- ret.push_back(Pair("script", GetTxnOutputType(whichType)));
- Array a;
- BOOST_FOREACH(const CBitcoinAddress& addr, addresses)
- a.push_back(addr.ToString());
- ret.push_back(Pair("addresses", a));
- if (whichType == TX_MULTISIG)
- ret.push_back(Pair("sigsrequired", nRequired));
+ bool fMine = IsMine(*pwalletMain, dest);
+ ret.push_back(Pair("ismine", fMine));
+ if (fMine) {
+ Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
+ ret.insert(ret.end(), detail.begin(), detail.end());
}
- else
- ret.push_back(Pair("ismine", false));
- if (pwalletMain->mapAddressBook.count(address))
- ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
+ if (pwalletMain->mapAddressBook.count(dest))
+ ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest]));
}
return ret;
}
CInv inv(MSG_TX, tx.GetHash());
RelayInventory(inv);
- return true;
+ return tx.GetHash().GetHex();
}
stream << HTTPReply(nStatus, strReply, false) << std::flush;
}
-bool ClientAllowed(const string& strAddress)
+bool ClientAllowed(const boost::asio::ip::address& address)
{
- if (strAddress == asio::ip::address_v4::loopback().to_string())
+ // 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()
+ || address == asio::ip::address_v6::loopback()
+ || (address.is_v4()
+ // Chech whether IPv4 addresses match 127.0.0.0/8 (loopback subnet)
+ && (address.to_v4().to_ulong() & 0xff000000) == 0x7f000000))
return true;
+
+ const string strAddress = address.to_string();
const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
BOOST_FOREACH(string strAllow, vAllow)
if (WildcardMatch(strAddress, strAllow))
//
// IOStream device that speaks SSL but can also speak non-SSL
//
+template <typename Protocol>
class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
public:
- SSLIOStreamDevice(SSLStream &streamIn, bool fUseSSLIn) : stream(streamIn)
+ SSLIOStreamDevice(asio::ssl::stream<typename Protocol::socket> &streamIn, bool fUseSSLIn) : stream(streamIn)
{
fUseSSL = fUseSSLIn;
fNeedHandshake = fUseSSLIn;
private:
bool fNeedHandshake;
bool fUseSSL;
- SSLStream& stream;
+ asio::ssl::stream<typename Protocol::socket>& stream;
};
class AcceptedConnection
{
- public:
- SSLStream sslStream;
- SSLIOStreamDevice d;
- iostreams::stream<SSLIOStreamDevice> stream;
+public:
+ virtual ~AcceptedConnection() {}
+
+ virtual std::iostream& stream() = 0;
+ virtual std::string peer_address_to_string() const = 0;
+ virtual void close() = 0;
+};
+
+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)
+ {
+ }
- ip::tcp::endpoint peer;
+ virtual std::iostream& stream()
+ {
+ return _stream;
+ }
+
+ virtual std::string peer_address_to_string() const
+ {
+ return peer.address().to_string();
+ }
- AcceptedConnection(asio::io_service &io_service, ssl::context &context,
- bool fUseSSL) : sslStream(io_service, context), d(sslStream, fUseSSL),
- stream(d) { ; }
+ 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;
};
void ThreadRPCServer(void* parg)
printf("ThreadRPCServer exited\n");
}
+// Forward declaration required for RPCListen
+template <typename Protocol, typename SocketAcceptorService>
+static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
+ 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.
+ */
+template <typename Protocol, typename SocketAcceptorService>
+static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
+ ssl::context& context,
+ const bool fUseSSL)
+{
+
+ // Accept connection
+ AcceptedConnectionImpl<Protocol>* conn = new AcceptedConnectionImpl<Protocol>(acceptor->get_io_service(), context, fUseSSL);
+
+ acceptor->async_accept(
+ conn->sslStream.lowest_layer(),
+ conn->peer,
+ boost::bind(&RPCAcceptHandler<Protocol, SocketAcceptorService>,
+ acceptor,
+ boost::ref(context),
+ fUseSSL,
+ conn,
+ boost::asio::placeholders::error));
+}
+
+/**
+ * Accept and handle incoming connection.
+ */
+template <typename Protocol, typename SocketAcceptorService>
+static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
+ ssl::context& context,
+ const bool fUseSSL,
+ AcceptedConnection* conn,
+ const boost::system::error_code& error)
+{
+ vnThreadsRunning[THREAD_RPCLISTENER]++;
+
+ // Immediately start accepting new connections
+ RPCListen(acceptor, context, fUseSSL);
+
+ AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn);
+
+ // 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.
+ else if (tcp_conn
+ && !ClientAllowed(tcp_conn->peer.address()))
+ {
+ // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
+ if (!fUseSSL)
+ conn->stream() << HTTPReply(403, "", false) << std::flush;
+ delete conn;
+ }
+
+ // start HTTP client thread
+ else if (!CreateThread(ThreadRPCServer3, conn)) {
+ printf("Failed to create RPC server client thread\n");
+ delete conn;
+ }
+
+ vnThreadsRunning[THREAD_RPCLISTENER]--;
+}
+
void ThreadRPCServer2(void* parg)
{
printf("ThreadRPCServer started\n");
GetConfigFile().string().c_str(),
EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
_("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
- uiInterface.QueueShutdown();
+ StartShutdown();
return;
}
- bool fUseSSL = GetBoolArg("-rpcssl");
- asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
+ const bool fUseSSL = GetBoolArg("-rpcssl");
asio::io_service io_service;
- ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
- ip::tcp::acceptor acceptor(io_service);
- try
- {
- acceptor.open(endpoint.protocol());
- acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
- acceptor.bind(endpoint);
- acceptor.listen(socket_base::max_connections);
- }
- catch(boost::system::system_error &e)
- {
- uiInterface.ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()),
- _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
- StartShutdown();
- return;
- }
+
+ // Make sure that we'll get stopped when the application shuts down
+ boost::signals2::scoped_connection rpc_listen_thread_stop(
+ uiInterface.QueueShutdown.connect(boost::bind(
+ &asio::io_service::stop, &io_service)));
ssl::context context(io_service, ssl::context::sslv23);
if (fUseSSL)
SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str());
}
- loop
+ // 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();
+ ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
+
+ std::list< boost::shared_ptr<ip::tcp::acceptor> > acceptors;
+ try
{
- // Accept connection
- AcceptedConnection *conn =
- new AcceptedConnection(io_service, context, fUseSSL);
+ acceptors.push_back(boost::shared_ptr<ip::tcp::acceptor>(new ip::tcp::acceptor(io_service)));
+ acceptors.back()->open(endpoint.protocol());
+ acceptors.back()->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
- vnThreadsRunning[THREAD_RPCLISTENER]--;
- acceptor.accept(conn->sslStream.lowest_layer(), conn->peer);
- vnThreadsRunning[THREAD_RPCLISTENER]++;
+ // Try making the socket dual IPv6/IPv4 (if listening on the "any" address)
+ boost::system::error_code v6_only_error;
+ acceptors.back()->set_option(boost::asio::ip::v6_only(loopback), v6_only_error);
- if (fShutdown)
- {
- delete conn;
- return;
- }
+ acceptors.back()->bind(endpoint);
+ acceptors.back()->listen(socket_base::max_connections);
- // Restrict callers by IP. It is important to
- // do this before starting client thread, to filter out
- // certain DoS and misbehaving clients.
- if (!ClientAllowed(conn->peer.address().to_string()))
+ RPCListen(acceptors.back(), context, fUseSSL);
+
+ // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately
+ if (loopback || v6_only_error)
{
- // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
- if (!fUseSSL)
- conn->stream << HTTPReply(403, "", false) << std::flush;
- delete conn;
- }
+ bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any();
+ endpoint.address(bindAddress);
- // start HTTP client thread
- else if (!CreateThread(ThreadRPCServer3, conn)) {
- printf("Failed to create RPC server client thread\n");
- delete conn;
+ acceptors.push_back(boost::shared_ptr<ip::tcp::acceptor>(new ip::tcp::acceptor(io_service)));
+ acceptors.back()->open(endpoint.protocol());
+ acceptors.back()->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
+ acceptors.back()->bind(endpoint);
+ acceptors.back()->listen(socket_base::max_connections);
+
+ RPCListen(acceptors.back(), context, fUseSSL);
}
}
- uiInterface.QueueShutdown();
+ catch(boost::system::system_error &e)
+ {
+ uiInterface.ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()),
+ _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
- io_service.run();
++ StartShutdown();
+ return;
+ }
+
+ vnThreadsRunning[THREAD_RPCLISTENER]--;
++ while (!fShutdown)
++ io_service.run_one();
+ vnThreadsRunning[THREAD_RPCLISTENER]++;
+
+ // Terminate all outstanding accept-requests
+ BOOST_FOREACH(boost::shared_ptr<ip::tcp::acceptor>& acceptor, acceptors)
+ {
+ acceptor->cancel();
+ acceptor->close();
+ }
+ acceptors.clear();
}
void ThreadRPCServer3(void* parg)
loop {
if (fShutdown || !fRun)
{
- conn->stream.close();
+ conn->close();
delete conn;
--vnThreadsRunning[THREAD_RPCHANDLER];
return;
map<string, string> mapHeaders;
string strRequest;
- ReadHTTP(conn->stream, mapHeaders, strRequest);
+ ReadHTTP(conn->stream(), mapHeaders, strRequest);
// Check authorization
if (mapHeaders.count("authorization") == 0)
{
- conn->stream << HTTPReply(401, "", false) << std::flush;
+ conn->stream() << HTTPReply(401, "", false) << std::flush;
break;
}
if (!HTTPAuthorized(mapHeaders))
{
- printf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer.address().to_string().c_str());
+ printf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str());
/* 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)
Sleep(250);
- conn->stream << HTTPReply(401, "", false) << std::flush;
+ conn->stream() << HTTPReply(401, "", false) << std::flush;
break;
}
if (mapHeaders["connection"] == "close")
// Send reply
string strReply = JSONRPCReply(result, Value::null, id);
- conn->stream << HTTPReply(200, strReply, fRun) << std::flush;
+ conn->stream() << HTTPReply(200, strReply, fRun) << std::flush;
}
catch (Object& objError)
{
- ErrorReply(conn->stream, objError, id);
+ ErrorReply(conn->stream(), objError, id);
break;
}
catch (std::exception& e)
{
- ErrorReply(conn->stream, JSONRPCError(-32700, e.what()), id);
+ ErrorReply(conn->stream(), JSONRPCError(-32700, e.what()), id);
break;
}
}
asio::io_service io_service;
ssl::context context(io_service, ssl::context::sslv23);
context.set_options(ssl::context::no_sslv2);
- SSLStream sslStream(io_service, context);
- SSLIOStreamDevice d(sslStream, fUseSSL);
- iostreams::stream<SSLIOStreamDevice> stream(d);
+ 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);
if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")))
throw runtime_error("couldn't connect to server");
if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
- if (strMethod == "sendmany" && n > 1)
- {
- string s = params[1].get_str();
- Value v;
- if (!read_string(s, v) || v.type() != obj_type)
- throw runtime_error("type mismatch");
- params[1] = v.get_obj();
- }
- 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)
- {
- string s = params[1].get_str();
- Value v;
- if (!read_string(s, v) || v.type() != array_type)
- throw runtime_error("type mismatch "+s);
- params[1] = v.get_array();
- }
+ 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]);
+
return params;
}