Changeset View
Changeset View
Standalone View
Standalone View
src/addrdb.cpp
Show All 9 Lines | |||||
#include <clientversion.h> | #include <clientversion.h> | ||||
#include <fs.h> | #include <fs.h> | ||||
#include <hash.h> | #include <hash.h> | ||||
#include <random.h> | #include <random.h> | ||||
#include <streams.h> | #include <streams.h> | ||||
#include <tinyformat.h> | #include <tinyformat.h> | ||||
#include <util.h> | #include <util.h> | ||||
CBanDB::CBanDB(const CChainParams &chainParamsIn) : chainParams(chainParamsIn) { | namespace { | ||||
pathBanlist = GetDataDir() / "banlist.dat"; | |||||
template <typename Stream, typename Data> | |||||
bool SerializeDB(const CChainParams &chainParams, Stream &stream, | |||||
const Data &data) { | |||||
// Write and commit header, data | |||||
try { | |||||
CHashWriter hasher(SER_DISK, CLIENT_VERSION); | |||||
stream << FLATDATA(chainParams.DiskMagic()) << data; | |||||
hasher << FLATDATA(chainParams.DiskMagic()) << data; | |||||
stream << hasher.GetHash(); | |||||
} catch (const std::exception &e) { | |||||
return error("%s: Serialize or I/O error - %s", __func__, e.what()); | |||||
} | } | ||||
bool CBanDB::Write(const banmap_t &banSet) { | return true; | ||||
} | |||||
template <typename Data> | |||||
bool SerializeFileDB(const CChainParams &chainParams, const std::string &prefix, | |||||
const fs::path &path, const Data &data) { | |||||
// Generate random temporary filename | // Generate random temporary filename | ||||
unsigned short randv = 0; | unsigned short randv = 0; | ||||
GetRandBytes((uint8_t *)&randv, sizeof(randv)); | GetRandBytes((uint8_t *)&randv, sizeof(randv)); | ||||
std::string tmpfn = strprintf("banlist.dat.%04x", randv); | std::string tmpfn = strprintf("%s.%04x", prefix, randv); | ||||
// serialize banlist, checksum data up to that point, then append csum | |||||
CDataStream ssBanlist(SER_DISK, CLIENT_VERSION); | |||||
ssBanlist << FLATDATA(chainParams.DiskMagic()); | |||||
ssBanlist << banSet; | |||||
uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end()); | |||||
ssBanlist << hash; | |||||
// open temp output file, and associate with CAutoFile | // open temp output file, and associate with CAutoFile | ||||
fs::path pathTmp = GetDataDir() / tmpfn; | fs::path pathTmp = GetDataDir() / tmpfn; | ||||
FILE *file = fsbridge::fopen(pathTmp, "wb"); | FILE *file = fsbridge::fopen(pathTmp, "wb"); | ||||
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); | CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); | ||||
if (fileout.IsNull()) | if (fileout.IsNull()) { | ||||
return error("%s: Failed to open file %s", __func__, pathTmp.string()); | return error("%s: Failed to open file %s", __func__, pathTmp.string()); | ||||
} | |||||
// Write and commit header, data | // Serialize | ||||
try { | if (!SerializeDB(chainParams, fileout, data)) { | ||||
fileout << ssBanlist; | return false; | ||||
} catch (const std::exception &e) { | |||||
return error("%s: Serialize or I/O error - %s", __func__, e.what()); | |||||
} | } | ||||
FileCommit(fileout.Get()); | FileCommit(fileout.Get()); | ||||
fileout.fclose(); | fileout.fclose(); | ||||
// replace existing banlist.dat, if any, with new banlist.dat.XXXX | // replace existing file, if any, with new file | ||||
if (!RenameOver(pathTmp, pathBanlist)) | if (!RenameOver(pathTmp, path)) { | ||||
return error("%s: Rename-into-place failed", __func__); | return error("%s: Rename-into-place failed", __func__); | ||||
return true; | |||||
} | } | ||||
bool CBanDB::Read(banmap_t &banSet) { | return true; | ||||
// open input file, and associate with CAutoFile | |||||
FILE *file = fsbridge::fopen(pathBanlist, "rb"); | |||||
CAutoFile filein(file, SER_DISK, CLIENT_VERSION); | |||||
if (filein.IsNull()) | |||||
return error("%s: Failed to open file %s", __func__, | |||||
pathBanlist.string()); | |||||
// use file size to size memory buffer | |||||
uint64_t fileSize = fs::file_size(pathBanlist); | |||||
uint64_t dataSize = 0; | |||||
// Don't try to resize to a negative number if file is small | |||||
if (fileSize >= sizeof(uint256)) dataSize = fileSize - sizeof(uint256); | |||||
std::vector<uint8_t> vchData; | |||||
vchData.resize(dataSize); | |||||
uint256 hashIn; | |||||
// read data and checksum from file | |||||
try { | |||||
filein.read((char *)&vchData[0], dataSize); | |||||
filein >> hashIn; | |||||
} catch (const std::exception &e) { | |||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what()); | |||||
} | } | ||||
filein.fclose(); | |||||
CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION); | |||||
// verify stored checksum matches input data | template <typename Stream, typename Data> | ||||
uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.end()); | bool DeserializeDB(const CChainParams &chainParams, Stream &stream, Data &data, | ||||
if (hashIn != hashTmp) | bool fCheckSum = true) { | ||||
return error("%s: Checksum mismatch, data corrupted", __func__); | |||||
uint8_t pchMsgTmp[4]; | |||||
try { | try { | ||||
CHashVerifier<Stream> verifier(&stream); | |||||
// de-serialize file header (network specific magic number) and .. | // de-serialize file header (network specific magic number) and .. | ||||
ssBanlist >> FLATDATA(pchMsgTmp); | uint8_t pchMsgTmp[4]; | ||||
verifier >> FLATDATA(pchMsgTmp); | |||||
// ... verify the network matches ours | // ... verify the network matches ours | ||||
if (memcmp(pchMsgTmp, std::begin(chainParams.DiskMagic()), | if (memcmp(pchMsgTmp, std::begin(chainParams.DiskMagic()), | ||||
sizeof(pchMsgTmp))) { | sizeof(pchMsgTmp))) { | ||||
return error("%s: Invalid network magic number", __func__); | return error("%s: Invalid network magic number", __func__); | ||||
} | } | ||||
// de-serialize ban data | // de-serialize data | ||||
ssBanlist >> banSet; | verifier >> data; | ||||
} catch (const std::exception &e) { | |||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what()); | |||||
} | |||||
return true; | // verify checksum | ||||
if (fCheckSum) { | |||||
uint256 hashTmp; | |||||
stream >> hashTmp; | |||||
if (hashTmp != verifier.GetHash()) { | |||||
return error("%s: Checksum mismatch, data corrupted", __func__); | |||||
} | } | ||||
CAddrDB::CAddrDB(const CChainParams &chainParamsIn) | |||||
: chainParams(chainParamsIn) { | |||||
pathAddr = GetDataDir() / "peers.dat"; | |||||
} | } | ||||
bool CAddrDB::Write(const CAddrMan &addr) { | |||||
// Generate random temporary filename | |||||
unsigned short randv = 0; | |||||
GetRandBytes((uint8_t *)&randv, sizeof(randv)); | |||||
std::string tmpfn = strprintf("peers.dat.%04x", randv); | |||||
// serialize addresses, checksum data up to that point, then append csum | |||||
CDataStream ssPeers(SER_DISK, CLIENT_VERSION); | |||||
ssPeers << FLATDATA(chainParams.DiskMagic()); | |||||
ssPeers << addr; | |||||
uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); | |||||
ssPeers << hash; | |||||
// open temp output file, and associate with CAutoFile | |||||
fs::path pathTmp = GetDataDir() / tmpfn; | |||||
FILE *file = fsbridge::fopen(pathTmp, "wb"); | |||||
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); | |||||
if (fileout.IsNull()) | |||||
return error("%s: Failed to open file %s", __func__, pathTmp.string()); | |||||
// Write and commit header, data | |||||
try { | |||||
fileout << ssPeers; | |||||
} catch (const std::exception &e) { | } catch (const std::exception &e) { | ||||
return error("%s: Serialize or I/O error - %s", __func__, e.what()); | return error("%s: Deserialize or I/O error - %s", __func__, e.what()); | ||||
} | } | ||||
FileCommit(fileout.Get()); | |||||
fileout.fclose(); | |||||
// replace existing peers.dat, if any, with new peers.dat.XXXX | |||||
if (!RenameOver(pathTmp, pathAddr)) | |||||
return error("%s: Rename-into-place failed", __func__); | |||||
return true; | return true; | ||||
} | } | ||||
bool CAddrDB::Read(CAddrMan &addr) { | template <typename Data> | ||||
bool DeserializeFileDB(const CChainParams &chainParams, const fs::path &path, | |||||
Data &data) { | |||||
// open input file, and associate with CAutoFile | // open input file, and associate with CAutoFile | ||||
FILE *file = fsbridge::fopen(pathAddr, "rb"); | FILE *file = fsbridge::fopen(path, "rb"); | ||||
CAutoFile filein(file, SER_DISK, CLIENT_VERSION); | CAutoFile filein(file, SER_DISK, CLIENT_VERSION); | ||||
if (filein.IsNull()) | if (filein.IsNull()) { | ||||
return error("%s: Failed to open file %s", __func__, pathAddr.string()); | return error("%s: Failed to open file %s", __func__, path.string()); | ||||
} | |||||
// use file size to size memory buffer | return DeserializeDB(chainParams, filein, data); | ||||
uint64_t fileSize = fs::file_size(pathAddr); | } | ||||
uint64_t dataSize = 0; | |||||
// Don't try to resize to a negative number if file is small | |||||
if (fileSize >= sizeof(uint256)) dataSize = fileSize - sizeof(uint256); | |||||
std::vector<uint8_t> vchData; | |||||
vchData.resize(dataSize); | |||||
uint256 hashIn; | |||||
// read data and checksum from file | |||||
try { | |||||
filein.read((char *)&vchData[0], dataSize); | |||||
filein >> hashIn; | |||||
} catch (const std::exception &e) { | |||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what()); | |||||
} | } | ||||
filein.fclose(); | |||||
CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); | CBanDB::CBanDB(const CChainParams &chainParamsIn) : chainParams(chainParamsIn) { | ||||
pathBanlist = GetDataDir() / "banlist.dat"; | |||||
} | |||||
// verify stored checksum matches input data | bool CBanDB::Write(const banmap_t &banSet) { | ||||
uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); | return SerializeFileDB(chainParams, "banlist", pathBanlist, banSet); | ||||
if (hashIn != hashTmp) | } | ||||
return error("%s: Checksum mismatch, data corrupted", __func__); | |||||
return Read(addr, ssPeers); | bool CBanDB::Read(banmap_t &banSet) { | ||||
return DeserializeFileDB(chainParams, pathBanlist, banSet); | |||||
} | } | ||||
bool CAddrDB::Read(CAddrMan &addr, CDataStream &ssPeers) { | CAddrDB::CAddrDB(const CChainParams &chainParamsIn) | ||||
uint8_t pchMsgTmp[4]; | : chainParams(chainParamsIn) { | ||||
try { | pathAddr = GetDataDir() / "peers.dat"; | ||||
// de-serialize file header (network specific magic number) and .. | } | ||||
ssPeers >> FLATDATA(pchMsgTmp); | |||||
// ... verify the network matches ours | bool CAddrDB::Write(const CAddrMan &addr) { | ||||
if (memcmp(pchMsgTmp, std::begin(chainParams.DiskMagic()), | return SerializeFileDB(chainParams, "peers", pathAddr, addr); | ||||
sizeof(pchMsgTmp))) { | |||||
return error("%s: Invalid network magic number", __func__); | |||||
} | } | ||||
// de-serialize address data into one CAddrMan object | bool CAddrDB::Read(CAddrMan &addr) { | ||||
ssPeers >> addr; | return DeserializeFileDB(chainParams, pathAddr, addr); | ||||
} catch (const std::exception &e) { | |||||
// de-serialization has failed, ensure addrman is left in a clean state | |||||
addr.Clear(); | |||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what()); | |||||
} | } | ||||
return true; | bool CAddrDB::Read(CAddrMan &addr, CDataStream &ssPeers) { | ||||
bool ret = DeserializeDB(chainParams, ssPeers, addr, false); | |||||
if (!ret) { | |||||
// Ensure addrman is left in a clean state | |||||
addr.Clear(); | |||||
} | |||||
return ret; | |||||
} | } |