mirror of https://github.com/oxen-io/lokinet
macos sort of works now
parent
81d27c35c1
commit
7db2459469
@ -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>
|
Binary file not shown.
@ -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
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue