Unverified Commit ece38d7b authored by anonimal's avatar anonimal

ClientContext: local destinations rewrite + tests

parent ad0a51ab
This diff is collapsed.
/** //
* Copyright (c) 2013-2017, The Kovri I2P Router Project //
* Copyright (c) 2015-2019, The Kovri I2P Router Project //
* //
* All rights reserved. //
* //
......@@ -48,12 +48,83 @@
#include "core/util/exception.h"
namespace kovri {
namespace client {
namespace kovri
{
namespace client
{
/// @brief Class to manage context local destinations
/// @param t_destination Destination type, currently applicable to *client lib* local destinations
/// @notes The template could be useful for other types of future context (not just client) destination handling
template <typename t_destination>
class LocalDestinations final
{
public:
LocalDestinations() = default;
~LocalDestinations() = default;
LocalDestinations(const LocalDestinations&) = delete;
LocalDestinations& operator=(const LocalDestinations&) = delete;
LocalDestinations(LocalDestinations&&) = delete;
LocalDestinations& operator=(LocalDestinations&&) = delete;
/// @brief Create and store a new local destination
/// @param keys Keypair to be used for destination
/// @param is_public Is this a public destination (for public lease-set, such as for server tunnel)
/// @param params Destination parameters, currently I2CP parameters
/// @notes Automatically starts the destination
std::shared_ptr<t_destination> Create(
const core::PrivateKeys& keys = core::PrivateKeys::CreateRandomKeys(),
const bool is_public = false,
const std::map<std::string, std::string>* params = nullptr);
/// @brief Load and store a new local destination
/// @param filename Filename (without path)
/// @param is_public Is this a public destination (for public lease-set, such as for server tunnel)
/// @notes Automatically starts the destination
std::shared_ptr<t_destination> LoadFromFile(
const std::string& filename,
const bool is_public = false);
/// @brief Find local destination
std::shared_ptr<t_destination> Find(
std::shared_ptr<t_destination> destination);
/// @brief Find local destination by public hash
std::shared_ptr<t_destination> Find(const core::IdentHash& ident);
/// @brief Delete local destination
/// @return True if deleted
/// @note Also stops destination
bool Delete(std::shared_ptr<t_destination> destination);
/// @brief Delete local destination by public hash
/// @return True if deleted
/// @note Also stops destination
bool Delete(const core::IdentHash& ident);
/// @brief Start *all* object destinations
void Start();
/// @brief Stop *all* object destinations
void Stop();
private:
std::mutex m_Mutex;
std::map<core::IdentHash, std::shared_ptr<t_destination>> m_Destinations;
};
class ClientContext {
class ClientContext final
{
public:
ClientContext();
~ClientContext() = default;
ClientContext(const ClientContext&) = delete;
ClientContext& operator=(const ClientContext&) = delete;
ClientContext(ClientContext&&) = delete;
ClientContext& operator=(ClientContext&&) = delete;
void Start();
void Stop();
......@@ -71,41 +142,6 @@ class ClientContext {
return m_SharedLocalDestination;
}
// Non-public
std::shared_ptr<ClientDestination> CreateNewLocalDestination(
kovri::core::SigningKeyType sig_type =
kovri::core::SigningKeyType::DefaultClient,
const bool is_public = false,
const std::map<std::string, std::string>* params = nullptr); // transient
// Public
std::shared_ptr<ClientDestination> CreateNewLocalDestination(
const kovri::core::PrivateKeys& keys,
const bool is_public = true,
const std::map<std::string, std::string>* params = nullptr);
void DeleteLocalDestination(
std::shared_ptr<ClientDestination> destination);
std::shared_ptr<ClientDestination> FindLocalDestination(
const kovri::core::IdentHash& destination) const;
/// @brief Creates private keys from given filename if they don't exist
/// @param filename the relative name of the private key file
/// @return Created private keys
kovri::core::PrivateKeys CreatePrivateKeys(
const std::string& filename);
/// @brief Loads private keys from given filename
/// @param filename Relative name of the private key file
/// @return Loaded private keys
kovri::core::PrivateKeys LoadPrivateKeys(
const std::string& filename);
std::shared_ptr<ClientDestination> LoadLocalDestination(
const std::string& filename,
bool is_public);
AddressBook& GetAddressBook() {
return m_AddressBook;
}
......@@ -192,18 +228,17 @@ class ClientContext {
boost::asio::io_service& GetIoService();
private:
/// @brief Creates text file containing private key's public base address
/// @param keys Private keys to derive public ident (and base encoded hash) from
/// @param filename The root name of the file (i.e., website-keys = website-keys.b32.txt, website-keys.b64.txt)
void CreateDestTextFiles(
const kovri::core::PrivateKeys& keys,
const std::string& filename);
/// @return Reference to local I2P destinations for tunnel usage
auto& LocalDestinations()
{
return m_LocalDestinations;
}
private:
std::mutex m_DestinationsMutex;
std::map<kovri::core::IdentHash, std::shared_ptr<ClientDestination>> m_Destinations;
std::shared_ptr<ClientDestination> m_SharedLocalDestination;
client::LocalDestinations<client::ClientDestination> m_LocalDestinations;
// Private (not published)
std::shared_ptr<client::ClientDestination> m_SharedLocalDestination;
AddressBook m_AddressBook;
......
/** //
* Copyright (c) 2015-2018, The Kovri I2P Router Project //
* Copyright (c) 2015-2019, The Kovri I2P Router Project //
* //
* All rights reserved. //
* //
......@@ -86,7 +86,7 @@ void Instance::Initialize()
auto const proxy_keys = map["proxykeys"].as<std::string>();
if (!proxy_keys.empty())
local_destination = context.LoadLocalDestination(proxy_keys, false);
local_destination = context.LocalDestinations().LoadFromFile(proxy_keys);
context.SetHTTPProxy(std::make_unique<HTTPProxy>(
"HTTP Proxy", // TODO(unassigned): what if we want to change the name?
......
/** //
* Copyright (c) 2013-2017, The Kovri I2P Router Project //
* Copyright (c) 2015-2019, The Kovri I2P Router Project //
* //
* All rights reserved. //
* //
......@@ -34,20 +34,24 @@
#include "client/context.h"
namespace kovri {
namespace client {
I2PService::I2PService(
std::shared_ptr<ClientDestination> local_destination)
namespace kovri
{
namespace client
{
I2PService::I2PService(std::shared_ptr<ClientDestination> local_destination)
: m_LocalDestination(
local_destination
? local_destination
: kovri::client::context.CreateNewLocalDestination()) {}
local_destination ? local_destination
: client::context.LocalDestinations().Create())
{
}
I2PService::I2PService(
kovri::core::SigningKeyType key_type)
: m_LocalDestination(
kovri::client::context.CreateNewLocalDestination(key_type)) {}
// TODO(unassigned): key type is currently useless since key types were
// clobbered into a single *non-optional* type for client key sigining
I2PService::I2PService(core::SigningKeyType key_type)
: m_LocalDestination(client::context.LocalDestinations().Create(
core::PrivateKeys::CreateRandomKeys(key_type)))
{
}
void I2PService::CreateStream(
StreamRequestComplete stream_request_complete,
......
/** //
* Copyright (c) 2013-2018, The Kovri I2P Router Project //
* Copyright (c) 2013-2019, The Kovri I2P Router Project //
* //
* All rights reserved. //
* //
......@@ -577,6 +577,105 @@ Keys CreateRandomKeys() {
return keys;
}
void PrivateKeys::LoadFromFile(const std::string& filename)
{
try
{
// TODO(unassigned): util/filesystem
const auto file_path =
(core::GetPath(core::Path::ClientKeys) / filename).string();
std::ifstream file(file_path, std::ifstream::binary);
if (!file)
{
LOG(debug) << "PrivateKeys: " << file_path
<< " does not exist, creating";
*this = CreateFile(filename);
return;
}
file.seekg(0, std::ios::end);
const std::size_t len = file.tellg();
file.seekg(0, std::ios::beg);
std::unique_ptr<std::uint8_t[]> buf(
std::make_unique<std::uint8_t[]>(len));
file.read(reinterpret_cast<char*>(buf.get()), len);
PrivateKeys keys;
keys.FromBuffer(buf.get(), len);
// Contingency: create associated address text file if the private keys
// filename is swapped out with another set of keys with the same filename
CreateDestTextFiles(keys, filename);
LOG(info) << "PrivateKeys: " << file_path
<< " loaded: uses local address "
<< core::GetB32Address(keys.GetPublic().GetIdentHash());
*this = keys;
}
catch (...)
{
core::Exception ex("PrivateKeys");
ex.Dispatch(__func__);
throw;
}
}
PrivateKeys PrivateKeys::CreateFile(const std::string& filename) const
{
const auto path = core::EnsurePath(core::GetPath(core::Path::ClientKeys));
const auto file_path = (path / filename).string();
// TODO(unassigned): util/filesystem
// Create binary keys file
std::ofstream file(file_path, std::ofstream::binary);
if (!file)
throw std::runtime_error("PrivateKeys: could not open file for writing");
const auto& keys = CreateRandomKeys(); // Generate default type
std::size_t len = keys.GetFullLen();
std::unique_ptr<std::uint8_t[]> buf(std::make_unique<std::uint8_t[]>(len));
len = keys.ToBuffer(buf.get(), len);
file.write(reinterpret_cast<char*>(buf.get()), len);
// Create associated address text file
CreateDestTextFiles(keys, filename);
LOG(info) << "PrivateKeys: created new keypair " << file_path;
return keys;
}
void PrivateKeys::CreateDestTextFiles(
const kovri::core::PrivateKeys& keys,
const std::string& filename) const
{
LOG(debug) << "PrivateKeys: creating destination text files";
// Get the identity to encode
const core::IdentityEx& ident = keys.GetPublic();
// Ensure real-time path to write to
const std::string file =
(core::EnsurePath(core::GetPath(core::Path::ClientKeys)) / filename)
.string();
// Create base32 address file
LOG(info) << "PrivateKeys: saving base32 address to " << file << ".b32.txt";
std::ofstream b32(file + ".b32.txt");
if (!b32)
throw std::runtime_error("couldn't open b32 text file for writing");
std::string address = core::GetB32Address(ident.GetIdentHash());
b32 << address;
LOG(info) << "PrivateKeys: this destination's base32 address is: " << address;
// Create base64 address file
LOG(info) << "PrivateKeys: saving base64 address to " << file << ".b64.txt";
std::ofstream b64(file + ".b64.txt");
if (!b64)
throw std::runtime_error("couldn't open b64 text file for writing");
address = ident.ToBase64();
b64 << address;
LOG(info) << "PrivateKeys: this destination's base64 address is: " << address;
}
// TODO(unassigned): should not be a free function
IdentHash CreateRoutingKey(const IdentHash& ident)
{
......
/** //
* Copyright (c) 2013-2018, The Kovri I2P Router Project //
* Copyright (c) 2013-2019, The Kovri I2P Router Project //
* //
* All rights reserved. //
* //
......@@ -213,7 +213,9 @@ class IdentityEx {
core::Exception m_Exception;
};
class PrivateKeys { // for eepsites
/// @brief Keypair class intended for local client destinations (eepsites)
class PrivateKeys
{
public:
PrivateKeys();
~PrivateKeys();
......@@ -266,7 +268,23 @@ class PrivateKeys { // for eepsites
static PrivateKeys CreateRandomKeys(
SigningKeyType type = SigningKeyType::DefaultClient);
/// @brief Loads keys from given filename
/// @param filename Relative name of the private key file
void LoadFromFile(const std::string& filename);
private:
/// @brief Creates keys into given filename (if file doesn't exist)
/// @param filename The name of the file (without path)
/// @return Keypair from file
PrivateKeys CreateFile(const std::string& filename) const;
/// @brief Create b32/64 files for given keys
/// @param keys Given keys
/// @param filename Filename to prepend to extensions
void CreateDestTextFiles(
const kovri::core::PrivateKeys& keys,
const std::string& filename) const;
void CreateSigner();
private:
......
......@@ -7,6 +7,7 @@ add_executable(kovri-tests
client/api/i2p_control/data.cc
client/api/i2p_control/parser.cc
client/api/streaming.cc
client/context.cc
client/proxy/http.cc
client/destination.cc
client/reseed.cc
......
/** //
* Copyright (c) 2015-2019, The Kovri I2P Router Project //
* //
* All rights reserved. //
* //
* Redistribution and use in source and binary forms, with or without modification, are //
* permitted provided that the following conditions are met: //
* //
* 1. Redistributions of source code must retain the above copyright notice, this list of //
* conditions and the following disclaimer. //
* //
* 2. Redistributions in binary form must reproduce the above copyright notice, this list //
* of conditions and the following disclaimer in the documentation and/or other //
* materials provided with the distribution. //
* //
* 3. Neither the name of the copyright holder nor the names of its contributors may be //
* used to endorse or promote products derived from this software without specific //
* prior written permission. //
* //
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY //
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF //
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL //
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, //
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, //
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS //
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, //
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF //
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //
*/
#include "tests/unit_tests/main.h"
#include "client/context.h"
struct Context
{
Context()
: keys(core::PrivateKeys::CreateRandomKeys()),
// Destination derived from fixture keypair, should always produce same destination
destination(
std::make_shared<client::ClientDestination>(keys, false, nullptr))
{
}
core::PrivateKeys keys;
std::shared_ptr<client::ClientDestination> destination;
client::LocalDestinations<client::ClientDestination> local_dests;
};
BOOST_FIXTURE_TEST_SUITE(ClientContext, Context);
BOOST_AUTO_TEST_CASE(Create)
{
// Store and compare fixture destination
auto dest = local_dests.Create(keys);
BOOST_CHECK_EQUAL(dest->GetIdentHash(), destination->GetIdentHash());
// Should have started automatically
BOOST_CHECK_EQUAL(dest->IsRunning(), true);
// If exists, should *not* return nullptr
auto exists = local_dests.Create(keys);
BOOST_REQUIRE_NE(exists, nullptr);
BOOST_CHECK_EQUAL(exists->IsRunning(), true);
}
BOOST_AUTO_TEST_CASE(Find)
{
// Not found because not stored
auto dest = local_dests.Find(destination->GetIdentHash());
BOOST_REQUIRE_EQUAL(dest, nullptr);
// Create/store, then get hash
dest = local_dests.Create(keys);
const auto& ident = dest->GetIdentHash();
// Should exist and equal fixture destination
auto exists = local_dests.Find(ident);
BOOST_REQUIRE_NE(exists, nullptr);
BOOST_CHECK_EQUAL(exists->GetIdentHash(), ident);
BOOST_CHECK_EQUAL(ident, destination->GetIdentHash());
// Should not exist
auto non_exists = std::make_shared<client::ClientDestination>(
core::PrivateKeys::CreateRandomKeys(), false, nullptr);
BOOST_REQUIRE_EQUAL(local_dests.Find(non_exists->GetIdentHash()), nullptr);
// Uncommented when used in impl
//BOOST_REQUIRE_EQUAL(local_dests.Find(non_exists), nullptr);
}
BOOST_AUTO_TEST_CASE(StartAndStop)
{
BOOST_CHECK_EQUAL(destination->IsRunning(), false);
BOOST_REQUIRE_NO_THROW(destination->Start());
BOOST_CHECK_EQUAL(destination->IsRunning(), true);
auto dest = local_dests.Create();
BOOST_REQUIRE_NO_THROW(dest->Start());
BOOST_CHECK_EQUAL(dest->IsRunning(), true);
BOOST_REQUIRE_NO_THROW(dest->Stop());
BOOST_CHECK_EQUAL(dest->IsRunning(), false);
BOOST_REQUIRE_NO_THROW(local_dests.Start());
BOOST_CHECK_EQUAL(dest->IsRunning(), true);
BOOST_REQUIRE_NO_THROW(local_dests.Stop());
BOOST_CHECK_EQUAL(dest->IsRunning(), false);
}
// Uncomment when used in impl
//BOOST_AUTO_TEST_CASE(Delete)
//{
// auto dest = local_dests.Create();
// BOOST_CHECK_EQUAL(local_dests.Delete(dest), true);
//
// dest = local_dests.Create();
// BOOST_CHECK_EQUAL(local_dests.Delete(dest->GetIdentHash()), true);
//}
BOOST_AUTO_TEST_SUITE_END()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment