Back to home page

Bitcoin sources

 
 

    


0001 // Copyright (c) 2010 Satoshi Nakamoto
0002 // Copyright (c) 2009-2014 The Bitcoin developers
0003 // Distributed under the MIT software license, see the accompanying
0004 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
0005 
0006 #include "alert.h"
0007 
0008 #include "chainparams.h"
0009 #include "clientversion.h"
0010 #include "net.h"
0011 #include "pubkey.h"
0012 #include "timedata.h"
0013 #include "ui_interface.h"
0014 #include "util.h"
0015 
0016 #include <stdint.h>
0017 #include <algorithm>
0018 #include <map>
0019 
0020 #include <boost/algorithm/string/classification.hpp>
0021 #include <boost/algorithm/string/replace.hpp>
0022 #include <boost/foreach.hpp>
0023 #include <boost/thread.hpp>
0024 
0025 using namespace std;
0026 
0027 map<uint256, CAlert> mapAlerts;
0028 CCriticalSection cs_mapAlerts;
0029 
0030 void CUnsignedAlert::SetNull()
0031 {
0032     nVersion = 1;
0033     nRelayUntil = 0;
0034     nExpiration = 0;
0035     nID = 0;
0036     nCancel = 0;
0037     setCancel.clear();
0038     nMinVer = 0;
0039     nMaxVer = 0;
0040     setSubVer.clear();
0041     nPriority = 0;
0042 
0043     strComment.clear();
0044     strStatusBar.clear();
0045     strReserved.clear();
0046 }
0047 
0048 std::string CUnsignedAlert::ToString() const
0049 {
0050     std::string strSetCancel;
0051     BOOST_FOREACH(int n, setCancel)
0052         strSetCancel += strprintf("%d ", n);
0053     std::string strSetSubVer;
0054     BOOST_FOREACH(std::string str, setSubVer)
0055         strSetSubVer += "\"" + str + "\" ";
0056     return strprintf(
0057         "CAlert(\n"
0058         "    nVersion     = %d\n"
0059         "    nRelayUntil  = %d\n"
0060         "    nExpiration  = %d\n"
0061         "    nID          = %d\n"
0062         "    nCancel      = %d\n"
0063         "    setCancel    = %s\n"
0064         "    nMinVer      = %d\n"
0065         "    nMaxVer      = %d\n"
0066         "    setSubVer    = %s\n"
0067         "    nPriority    = %d\n"
0068         "    strComment   = \"%s\"\n"
0069         "    strStatusBar = \"%s\"\n"
0070         ")\n",
0071         nVersion,
0072         nRelayUntil,
0073         nExpiration,
0074         nID,
0075         nCancel,
0076         strSetCancel,
0077         nMinVer,
0078         nMaxVer,
0079         strSetSubVer,
0080         nPriority,
0081         strComment,
0082         strStatusBar);
0083 }
0084 
0085 void CAlert::SetNull()
0086 {
0087     CUnsignedAlert::SetNull();
0088     vchMsg.clear();
0089     vchSig.clear();
0090 }
0091 
0092 bool CAlert::IsNull() const
0093 {
0094     return (nExpiration == 0);
0095 }
0096 
0097 uint256 CAlert::GetHash() const
0098 {
0099     return Hash(this->vchMsg.begin(), this->vchMsg.end());
0100 }
0101 
0102 bool CAlert::IsInEffect() const
0103 {
0104     return (GetAdjustedTime() < nExpiration);
0105 }
0106 
0107 bool CAlert::Cancels(const CAlert& alert) const
0108 {
0109     if (!IsInEffect())
0110         return false; // this was a no-op before 31403
0111     return (alert.nID <= nCancel || setCancel.count(alert.nID));
0112 }
0113 
0114 bool CAlert::AppliesTo(int nVersion, std::string strSubVerIn) const
0115 {
0116     // TODO: rework for client-version-embedded-in-strSubVer ?
0117     return (IsInEffect() &&
0118             nMinVer <= nVersion && nVersion <= nMaxVer &&
0119             (setSubVer.empty() || setSubVer.count(strSubVerIn)));
0120 }
0121 
0122 bool CAlert::AppliesToMe() const
0123 {
0124     return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<std::string>()));
0125 }
0126 
0127 bool CAlert::RelayTo(CNode* pnode) const
0128 {
0129     if (!IsInEffect())
0130         return false;
0131     // don't relay to nodes which haven't sent their version message
0132     if (pnode->nVersion == 0)
0133         return false;
0134     // returns true if wasn't already contained in the set
0135     if (pnode->setKnown.insert(GetHash()).second)
0136     {
0137         if (AppliesTo(pnode->nVersion, pnode->strSubVer) ||
0138             AppliesToMe() ||
0139             GetAdjustedTime() < nRelayUntil)
0140         {
0141             pnode->PushMessage("alert", *this);
0142             return true;
0143         }
0144     }
0145     return false;
0146 }
0147 
0148 bool CAlert::CheckSignature() const
0149 {
0150     CPubKey key(Params().AlertKey());
0151     if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
0152         return error("CAlert::CheckSignature() : verify signature failed");
0153 
0154     // Now unserialize the data
0155     CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
0156     sMsg >> *(CUnsignedAlert*)this;
0157     return true;
0158 }
0159 
0160 CAlert CAlert::getAlertByHash(const uint256 &hash)
0161 {
0162     CAlert retval;
0163     {
0164         LOCK(cs_mapAlerts);
0165         map<uint256, CAlert>::iterator mi = mapAlerts.find(hash);
0166         if(mi != mapAlerts.end())
0167             retval = mi->second;
0168     }
0169     return retval;
0170 }
0171 
0172 bool CAlert::ProcessAlert(bool fThread)
0173 {
0174     if (!CheckSignature())
0175         return false;
0176     if (!IsInEffect())
0177         return false;
0178 
0179     // alert.nID=max is reserved for if the alert key is
0180     // compromised. It must have a pre-defined message,
0181     // must never expire, must apply to all versions,
0182     // and must cancel all previous
0183     // alerts or it will be ignored (so an attacker can't
0184     // send an "everything is OK, don't panic" version that
0185     // cannot be overridden):
0186     int maxInt = std::numeric_limits<int>::max();
0187     if (nID == maxInt)
0188     {
0189         if (!(
0190                 nExpiration == maxInt &&
0191                 nCancel == (maxInt-1) &&
0192                 nMinVer == 0 &&
0193                 nMaxVer == maxInt &&
0194                 setSubVer.empty() &&
0195                 nPriority == maxInt &&
0196                 strStatusBar == "URGENT: Alert key compromised, upgrade required"
0197                 ))
0198             return false;
0199     }
0200 
0201     {
0202         LOCK(cs_mapAlerts);
0203         // Cancel previous alerts
0204         for (map<uint256, CAlert>::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();)
0205         {
0206             const CAlert& alert = (*mi).second;
0207             if (Cancels(alert))
0208             {
0209                 LogPrint("alert", "cancelling alert %d\n", alert.nID);
0210                 uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
0211                 mapAlerts.erase(mi++);
0212             }
0213             else if (!alert.IsInEffect())
0214             {
0215                 LogPrint("alert", "expiring alert %d\n", alert.nID);
0216                 uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
0217                 mapAlerts.erase(mi++);
0218             }
0219             else
0220                 mi++;
0221         }
0222 
0223         // Check if this alert has been cancelled
0224         BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
0225         {
0226             const CAlert& alert = item.second;
0227             if (alert.Cancels(*this))
0228             {
0229                 LogPrint("alert", "alert already cancelled by %d\n", alert.nID);
0230                 return false;
0231             }
0232         }
0233 
0234         // Add to mapAlerts
0235         mapAlerts.insert(make_pair(GetHash(), *this));
0236         // Notify UI and -alertnotify if it applies to me
0237         if(AppliesToMe())
0238         {
0239             uiInterface.NotifyAlertChanged(GetHash(), CT_NEW);
0240             Notify(strStatusBar, fThread);
0241         }
0242     }
0243 
0244     LogPrint("alert", "accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
0245     return true;
0246 }
0247 
0248 void
0249 CAlert::Notify(const std::string& strMessage, bool fThread)
0250 {
0251     std::string strCmd = GetArg("-alertnotify", "");
0252     if (strCmd.empty()) return;
0253 
0254     // Alert text should be plain ascii coming from a trusted source, but to
0255     // be safe we first strip anything not in safeChars, then add single quotes around
0256     // the whole string before passing it to the shell:
0257     std::string singleQuote("'");
0258     std::string safeStatus = SanitizeString(strMessage);
0259     safeStatus = singleQuote+safeStatus+singleQuote;
0260     boost::replace_all(strCmd, "%s", safeStatus);
0261 
0262     if (fThread)
0263         boost::thread t(runCommand, strCmd); // thread runs free
0264     else
0265         runCommand(strCmd);
0266 }