Make new header for json type conversions

This commit is contained in:
dan 2023-01-26 09:51:55 -08:00
parent 02b392881b
commit 0632e88de0
12 changed files with 140 additions and 166 deletions

View File

@ -225,7 +225,7 @@ main(int argc, char* argv[])
{
nlohmann::json opts{{"address", options.exitAddress}, {"token", options.token}};
if (options.range)
opts["IP_range"] = *options.range;
opts["ip_range"] = *options.range;
auto maybe_result = OMQ_Request(omq, connectionID, "llarp.map_exit", std::move(opts));
@ -240,10 +240,10 @@ main(int argc, char* argv[])
}
if (options.vpnDown)
{
nlohmann::json opts{{"unmap", true}};
nlohmann::json opts{{"unmap_exit", true}};
if (options.range)
opts["range"] = *options.range;
if (not OMQ_Request(omq, connectionID, "llarp.exit", std::move(opts)))
opts["ip_range"] = *options.range;
if (not OMQ_Request(omq, connectionID, "llarp.unmap_exit", std::move(opts)))
return exit_error("failed to unmap exit");
}

View File

@ -247,6 +247,7 @@ add_library(lokinet-context
add_library(lokinet-rpc
STATIC
rpc/json_binary_proxy.cpp
rpc/json_conversions.cpp
rpc/lokid_rpc_client.cpp
rpc/rpc_request_parser.cpp
rpc/rpc_server.cpp

View File

@ -8,6 +8,7 @@
#include <list>
#include <optional>
#include <stdexcept>
#include <string>
namespace llarp
@ -24,6 +25,12 @@ namespace llarp
: addr{std::move(address)}, netmask_bits{std::move(netmask)}
{}
explicit IPRange(std::string _range)
{
if (not FromString(_range))
throw std::invalid_argument{"IP string '{}' cannot be parsed as IP range"_format(_range)};
}
static IPRange
StringInit(std::string _range)
{
@ -32,6 +39,13 @@ namespace llarp
return range;
}
static bool
IsValidString(std::string _range)
{
IPRange range;
return (range.FromString(_range));
}
static constexpr IPRange
V4MappedRange()
{

View File

@ -156,26 +156,3 @@ namespace llarp::rpc
};
} // namespace llarp::rpc
// Specializations of binary types for deserialization; when receiving these from json we expect
// them encoded in hex or base64. These may *not* be used for serialization, and will throw if so
// invoked; for serialization you need to use RPC_COMMAND::response_hex (or _b64) instead.
namespace nlohmann
{
template <typename T>
struct adl_serializer<T, std::enable_if_t<llarp::rpc::json_is_binary<T>>>
{
static_assert(std::is_trivially_copyable_v<T> && std::has_unique_object_representations_v<T>);
static void
to_json(const T&)
{
throw std::logic_error{"Internal error: binary types are not directly serializable"};
}
static void
from_json(const json& j, T& val)
{
llarp::rpc::load_binary_parameter(j.get<std::string_view>(), false /*no raw*/, val);
}
};
} // namespace nlohmann

View File

@ -0,0 +1,18 @@
#include "json_conversions.hpp"
#include <nlohmann/json.hpp>
namespace llarp
{
void
to_json(nlohmann::json& j, const IPRange& ipr)
{
j = ipr.ToString();
}
void
from_json(const nlohmann::json& j, IPRange& ipr)
{
ipr = IPRange{j.get<std::string>()};
}
} // namespace llarp

View File

@ -0,0 +1,37 @@
#pragma once
#include <llarp/net/ip_range.hpp>
#include <nlohmann/json_fwd.hpp>
#include "json_binary_proxy.hpp"
namespace llarp
{
void
to_json(nlohmann::json& j, const IPRange& ipr);
void
from_json(const nlohmann::json& j, IPRange& ipr);
} // namespace llarp
namespace nlohmann
{
// Specializations of binary types for deserialization; when receiving these from json we expect
// them encoded in hex or base64. These may *not* be used for serialization, and will throw if so
// invoked; for serialization you need to use RPC_COMMAND::response_hex (or _b64) instead.
template <typename T>
struct adl_serializer<T, std::enable_if_t<llarp::rpc::json_is_binary<T>>>
{
static_assert(std::is_trivially_copyable_v<T> && std::has_unique_object_representations_v<T>);
static void
to_json(json&, const T&)
{
throw std::logic_error{"Internal error: binary types are not directly serializable"};
}
static void
from_json(const json& j, T& val)
{
llarp::rpc::load_binary_parameter(j.get<std::string_view>(), false /*no raw*/, val);
}
};
} // namespace nlohmann

View File

@ -1,14 +1,17 @@
#pragma once
#include "json_binary_proxy.hpp"
#include "json_bt.hpp"
#include "json_conversions.hpp"
#include <oxenc/bt_serialize.h>
#include <nlohmann/json.hpp>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <optional>
namespace llarp::rpc
{
using json_range = std::pair<nlohmann::json::const_iterator, nlohmann::json::const_iterator>;
using rpc_input = std::variant<std::monostate, nlohmann::json, oxenc::bt_dict_consumer>;
@ -116,6 +119,12 @@ namespace llarp::rpc
template <typename T>
constexpr bool is_expandable_list<std::vector<T>> = true;
// Types that are constructible from string
template <typename T>
constexpr bool is_string_constructible = false;
template <>
inline constexpr bool is_string_constructible<IPRange> = true;
// Fixed size elements: tuples, pairs, and std::array's; we accept list input as long as the
// list length matches exactly.
template <typename T>
@ -147,32 +156,34 @@ namespace llarp::rpc
oxenc::bt_dict_consumer> || std::is_same_v<BTConsumer, oxenc::bt_list_consumer>,
int> = 0>
void
load_value(BTConsumer& c, T& val)
load_value(BTConsumer& c, T& target)
{
if constexpr (std::is_integral_v<T>)
val = c.template consume_integer<T>();
target = c.template consume_integer<T>();
else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>)
val = c.consume_string_view();
target = c.consume_string_view();
else if constexpr (is_string_constructible<T>)
target = T{c.consume_string()};
else if constexpr (llarp::rpc::json_is_binary<T>)
llarp::rpc::load_binary_parameter(c.consume_string_view(), true /*allow raw*/, val);
llarp::rpc::load_binary_parameter(c.consume_string_view(), true /*allow raw*/, target);
else if constexpr (is_expandable_list<T>)
{
auto lc = c.consume_list_consumer();
val.clear();
target.clear();
while (!lc.is_finished())
load_value(lc, val.emplace_back());
load_value(lc, target.emplace_back());
}
else if constexpr (is_tuple_like<T>)
{
auto lc = c.consume_list_consumer();
load_tuple_values(lc, val, std::make_index_sequence<std::tuple_size_v<T>>{});
load_tuple_values(lc, target, std::make_index_sequence<std::tuple_size_v<T>>{});
}
else if constexpr (is_unordered_string_map<T>)
{
auto dc = c.consume_dict_consumer();
val.clear();
target.clear();
while (!dc.is_finished())
load_value(dc, val[std::string{dc.key()}]);
load_value(dc, target[std::string{dc.key()}]);
}
else
static_assert(std::is_same_v<T, void>, "Unsupported load_value type");
@ -182,21 +193,21 @@ namespace llarp::rpc
// on unconvertible values.
template <typename T>
void
load_value(json_range& r, T& val)
load_value(json_range& range_itr, T& target)
{
auto& key = r.first.key();
auto& e = *r.first;
auto& key = range_itr.first.key();
auto& current = *range_itr.first; // value currently pointed to by range_itr.first
if constexpr (std::is_same_v<T, bool>)
{
if (e.is_boolean())
val = e.get<bool>();
else if (e.is_number_unsigned())
if (current.is_boolean())
target = current.get<bool>();
else if (current.is_number_unsigned())
{
// Also accept 0 or 1 for bools (mainly to be compatible with bt-encoding which doesn't
// have a distinct bool type).
auto b = e.get<uint64_t>();
auto b = current.get<uint64_t>();
if (b <= 1)
val = b;
target = b;
else
throw std::domain_error{"Invalid value for '" + key + "': expected boolean"};
}
@ -207,18 +218,18 @@ namespace llarp::rpc
}
else if constexpr (std::is_unsigned_v<T>)
{
if (!e.is_number_unsigned())
if (!current.is_number_unsigned())
throw std::domain_error{"Invalid value for '" + key + "': non-negative value required"};
auto i = e.get<uint64_t>();
auto i = current.get<uint64_t>();
if (sizeof(T) < sizeof(uint64_t) && i > std::numeric_limits<T>::max())
throw std::domain_error{"Invalid value for '" + key + "': value too large"};
val = i;
target = i;
}
else if constexpr (std::is_integral_v<T>)
{
if (!e.is_number_integer())
if (!current.is_number_integer())
throw std::domain_error{"Invalid value for '" + key + "': value is not an integer"};
auto i = e.get<int64_t>();
auto i = current.get<int64_t>();
if (sizeof(T) < sizeof(int64_t))
{
if (i < std::numeric_limits<T>::lowest())
@ -227,11 +238,11 @@ namespace llarp::rpc
if (i > std::numeric_limits<T>::max())
throw std::domain_error{"Invalid value for '" + key + "': value is too large"};
}
val = i;
target = i;
}
else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>)
{
val = e.get<std::string_view>();
target = current.get<std::string_view>();
}
else if constexpr (
llarp::rpc::json_is_binary<
@ -239,7 +250,7 @@ namespace llarp::rpc
{
try
{
e.get_to(val);
current.get_to(target);
}
catch (const std::exception& e)
{
@ -250,7 +261,7 @@ namespace llarp::rpc
{
static_assert(std::is_same_v<T, void>, "Unsupported load type");
}
++r.first;
++range_itr.first;
}
template <typename TupleLike, size_t... Is>
@ -355,5 +366,4 @@ namespace llarp::rpc
}
}
}
} // namespace llarp::rpc

