Line data Source code
1 : // Copyright (c) 2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2014 The Bitcoin Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "alert.h"
7 :
8 : #include "clientversion.h"
9 : #include "net.h"
10 : #include "pubkey.h"
11 : #include "timedata.h"
12 : #include "ui_interface.h"
13 : #include "util.h"
14 : #include "utilstrencodings.h"
15 :
16 : #include <stdint.h>
17 : #include <algorithm>
18 : #include <map>
19 :
20 : #include <boost/algorithm/string/classification.hpp>
21 : #include <boost/algorithm/string/replace.hpp>
22 : #include <boost/foreach.hpp>
23 : #include <boost/thread.hpp>
24 :
25 : using namespace std;
26 :
27 96 : map<uint256, CAlert> mapAlerts;
28 96 : CCriticalSection cs_mapAlerts;
29 :
30 24 : void CUnsignedAlert::SetNull()
31 : {
32 24 : nVersion = 1;
33 24 : nRelayUntil = 0;
34 24 : nExpiration = 0;
35 24 : nID = 0;
36 24 : nCancel = 0;
37 24 : setCancel.clear();
38 24 : nMinVer = 0;
39 24 : nMaxVer = 0;
40 24 : setSubVer.clear();
41 24 : nPriority = 0;
42 :
43 24 : strComment.clear();
44 24 : strStatusBar.clear();
45 24 : strReserved.clear();
46 24 : }
47 :
48 0 : std::string CUnsignedAlert::ToString() const
49 : {
50 : std::string strSetCancel;
51 0 : BOOST_FOREACH(int n, setCancel)
52 0 : strSetCancel += strprintf("%d ", n);
53 : std::string strSetSubVer;
54 0 : BOOST_FOREACH(const std::string& str, setSubVer)
55 0 : strSetSubVer += "\"" + str + "\" ";
56 : return strprintf(
57 : "CAlert(\n"
58 : " nVersion = %d\n"
59 : " nRelayUntil = %d\n"
60 : " nExpiration = %d\n"
61 : " nID = %d\n"
62 : " nCancel = %d\n"
63 : " setCancel = %s\n"
64 : " nMinVer = %d\n"
65 : " nMaxVer = %d\n"
66 : " setSubVer = %s\n"
67 : " nPriority = %d\n"
68 : " strComment = \"%s\"\n"
69 : " strStatusBar = \"%s\"\n"
70 : ")\n",
71 : nVersion,
72 : nRelayUntil,
73 : nExpiration,
74 : nID,
75 : nCancel,
76 : strSetCancel,
77 : nMinVer,
78 : nMaxVer,
79 : strSetSubVer,
80 : nPriority,
81 : strComment,
82 0 : strStatusBar);
83 : }
84 :
85 24 : void CAlert::SetNull()
86 : {
87 24 : CUnsignedAlert::SetNull();
88 24 : vchMsg.clear();
89 24 : vchSig.clear();
90 24 : }
91 :
92 0 : bool CAlert::IsNull() const
93 : {
94 0 : return (nExpiration == 0);
95 : }
96 :
97 0 : uint256 CAlert::GetHash() const
98 : {
99 36 : return Hash(this->vchMsg.begin(), this->vchMsg.end());
100 : }
101 :
102 0 : bool CAlert::IsInEffect() const
103 : {
104 83 : return (GetAdjustedTime() < nExpiration);
105 : }
106 :
107 29 : bool CAlert::Cancels(const CAlert& alert) const
108 : {
109 29 : if (!IsInEffect())
110 : return false; // this was a no-op before 31403
111 55 : return (alert.nID <= nCancel || setCancel.count(alert.nID));
112 : }
113 :
114 33 : bool CAlert::AppliesTo(int nVersion, const std::string& strSubVerIn) const
115 : {
116 : // TODO: rework for client-version-embedded-in-strSubVer ?
117 33 : return (IsInEffect() &&
118 89 : nMinVer <= nVersion && nVersion <= nMaxVer &&
119 53 : (setSubVer.empty() || setSubVer.count(strSubVerIn)));
120 : }
121 :
122 16 : bool CAlert::AppliesToMe() const
123 : {
124 32 : return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<std::string>()));
125 : }
126 :
127 0 : bool CAlert::RelayTo(CNode* pnode) const
128 : {
129 0 : if (!IsInEffect())
130 : return false;
131 : // don't relay to nodes which haven't sent their version message
132 0 : if (pnode->nVersion == 0)
133 : return false;
134 : // returns true if wasn't already contained in the set
135 0 : if (pnode->setKnown.insert(GetHash()).second)
136 : {
137 0 : if (AppliesTo(pnode->nVersion, pnode->strSubVer) ||
138 0 : AppliesToMe() ||
139 0 : GetAdjustedTime() < nRelayUntil)
140 : {
141 0 : pnode->PushMessage("alert", *this);
142 0 : return true;
143 : }
144 : }
145 : return false;
146 : }
147 :
148 16 : bool CAlert::CheckSignature(const std::vector<unsigned char>& alertKey) const
149 : {
150 : CPubKey key(alertKey);
151 48 : if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
152 0 : return error("CAlert::CheckSignature(): verify signature failed");
153 :
154 : // Now unserialize the data
155 16 : CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
156 16 : sMsg >> *(CUnsignedAlert*)this;
157 16 : return true;
158 : }
159 :
160 0 : CAlert CAlert::getAlertByHash(const uint256 &hash)
161 : {
162 0 : CAlert retval;
163 : {
164 0 : LOCK(cs_mapAlerts);
165 0 : map<uint256, CAlert>::iterator mi = mapAlerts.find(hash);
166 0 : if(mi != mapAlerts.end())
167 0 : retval = mi->second;
168 : }
169 0 : return retval;
170 : }
171 :
172 8 : bool CAlert::ProcessAlert(const std::vector<unsigned char>& alertKey, bool fThread)
173 : {
174 8 : if (!CheckSignature(alertKey))
175 : return false;
176 8 : if (!IsInEffect())
177 : return false;
178 :
179 : // alert.nID=max is reserved for if the alert key is
180 : // compromised. It must have a pre-defined message,
181 : // must never expire, must apply to all versions,
182 : // and must cancel all previous
183 : // alerts or it will be ignored (so an attacker can't
184 : // send an "everything is OK, don't panic" version that
185 : // cannot be overridden):
186 8 : int maxInt = std::numeric_limits<int>::max();
187 8 : if (nID == maxInt)
188 : {
189 0 : if (!(
190 0 : nExpiration == maxInt &&
191 0 : nCancel == (maxInt-1) &&
192 0 : nMinVer == 0 &&
193 0 : nMaxVer == maxInt &&
194 0 : setSubVer.empty() &&
195 0 : nPriority == maxInt &&
196 0 : strStatusBar == "URGENT: Alert key compromised, upgrade required"
197 0 : ))
198 : return false;
199 : }
200 :
201 : {
202 8 : LOCK(cs_mapAlerts);
203 : // Cancel previous alerts
204 32 : for (map<uint256, CAlert>::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();)
205 : {
206 16 : const CAlert& alert = (*mi).second;
207 16 : if (Cancels(alert))
208 : {
209 3 : LogPrint("alert", "cancelling alert %d\n", alert.nID);
210 3 : uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
211 3 : mapAlerts.erase(mi++);
212 : }
213 13 : else if (!alert.IsInEffect())
214 : {
215 0 : LogPrint("alert", "expiring alert %d\n", alert.nID);
216 0 : uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
217 0 : mapAlerts.erase(mi++);
218 : }
219 : else
220 13 : mi++;
221 : }
222 :
223 : // Check if this alert has been cancelled
224 126 : BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
225 : {
226 13 : const CAlert& alert = item.second;
227 13 : if (alert.Cancels(*this))
228 : {
229 0 : LogPrint("alert", "alert already cancelled by %d\n", alert.nID);
230 : return false;
231 : }
232 : }
233 :
234 : // Add to mapAlerts
235 24 : mapAlerts.insert(make_pair(GetHash(), *this));
236 : // Notify UI and -alertnotify if it applies to me
237 8 : if(AppliesToMe())
238 : {
239 4 : uiInterface.NotifyAlertChanged(GetHash(), CT_NEW);
240 4 : Notify(strStatusBar, fThread);
241 : }
242 : }
243 :
244 8 : LogPrint("alert", "accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
245 8 : return true;
246 : }
247 :
248 : void
249 6 : CAlert::Notify(const std::string& strMessage, bool fThread)
250 : {
251 30 : std::string strCmd = GetArg("-alertnotify", "");
252 12 : if (strCmd.empty()) return;
253 :
254 : // Alert text should be plain ascii coming from a trusted source, but to
255 : // be safe we first strip anything not in safeChars, then add single quotes around
256 : // the whole string before passing it to the shell:
257 12 : std::string singleQuote("'");
258 6 : std::string safeStatus = SanitizeString(strMessage);
259 18 : safeStatus = singleQuote+safeStatus+singleQuote;
260 6 : boost::replace_all(strCmd, "%s", safeStatus);
261 :
262 6 : if (fThread)
263 4 : boost::thread t(runCommand, strCmd); // thread runs free
264 : else
265 4 : runCommand(strCmd);
266 288 : }
|