#include #include #include #include #include #include #include namespace llarp::apple { struct FrameworkContext : public llarp::Context { explicit FrameworkContext(NEPacketTunnelProvider* tunnel); ~FrameworkContext() {} std::shared_ptr makeVPNPlatform() override; void Start(); private: NEPacketTunnelProvider* const m_Tunnel; std::unique_ptr m_Runner; }; class VPNInterface final : public vpn::NetworkInterface { NEPacketTunnelProvider* const m_Tunnel; static inline constexpr auto PacketQueueSize = 1024; thread::Queue m_ReadQueue; void OfferReadPacket(NSData* data) { llarp::net::IPPacket pkt; const llarp_buffer_t buf{static_cast(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* packets, NSArray*) { 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 ObtainInterface(vpn::InterfaceInfo) override { return std::make_shared(m_Tunnel); } }; FrameworkContext::FrameworkContext(NEPacketTunnelProvider* tunnel) : llarp::Context{}, m_Tunnel{tunnel} {} void FrameworkContext::Start() { std::promise result; m_Runner = std::make_unique([&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 FrameworkContext::makeVPNPlatform() { return std::make_shared(m_Tunnel); } } struct ContextWrapper { std::unique_ptr m_Context; public: explicit ContextWrapper(NEPacketTunnelProvider* tunnel) : m_Context{std::make_unique(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(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*)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