mirror of
https://github.com/oxen-io/lokinet.git
synced 2024-11-03 23:15:52 +00:00
216 lines
4.8 KiB
Plaintext
216 lines
4.8 KiB
Plaintext
#include <lokinet-extension.hpp>
|
|
#include <llarp.hpp>
|
|
|
|
#include <llarp/config/config.hpp>
|
|
#include <llarp/ev/vpn.hpp>
|
|
#include <llarp/util/thread/queue.hpp>
|
|
#include <llarp/util/logging/apple_logger.hpp>
|
|
|
|
#include <string>
|
|
|
|
namespace llarp::apple
|
|
{
|
|
struct FrameworkContext : public llarp::Context
|
|
{
|
|
explicit FrameworkContext(NEPacketTunnelProvider* tunnel);
|
|
|
|
~FrameworkContext()
|
|
{}
|
|
|
|
std::shared_ptr<vpn::Platform>
|
|
makeVPNPlatform() override;
|
|
|
|
void
|
|
Start();
|
|
|
|
private:
|
|
NEPacketTunnelProvider* const m_Tunnel;
|
|
std::unique_ptr<std::thread> m_Runner;
|
|
};
|
|
|
|
class VPNInterface final : public vpn::NetworkInterface
|
|
{
|
|
NEPacketTunnelProvider* const m_Tunnel;
|
|
|
|
static inline constexpr auto PacketQueueSize = 1024;
|
|
|
|
thread::Queue<net::IPPacket> m_ReadQueue;
|
|
|
|
void
|
|
OfferReadPacket(NSData* data)
|
|
{
|
|
llarp::net::IPPacket pkt;
|
|
const llarp_buffer_t buf{static_cast<const uint8_t*>(data.bytes), data.length};
|
|
if (pkt.Load(buf))
|
|
m_ReadQueue.tryPushBack(std::move(pkt));
|
|
}
|
|
|
|
public:
|
|
explicit VPNInterface(NEPacketTunnelProvider* tunnel)
|
|
: m_Tunnel{tunnel}, m_ReadQueue{PacketQueueSize}
|
|
{
|
|
auto handler = [this](NSArray<NSData*>* packets, NSArray<NSNumber*>*) {
|
|
NSUInteger num = [packets count];
|
|
for (NSUInteger idx = 0; idx < num; ++idx)
|
|
{
|
|
NSData* pkt = [packets objectAtIndex:idx];
|
|
OfferReadPacket(pkt);
|
|
}
|
|
};
|
|
[m_Tunnel.packetFlow readPacketsWithCompletionHandler:handler];
|
|
}
|
|
|
|
int
|
|
PollFD() const override
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
std::string
|
|
IfName() const override
|
|
{
|
|
return "";
|
|
}
|
|
|
|
net::IPPacket
|
|
ReadNextPacket() override
|
|
{
|
|
net::IPPacket pkt{};
|
|
if (not m_ReadQueue.empty())
|
|
pkt = m_ReadQueue.popFront();
|
|
return pkt;
|
|
}
|
|
|
|
bool
|
|
WritePacket(net::IPPacket pkt) override
|
|
{
|
|
const sa_family_t fam = pkt.IsV6() ? AF_INET6 : AF_INET;
|
|
const uint8_t* pktbuf = pkt.buf;
|
|
const size_t pktsz = pkt.sz;
|
|
NSData* datapkt = [NSData dataWithBytes:pktbuf length:pktsz];
|
|
NEPacket* npkt = [[NEPacket alloc] initWithData:datapkt protocolFamily:fam];
|
|
NSArray* pkts = @[npkt];
|
|
return [m_Tunnel.packetFlow writePacketObjects:pkts];
|
|
}
|
|
};
|
|
|
|
class VPNPlatform final : public vpn::Platform
|
|
{
|
|
NEPacketTunnelProvider* const m_Tunnel;
|
|
|
|
public:
|
|
explicit VPNPlatform(NEPacketTunnelProvider* tunnel) : m_Tunnel{tunnel}
|
|
{}
|
|
|
|
std::shared_ptr<vpn::NetworkInterface> ObtainInterface(vpn::InterfaceInfo) override
|
|
{
|
|
return std::make_shared<VPNInterface>(m_Tunnel);
|
|
}
|
|
};
|
|
|
|
FrameworkContext::FrameworkContext(NEPacketTunnelProvider* tunnel)
|
|
: llarp::Context{}, m_Tunnel{tunnel}
|
|
{}
|
|
|
|
void
|
|
FrameworkContext::Start()
|
|
{
|
|
std::promise<void> result;
|
|
|
|
m_Runner = std::make_unique<std::thread>([&result, this]() {
|
|
const RuntimeOptions opts{};
|
|
try
|
|
{
|
|
Setup(opts);
|
|
Configure(llarp::Config::NetworkExtensionConfig());
|
|
}
|
|
catch (std::exception&)
|
|
{
|
|
result.set_exception(std::current_exception());
|
|
return;
|
|
}
|
|
result.set_value();
|
|
Run(opts);
|
|
});
|
|
|
|
auto ftr = result.get_future();
|
|
ftr.get();
|
|
}
|
|
|
|
std::shared_ptr<vpn::Platform>
|
|
FrameworkContext::makeVPNPlatform()
|
|
{
|
|
return std::make_shared<VPNPlatform>(m_Tunnel);
|
|
}
|
|
}
|
|
|
|
struct ContextWrapper
|
|
{
|
|
std::unique_ptr<llarp::apple::FrameworkContext> m_Context;
|
|
|
|
public:
|
|
explicit ContextWrapper(NEPacketTunnelProvider* tunnel)
|
|
: m_Context{std::make_unique<llarp::apple::FrameworkContext>(tunnel)}
|
|
{}
|
|
|
|
void
|
|
Start()
|
|
{
|
|
m_Context->Start();
|
|
}
|
|
|
|
void
|
|
Stop()
|
|
{
|
|
m_Context->CloseAsync();
|
|
m_Context->Wait();
|
|
}
|
|
};
|
|
|
|
static std::string_view
|
|
DataAsStringView(NSData* data)
|
|
{
|
|
return std::string_view{reinterpret_cast<const char*>(data.bytes), data.length};
|
|
}
|
|
|
|
static NSData*
|
|
StringViewToData(std::string_view data)
|
|
{
|
|
const char* ptr = data.data();
|
|
const size_t sz = data.size();
|
|
return [NSData dataWithBytes:ptr length:sz];
|
|
}
|
|
|
|
@implementation LLARPPacketTunnel
|
|
|
|
- (void)startTunnelWithOptions:(NSDictionary<NSString*, NSObject*>*)options
|
|
completionHandler:(void (^)(NSError*))completionHandler
|
|
{
|
|
m_Context = new ContextWrapper{self};
|
|
m_Context->Start();
|
|
[self setTunnelNetworkSettings:nullptr completionHandler:completionHandler];
|
|
}
|
|
|
|
- (void)stopTunnelWithReason:(NEProviderStopReason)reason
|
|
completionHandler:(void (^)(void))completionHandler
|
|
{
|
|
if (m_Context)
|
|
{
|
|
m_Context->Stop();
|
|
delete m_Context;
|
|
m_Context = nullptr;
|
|
}
|
|
completionHandler();
|
|
}
|
|
|
|
- (void)handleAppMessage:(NSData*)messageData
|
|
completionHandler:(void (^)(NSData* responseData))completionHandler
|
|
{
|
|
const auto data = DataAsStringView(messageData);
|
|
LogInfo("app message: ", data);
|
|
|
|
completionHandler(StringViewToData("ok"));
|
|
}
|
|
|
|
@end
|