Skip to content

Commit

Permalink
Make (Read/Write)BinaryFile work with char vector
Browse files Browse the repository at this point in the history
ReadBinaryFile and WriteBinaryFile current work with std::string. This commit adds support for std::vector<unsigned char>>.

It uses a template and leverages the fact that both std::string and std::vector<unsigned char>> have an insert() method that can take a char array.
  • Loading branch information
Sjors committed Jan 11, 2024
1 parent 4ae5171 commit 634f29f
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 36 deletions.
3 changes: 2 additions & 1 deletion src/i2p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,8 @@ void Session::CreateIfNotCreatedAlready()
} else {
// Read our persistent destination (private key) from disk or generate
// one and save it to disk. Then use it when creating the session.
const auto& [read_ok, data] = ReadBinaryFile(m_private_key_file);
std::string data;
bool read_ok = ReadBinaryFile(m_private_key_file, data);
if (read_ok) {
m_private_key.assign(data.begin(), data.end());
} else {
Expand Down
44 changes: 35 additions & 9 deletions src/test/util_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1759,36 +1759,62 @@ BOOST_AUTO_TEST_CASE(util_ReadBinaryFile)
}
{
// read all contents in file
auto [valid, text] = ReadBinaryFile(tmpfile);
std::string text;
bool valid = ReadBinaryFile(tmpfile, text);
BOOST_CHECK(valid);
BOOST_CHECK_EQUAL(text, expected_text);
}
{
// read half contents in file
auto [valid, text] = ReadBinaryFile(tmpfile, expected_text.size() / 2);
std::string text;
bool valid = ReadBinaryFile(tmpfile, text, expected_text.size() / 2);
BOOST_CHECK(valid);
BOOST_CHECK_EQUAL(text, expected_text.substr(0, expected_text.size() / 2));
}
{
// read from non-existent file
std::string text;
fs::path invalid_file = tmpfolder / "invalid_binary.dat";
auto [valid, text] = ReadBinaryFile(invalid_file);
bool valid = ReadBinaryFile(invalid_file, text);
BOOST_CHECK(!valid);
BOOST_CHECK(text.empty());
}
{
std::vector<unsigned char> expected_data;
for (int i = 0; i < 30; i++) {
// store result as bytes (ASCII "0" is character 48)
expected_data.insert(expected_data.end(), {48,49,50,51,52,53,54,55,56,57});
}
std::vector<unsigned char> data;
bool valid = ReadBinaryFile(tmpfile, data);
BOOST_CHECK(valid);
BOOST_REQUIRE(data == expected_data);
}
}

BOOST_AUTO_TEST_CASE(util_WriteBinaryFile)
{
fs::path tmpfolder = m_args.GetDataDirBase();
fs::path tmpfile = tmpfolder / "write_binary.dat";
std::string expected_text = "bitcoin";
auto valid = WriteBinaryFile(tmpfile, expected_text);
std::string actual_text;
std::ifstream file{tmpfile};
file >> actual_text;
BOOST_CHECK(valid);
BOOST_CHECK_EQUAL(actual_text, expected_text);
{
auto valid = WriteBinaryFile(tmpfile, expected_text);
std::string actual_text;
std::ifstream file{tmpfile};
file >> actual_text;
BOOST_CHECK(valid);
BOOST_CHECK_EQUAL(actual_text, expected_text);
}
{
std::vector<unsigned char> bytes{98, 105, 116, 99, 111, 105, 110}; // "bitcoin"
auto valid = WriteBinaryFile(tmpfile, bytes);
std::string actual_text;
std::ifstream file{tmpfile};
file >> actual_text;
BOOST_CHECK(valid);
BOOST_CHECK_EQUAL(actual_text, expected_text);
}

}

BOOST_AUTO_TEST_CASE(clearshrink_test)
Expand Down
19 changes: 11 additions & 8 deletions src/torcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,10 +328,12 @@ TorController::TorController(struct event_base* _base, const std::string& tor_co
LogPrintf("tor: Initiating connection to Tor control port %s failed\n", m_tor_control_center);
}
// Read service private key if cached
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
if (pkf.first) {
std::string data;
bool read_ok = ReadBinaryFile(GetPrivateKeyFile(), data);

if (read_ok) {
LogPrint(BCLog::TOR, "Reading cached private key from %s\n", fs::PathToString(GetPrivateKeyFile()));
private_key = pkf.second;
private_key = data;
}
}

