macos sort of works now

pull/1688/head
jeff 3 years ago committed by Jeff Becker
parent 81d27c35c1
commit 7db2459469
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.10) # bionic's cmake version
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Has to be set before `project()`, and ignored on non-macos:
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12 CACHE STRING "macOS deployment target (Apple clang only)")
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15 CACHE STRING "macOS deployment target (Apple clang only)")
set(LANGS ASM C CXX)
if(APPLE)

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>Lokinet</string>
<key>CFBundleExecutable</key>
<string>lokinet-dnsproxy</string>
<key>CFBundleIdentifier</key>
<string>com.loki-project.lokinet.dns-proxy</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleName</key>
<string>lokinet</string>
<key>CFBundleVersion</key>
<string>@LOKINET_VERSION@</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.networkextension.dns-proxy</string>
<key>NSExtensionPrincipalClass</key>
<string>DNSProvider</string>
</dict>
</dict>
</plist>

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.application-identifier</key>
<string>SUQ8J2PCT7.com.loki-project.lokinet.dns-proxy</string>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>dns-proxy</string>
</array>
<key>com.apple.developer.team-identifier</key>
<string>SUQ8J2PCT7</string>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>

@ -8,6 +8,8 @@
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>packet-tunnel-provider</string>
<string>dns-proxy</string>
<string>dns-settings</string>
</array>
<key>com.apple.developer.team-identifier</key>
@ -18,5 +20,12 @@
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>

@ -3,6 +3,9 @@ set -e
codesign --verbose=4 --force -s "@CODESIGN_APPEX@" \
--entitlements "@PROJECT_SOURCE_DIR@/contrib/macos/lokinet-extension.entitlements.plist" \
--deep --strict --timestamp --options=runtime "@SIGN_TARGET@/Contents/PlugIns/lokinet-extension.appex"
codesign --verbose=4 --force -s "@CODESIGN_APPEX@" \
--entitlements "@PROJECT_SOURCE_DIR@/contrib/macos/lokinet-dnsproxy.entitlements.plist" \
--deep --strict --timestamp --options=runtime "@SIGN_TARGET@/Contents/PlugIns/lokinet-dnsproxy.appex"
for file in "@SIGN_TARGET@/Contents/MacOS/lokinet" "@SIGN_TARGET@" ; do
codesign --verbose=4 --force -s "@CODESIGN_APP@" \
--entitlements "@PROJECT_SOURCE_DIR@/contrib/macos/lokinet.entitlements.plist" \

@ -1,14 +1,17 @@
add_executable(lokinet-vpn lokinet-vpn.cpp)
if(APPLE)
set(LOKINET_SWIFT_SOURCES lokinet.swift)
add_executable(lokinet ${LOKINET_SWIFT_SOURCES})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Lokinet.modulemap.in ${CMAKE_CURRENT_BINARY_DIR}/swift/LokinetExtension/module.modulemap ESCAPE_QUOTES @ONLY)
target_include_directories(lokinet PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/swift)
else()
add_executable(lokinet lokinet.cpp)
add_executable(lokinet-vpn lokinet-vpn.cpp)
add_executable(lokinet-bootstrap lokinet-bootstrap.cpp)
enable_lto(lokinet lokinet-vpn lokinet-bootstrap)
endif()
if(TRACY_ROOT)
target_sources(lokinet PRIVATE ${TRACY_ROOT}/TracyClient.cpp)
endif()
@ -38,9 +41,9 @@ if(NOT APPLE)
endif()
endif()
set(exetargets lokinet)
set(exetargets lokinet lokinet-vpn)
if(NOT APPLE)
list(APPEND exetargets lokinet-vpn lokinet-bootstrap)
list(APPEND exetargets lokinet-bootstrap)
endif()
foreach(exe ${exetargets})
@ -76,10 +79,14 @@ if(APPLE)
COMMAND ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh ${PROJECT_SOURCE_DIR}/contrib/lokinet.svg ${CMAKE_CURRENT_BINARY_DIR}/lokinet.icns
DEPENDS ${PROJECT_SOURCE_DIR}/contrib/lokinet.svg ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh)
add_dependencies(lokinet icons lokinet-extension)
file(DOWNLOAD "https://seed.lokinet.org/lokinet.signed" ${CMAKE_CURRENT_BINARY_DIR}/bootstrap.signed)
add_custom_command(TARGET lokinet
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/bootstrap.signed
$<TARGET_BUNDLE_DIR:lokinet-extension>/Contents/Resources/bootstrap.signed
COMMAND mkdir -p $<TARGET_BUNDLE_DIR:lokinet>/Contents/PlugIns
COMMAND cp -au $<TARGET_BUNDLE_DIR:lokinet-extension> $<TARGET_BUNDLE_DIR:lokinet>/Contents/PlugIns/
COMMAND cp -a $<TARGET_BUNDLE_DIR:lokinet-extension> $<TARGET_BUNDLE_DIR:lokinet>/Contents/PlugIns/
COMMAND cp -a $<TARGET_BUNDLE_DIR:lokinet-dnsproxy> $<TARGET_BUNDLE_DIR:lokinet>/Contents/PlugIns/
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_SOURCE_DIR}/contrib/macos/lokinet.provisionprofile
$<TARGET_BUNDLE_DIR:lokinet>/Contents/embedded.provisionprofile
)
@ -105,7 +112,7 @@ if(APPLE)
@ONLY)
add_custom_target(
sign
DEPENDS "${PROJECT_BINARY_DIR}/sign.sh" lokinet lokinet-extension
DEPENDS "${PROJECT_BINARY_DIR}/sign.sh" lokinet lokinet-extension lokinet-dnsproxy
COMMAND "${PROJECT_BINARY_DIR}/sign.sh"
)
else()

