Unverified Commit 73d2f7b8 authored by anonimal's avatar anonimal

Client: tunnel API hooks

parent d8618cb4
......@@ -4,9 +4,11 @@ project(kovri-client CXX)
add_library(kovri-client "")
target_sources(kovri-client
PUBLIC
# API header
# API headers
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/instance.h>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/tunnel_data.h>
$<INSTALL_INTERFACE:include/kovri/client/instance.h>
$<INSTALL_INTERFACE:include/kovri/client/tunnel_data.h>
PRIVATE
address_book/impl.cc
address_book/storage.cc
......
......@@ -221,15 +221,24 @@ void ClientContext::RequestShutdown() {
m_ShutdownHandler();
}
void ClientContext::RemoveServerTunnels(
std::function<bool(I2PServerTunnel*)> predicate) {
std::lock_guard<std::mutex> lock(m_ServerMutex);
for (auto it = m_ServerTunnels.begin(); it != m_ServerTunnels.end();) {
if (predicate(it->second.get()))
it = m_ServerTunnels.erase(it);
else
++it;
}
void ClientContext::RemoveTunnels(
const std::vector<std::string>& client,
const std::vector<std::string>& server)
{
RemoveClientTunnels([&client](I2PClientTunnel* tunnel) {
return std::find(
client.begin(),
client.end(),
tunnel->GetTunnelAttributes().name)
== client.end();
});
RemoveServerTunnels([&server](I2PServerTunnel* tunnel) {
return std::find(
server.begin(),
server.end(),
tunnel->GetTunnelAttributes().name)
== server.end();
});
}
void ClientContext::RemoveClientTunnels(
......@@ -243,6 +252,17 @@ void ClientContext::RemoveClientTunnels(
}
}
void ClientContext::RemoveServerTunnels(
std::function<bool(I2PServerTunnel*)> predicate) {
std::lock_guard<std::mutex> lock(m_ServerMutex);
for (auto it = m_ServerTunnels.begin(); it != m_ServerTunnels.end();) {
if (predicate(it->second.get()))
it = m_ServerTunnels.erase(it);
else
++it;
}
}
void ClientContext::UpdateServerTunnel(
const TunnelAttributes& tunnel,
bool is_http) {
......
......@@ -160,6 +160,13 @@ class ClientContext final
void RemoveClientTunnels(
std::function<bool(I2PClientTunnel*)> predicate);
/// @brief Removes all client/server tunnels by name
/// @param client Vector of client tunnel names to remove
/// @param server Vector of server tunnel names to remove
void RemoveTunnels(
const std::vector<std::string>& client,
const std::vector<std::string>& server);
/// @brief Updates or creates the specified server tunnel
/// @param tunnel Const reference to populated/initialized tunnel attributes class
/// @param http true if server tunnel is an HTTP tunnel
......
......@@ -125,8 +125,8 @@ void Instance::SetupTunnels()
try
{
// Test which type of tunnel (client or server)
if (tunnel.type == m_Config->GetAttribute(Key::Client)
|| tunnel.type == m_Config->GetAttribute(Key::IRC))
if (tunnel.type == GetAttribute(Key::Client)
|| tunnel.type == GetAttribute(Key::IRC))
{ // TODO(unassigned): see #9
if (m_IsReloading)
{
......@@ -159,7 +159,7 @@ void Instance::SetupTunnels()
else
{ // TODO(unassigned): currently, anything that's not client
bool const is_http =
(tunnel.type == m_Config->GetAttribute(Key::HTTP));
(tunnel.type == GetAttribute(Key::HTTP));
if (m_IsReloading)
{
context.UpdateServerTunnel(tunnel, is_http);
......@@ -184,33 +184,140 @@ void Instance::SetupTunnels()
{
LOG(info) << "Instance: " << client_count << " client tunnels updated";
LOG(info) << "Instance: " << server_count << " server tunnels updated";
RemoveOldTunnels(updated_client_tunnels, updated_server_tunnels);
context.RemoveTunnels(updated_client_tunnels, updated_server_tunnels);
return;
}
LOG(info) << "Instance: " << client_count << " client tunnels created";
LOG(info) << "Instance: " << server_count << " server tunnels created";
}
void Instance::RemoveOldTunnels(
const std::vector<std::string>& updated_client_tunnels,
const std::vector<std::string>& updated_server_tunnels)
TunnelData::TunnelData(const TunnelAttributes& tunnel) : m_Tunnel(tunnel)
{
context.RemoveServerTunnels(
[&updated_server_tunnels](I2PServerTunnel* tunnel) {
return std::find(
updated_server_tunnels.begin(),
updated_server_tunnels.end(),
tunnel->GetTunnelAttributes().name)
== updated_server_tunnels.end();
});
context.RemoveClientTunnels(
[&updated_client_tunnels](I2PClientTunnel* tunnel) {
return std::find(
updated_client_tunnels.begin(),
updated_client_tunnels.end(),
tunnel->GetTunnelAttributes().name)
== updated_client_tunnels.end();
});
if (m_Tunnel.type == GetAttribute(Key::Client)
|| m_Tunnel.type == GetAttribute(Key::IRC))
{
m_IsClient = true;
}
else if (
m_Tunnel.type == GetAttribute(Key::Server)
|| m_Tunnel.type == GetAttribute(Key::HTTP))
{
m_IsServer = true;
}
else
{
throw std::invalid_argument("Invalid or unsupported tunnel type");
}
}
std::pair<bool, std::string> Tunnel::Configure()
{
try
{
if (m_IsClient)
{
auto const* client = context.GetClientTunnel(m_Tunnel.port);
if (client && client->GetName() != m_Tunnel.name)
{
std::string const name = client->GetName();
context.RemoveClientTunnels([&name](I2PClientTunnel* old_tunnel) {
return name == old_tunnel->GetName();
});
}
}
else if (m_IsServer)
{
// TODO(unassigned): remove any existing conflicts?
}
assert(false);
}
catch (...)
{
core::Exception ex;
return {false, ex.Dispatch(__func__)};
}
return {true, {}};
}
std::pair<bool, std::string> Tunnel::Start()
{
try
{
// Both will add tunnel internally if it does not exist
if (m_IsClient)
{
// TODO(unassigned): refactor to have Update*Tunnel return X failure
context.UpdateClientTunnel(m_Tunnel);
}
else if (m_IsServer)
{
bool const is_http = (m_Tunnel.type == GetAttribute(Key::HTTP));
// TODO(unassigned): refactor to have Update*Tunnel return X on failure
context.UpdateServerTunnel(m_Tunnel, is_http);
}
assert(false);
}
catch (...)
{
core::Exception ex;
return {false, ex.Dispatch(__func__)};
}
return {true, {}};
}
std::pair<bool, std::string> Tunnel::Restart()
{
std::pair<bool, std::string> stop = Stop();
if (!stop.first)
return stop;
std::pair<bool, std::string> start = Start();
if (!start.first)
return start;
return {true, {}};
}
std::pair<bool, std::string> Tunnel::Stop()
{
try
{
if (m_IsClient)
{
context.RemoveClientTunnels([&](I2PClientTunnel* tunnel) {
return m_Tunnel.name == tunnel->GetName();
});
}
else if (m_IsServer)
{
context.RemoveServerTunnels([&](I2PServerTunnel* tunnel) {
return m_Tunnel.name == tunnel->GetName();
});
}
assert(false);
}
catch (...)
{
core::Exception ex;
return {false, ex.Dispatch(__func__)};
}
return {true, {}};
}
bool TunnelStatus::IsRunning() const
{
return false; // TODO(unassigned): implement
}
std::string TunnelStatus::GetState() const
{
return {}; // TODO(unassigned): implement
}
std::uint64_t TunnelStatus::GetUptime() const
{
return {}; // TODO(unassigned): implement
}
void Instance::Start()
......
......@@ -35,6 +35,7 @@
#include <string>
#include <vector>
#include "client/tunnel_data.h"
#include "core/instance.h"
namespace kovri
......@@ -44,9 +45,49 @@ namespace client
// TODO(unassigned): properly remove the need to forward declare for external API usage
class Configuration;
// TODO(anonimal): we currently want to limit core interaction through client only for all apps and most API cases.
// A member function getter can return the object which will (should) change mutable data via the core API.
// In essence, the core and client Instance objects are essentially a preliminary API.
/// @notes Not to be confused with client TunnelData message
class TunnelData
{
public:
explicit TunnelData(const TunnelAttributes& tunnel);
virtual ~TunnelData() = default;
TunnelData(const TunnelData&) = default;
TunnelData& operator=(const TunnelData&) = default;
TunnelData(TunnelData&&) = default;
TunnelData& operator=(TunnelData&&) = default;
protected:
TunnelAttributes m_Tunnel;
bool m_IsClient{false}, m_IsServer{false};
};
/// @notes Because of context singletons, there is no need to hook-in implementations
/// @todo Finish
class TunnelStatus final : private TunnelData
{
public:
explicit TunnelStatus(const TunnelAttributes& tunnel) : TunnelData(tunnel) {}
public:
bool IsRunning() const;
std::string GetState() const;
std::uint64_t GetUptime() const;
};
/// @notes Because of context singletons, there is no need to hook-in implementations
class Tunnel final : private TunnelData
{
public:
explicit Tunnel(const TunnelAttributes& tunnel) : TunnelData(tunnel) {}
public:
std::pair<bool, std::string> Configure();
std::pair<bool, std::string> Start();
std::pair<bool, std::string> Restart();
std::pair<bool, std::string> Stop();
};
/// @class Instance
/// @brief Client instance implementation
......@@ -94,18 +135,34 @@ class Instance
return m_Core->GetStatus();
}
/// @brief Entry point to a client tunnel
/// @param tunnel Tunnel's attributes for tunnel creation
/// @return Copy of initialized pointer to shared tunnel object
std::shared_ptr<Tunnel> ClientTunnel(const TunnelAttributes& tunnel)
{
// TODO(unassigned): we don't *need* to throw (this should be done better)
if (m_IsReloading)
throw std::runtime_error("Client is reloading, try again later");
return std::make_shared<Tunnel>(tunnel);
}
/// @brief Entry point to a client tunnel status
/// @param tunnel Tunnel's attributes for tunnel creation
/// @return Copy of initialized pointer to shared tunnel status object
std::shared_ptr<TunnelStatus> ClientTunnelStatus(
const TunnelAttributes& tunnel)
{
// TODO(unassigned): we don't *need* to throw (this should be done better)
if (m_IsReloading)
throw std::runtime_error("Client is reloading, try again later");
return std::make_shared<TunnelStatus>(tunnel);
}
private:
/// @brief Sets up (or reloads) client/server tunnels
/// @warning Configuration files must be parsed prior to setup
void SetupTunnels();
/// @brief Should remove old tunnels after tunnels config is updated
/// @param updated_client_tunnels List of client tunnels to keep
/// @param updated_server_tunnels List of server tunnels to keep
void RemoveOldTunnels(
const std::vector<std::string>& updated_client_tunnels,
const std::vector<std::string>& updated_server_tunnels);
private:
/// @brief Exception dispatcher
static core::Exception m_Exception;
......
......@@ -45,31 +45,13 @@
#include "client/api/streaming.h"
#include "client/destination.h"
#include "client/service.h"
#include "client/tunnel_data.h"
#include "core/router/identity.h"
namespace kovri {
namespace client {
/// @class ACL
/// @brief Access Control List for tunnel attributes
struct ACL {
ACL() : is_white(false), is_black(false) {}
std::string list;
bool is_white, is_black;
};
// TODO(anonimal): signature type (see #369)
/// @class TunnelAttributes
/// @brief Attributes for client/server tunnel
/// @notes For details, see tunnels configuration key
struct TunnelAttributes {
TunnelAttributes() : port(0), dest_port(0), in_port(0) {}
std::string name, type, dest, address, keys;
std::uint16_t port, dest_port, in_port;
ACL acl{};
};
const std::size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192;
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
......
/** //
* 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. //
*/
#ifndef SRC_CLIENT_TUNNEL_DATA_H_
#define SRC_CLIENT_TUNNEL_DATA_H_
namespace kovri
{
namespace client
{
/// @class ACL
/// @brief Access Control List for tunnel attributes
struct ACL
{
ACL() : is_white(false), is_black(false) {}
std::string list;
bool is_white, is_black;
};
// TODO(unassigned): signature type (see #369)
/// @class TunnelAttributes
/// @brief Attributes for client/server tunnel
/// @notes For details, see tunnels configuration key
struct TunnelAttributes
{
TunnelAttributes() : port(0), dest_port(0), in_port(0) {}
std::string name, type, dest, address, keys;
std::uint16_t port, dest_port, in_port;
ACL acl{};
};
/// @enum Key
/// @brief Tunnels config attribute key for const tunnel param string
enum struct Key : std::uint8_t
{
/// @var Type
/// @brief Key for type of tunnel (client/server/HTTP, etc.)
Type,
/// @var Client
/// @brief Key for client tunnel
Client,
/// @var IRC
/// @brief Key for IRC tunnel
IRC,
/// @var Server
/// @brief Key for server tunnel
Server,
/// @var HTTP
/// @brief Key for HTTP tunnel
HTTP,
/// @var Address
/// @brief Key for local listening address that you or service connects to
/// @notes Should default to 127.0.0.1
Address,
/// @var Dest
/// @brief Key for I2P hostname or .b32 address
Dest,
/// @var DestPort
/// @brief Key for I2P destination port used in destination
DestPort,
/// @var InPort
/// @brief Key for I2P service port. If unset, should be the same as 'port'
InPort,
/// @var Whitelist
/// @brief Key for Access Control whitelist of I2P addresses for server tunnel
Whitelist,
/// @var Blackslist
/// @brief Key for Access Control blacklist of I2P addresses for server tunnel
Blacklist,
/// @var Port
/// @brief Key for port of our listening client or server tunnel
/// (example: port 80 if you are hosting website)
Port,
/// @var Keys
/// @brief Key for client tunnel identity
/// or file with LeaseSet of local service I2P address
Keys,
};
/// @brief Tunnel attribute accessor
/// @tparam t_attr String type (forward-compatible for string_view)
/// @param key Key enumerator
template <typename t_attr = std::string>
t_attr GetAttribute(Key key)
{
switch (key)
{
// Section types
case Key::Type:
return "type";
break;
case Key::Client:
return "client";
break;
case Key::IRC:
return "irc";
break;
case Key::Server:
return "server";
break;
case Key::HTTP:
return "http";
break;
// Client-tunnel specific
case Key::Dest:
return "dest";
break;
case Key::DestPort:
return "dest_port";
break;
// Server-tunnel specific
case Key::InPort:
return "in_port";
break;
case Key::Whitelist:
return "white_list";
break;
case Key::Blacklist:
return "black_list";
break;
// Tunnel-agnostic
case Key::Address:
return "address";
break;
case Key::Port:
return "port";
break;
case Key::Keys:
return "keys";
break;
default:
return ""; // not needed (avoids nagging -Wreturn-type)
break;
};
}
} // namespace client
} // namespace kovri
#endif // SRC_CLIENT_TUNNEL_DATA_H_
......@@ -166,58 +166,5 @@ void Configuration::ParseConfig()
}
}
const std::string Configuration::GetAttribute(Key key) const
{
switch (key)
{
// Section types
case Key::Type:
return "type";
break;
case Key::Client:
return "client";
break;
case Key::IRC:
return "irc";
break;
case Key::Server:
return "server";
break;
case Key::HTTP:
return "http";
break;
// Client-tunnel specific
case Key::Dest:
return "dest";
break;
case Key::DestPort:
return "dest_port";
break;
// Server-tunnel specific
case Key::InPort:
return "in_port";
break;
case Key::Whitelist:
return "white_list";
break;
case Key::Blacklist:
return "black_list";
break;
// Tunnel-agnostic
case Key::Address:
return "address";
break;
case Key::Port:
return "port";
break;
case Key::Keys:
return "keys";
break;
default:
return ""; // not needed (avoids nagging -Wreturn-type)
break;
};
}
} // namespace client
} // namespace kovri
......@@ -50,54 +50,6 @@ namespace kovri
{
namespace client
{
/// @enum Key
/// @brief Tunnels config attribute key for const tunnel param string
enum struct Key : std::uint8_t
{
/// @var Type
/// @brief Key for type of tunnel (client/server/HTTP, etc.)
Type,
/// @var Client
/// @brief Key for client tunnel
Client,
/// @var IRC
/// @brief Key for IRC tunnel
IRC,
/// @var Server
/// @brief Key for server tunnel
Server,
/// @var HTTP
/// @brief Key for HTTP tunnel
HTTP,
/// @var Address
/// @brief Key for local listening address that you or service connects to
/// @notes Should default to 127.0.0.1
Address,
/// @var Dest
/// @brief Key for I2P hostname or .b32 address
Dest,
/// @var DestPort
/// @brief Key for I2P destination port used in destination
DestPort,
/// @var InPort
/// @brief Key for I2P service port. If unset, should be the same as 'port'
InPort,
/// @var Whitelist
/// @brief Key for Access Control whitelist of I2P addresses for server tunnel
Whitelist,
/// @var Blackslist
/// @brief Key for Access Control blacklist of I2P addresses for server tunnel
Blacklist,
/// @var Port
/// @brief Key for port of our listening client or server tunnel
/// (example: port 80 if you are hosting website)
Port,
/// @var Keys
/// @brief Key for client tunnel identity
/// or file with LeaseSet of local service I2P address
Keys,
};
/// @class Configuration
/// @brief Client configuration implementation
/// @note Core configuration SHOULD be initialized first
......@@ -113,10 +65,6 @@ class Configuration
/// @warning Logging must be setup to see any debug output
void ParseConfig();
/// @brief Gets pre-defined tunnel attribute from tunnel config
/// @param key Key for tunnels config attribute
const std::string GetAttribute(Key key) const;
/// @brief Gets tunnels config member
/// @return Reference to tunnels attributes vector member
const std::vector<TunnelAttributes>& GetParsedTunnelsConfig() const noexcept
......
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