Expand Down Expand Up @@ -586,15 +588,16 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro
} else if (methods.count("SAFECOOKIE")) {
// Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie
LogPrint(BCLog::TOR, "Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile);
std::pair<bool,std::string> status_cookie = ReadBinaryFile(fs::PathFromString(cookiefile), TOR_COOKIE_SIZE);
if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) {
// _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end());
std::string status_cookie;
bool read_ok = ReadBinaryFile(fs::PathFromString(cookiefile), status_cookie, TOR_COOKIE_SIZE);
if (read_ok && status_cookie.size() == TOR_COOKIE_SIZE) {
// _conn.Command("AUTHENTICATE " + HexStr(status_cookie), std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2));
cookie = std::vector<uint8_t>(status_cookie.begin(), status_cookie.end());
clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0);
GetRandBytes(clientNonce);
_conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), std::bind(&TorController::authchallenge_cb, this, std::placeholders::_1, std::placeholders::_2));
} else {
if (status_cookie.first) {
if (read_ok) {
LogPrintf("tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile, TOR_COOKIE_SIZE);
} else {
LogPrintf("tor: Authentication cookie %s could not be opened (check permissions)\n", cookiefile);
Expand Down
27 changes: 17 additions & 10 deletions src/util/readwritefile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,32 @@
#include <string>
#include <utility>

std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize)
template <typename T>
bool ReadBinaryFile(const fs::path& filename, T& output, size_t maxsize)
{
FILE *f = fsbridge::fopen(filename, "rb");
if (f == nullptr)
return std::make_pair(false,"");
std::string retval;
if (f == nullptr) return false;
char buffer[128];
do {
const size_t n = fread(buffer, 1, std::min(sizeof(buffer), maxsize - retval.size()), f);
const size_t n = fread(buffer, 1, std::min(sizeof(buffer), maxsize - output.size()), f);
// Check for reading errors so we don't return any data if we couldn't
// read the entire file (or up to maxsize)
if (ferror(f)) {
fclose(f);
return std::make_pair(false,"");
output = T{};
return false;
}
retval.append(buffer, buffer+n);
} while (!feof(f) && retval.size() < maxsize);
output.insert(output.end(), buffer, buffer + n);
} while (!feof(f) && output.size() < maxsize);
fclose(f);
return std::make_pair(true,retval);
return true;
}

bool WriteBinaryFile(const fs::path &filename, const std::string &data)
template bool ReadBinaryFile<std::string>(const fs::path &filename, std::string& output, size_t maxsize);
template bool ReadBinaryFile<std::vector<unsigned char>>(const fs::path &filename, std::vector<unsigned char>& output, size_t maxsize);

template <typename T>
bool WriteBinaryFile(const fs::path& filename, const T& data)
{
FILE *f = fsbridge::fopen(filename, "wb");
if (f == nullptr)
Expand All @@ -48,3 +52,6 @@ bool WriteBinaryFile(const fs::path &filename, const std::string &data)
}
return true;
}

template bool WriteBinaryFile<std::string>(const fs::path& filename, const std::string& data);
template bool WriteBinaryFile<std::vector<unsigned char>>(const fs::path& filename, const std::vector<unsigned char>& data);
22 changes: 14 additions & 8 deletions src/util/readwritefile.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,24 @@
#include <string>
#include <utility>

/** Read full contents of a file and return them in a std::string.
* Returns a pair <status, string>.
* If an error occurred, status will be false, otherwise status will be true and the data will be returned in string.
/**
* Read full contents of a file and return one of the following formats:
* 1. std::vector<unsigned char>
* 2. std::string
*
* @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
* (with len > maxsize) will be returned.
* @param[in] filename Filename. Returns false it doesn't exist.
* @param[out] output Result
* @param[in] maxsize Puts a maximum size limit on the file that is read. If the file
* is larger than this, truncated data (with len > maxsize) will be returned.
* @return true if successful, false if an error occured.
*/
std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max());
template <typename T>
bool ReadBinaryFile(const fs::path& filename, T& output, size_t maxsize=std::numeric_limits<size_t>::max());

/** Write contents of std::string to a file.
/** Write contents of std::string or std::vector<unsigned char> to a file.
* @return true on success.
*/
bool WriteBinaryFile(const fs::path &filename, const std::string &data);
template <typename T>
bool WriteBinaryFile(const fs::path& filename, const T& data);

#endif // BITCOIN_UTIL_READWRITEFILE_H

0 comments on commit 634f29f

Please sign in to comment.