@ -8,7 +8,7 @@ let app = NSApplication.shared
class LokinetMain: NSObject, NSApplicationDelegate {
var vpnManager = NETunnelProviderManager()
let lokinetComponent = "com.loki-project.lokinet.network-extension"
var lokinetAdminTimer: DispatchSourceTimer?
var dnsComponent = "com.loki-project.lokinet.dns-proxy"
func applicationDidFinishLaunching(_: Notification) {
setupVPNJizz()
@ -18,12 +18,67 @@ class LokinetMain: NSObject, NSApplicationDelegate {
app.terminate(self)
}
func setupDNSJizz() {
NSLog("setting up dns settings")
let dns = NEDNSSettingsManager.shared()
let settings = NEDNSSettings(servers: ["172.16.0.1"])
dns.dnsSettings = settings
dns.loadFromPreferences { [self] (error: Error?) -> Void in
if let error = error {
NSLog(error.localizedDescription)
bail()
return
}
dns.saveToPreferences { [self] (error: Error?) -> Void in
if let error = error {
NSLog(error.localizedDescription)
bail()
return
}
NSLog("dns setting set up probably")
}
}
}
func setupDNSProxyJizz() {
NSLog("setting up dns proxy")
let dns = NEDNSProxyManager.shared()
let provider = NEDNSProxyProviderProtocol()
provider.providerBundleIdentifier = dnsComponent
provider.username = "Anonymous"
provider.serverAddress = "loki.loki"
provider.includeAllNetworks = true
provider.enforceRoutes = true
dns.providerProtocol = provider
dns.localizedDescription = "lokinet dns"
dns.loadFromPreferences { [self] (error: Error?) -> Void in
if let error = error {
NSLog(error.localizedDescription)
bail()
return
}
provider.includeAllNetworks = true
provider.enforceRoutes = true
dns.isEnabled = true
dns.saveToPreferences { [self] (error: Error?) -> Void in
if let error = error {
NSLog(error.localizedDescription)
bail()
return
}
self.initDNSObserver()
NSLog("dns is up probably")
}
}
}
func setupVPNJizz() {
NSLog("Starting up lokinet")
NETunnelProviderManager.loadAllFromPreferences { [self] (savedManagers: [NETunnelProviderManager]?, error: Error?) in
if let error = error {
NSLog(error.localizedDescription)
bail()
return
}
if let savedManagers = savedManagers {
@ -44,7 +99,10 @@ class LokinetMain: NSObject, NSApplicationDelegate {
providerProtocol.includeAllNetworks = false
self.vpnManager.protocolConfiguration = providerProtocol
self.vpnManager.isEnabled = true
self.vpnManager.isOnDemandEnabled = true
// self.vpnManager.isOnDemandEnabled = true
let rules = NEAppRule()
rules.matchDomains = ["*.snode", "*.loki"]
self.vpnManager.appRules = [rules]
self.vpnManager.localizedDescription = "lokinet"
self.vpnManager.saveToPreferences(completionHandler: { error -> Void in
if error != nil {
@ -76,6 +134,13 @@ class LokinetMain: NSObject, NSApplicationDelegate {
}
}
func initDNSObserver() {
NotificationCenter.default.addObserver(forName: NSNotification.Name.NEDNSProxyConfigurationDidChange, object: NEDNSProxyManager.shared(), queue: OperationQueue.main) { _ -> Void in
let dns = NEDNSProxyManager.shared()
NSLog("%@", dns)
}
}
func initializeConnectionObserver() {
NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: vpnManager.connection, queue: OperationQueue.main) { _ -> Void in
if self.vpnManager.connection.status == .invalid {
@ -88,6 +153,9 @@ class LokinetMain: NSObject, NSApplicationDelegate {
NSLog("VPN is reasserting...")
} else if self.vpnManager.connection.status == .disconnecting {
NSLog("VPN is disconnecting...")
} else if self.vpnManager.connection.status == .connected {
NSLog("VPN Connected")
self.setupDNSJizz()
}
}
}

@ -0,0 +1,19 @@
#pragma once
#include <Foundation/Foundation.h>
#include <NetworkExtension/NetworkExtension.h>
struct DNSImpl;
@interface DNSProvider : NEDNSProxyProvider
{
struct DNSImpl* m_Impl;
}
- (void)startProxyWithOptions:(NSDictionary<NSString*, id>*)options
completionHandler:(void (^)(NSError* error))completionHandler;
- (void)stopProxyWithReason:(NEProviderStopReason)reason
completionHandler:(void (^)(void))completionHandler;
- (BOOL)handleNewFlow:(NEAppProxyFlow*)flow;
@end

@ -1,14 +1,12 @@
include(Version)
add_library(lokinet-util
STATIC
set(lokinet_util_src
${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp
util/bencode.cpp
util/buffer.cpp
util/fs.cpp
util/json.cpp
util/logging/android_logger.cpp
util/logging/apple_logger.mm
util/logging/buffer.cpp
util/logging/file_logger.cpp
util/logging/json_logger.cpp
@ -24,8 +22,18 @@ add_library(lokinet-util
util/str.cpp
util/thread/queue_manager.cpp
util/thread/threading.cpp
util/time.cpp
)
util/time.cpp)
if(APPLE)
list(APPEND lokinet_util_src
util/logging/apple_logger.mm)
endif()
add_library(lokinet-util
STATIC
${lokinet_util_src})
add_dependencies(lokinet-util genversion)
target_include_directories(lokinet-util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR})
@ -274,11 +282,16 @@ if(APPLE)
find_library(COREFOUNDATION CoreFoundation REQUIRED)
add_executable(lokinet-extension MACOSX_BUNDLE framework.mm)
add_executable(lokinet-dnsproxy MACOSX_BUNDLE dnsproxy.mm)
target_link_libraries(lokinet-extension PUBLIC
liblokinet
${COREFOUNDATION}
${NETEXT})
target_link_libraries(lokinet-dnsproxy PUBLIC
liblokinet
${COREFOUNDATION}
${NETEXT})
set_target_properties(lokinet-extension PROPERTIES
BUNDLE TRUE
BUNDLE_EXTENSION appex
@ -289,7 +302,7 @@ if(APPLE)
# Not sure what -fapplication-extension does, but XCode puts it in so...
# -e _NSExtensionMain because it has that instead of a `main` function entry point, of course.
target_link_options(lokinet-extension PRIVATE -fapplication-extension -e _NSExtensionMain)
target_compile_options(lokinet-extension PRIVATE -fapplication-extension)
target_compile_options(lokinet-extension PRIVATE -fapplication-extension -fobjc-arc)
add_custom_command(TARGET lokinet-extension
POST_BUILD
@ -297,6 +310,21 @@ if(APPLE)
$<TARGET_BUNDLE_DIR:lokinet-extension>/Contents/embedded.provisionprofile
)
set_target_properties(lokinet-dnsproxy PROPERTIES
BUNDLE TRUE
BUNDLE_EXTENSION appex
MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/contrib/macos/LokinetDNSProxy.Info.plist.in
XCODE_PRODUCT_TYPE com.apple.product-type.app-extension
)
target_link_options(lokinet-dnsproxy PRIVATE -fapplication-extension -e _NSExtensionMain)
target_compile_options(lokinet-dnsproxy PRIVATE -fapplication-extension -fobjc-arc)
add_custom_command(TARGET lokinet-dnsproxy
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_SOURCE_DIR}/contrib/macos/lokinet-dnsproxy.provisionprofile
$<TARGET_BUNDLE_DIR:lokinet-dnsproxy>/Contents/embedded.provisionprofile
)
endif()

@ -0,0 +1,40 @@
#pragma once
#ifdef __APPLE__
#include <string>
#include <string_view>
#include <Foundation/Foundation.h>
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];
}
static NSString*
StringToNSString(std::string data)
{
NSData* ptr = StringViewToData(std::string_view{data});
return [[NSString alloc] initWithData:ptr encoding:NSUTF8StringEncoding];
}
static std::string
NSStringToString(NSString* str)
{
return std::string{[str UTF8String]};
}
static std::string
NSObjectToString(NSObject* obj)
{
return NSStringToString([NSString stringWithFormat:@"%@", obj]);
}
#endif

@ -1453,7 +1453,6 @@ namespace llarp
auto config = std::make_shared<Config>(fs::path{});
config->Load();
config->logging.m_logLevel = eLogInfo;
config->api.m_enableRPCServer = false;
config->network.m_saveProfiles = false;
config->bootstrap.files.clear();
return config;

@ -48,6 +48,12 @@ namespace llarp
return true;
}
util::StatusObject
MessageHeader::ToJSON() const
{
return util::StatusObject{};
}
Message::Message(Message&& other)
: hdr_id(std::move(other.hdr_id))
, hdr_fields(std::move(other.hdr_fields))
@ -74,6 +80,11 @@ namespace llarp
additional.resize(size_t(hdr.ar_count));
}
Message::Message(const Question& question) : hdr_id{0}, hdr_fields{}
{
questions.emplace_back(question);
}
bool
Message::Encode(llarp_buffer_t* buf) const
{
@ -122,6 +133,22 @@ namespace llarp
return true;
}
util::StatusObject
Message::ToJSON() const
{
std::vector<util::StatusObject> ques;
std::vector<util::StatusObject> ans;
for (const auto& q : questions)
{
ques.push_back(q.ToJSON());
}
for (const auto& a : answers)
{
ans.push_back(a.ToJSON());
}
return util::StatusObject{{"questions", ques}, {"answers", ans}};
}
OwnedBuffer
Message::ToBuffer() const
{

@ -33,6 +33,9 @@ namespace llarp
bool
Decode(llarp_buffer_t* buf) override;
util::StatusObject
ToJSON() const override;
bool
operator==(const MessageHeader& other) const
{
@ -44,11 +47,15 @@ namespace llarp
struct Message : public Serialize
{
Message(const MessageHeader& hdr);
explicit Message(const MessageHeader& hdr);
explicit Message(const Question& question);
Message(Message&& other);
Message(const Message& other);
util::StatusObject
ToJSON() const override;
void
AddNXReply(RR_TTL_t ttl = 1);

@ -3,6 +3,7 @@
#include <llarp/util/logging/logger.hpp>
#include <llarp/util/printer.hpp>
#include <llarp/util/str.hpp>
#include "dns.hpp"
namespace llarp
{
@ -17,6 +18,13 @@ namespace llarp
: qname(other.qname), qtype(other.qtype), qclass(other.qclass)
{}
Question::Question(std::string name, QType_t type)
: qname{std::move(name)}, qtype{type}, qclass{qClassIN}
{
if (qname.empty())
throw std::invalid_argument{"qname cannot be empty"};
}
bool
Question::Encode(llarp_buffer_t* buf) const
{
@ -48,6 +56,12 @@ namespace llarp
return true;
}
util::StatusObject
Question::ToJSON() const
{
return util::StatusObject{{"qname", qname}, {"qtype", qtype}, {"qclass", qclass}};
}
bool
Question::IsName(const std::string& other) const
{

@ -14,6 +14,9 @@ namespace llarp
struct Question : public Serialize
{
Question() = default;
explicit Question(std::string name, QType_t type);
Question(Question&& other);
Question(const Question& other);
bool
@ -58,6 +61,9 @@ namespace llarp
/// determine if we are using this TLD
bool
HasTLD(const std::string& tld) const;
util::StatusObject
ToJSON() const override;
};
inline std::ostream&

@ -24,6 +24,14 @@ namespace llarp
, rData(std::move(other.rData))
{}
ResourceRecord::ResourceRecord(Name_t name, RRType_t type, RR_RData_t data)
: rr_name{std::move(name)}
, rr_type{type}
, rr_class{qClassIN}
, ttl{1}
, rData{std::move(data)}
{}
bool
ResourceRecord::Encode(llarp_buffer_t* buf) const
{
@ -77,6 +85,17 @@ namespace llarp
return true;
}
util::StatusObject
ResourceRecord::ToJSON() const
{
return util::StatusObject{
{"name", rr_name},
{"type", rr_type},
{"class", rr_class},
{"ttl", ttl},
{"rdata", std::string{reinterpret_cast<const char*>(rData.data()), rData.size()}}};
}
std::ostream&
ResourceRecord::print(std::ostream& stream, int level, int spaces) const
{

@ -22,12 +22,17 @@ namespace llarp
ResourceRecord(const ResourceRecord& other);
ResourceRecord(ResourceRecord&& other);
explicit ResourceRecord(Name_t name, RRType_t type, RR_RData_t rdata);
bool
Encode(llarp_buffer_t* buf) const override;
bool
Decode(llarp_buffer_t* buf) override;
util::StatusObject
ToJSON() const override;
std::ostream&
print(std::ostream& stream, int level, int spaces) const;

@ -1,7 +1,7 @@
#pragma once
#include <llarp/util/buffer.hpp>
#include <llarp/util/status.hpp>
#include <vector>
namespace llarp
@ -20,6 +20,10 @@ namespace llarp
/// decode entity from buffer
virtual bool
Decode(llarp_buffer_t* buf) = 0;
/// convert this whatever into json
virtual util::StatusObject
ToJSON() const = 0;
};
bool

@ -0,0 +1,154 @@
#include <lokinet-dnsproxy.hpp>
#include <llarp/apple.hpp>
#include <oxenmq/oxenmq.h>
#include <llarp/util/logging/logger.hpp>
#include <thread>
#include <memory>
#include <llarp/util/buffer.hpp>
#include <llarp/dns/message.hpp>
struct DNSImpl
{
oxenmq::OxenMQ m_MQ;
std::optional<oxenmq::ConnectionID> m_Conn;
explicit DNSImpl(oxenmq::address rpc)
{
m_MQ.start();
m_MQ.connect_remote(
rpc, [this](auto conn) { m_Conn = conn; }, nullptr);
}
bool
ShouldHookFlow(NEAppProxyFlow* flow) const
{
LogInfo(NSObjectToString(flow));
return true;
}
void
RelayDNSData(NEAppProxyUDPFlow* flow, NWEndpoint* remote, NSData* data)
{
if (not m_Conn)
return;
auto view = DataAsStringView(data);
llarp_buffer_t buf{view};
llarp::dns::MessageHeader hdr{};
if (not hdr.Decode(&buf))
return;
llarp::dns::Message msg{hdr};
if (not msg.Decode(&buf))
return;
llarp::util::StatusObject request{
{"qname", msg.questions[0].qname}, {"qtype", msg.questions[0].qtype}};
m_MQ.request(
*m_Conn,
"llarp.dns_query",
[flow, remote, msg = std::make_shared<llarp::dns::Message>(std::move(msg))](
bool good, std::vector<std::string> parts) {
auto closeHandler = [flow](NSError* err) {
[flow closeWriteWithError:err];
[flow closeReadWithError:err];
};
if (good and parts.size() == 1)
{
try
{
const auto obj = nlohmann::json::parse(parts[0]);
const auto result = obj["result"];
if (const auto itr = result.find("answers"); itr != result.end())
{
for (const auto& result : (*itr))
{
llarp::dns::RR_RData_t rdata;
if (const auto data_itr = result.find("rdata"); data_itr != result.end())
{
const auto data = data_itr->get<std::string>();
rdata.resize(data.size());
std::copy_n(data.begin(), data.size(), rdata.begin());
}
else
continue;
msg->answers.emplace_back(
result["name"].get<std::string>(),
result["type"].get<llarp::dns::RRType_t>(),
rdata);
}
}
}
catch (std::exception& ex)
{
LogError("dns query failed: ", ex.what());
return;
}
const auto buf = msg->ToBuffer();
NSData* data = StringViewToData(
std::string_view{reinterpret_cast<const char*>(buf.buf.get()), buf.sz});
[flow writeDatagrams:@[data] sentByEndpoints:@[remote] completionHandler:closeHandler];
}
else
closeHandler(nullptr);
},
request.dump());
}
void
HandleUDPFlow(NEAppProxyUDPFlow* flow)
{
auto handler =
[this, flow](
NSArray<NSData*>* datagrams, NSArray<NWEndpoint*>* remoteEndpoints, NSError* error) {
if (error)
return;
NSInteger num = [datagrams count];
for (NSInteger idx = 0; idx < num; ++idx)
{
RelayDNSData(flow, [remoteEndpoints objectAtIndex:idx], [datagrams objectAtIndex:idx]);
}
};
[flow readDatagramsWithCompletionHandler:handler];
}
};
@implementation DNSProvider
- (void)startProxyWithOptions:(NSDictionary<NSString*, id>*)options
completionHandler:(void (^)(NSError* error))completionHandler
{
m_Impl = new DNSImpl{oxenmq::address{"tcp://127.0.0.1:1190"}};
completionHandler(nil);
}
- (void)stopProxyWithReason:(NEProviderStopReason)reason
completionHandler:(void (^)(void))completionHandler
{
if (m_Impl)
{
delete m_Impl;
m_Impl = nullptr;
}
completionHandler();
}
- (BOOL)handleNewFlow:(NEAppProxyFlow*)flow
{
if (not [flow isKindOfClass:[NEAppProxyUDPFlow class]])
return NO;
if (m_Impl->ShouldHookFlow(flow))
{
NEAppProxyUDPFlow* udp = (NEAppProxyUDPFlow*)flow;
auto handler = [impl = m_Impl, udp](NSError* err) {
if (err)
return;
impl->HandleUDPFlow(udp);
};
[flow openWithLocalEndpoint:nil completionHandler:handler];
return YES;
}
return NO;
}
@end

@ -244,7 +244,7 @@ namespace llarp::uv
std::shared_ptr<llarp::vpn::NetworkInterface> netif,
std::function<void(llarp::net::IPPacket)> handler)
{
#ifndef _WIN32
#ifdef __linux__
using event_t = uvw::PollEvent;
auto handle = m_Impl->resource<uvw::PollHandle>(netif->PollFD());
#else
@ -264,7 +264,7 @@ namespace llarp::uv
}
});
#ifndef _WIN32
#ifdef __linux__
handle->start(uvw::PollHandle::Event::READABLE);
#else
handle->start();

@ -5,8 +5,13 @@
#include <llarp/ev/vpn.hpp>
#include <llarp/util/thread/queue.hpp>
#include <llarp/util/logging/apple_logger.hpp>
#include <llarp/util/logging/buffer.hpp>
#include <llarp/net/ip_range.hpp>
#include <llarp/net/sock_addr.hpp>
#include <llarp/apple.hpp>
#include <string>
const llarp::SockAddr DefaultDNSBind{"127.0.0.1:1153"};
const llarp::SockAddr DefaultUpstreamDNS{"9.9.9.9:53"};
namespace llarp::apple
{
@ -21,7 +26,7 @@ namespace llarp::apple
makeVPNPlatform() override;
void
Start();
Start(std::string_view bootstrap);
private:
NEPacketTunnelProvider* const m_Tunnel;
@ -42,20 +47,39 @@ namespace llarp::apple
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));
}
else
{
LogError("invalid IP packet: ", llarp::buffer_printer(DataAsStringView(data)));
}
}
public:
explicit VPNInterface(NEPacketTunnelProvider* tunnel)
explicit VPNInterface(NEPacketTunnelProvider* tunnel, llarp::Context* context)
: 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);
}
context->loop->call_soon([this]() { Read(); });
}
void
HandleReadEvent(NSArray<NSData*>* packets, NSArray<NSNumber*>* protos)
{
NSUInteger num = [packets count];
for (NSUInteger idx = 0; idx < num; ++idx)
{
NSData* pkt = [packets objectAtIndex:idx];
OfferReadPacket(pkt);
}
Read();
}
void
Read()
{
auto handler = [this](NSArray<NSData*>* packets, NSArray<NSNumber*>* protos) {
HandleReadEvent(packets, protos);
};
[m_Tunnel.packetFlow readPacketsWithCompletionHandler:handler];
}
@ -84,27 +108,27 @@ namespace llarp::apple
bool
WritePacket(net::IPPacket pkt) override
{
const sa_family_t fam = pkt.IsV6() ? AF_INET6 : AF_INET;
const uint8_t* pktbuf = pkt.buf;
NSNumber* fam = [NSNumber numberWithInteger:(pkt.IsV6() ? AF_INET6 : AF_INET)];
void* 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];
NSData* datapkt = [NSData dataWithBytesNoCopy:pktbuf length:pktsz];
return [m_Tunnel.packetFlow writePackets:@[datapkt] withProtocols:@[fam]];
}
};
class VPNPlatform final : public vpn::Platform
{
NEPacketTunnelProvider* const m_Tunnel;
Context* const m_Context;
public:
explicit VPNPlatform(NEPacketTunnelProvider* tunnel) : m_Tunnel{tunnel}
explicit VPNPlatform(NEPacketTunnelProvider* tunnel, Context* context)
: m_Tunnel{tunnel}, m_Context{context}
{}
std::shared_ptr<vpn::NetworkInterface> ObtainInterface(vpn::InterfaceInfo) override
{
return std::make_shared<VPNInterface>(m_Tunnel);
return std::make_shared<VPNInterface>(m_Tunnel, m_Context);
}
};
@ -113,16 +137,20 @@ namespace llarp::apple
{}
void
FrameworkContext::Start()
FrameworkContext::Start(std::string_view bootstrap)
{
std::promise<void> result;
m_Runner = std::make_unique<std::thread>([&result, this]() {
m_Runner = std::make_unique<std::thread>([&result, bootstrap = std::string{bootstrap}, this]() {
const RuntimeOptions opts{};
try
{
auto config = llarp::Config::NetworkExtensionConfig();
config->bootstrap.files.emplace_back(bootstrap);
config->dns.m_bind = DefaultDNSBind;
config->dns.m_upstreamDNS.push_back(DefaultUpstreamDNS);
Configure(std::move(config));
Setup(opts);
Configure(llarp::Config::NetworkExtensionConfig());
}
catch (std::exception&)
{
@ -140,7 +168,7 @@ namespace llarp::apple
std::shared_ptr<vpn::Platform>
FrameworkContext::makeVPNPlatform()
{
return std::make_shared<VPNPlatform>(m_Tunnel);
return std::make_shared<VPNPlatform>(m_Tunnel, this);
}
}
@ -154,9 +182,10 @@ struct ContextWrapper
{}
void
Start()
Start(std::string_view bootstrap)
{
m_Context->Start();
llarp::LogContext::Instance().logStream.reset(new llarp::NSLogStream{});
m_Context->Start(std::move(bootstrap));
}
void
@ -167,35 +196,42 @@ struct ContextWrapper
}
};
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
{
NSLog(@"OMG startTunnelWithOptions");
llarp::huint32_t addr_{};
llarp::huint32_t mask_{};
if (auto maybe = llarp::FindFreeRange())
{
addr_ = llarp::net::TruncateV6(maybe->addr);
mask_ = llarp::net::TruncateV6(maybe->netmask_bits);
}
NSString* addr = StringToNSString(addr_.ToString());
NSString* mask = StringToNSString(mask_.ToString());
NSBundle* main = [NSBundle mainBundle];
NSString* res = [main pathForResource:@"bootstrap" ofType:@"signed"];
NSData* path = [res dataUsingEncoding:NSUTF8StringEncoding];
m_Context = new ContextWrapper{self};
m_Context->Start();
[self setTunnelNetworkSettings:nullptr completionHandler:completionHandler];
m_Context->Start(DataAsStringView(path));
NEPacketTunnelNetworkSettings* settings =
[[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:@"127.0.0.1"];
NEDNSSettings* dns = [[NEDNSSettings alloc] initWithServers:@[addr]];
NEIPv4Settings* ipv4 = [[NEIPv4Settings alloc] initWithAddresses:@[addr]
subnetMasks:@[@"255.255.255.255"]];
ipv4.includedRoutes = @[[[NEIPv4Route alloc] initWithDestinationAddress:addr subnetMask:mask]];
settings.IPv4Settings = ipv4;
settings.DNSSettings = dns;
[self setTunnelNetworkSettings:settings completionHandler:completionHandler];
}
- (void)stopTunnelWithReason:(NEProviderStopReason)reason
completionHandler:(void (^)(void))completionHandler
{
NSLog(@"STOP TUNNEL");
if (m_Context)
{
m_Context->Stop();
@ -209,9 +245,6 @@ StringViewToData(std::string_view data)
completionHandler:(void (^)(NSData* responseData))completionHandler
{
const auto data = DataAsStringView(messageData);
LogInfo("app message: ", data);
completionHandler(StringViewToData("ok"));
}
@end

@ -74,7 +74,7 @@ namespace llarp
});
m_PacketRouter = std::make_unique<vpn::PacketRouter>(
[this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); });
#ifdef ANDROID
#if defined(ANDROID) || defined(__APPLE__)
m_Resolver = std::make_shared<DnsInterceptor>(r, this);
m_PacketRouter->AddUDPHandler(huint16_t{53}, [&](net::IPPacket pkt) {
const size_t ip_header_size = (pkt.Header()->ihl * 4);

@ -10,6 +10,7 @@
#include <llarp/service/auth.hpp>
#include <llarp/service/name.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/dns/dns.hpp>
namespace llarp::rpc
{
@ -561,6 +562,47 @@ namespace llarp::rpc
});
});
})
.add_request_command(
"dns_query",
[&](oxenmq::Message& msg) {
HandleJSONRequest(msg, [r = m_Router](nlohmann::json obj, ReplyFunction_t reply) {
std::string endpoint{"default"};
if (const auto itr = obj.find("endpoint"); itr != obj.end())
{
endpoint = itr->get<std::string>();
}
std::string qname{};
dns::QType_t qtype = dns::qTypeA;
if (const auto itr = obj.find("qname"); itr != obj.end())
{
qname = itr->get<std::string>();
}
if (const auto itr = obj.find("qtype"); itr != obj.end())
{
qtype = itr->get<dns::QType_t>();
}
dns::Message msg{dns::Question{qname, qtype}};
if (auto ep_ptr = (GetEndpointByName(r, endpoint)))
{
if (auto ep = reinterpret_cast<dns::IQueryHandler*>(ep_ptr.get()))
{
if (ep->ShouldHookDNSMessage(msg))
{
ep->HandleHookedDNSMessage(std::move(msg), [reply](dns::Message msg) {
reply(CreateJSONResponse(msg.ToJSON()));
});
return;
}
}
reply(CreateJSONError("dns query not accepted by endpoint"));
return;
}
reply(CreateJSONError("no such endpoint for dns query"));
});
})
.add_request_command("config", [&](oxenmq::Message& msg) {
HandleJSONRequest(msg, [r = m_Router](nlohmann::json obj, ReplyFunction_t reply) {
{

Loading…
Cancel
Save