Skip to content
This repository has been archived by the owner on Sep 1, 2022. It is now read-only.

Commit

Permalink
Merge pull request #835
Browse files Browse the repository at this point in the history
a673466 Tests: Address Book unit-test checks unique entry (oneiric)
05ee550 AddressBook: store only unique addresses (oneiric)
  • Loading branch information
anonimal committed Apr 3, 2018
2 parents ec7dbbb + a673466 commit 5ba66f6
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 14 deletions.
54 changes: 43 additions & 11 deletions src/client/address_book/impl.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** //
* Copyright (c) 2013-2017, The Kovri I2P Router Project //
* Copyright (c) 2013-2018, The Kovri I2P Router Project //
* //
* All rights reserved. //
* //
Expand Down Expand Up @@ -228,6 +228,7 @@ void AddressBook::DownloadSubscription() {
<< "AddressBook: picking random subscription from total publisher count: "
<< publisher_count;
// Pick a random publisher to subscribe from
// TODO(oneiric): download all subscriptions not already stored
auto publisher = kovri::core::RandInRange32(0, publisher_count - 1);
m_SubscriberIsDownloading = true;
try {
Expand Down Expand Up @@ -295,6 +296,7 @@ bool AddressBook::SaveSubscription(
LOG(debug) << "AddressBook: processing " << addresses.size() << " addresses";
// Stream may be a file or downloaded stream.
// Regardless, we want to write/overwrite the subscription file.
// TODO(oneiric): save unique entries to userhosts.txt
if (file_name.empty()) // Use default filename if none given.
file_name = (core::GetPath(core::Path::AddressBook) / GetDefaultSubscriptionFilename()).string();
LOG(debug) << "AddressBook: opening subscription file " << file_name;
Expand All @@ -307,22 +309,29 @@ bool AddressBook::SaveSubscription(
for (auto const& address : addresses) {
const std::string& host = address.first;
const auto& ident = address.second;
// Write/overwrite Hostname=Base64Address pairing to subscription file
file << host << "=" << ident.ToBase64() << '\n'; // TODO(anonimal): this is not optimal, especially for large subscriptions
// Add to address book
m_Storage->AddAddress(ident);
m_Addresses[host] = ident.GetIdentHash(); // TODO(anonimal): setter?
try
{
// Only stores subscription lines for addresses not already loaded
InsertAddress(host, ident.GetIdentHash());
// Write/overwrite Hostname=Base64Address pairing to subscription file
// TODO(anonimal): this is not optimal, especially for large subscriptions
file << host << "=" << ident.ToBase64() << '\n';
m_Storage->AddAddress(ident);
}
catch (...)
{
m_Exception.Dispatch(__func__);
continue;
}
}
// Flush subscription file
file << std::flush;
// Save a *list* of hosts within subscription to a catalog (CSV) file
m_Storage->Save(m_Addresses);
m_SubscriptionIsLoaded = true;
}
} catch (const std::exception& ex) {
LOG(error) << "AddressBook: exception in " << __func__ << ": " << ex.what();
} catch (...) {
LOG(error) << "AddressBook: unknown exception in " << __func__;
m_Exception.Dispatch(__func__);
}
return m_SubscriptionIsLoaded;
}
Expand All @@ -341,6 +350,7 @@ AddressBook::ValidateSubscription(std::istream& stream) {
//const std::string alpha = "abcdefghijklmnopqrstuvwxyz";
// TODO(unassigned): expand when we want to venture beyond the .i2p TLD
// TODO(unassigned): IDN ccTLDs support?
// TODO(unassigned): investigate alternatives to regex (maybe Boost.Spirit?)
std::regex regex("(?=^.{1,253}$)(^(((?!-)[a-zA-Z0-9-]{1,63})|((?!-)[a-zA-Z0-9-]{1,63}\\.)+[a-zA-Z]+[(i2p)]{2,63})$)");
try {
// Read through stream, add to address book
Expand Down Expand Up @@ -441,6 +451,28 @@ std::unique_ptr<const kovri::core::IdentHash> AddressBook::GetLoadedAddressIdent
return nullptr;
}

void AddressBook::InsertAddress(
const std::string& host,
const kovri::core::IdentHash& address)
{
try
{
// Ensure address book only inserts unique entries
if (!m_Addresses.empty())
{
for (const auto& entry : m_Addresses)
if (entry.second == address)
throw std::runtime_error("AddressBook: address already loaded");
}
m_Addresses[host] = address;
}
catch (...)
{
m_Exception.Dispatch(__func__);
throw;
}
}

// Used only by HTTP Proxy
void AddressBook::InsertAddressIntoStorage(
const std::string& address,
Expand All @@ -450,11 +482,11 @@ void AddressBook::InsertAddressIntoStorage(
{
kovri::core::IdentityEx ident;
ident.FromBase64(base64);
const auto& ident_hash = ident.GetIdentHash();
InsertAddress(address, ident_hash);
if (!m_Storage)
m_Storage = GetNewStorageInstance();
m_Storage->AddAddress(ident);
const auto& ident_hash = ident.GetIdentHash();
m_Addresses[address] = ident_hash;
LOG(info) << "AddressBook: " << address << "->"
<< kovri::core::GetB32Address(ident_hash)
<< " added";
Expand Down
11 changes: 10 additions & 1 deletion src/client/address_book/impl.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** //
* Copyright (c) 2013-2017, The Kovri I2P Router Project //
* Copyright (c) 2013-2018, The Kovri I2P Router Project //
* //
* All rights reserved. //
* //
Expand Down Expand Up @@ -107,9 +107,18 @@ class AddressBook : public AddressBookDefaults {
return m_SharedLocalDestination;
}

/// @brief Insert address into in-memory storage
/// @param host Human-readable hostname to insert
/// @param address Hash of address to insert
/// @notes Throws if host or address are duplicates
void InsertAddress(
const std::string& host,
const kovri::core::IdentHash& address);

/// @brief Inserts address into address book from HTTP Proxy jump service
/// @param address Const reference to human-readable address
/// @param base64 Const reference to Base64 address
// TODO(oneiric): remove after separating HTTP Proxy from Address Book
void InsertAddressIntoStorage(
const std::string& address,
const std::string& base64);
Expand Down
59 changes: 57 additions & 2 deletions tests/unit_tests/client/address_book/impl.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** //
* Copyright (c) 2015-2017, The Kovri I2P Router Project //
* Copyright (c) 2015-2018, The Kovri I2P Router Project //
* //
* All rights reserved. //
* //
Expand Down Expand Up @@ -44,6 +44,44 @@

/// @class SubscriptionFixture
struct SubscriptionFixture {
struct AddressBookEntry
{
public:
explicit AddressBookEntry(const std::string& subscription_line)
{
try
{
std::size_t pos = subscription_line.find('=');
if (pos == std::string::npos)
throw std::runtime_error(
"AddressBookEntry: invalid subscription line");
m_Host = subscription_line.substr(0, pos++);
kovri::core::IdentityEx ident;
ident.FromBase64(subscription_line.substr(pos));
m_Address = ident.GetIdentHash();
}
catch (...)
{
kovri::core::Exception ex;
ex.Dispatch(__func__);
throw;
}
}

const std::string& host() const
{
return m_Host;
}

const kovri::core::IdentHash& address() const
{
return m_Address;
}

private:
std::string m_Host; ///< Human-readable I2P hostname
kovri::core::IdentHash m_Address; ///< I2P address hash
};

/// @brief Validates given lines as proven addressbook host/address pairs
/// @param lines Lines to validate
Expand Down Expand Up @@ -170,4 +208,21 @@ BOOST_AUTO_TEST_CASE(PGPClearSign) {

// TODO(unassigned): more cases?

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(RejectDuplicateEntry)
{
// Ensure valid subscription line creates an entry
BOOST_CHECK_NO_THROW(AddressBookEntry entry(subscription.front()));

AddressBookEntry entry(subscription.front());
// Ensure valid entry is inserted
BOOST_CHECK_NO_THROW(book.InsertAddress(entry.host(), entry.address()));
// Ensure address book throws for duplicate host
BOOST_CHECK_THROW(
book.InsertAddress(entry.host(), entry.address()), std::runtime_error);
// Ensure address book throws for duplicate address
BOOST_CHECK_THROW(
book.InsertAddress("unique." + entry.host(), entry.address()),
std::runtime_error);
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 5ba66f6

Please sign in to comment.