View File

@ -14,7 +14,6 @@
namespace llarp::rpc
{
using nlohmann::json;
template <typename RPC>

View File

@ -176,78 +176,16 @@ namespace llarp::rpc
struct request_parameters
{
std::string address;
std::vector<std::string> ip_range;
std::vector<IPRange> ip_range;
std::string token;
} request;
void
onGoodResult(std::string reason, bool hasClient)
{
response = (hasClient) ? nlohmann::json{{"result", reason}}.dump()
: nlohmann::json{{"error", "We don't have an exit?"}}.dump();
}
void
onBadResult(
std::string reason, AbstractRouter& abs, llarp::service::Endpoint_ptr eptr, IPRange range)
{
abs.routePoker()->Down();
eptr->UnmapExitRange(range);
response = nlohmann::json{{"result", reason}}.dump();
}
void
mapExit(
service::Address addr,
AbstractRouter& router,
llarp::service::Endpoint_ptr eptr,
IPRange range,
service::Address exitAddr)
{
eptr->MapExitRange(range, addr);
bool sendAuth = (request.token.empty()) ? false : true;
if (sendAuth)
eptr->SetAuthInfoForEndpoint(exitAddr, service::AuthInfo{request.token});
if (addr.IsZero())
{
onGoodResult("Null exit added", router.HasClientExit());
return;
}
eptr->MarkAddressOutbound(addr);
eptr->EnsurePathToService(addr, [&](auto, service::OutboundContext* ctx) {
if (ctx == nullptr)
{
onBadResult("Could not find exit", router, eptr, range);
return;
}
if (not sendAuth)
{
onGoodResult("OK: connected to " + addr.ToString(), router.HasClientExit());
return;
}
// only lambda that we will keep
ctx->AsyncSendAuth([&](service::AuthResult result) {
if (result.code != service::AuthResultCode::eAuthAccepted)
{
onBadResult(result.reason, router, eptr, range);
return;
}
onGoodResult(result.reason, router.HasClientExit());
return;
});
});
}
};
// RPC: list_exits
// List all currently mapped exit node connections
//
// Inputs: none
//
//
// Returns:
//
struct ListExits : NoArgs
@ -271,15 +209,13 @@ namespace llarp::rpc
struct request_parameters
{
std::vector<std::string> ip_range;
std::vector<IPRange> ip_range;
} request;
};
// RPC: dns_query
// Attempts to query endpoint by domain name
//
// Note: ask Jason about the internals of this
//
// Inputs:
// "endpoint" : endpoint ID to query (string)
// "qname" : query name (string)

