1
1
Fork 0
mirror of https://github.com/oxen-io/lokinet synced 2023-12-14 06:53:00 +01:00
lokinet/test/iwp/test_iwp_session.cpp
Thomas Winget 4c630e0437 Large collection of changes to make android work
- Previous android java and jni code updated to work, but with much love
  still needed to make it work nicely, e.g. handling when the VPN is
  turned off.

- DNS handling refactored to allow android to intercept and handle DNS
  requests as we can't set the system DNS to use a high port
  (and apparently Chrome ignores system DNS settings anyway)

- add packet router structure to allow separate handling of specific
  intercepted traffic, e.g. UDP traffic to port 53 gets handled by our
  DNS handler rather than being naively forwarded as exit traffic.

- For now, android lokinet is exit-only and hard-coded to use exit.loki
  as its exit.  The exit will be configurable before release, but
  allowing to not use exit-only mode is more of a challenge.

- some old gitignore remnants which were matching to things we don't
  want them to (and are no longer relevant) removed

- some minor changes to CI configuration
2021-03-02 13:18:22 -05:00

317 lines
10 KiB
C++

#include <catch2/catch.hpp>
#include <crypto/crypto.hpp>
#include <crypto/crypto_libsodium.hpp>
#include <string_view>
#include <router_contact.hpp>
#include <iwp/iwp.hpp>
#include <util/meta/memfn.hpp>
#include <messages/link_message_parser.hpp>
#include <messages/discard.hpp>
#include <util/time.hpp>
#include <net/net_if.hpp>
#undef LOG_TAG
#define LOG_TAG __FILE__
namespace iwp = llarp::iwp;
namespace util = llarp::util;
/// make an iwp link
template <bool inbound, typename... Args_t>
static llarp::LinkLayer_ptr
make_link(Args_t... args)
{
if (inbound)
return iwp::NewInboundLink(args...);
else
return iwp::NewOutboundLink(args...);
}
using Logic_ptr = std::shared_ptr<llarp::Logic>;
/// a single iwp link with associated keys and members to make unit tests work
struct IWPLinkContext
{
llarp::RouterContact rc;
llarp::IpAddress localAddr;
llarp::LinkLayer_ptr link;
std::shared_ptr<llarp::KeyManager> keyManager;
llarp::LinkMessageParser m_Parser;
llarp_ev_loop_ptr m_Loop;
/// is the test done on this context ?
bool gucci = false;
IWPLinkContext(std::string_view addr, llarp_ev_loop_ptr loop)
: localAddr{std::move(addr)}
, keyManager{std::make_shared<llarp::KeyManager>()}
, m_Parser{nullptr}
, m_Loop{std::move(loop)}
{
// generate keys
llarp::CryptoManager::instance()->identity_keygen(keyManager->identityKey);
llarp::CryptoManager::instance()->encryption_keygen(keyManager->encryptionKey);
llarp::CryptoManager::instance()->encryption_keygen(keyManager->transportKey);
// set keys in rc
rc.pubkey = keyManager->identityKey.toPublic();
rc.enckey = keyManager->encryptionKey.toPublic();
}
template <typename Func_t>
void
Call(Func_t work)
{
m_Loop->call_soon(std::move(work));
}
bool
HandleMessage(llarp::ILinkSession* from, const llarp_buffer_t& buf)
{
return m_Parser.ProcessFrom(from, buf);
}
/// initialize link
template <bool inbound>
void
InitLink(std::function<void(llarp::ILinkSession*)> established)
{
link = make_link<inbound>(
keyManager,
m_Loop,
// getrc
[&]() -> const llarp::RouterContact& { return rc; },
// link message handler
util::memFn(&IWPLinkContext::HandleMessage, this),
// sign buffer
[&](llarp::Signature& sig, const llarp_buffer_t& buf) {
REQUIRE(llarp::CryptoManager::instance()->sign(sig, keyManager->identityKey, buf));
return true;
},
// before connect
nullptr,
// established handler
[established](llarp::ILinkSession* s, bool linkIsInbound) {
REQUIRE(s != nullptr);
REQUIRE(inbound == linkIsInbound);
established(s);
return true;
},
// renegotiate handler
[](llarp::RouterContact newrc, llarp::RouterContact oldrc) {
REQUIRE(newrc.pubkey == oldrc.pubkey);
return true;
},
// timeout handler
[&](llarp::ILinkSession*) {
llarp_ev_loop_stop(m_Loop);
FAIL("session timeout");
},
// session closed handler
[](llarp::RouterID) {},
// pump done handler
[]() {},
// do work function
[l = m_Loop](llarp::Work_t work) { l->call_soon(work); });
REQUIRE(link->Configure(
m_Loop, llarp::net::LoopbackInterfaceName(), AF_INET, *localAddr.getPort()));
if (inbound)
{
// only add address info on the recipiant's rc
rc.addrs.emplace_back();
REQUIRE(link->GetOurAddressInfo(rc.addrs.back()));
}
// sign rc
REQUIRE(rc.Sign(keyManager->identityKey));
REQUIRE(keyManager != nullptr);
}
};
using Context_ptr = std::shared_ptr<IWPLinkContext>;
/// run an iwp unit test after setup
/// call take 2 parameters, test and a timeout
///
/// test is a callable that takes 5 arguments:
/// 0) std::function<Logic_ptr(void)> that starts the iwp links and gives a logic to call with
/// 1) std::function<void(void)> that ends the unit test if we are done
/// 2) std::function<void(void)> that ends the unit test right now as a success
/// 3) client iwp link context (shared_ptr)
/// 4) relay iwp link context (shared_ptr)
///
/// timeout is a std::chrono::duration that tells the driver how long to run the unit test for
/// before it should assume failure of unit test
template <typename Func_t, typename Duration_t = std::chrono::milliseconds>
void
RunIWPTest(Func_t test, Duration_t timeout = 10s)
{
// shut up logs
llarp::LogSilencer shutup;
// set up event loop
auto logic = std::make_shared<llarp::Logic>();
auto loop = llarp_make_ev_loop();
loop->set_logic(logic);
llarp::LogContext::Instance().Initialize(
llarp::eLogDebug, llarp::LogType::File, "stdout", "unit test", [loop](auto work) {
loop->call_soon(work);
});
// turn off bogon blocking
auto oldBlockBogons = llarp::RouterContact::BlockBogons;
llarp::RouterContact::BlockBogons = false;
// set up cryptography
llarp::sodium::CryptoLibSodium crypto{};
llarp::CryptoManager manager{&crypto};
// set up client
auto initiator = std::make_shared<IWPLinkContext>("127.0.0.1:3001", loop);
// set up server
auto recipiant = std::make_shared<IWPLinkContext>("127.0.0.1:3002", loop);
// function for ending unit test on success
auto endIfDone = [initiator, recipiant, loop, logic]() {
if (initiator->gucci and recipiant->gucci)
{
LogicCall(logic, [loop]() { llarp_ev_loop_stop(loop); });
}
};
// function to start test and give logic to unit test
auto start = [initiator, recipiant, logic]() {
REQUIRE(initiator->link->Start(logic));
REQUIRE(recipiant->link->Start(logic));
return logic;
};
// function to end test immediately
auto endTest = [logic, loop]() { LogicCall(logic, [loop]() { llarp_ev_loop_stop(loop); }); };
loop->call_after_delay(
std::chrono::duration_cast<llarp_time_t>(timeout), []() { FAIL("test timeout"); });
test(start, endIfDone, endTest, initiator, recipiant);
llarp_ev_loop_run_single_process(loop, logic);
llarp::RouterContact::BlockBogons = oldBlockBogons;
}
/// ensure clients can connect to relays
TEST_CASE("IWP handshake", "[iwp]")
{
RunIWPTest([](std::function<Logic_ptr(void)> start,
std::function<void(void)> endIfDone,
[[maybe_unused]] std::function<void(void)> endTestNow,
Context_ptr alice,
Context_ptr bob) {
// set up initiator
alice->InitLink<false>([=](auto remote) {
REQUIRE(remote->GetRemoteRC() == bob->rc);
alice->gucci = true;
endIfDone();
});
// set up recipiant
bob->InitLink<true>([=](auto remote) {
REQUIRE(remote->GetRemoteRC() == alice->rc);
bob->gucci = true;
endIfDone();
});
// start unit test
auto logic = start();
// try establishing a session
LogicCall(logic, [link = alice->link, rc = bob->rc]() { REQUIRE(link->TryEstablishTo(rc)); });
});
}
/// ensure relays cannot connect to clients
TEST_CASE("IWP handshake reverse", "[iwp]")
{
RunIWPTest([](std::function<Logic_ptr(void)> start,
[[maybe_unused]] std::function<void(void)> endIfDone,
std::function<void(void)> endTestNow,
Context_ptr alice,
Context_ptr bob) {
alice->InitLink<false>([](auto) {});
bob->InitLink<true>([](auto) {});
// start unit test
auto logic = start();
// try establishing a session in the wrong direction
LogicCall(logic, [logic, link = bob->link, rc = alice->rc, endTestNow]() {
REQUIRE(not link->TryEstablishTo(rc));
endTestNow();
});
});
}
/// ensure iwp can send messages between sessions
TEST_CASE("IWP send messages", "[iwp]")
{
int aliceNumSent = 0;
int bobNumSent = 0;
RunIWPTest([&aliceNumSent, &bobNumSent](std::function<Logic_ptr(void)> start,
std::function<void(void)> endIfDone,
std::function<void(void)> endTestNow,
Context_ptr alice,
Context_ptr bob) {
constexpr int numSend = 128;
// when alice makes a session to bob send `aliceNumSend` messages to him
alice->InitLink<false>([endIfDone, alice, &aliceNumSent](auto session) {
for (auto index = 0; index < numSend; index++)
{
alice->Call([session, endIfDone, alice, &aliceNumSent]() {
// generate a discard message that is 512 bytes long
llarp::DiscardMessage msg;
std::vector<byte_t> msgBuff(512);
llarp_buffer_t buf(msgBuff);
// add random padding
llarp::CryptoManager::instance()->randomize(buf);
// encode the discard message
msg.BEncode(&buf);
// send the message
session->SendMessageBuffer(msgBuff, [endIfDone, alice, &aliceNumSent](auto status) {
if (status == llarp::ILinkSession::DeliveryStatus::eDeliverySuccess)
{
// on successful transmit increment the number we sent
aliceNumSent++;
}
// if we sent all the messages sucessfully we end the unit test
alice->gucci = aliceNumSent == numSend;
endIfDone();
});
});
}
});
bob->InitLink<true>([endIfDone, bob, &bobNumSent](auto session) {
for (auto index = 0; index < numSend; index++)
{
bob->Call([session, endIfDone, bob, &bobNumSent]() {
// generate a discard message that is 512 bytes long
llarp::DiscardMessage msg;
std::vector<byte_t> msgBuff(512);
llarp_buffer_t buf(msgBuff);
// add random padding
llarp::CryptoManager::instance()->randomize(buf);
// encode the discard message
msg.BEncode(&buf);
// send the message
session->SendMessageBuffer(msgBuff, [endIfDone, bob, &bobNumSent](auto status) {
if (status == llarp::ILinkSession::DeliveryStatus::eDeliverySuccess)
{
// on successful transmit increment the number we sent
bobNumSent++;
}
// if we sent all the messages sucessfully we end the unit test
bob->gucci = bobNumSent == numSend;
endIfDone();
});
});
}
});
// start unit test
auto logic = start();
// try establishing a session from alice to bob
LogicCall(logic, [logic, link = alice->link, rc = bob->rc, endTestNow]() {
REQUIRE(link->TryEstablishTo(rc));
});
});
}