LCOV - code coverage report
Current view: top level - src - addrdb.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 15 128 11.7 %
Date: 2023-09-26 12:08:55 Functions: 4 21 19.0 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2022 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 <common/args.h>
      12             : #include <common/settings.h>
      13             : #include <cstdint>
      14             : #include <hash.h>
      15             : #include <logging.h>
      16             : #include <logging/timer.h>
      17             : #include <netbase.h>
      18             : #include <netgroup.h>
      19             : #include <random.h>
      20             : #include <streams.h>
      21             : #include <tinyformat.h>
      22             : #include <univalue.h>
      23             : #include <util/fs.h>
      24             : #include <util/fs_helpers.h>
      25             : #include <util/translation.h>
      26             : 
      27             : namespace {
      28             : 
      29             : class DbNotFoundError : public std::exception
      30             : {
      31             :     using std::exception::exception;
      32             : };
      33             : 
      34             : template <typename Stream, typename Data>
      35           0 : bool SerializeDB(Stream& stream, const Data& data)
      36             : {
      37             :     // Write and commit header, data
      38             :     try {
      39           0 :         HashedSourceWriter hashwriter{stream};
      40           0 :         hashwriter << Params().MessageStart() << data;
      41           0 :         stream << hashwriter.GetHash();
      42           0 :     } catch (const std::exception& e) {
      43           0 :         return error("%s: Serialize or I/O error - %s", __func__, e.what());
      44           0 :     }
      45             : 
      46           0 :     return true;
      47           0 : }
      48             : 
      49             : template <typename Data>
      50           0 : bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
      51             : {
      52             :     // Generate random temporary filename
      53           0 :     const uint16_t randv{GetRand<uint16_t>()};
      54           0 :     std::string tmpfn = strprintf("%s.%04x", prefix, randv);
      55             : 
      56             :     // open temp output file
      57           0 :     fs::path pathTmp = gArgs.GetDataDirNet() / fs::u8path(tmpfn);
      58           0 :     FILE *file = fsbridge::fopen(pathTmp, "wb");
      59           0 :     AutoFile fileout{file};
      60           0 :     if (fileout.IsNull()) {
      61           0 :         fileout.fclose();
      62           0 :         remove(pathTmp);
      63           0 :         return error("%s: Failed to open file %s", __func__, fs::PathToString(pathTmp));
      64             :     }
      65             : 
      66             :     // Serialize
      67           0 :     if (!SerializeDB(fileout, data)) {
      68           0 :         fileout.fclose();
      69           0 :         remove(pathTmp);
      70           0 :         return false;
      71             :     }
      72           0 :     if (!FileCommit(fileout.Get())) {
      73           0 :         fileout.fclose();
      74           2 :         remove(pathTmp);
      75           0 :         return error("%s: Failed to flush file %s", __func__, fs::PathToString(pathTmp));
      76             :     }
      77           0 :     fileout.fclose();
      78             : 
      79             :     // replace existing file, if any, with new file
      80           0 :     if (!RenameOver(pathTmp, path)) {
      81           0 :         remove(pathTmp);
      82           0 :         return error("%s: Rename-into-place failed", __func__);
      83             :     }
      84             : 
      85           0 :     return true;
      86           0 : }
      87             : 
      88             : template <typename Stream, typename Data>
      89           0 : void DeserializeDB(Stream& stream, Data&& data, bool fCheckSum = true)
      90             : {
      91           0 :     HashVerifier verifier{stream};
      92             :     // de-serialize file header (network specific magic number) and ..
      93             :     MessageStartChars pchMsgTmp;
      94           0 :     verifier >> pchMsgTmp;
      95             :     // ... verify the network matches ours
      96           0 :     if (pchMsgTmp != Params().MessageStart()) {
      97           0 :         throw std::runtime_error{"Invalid network magic number"};
      98             :     }
      99             : 
     100             :     // de-serialize data
     101           0 :     verifier >> data;
     102             : 
     103             :     // verify checksum
     104           0 :     if (fCheckSum) {
     105           0 :         uint256 hashTmp;
     106           0 :         stream >> hashTmp;
     107           0 :         if (hashTmp != verifier.GetHash()) {
     108           0 :             throw std::runtime_error{"Checksum mismatch, data corrupted"};
     109             :         }
     110           0 :     }
     111           0 : }
     112             : 
     113             : template <typename Data>
     114           0 : void DeserializeFileDB(const fs::path& path, Data&& data)
     115             : {
     116           0 :     FILE* file = fsbridge::fopen(path, "rb");
     117           0 :     AutoFile filein{file};
     118           0 :     if (filein.IsNull()) {
     119           0 :         throw DbNotFoundError{};
     120             :     }
     121           0 :     DeserializeDB(filein, data);
     122           0 : }
     123             : } // namespace
     124             : 
     125           1 : CBanDB::CBanDB(fs::path ban_list_path)
     126           1 :     : m_banlist_dat(ban_list_path + ".dat"),
     127           1 :       m_banlist_json(ban_list_path + ".json")
     128             : {
     129           1 : }
     130             : 
     131           1 : bool CBanDB::Write(const banmap_t& banSet)
     132             : {
     133           1 :     std::vector<std::string> errors;
     134           1 :     if (common::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
     135           1 :         return true;
     136             :     }
     137             : 
     138           0 :     for (const auto& err : errors) {
     139           0 :         error("%s", err);
     140             :     }
     141           0 :     return false;
     142           1 : }
     143             : 
     144           1 : bool CBanDB::Read(banmap_t& banSet)
     145             : {
     146           1 :     if (fs::exists(m_banlist_dat)) {
     147           0 :         LogPrintf("banlist.dat ignored because it can only be read by " PACKAGE_NAME " version 22.x. Remove %s to silence this warning.\n", fs::quoted(fs::PathToString(m_banlist_dat)));
     148           0 :     }
     149             :     // If the JSON banlist does not exist, then recreate it
     150           1 :     if (!fs::exists(m_banlist_json)) {
     151           1 :         return false;
     152             :     }
     153             : 
     154           0 :     std::map<std::string, common::SettingsValue> settings;
     155           0 :     std::vector<std::string> errors;
     156             : 
     157           0 :     if (!common::ReadSettings(m_banlist_json, settings, errors)) {
     158           0 :         for (const auto& err : errors) {
     159           0 :             LogPrintf("Cannot load banlist %s: %s\n", fs::PathToString(m_banlist_json), err);
     160             :         }
     161           0 :         return false;
     162             :     }
     163             : 
     164             :     try {
     165           0 :         BanMapFromJson(settings[JSON_KEY], banSet);
     166           0 :     } catch (const std::runtime_error& e) {
     167           0 :         LogPrintf("Cannot parse banlist %s: %s\n", fs::PathToString(m_banlist_json), e.what());
     168           0 :         return false;
     169           0 :     }
     170             : 
     171           0 :     return true;
     172           1 : }
     173             : 
     174           0 : bool DumpPeerAddresses(const ArgsManager& args, const AddrMan& addr)
     175             : {
     176           0 :     const auto pathAddr = args.GetDataDirNet() / "peers.dat";
     177           0 :     return SerializeFileDB("peers", pathAddr, addr);
     178           0 : }
     179             : 
     180           0 : void ReadFromStream(AddrMan& addr, DataStream& ssPeers)
     181             : {
     182           0 :     DeserializeDB(ssPeers, addr, false);
     183           0 : }
     184             : 
     185           0 : util::Result<std::unique_ptr<AddrMan>> LoadAddrman(const NetGroupManager& netgroupman, const ArgsManager& args)
     186             : {
     187           0 :     auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
     188           0 :     auto addrman{std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman)};
     189             : 
     190           0 :     const auto start{SteadyClock::now()};
     191           0 :     const auto path_addr{args.GetDataDirNet() / "peers.dat"};
     192             :     try {
     193           0 :         DeserializeFileDB(path_addr, *addrman);
     194           0 :         LogPrintf("Loaded %i addresses from peers.dat  %dms\n", addrman->Size(), Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
     195           0 :     } catch (const DbNotFoundError&) {
     196             :         // Addrman can be in an inconsistent state after failure, reset it
     197           0 :         addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
     198           0 :         LogPrintf("Creating peers.dat because the file was not found (%s)\n", fs::quoted(fs::PathToString(path_addr)));
     199           0 :         DumpPeerAddresses(args, *addrman);
     200           0 :     } catch (const InvalidAddrManVersionError&) {
     201           0 :         if (!RenameOver(path_addr, (fs::path)path_addr + ".bak")) {
     202           0 :             return util::Error{strprintf(_("Failed to rename invalid peers.dat file. Please move or delete it and try again."))};
     203             :         }
     204             :         // Addrman can be in an inconsistent state after failure, reset it
     205           0 :         addrman = std::make_unique<AddrMan>(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman);
     206           0 :         LogPrintf("Creating new peers.dat because the file version was not compatible (%s). Original backed up to peers.dat.bak\n", fs::quoted(fs::PathToString(path_addr)));
     207           0 :         DumpPeerAddresses(args, *addrman);
     208           0 :     } catch (const std::exception& e) {
     209           0 :         return util::Error{strprintf(_("Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start."),
     210           0 :                                      e.what(), PACKAGE_BUGREPORT, fs::quoted(fs::PathToString(path_addr)))};
     211           0 :     }
     212           0 :     return {std::move(addrman)}; // std::move should be unnecessary but is temporarily needed to work around clang bug
     213             :                                  // (https://github.com/bitcoin/bitcoin/pull/25977#issuecomment-1561270092)
     214           0 : }
     215             : 
     216           0 : void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
     217             : {
     218           0 :     LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
     219           0 :     SerializeFileDB("anchors", anchors_db_path, CAddress::V2_DISK(anchors));
     220           0 : }
     221             : 
     222           0 : std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
     223             : {
     224           0 :     std::vector<CAddress> anchors;
     225             :     try {
     226           0 :         DeserializeFileDB(anchors_db_path, CAddress::V2_DISK(anchors));
     227           0 :         LogPrintf("Loaded %i addresses from %s\n", anchors.size(), fs::quoted(fs::PathToString(anchors_db_path.filename())));
     228           0 :     } catch (const std::exception&) {
     229           0 :         anchors.clear();
     230           0 :     }
     231             : 
     232           0 :     fs::remove(anchors_db_path);
     233           0 :     return anchors;
     234           0 : }

Generated by: LCOV version 1.14