View File

@ -58,7 +58,7 @@ namespace llarp::rpc
input,
"address",
mapexit.request.address,
"IP_range",
"ip_range",
mapexit.request.ip_range,
"token",
mapexit.request.token);
@ -67,10 +67,7 @@ namespace llarp::rpc
void
parse_request(UnmapExit& unmapexit, rpc_input input)
{
get_values(
input,
"IP_range",
unmapexit.request.ip_range);
get_values(input, "ip_range", unmapexit.request.ip_range);
}
void

View File

@ -91,11 +91,11 @@ namespace llarp::rpc
register_rpc_command(std::unordered_map<std::string, rpc_callback>& regs)
{
static_assert(std::is_base_of_v<RPCRequest, RPC>);
auto cback = std::make_shared<rpc_callback>();
rpc_callback cback{};
cback->invoke = make_invoke<RPC>();
cback.invoke = make_invoke<RPC>();
regs.emplace(RPC::name, cback);
regs.emplace(RPC::name, std::move(cback));
}
RPCServer::RPCServer(LMQ_ptr lmq, AbstractRouter& r)
@ -355,12 +355,17 @@ namespace llarp::rpc
MapExit exit_request;
// steal replier from exit RPC endpoint
exit_request.replier.emplace(std::move(*mapexit.replier));
//
//
//
//
//
m_Router.hiddenServiceContext().GetDefault()->map_exit(
mapexit.request.address,
mapexit.request.token,
mapexit.request.ip_range,
[exit = std::move(exit_request)](bool success, std::string result) mutable {
if (success)
exit.send_response({{"result"}, std::move(result)});
else
exit.send_response({{"error"}, std::move(result)});
});
}
void
@ -369,8 +374,10 @@ namespace llarp::rpc
if (not m_Router.hiddenServiceContext().hasEndpoints())
listexits.response = CreateJSONError("No mapped endpoints found");
else
listexits.response = CreateJSONResponse(
m_Router.hiddenServiceContext().GetDefault()->ExtractStatus()["m_ExitMap"]);
listexits.response =
CreateJSONResponse(m_Router.hiddenServiceContext().GetDefault()->ExtractStatus()["m_"
"ExitMa"
"p"]);
}
void
@ -382,22 +389,14 @@ namespace llarp::rpc
return;
}
std::vector<IPRange> range{};
for (auto& ip : unmapexit.request.ip_range)
try
{
try {
range.push_back(IPRange::StringInit(ip));
} catch (std::exception& e) {
unmapexit.response = CreateJSONError(e.what());
}
}
try {
m_Router.routePoker()->Down();
for (auto& ip : range)
for (auto& ip : unmapexit.request.ip_range)
m_Router.hiddenServiceContext().GetDefault()->UnmapExitRange(ip);
} catch (std::exception& e) {
}
catch (std::exception& e)
{
unmapexit.response = CreateJSONError("Unable to unmap to given range");
}
@ -439,16 +438,6 @@ namespace llarp::rpc
return;
}
/*
only have simple filename ex: "persist_key.ini"
create .ini files inside conf.d
add delete functionality
delete parameter (bool)
use same filename parameter
*/
void
RPCServer::invoke(Config& config)
{

View File

@ -150,10 +150,6 @@ namespace llarp::rpc
fmt::format("RPC request 'rpc.{}' raised an exception: {}", rpc.name, e.what()));
}
// check if std::optional in rpc is present
// then rpc.send_response
// else
// do nothing because invoke stole RPC
if (rpc.replier.has_value())
rpc.send_response();
}