Bitcoin Core  22.0.0
P2P Digital Currency
addrdb.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2020 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 <addrdb.h>
7 
8 #include <addrman.h>
9 #include <chainparams.h>
10 #include <clientversion.h>
11 #include <cstdint>
12 #include <hash.h>
13 #include <logging/timer.h>
14 #include <netbase.h>
15 #include <random.h>
16 #include <streams.h>
17 #include <tinyformat.h>
18 #include <univalue.h>
19 #include <util/settings.h>
20 #include <util/system.h>
21 
23  : nVersion(json["version"].get_int()), nCreateTime(json["ban_created"].get_int64()),
24  nBanUntil(json["banned_until"].get_int64())
25 {
26 }
27 
29 {
31  json.pushKV("version", nVersion);
32  json.pushKV("ban_created", nCreateTime);
33  json.pushKV("banned_until", nBanUntil);
34  return json;
35 }
36 
37 namespace {
38 
39 static const char* BANMAN_JSON_ADDR_KEY = "address";
40 
47 UniValue BanMapToJson(const banmap_t& bans)
48 {
49  UniValue bans_json(UniValue::VARR);
50  for (const auto& it : bans) {
51  const auto& address = it.first;
52  const auto& ban_entry = it.second;
53  UniValue j = ban_entry.ToJson();
54  j.pushKV(BANMAN_JSON_ADDR_KEY, address.ToString());
55  bans_json.push_back(j);
56  }
57  return bans_json;
58 }
59 
67 void BanMapFromJson(const UniValue& bans_json, banmap_t& bans)
68 {
69  for (const auto& ban_entry_json : bans_json.getValues()) {
70  CSubNet subnet;
71  const auto& subnet_str = ban_entry_json[BANMAN_JSON_ADDR_KEY].get_str();
72  if (!LookupSubNet(subnet_str, subnet)) {
73  throw std::runtime_error(
74  strprintf("Cannot parse banned address or subnet: %s", subnet_str));
75  }
76  bans.insert_or_assign(subnet, CBanEntry{ban_entry_json});
77  }
78 }
79 
80 template <typename Stream, typename Data>
81 bool SerializeDB(Stream& stream, const Data& data)
82 {
83  // Write and commit header, data
84  try {
85  CHashWriter hasher(stream.GetType(), stream.GetVersion());
86  stream << Params().MessageStart() << data;
87  hasher << Params().MessageStart() << data;
88  stream << hasher.GetHash();
89  } catch (const std::exception& e) {
90  return error("%s: Serialize or I/O error - %s", __func__, e.what());
91  }
92 
93  return true;
94 }
95 
96 template <typename Data>
97 bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data, int version)
98 {
99  // Generate random temporary filename
100  uint16_t randv = 0;
101  GetRandBytes((unsigned char*)&randv, sizeof(randv));
102  std::string tmpfn = strprintf("%s.%04x", prefix, randv);
103 
104  // open temp output file, and associate with CAutoFile
105  fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
106  FILE *file = fsbridge::fopen(pathTmp, "wb");
107  CAutoFile fileout(file, SER_DISK, version);
108  if (fileout.IsNull()) {
109  fileout.fclose();
110  remove(pathTmp);
111  return error("%s: Failed to open file %s", __func__, pathTmp.string());
112  }
113 
114  // Serialize
115  if (!SerializeDB(fileout, data)) {
116  fileout.fclose();
117  remove(pathTmp);
118  return false;
119  }
120  if (!FileCommit(fileout.Get())) {
121  fileout.fclose();
122  remove(pathTmp);
123  return error("%s: Failed to flush file %s", __func__, pathTmp.string());
124  }
125  fileout.fclose();
126 
127  // replace existing file, if any, with new file
128  if (!RenameOver(pathTmp, path)) {
129  remove(pathTmp);
130  return error("%s: Rename-into-place failed", __func__);
131  }
132 
133  return true;
134 }
135 
136 template <typename Stream, typename Data>
137 bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
138 {
139  try {
140  CHashVerifier<Stream> verifier(&stream);
141  // de-serialize file header (network specific magic number) and ..
142  unsigned char pchMsgTmp[4];
143  verifier >> pchMsgTmp;
144  // ... verify the network matches ours
145  if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
146  return error("%s: Invalid network magic number", __func__);
147 
148  // de-serialize data
149  verifier >> data;
150 
151  // verify checksum
152  if (fCheckSum) {
153  uint256 hashTmp;
154  stream >> hashTmp;
155  if (hashTmp != verifier.GetHash()) {
156  return error("%s: Checksum mismatch, data corrupted", __func__);
157  }
158  }
159  }
160  catch (const std::exception& e) {
161  return error("%s: Deserialize or I/O error - %s", __func__, e.what());
162  }
163 
164  return true;
165 }
166 
167 template <typename Data>
168 bool DeserializeFileDB(const fs::path& path, Data& data, int version)
169 {
170  // open input file, and associate with CAutoFile
171  FILE* file = fsbridge::fopen(path, "rb");
172  CAutoFile filein(file, SER_DISK, version);
173  if (filein.IsNull()) {
174  LogPrintf("Missing or invalid file %s\n", path.string());
175  return false;
176  }
177  return DeserializeDB(filein, data);
178 }
179 } // namespace
180 
181 CBanDB::CBanDB(fs::path ban_list_path)
182  : m_banlist_dat(ban_list_path.string() + ".dat"),
183  m_banlist_json(ban_list_path.string() + ".json")
184 {
185 }
186 
187 bool CBanDB::Write(const banmap_t& banSet)
188 {
189  std::vector<std::string> errors;
190  if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
191  return true;
192  }
193 
194  for (const auto& err : errors) {
195  error("%s", err);
196  }
197  return false;
198 }
199 
200 bool CBanDB::Read(banmap_t& banSet, bool& dirty)
201 {
202  // If the JSON banlist does not exist, then try to read the non-upgraded banlist.dat.
203  if (!fs::exists(m_banlist_json)) {
204  // If this succeeds then we need to flush to disk in order to create the JSON banlist.
205  dirty = true;
206  return DeserializeFileDB(m_banlist_dat, banSet, CLIENT_VERSION);
207  }
208 
209  dirty = false;
210 
211  std::map<std::string, util::SettingsValue> settings;
212  std::vector<std::string> errors;
213 
214  if (!util::ReadSettings(m_banlist_json, settings, errors)) {
215  for (const auto& err : errors) {
216  LogPrintf("Cannot load banlist %s: %s\n", m_banlist_json.string(), err);
217  }
218  return false;
219  }
220 
221  try {
222  BanMapFromJson(settings[JSON_KEY], banSet);
223  } catch (const std::runtime_error& e) {
224  LogPrintf("Cannot parse banlist %s: %s\n", m_banlist_json.string(), e.what());
225  return false;
226  }
227 
228  return true;
229 }
230 
232 {
233  pathAddr = gArgs.GetDataDirNet() / "peers.dat";
234 }
235 
236 bool CAddrDB::Write(const CAddrMan& addr)
237 {
238  return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
239 }
240 
242 {
243  return DeserializeFileDB(pathAddr, addr, CLIENT_VERSION);
244 }
245 
246 bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
247 {
248  bool ret = DeserializeDB(ssPeers, addr, false);
249  if (!ret) {
250  // Ensure addrman is left in a clean state
251  addr.Clear();
252  }
253  return ret;
254 }
255 
256 void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
257 {
258  LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
259  SerializeFileDB("anchors", anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
260 }
261 
262 std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
263 {
264  std::vector<CAddress> anchors;
265  if (DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT)) {
266  LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
267  } else {
268  anchors.clear();
269  }
270 
271  fs::remove(anchors_db_path);
272  return anchors;
273 }
char const * json() noexcept
Template to generate JSON data.
bool FileCommit(FILE *file)
Ensure file contents are fully committed to disk, using a platform-specific feature analogous to fsyn...
Definition: system.cpp:1089
const std::vector< UniValue > & getValues() const
bool LookupSubNet(const std::string &strSubnet, CSubNet &ret, DNSLookupFn dns_lookup_function)
Parse and resolve a specified subnet string into the appropriate internal representation.
Definition: netbase.cpp:678
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:24
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164
static const char * BANMAN_JSON_ADDR_KEY
Definition: addrdb.cpp:39
CAddrDB()
Definition: addrdb.cpp:231
const char * prefix
Definition: rest.cpp:712
#define LOG_TIME_SECONDS(end_msg)
Definition: timer.h:98
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:204
void Clear() EXCLUSIVE_LOCKS_REQUIRED(!cs)
Definition: addrman.h:464
void GetRandBytes(unsigned char *buf, int num) noexcept
Overall design of the RNG and entropy sources.
Definition: random.cpp:584
bool Read(banmap_t &banSet, bool &dirty)
Read the banlist from disk.
Definition: addrdb.cpp:200
int nVersion
Definition: addrdb.h:25
Reads data from an underlying stream, while hashing the read data.
Definition: hash.h:157
void DumpAnchors(const fs::path &anchors_db_path, const std::vector< CAddress > &anchors)
Dump the anchor IP address database (anchors.dat)
Definition: addrdb.cpp:256
int64_t nCreateTime
Definition: addrdb.h:26
CBanEntry()
Definition: addrdb.h:29
Stochastical (IP) address manager.
Definition: addrman.h:173
static constexpr int ADDRV2_FORMAT
A flag that is ORed into the protocol version to designate that addresses should be serialized in (un...
Definition: netaddress.h:34
bool WriteSettings(const fs::path &path, const std::map< std::string, SettingsValue > &values, std::vector< std::string > &errors)
Write settings file.
Definition: settings.cpp:95
bool push_back(const UniValue &val)
Definition: univalue.cpp:108
bool Write(const CAddrMan &addr)
Definition: addrdb.cpp:236
CBanDB(fs::path ban_list_path)
Definition: addrdb.cpp:181
std::vector< CAddress > ReadAnchors(const fs::path &anchors_db_path)
Read the anchor IP address database (anchors.dat)
Definition: addrdb.cpp:262
const CMessageHeader::MessageStartChars & MessageStart() const
Definition: chainparams.h:83
bool pushKV(const std::string &key, const UniValue &val)
Definition: univalue.cpp:133
static constexpr const char * JSON_KEY
JSON key under which the data is stored in the json database.
Definition: addrdb.h:86
bool Write(const banmap_t &banSet)
Definition: addrdb.cpp:187
int64_t nBanUntil
Definition: addrdb.h:27
bool RenameOver(fs::path src, fs::path dest)
Definition: system.cpp:1059
256-bit opaque blob.
Definition: uint256.h:124
const fs::path m_banlist_dat
Definition: addrdb.h:88
const CChainParams & Params()
Return the currently selected parameters.
fs::path pathAddr
Definition: addrdb.h:71
const fs::path m_banlist_json
Definition: addrdb.h:89
ArgsManager gArgs
Definition: system.cpp:84
A writer stream (for serialization) that computes a 256-bit hash.
Definition: hash.h:100
std::map< CSubNet, CBanEntry > banmap_t
Definition: net_types.h:13
Definition: addrdb.h:21
#define LogPrintf(...)
Definition: logging.h:184
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:33
bool error(const char *fmt, const Args &... args)
Definition: system.h:49
UniValue ToJson() const
Generate a JSON representation of this ban entry.
Definition: addrdb.cpp:28
bool Read(CAddrMan &addr)
Definition: addrdb.cpp:241
Non-refcounted RAII wrapper for FILE*.
Definition: streams.h:564
bool ReadSettings(const fs::path &path, std::map< std::string, SettingsValue > &values, std::vector< std::string > &errors)
Read settings file.
Definition: settings.cpp:58
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: system.h:282