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
pull/1537/head
Thomas Winget 3 years ago
parent f2f0486f13
commit 4c630e0437

@ -62,8 +62,30 @@ local debian_pipeline(name, image,
}
],
};
// windows cross compile on alpine linux
local apk_builder(name, image, extra_cmds=[], allow_fail=true) = {
kind: 'pipeline',
type: 'docker',
name: name,
platform: {arch: "amd64"},
trigger: { branch: { exclude: ['debian/*', 'ubuntu/*'] } },
steps: [
submodules,
{
name: 'build',
image: image,
[if allow_fail then "failure"]: "ignore",
environment: { SSH_KEY: { from_secret: "SSH_KEY" }, ANDROID: "android" },
commands: [
"cd android",
"rm -f local.properties",
"echo 'sdk.dir=/usr/lib/android-sdk' >> local.properties",
"echo 'ndk.dir=/usr/lib/android-ndk' >> local.properties",
"GRADLE_USER_HOME=/cache/gradle gradle --no-daemon assembleDebug",
] + extra_cmds
}
]
};
// windows cross compile on debian
local windows_cross_pipeline(name, image,
arch='amd64',
build_type='Release',
@ -156,6 +178,7 @@ local deb_builder(image, distro, distro_branch, arch='amd64', loki_repo=true) =
]
};
// Macos build
local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra_cmds=[], allow_fail=false) = {
kind: 'pipeline',
@ -224,6 +247,8 @@ local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra
'../contrib/ci/drone-check-static-libs.sh',
'UPLOAD_OS=linux-armhf ../contrib/ci/drone-static-upload.sh'
]),
// android apk builder
apk_builder("android apk", "registry.oxen.rocks/lokinet-ci-android", extra_cmds=['UPLOAD_OS=anrdoid ../contrib/ci/drone-static-upload.sh']),
// Windows builds (x64)
windows_cross_pipeline("Windows (amd64)", "debian:testing",

3
.gitignore vendored

@ -40,9 +40,6 @@ vsproject/
.vs
daemon.ini
lokinet-win32.exe
lokinet
lokinet.exe
.gradle/

@ -33,7 +33,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".LokinetService"
<service android:name=".LokinetDaemon"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_VPN_SERVICE">

@ -27,11 +27,11 @@ android {
targetSdkVersion 28
minSdkVersion 23
versionCode 1
versionName '0.8.0'
versionName '0.8.3'
externalNativeBuild {
cmake {
// targets "lokinet-android"
arguments "-DWITH_LTO=OFF", "-DCXXOPTS_BUILD_TESTS=OFF","-DWITH_TESTS=OFF", "-DCMAKE_CROSSCOMPILING=ON", "-DNATIVE_BUILD=OFF", "-DANDROID=ON", "-DANDROID_STL=c++_static", "-DBUILD_STATIC_DEPS=ON", "-DBUILD_SHARED_LIBS=OFF", "-DSTATIC_LINK=ON", "-DDOWNLOAD_UV=FORCE", "-DANDROID_ARM_MODE=arm"
arguments "-DWITH_LTO=OFF", "-DCXXOPTS_BUILD_TESTS=OFF","-DWITH_TESTS=OFF", "-DCMAKE_CROSSCOMPILING=ON", "-DNATIVE_BUILD=OFF", "-DANDROID=ON", "-DANDROID_STL=c++_static", "-DBUILD_STATIC_DEPS=ON", "-DBUILD_SHARED_LIBS=OFF", "-DSTATIC_LINK=ON", "-DANDROID_ARM_MODE=arm", "-DFORCE_OXENMQ_SUBMODULE=ON"
cppFlags "-std=c++17"
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
// abiFilters 'armeabi-v7a'

@ -19,7 +19,9 @@ import android.content.Context;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.Manifest;
import android.net.VpnService;
import android.os.AsyncTask;
import android.content.Intent;
import android.os.Bundle;
@ -29,14 +31,18 @@ import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.util.Log;
public class LokiNetActivity extends Activity {
private static final String TAG = "lokinet-activity";
private TextView textView;
private static final String DefaultBootstrapURL = "https://seed.lokinet.org/bootstrap.signed";
private static final String DefaultBootstrapURL = "https://seed.lokinet.org/lokinet.signed";
private AsyncBootstrap bootstrapper;
public static final String LOG_TAG = "LokinetDaemon";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -71,11 +77,33 @@ public class LokiNetActivity extends Activity {
bootstrapper.execute(DefaultBootstrapURL);
}
public void runLokinetService()
{
startService(new Intent(LokiNetActivity.this,
LokinetService.class));
Intent intent = VpnService.prepare(getApplicationContext());
if (intent != null)
{
Log.d(LOG_TAG, "VpnService.prepare() returned an Intent, so launch that intent.");
startActivityForResult(intent, 0);
}
else
{
Log.w(LOG_TAG, "VpnService.prepare() returned null, not running.");
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (resultCode == RESULT_OK)
{
Log.d(LOG_TAG, "VpnService prepared intent RESULT_OK, launching LokinetDaemon Service");
startService(new Intent(LokiNetActivity.this,
LokinetDaemon.class));
}
else
{
Log.d(LOG_TAG, "VpnService prepared intent NOT RESULT_OK, shit.");
}
}
@Override

@ -0,0 +1,39 @@
package network.loki.lokinet;
import java.nio.ByteBuffer;
public class LokinetConfig
{
static {
System.loadLibrary("lokinet-android");
}
private static native ByteBuffer Obtain(String dataDir);
private static native void Free(ByteBuffer buf);
/*** load config file from disk */
public native boolean Load();
/*** save chages to disk */
public native boolean Save();
/** override default config value before loading from config file */
public native void AddDefaultValue(String section, String key, String value);
private final ByteBuffer impl;
public LokinetConfig(String dataDir)
{
impl = Obtain(dataDir);
if(impl == null)
throw new RuntimeException("cannot obtain config from "+dataDir);
}
public void finalize()
{
if (impl != null)
{
Free(impl);
}
}
}

@ -0,0 +1,167 @@
package network.loki.lokinet;
import java.lang.Thread;
import java.nio.ByteBuffer;
import java.io.File;
import android.net.VpnService;
import android.util.Log;
import android.content.Intent;
import android.os.ParcelFileDescriptor;
public class LokinetDaemon extends VpnService
{
static {
System.loadLibrary("lokinet-android");
}
private static native ByteBuffer Obtain();
private static native void Free(ByteBuffer buf);
public native boolean Configure(LokinetConfig config);
public native int Mainloop();
public native boolean IsRunning();
public native boolean Stop();
public native void InjectVPNFD();
public native int GetUDPSocket();
private static native String DetectFreeRange();
public static final String LOG_TAG = "LokinetDaemon";
ByteBuffer impl = null;
ParcelFileDescriptor iface;
int m_FD = -1;
int m_UDPSocket = -1;
@Override
public void onCreate()
{
super.onCreate();
}
@Override
public void onDestroy()
{
super.onDestroy();
if (IsRunning())
{
Stop();
}
if (impl != null)
{
Free(impl);
impl = null;
}
}
public int onStartCommand(Intent intent, int flags, int startID)
{
Log.d(LOG_TAG, "onStartCommand()");
if (!IsRunning())
{
if (impl != null)
{
Free(impl);
impl = null;
}
impl = Obtain();
if (impl == null)
{
Log.e(LOG_TAG, "got nullptr when creating llarp::Context in jni");
return START_NOT_STICKY;
}
String dataDir = getFilesDir().toString();
LokinetConfig config;
try
{
config = new LokinetConfig(dataDir);
}
catch(RuntimeException ex)
{
Log.e(LOG_TAG, ex.toString());
return START_NOT_STICKY;
}
// FIXME: make these configurable
String exitNode = "exit.loki";
String upstreamDNS = "1.1.1.1";
String ourRange = DetectFreeRange();
if(ourRange.isEmpty())
{
Log.e(LOG_TAG, "cannot detect free range");
return START_NOT_STICKY;
}
// set up config values
config.AddDefaultValue("network", "exit-node", exitNode);
config.AddDefaultValue("network", "ifaddr", ourRange);
config.AddDefaultValue("dns", "upstream", upstreamDNS);
if (!config.Load())
{
Log.e(LOG_TAG, "failed to load (or create) config file at: " + dataDir + "/lokinet.ini");
return START_NOT_STICKY;
}
VpnService.Builder builder = new VpnService.Builder();
builder.setMtu(1500);
String[] parts = ourRange.split("/");
String ourIP = parts[0];
int ourMask = Integer.parseInt(parts[1]);
builder.addAddress(ourIP, ourMask);
builder.addRoute("0.0.0.0", 0);
builder.addDnsServer(upstreamDNS);
builder.setSession("Lokinet");
builder.setConfigureIntent(null);
iface = builder.establish();
if (iface == null)
{
Log.e(LOG_TAG, "VPN Interface from builder.establish() came back null");
return START_NOT_STICKY;
}
m_FD = iface.detachFd();
InjectVPNFD();
if (!Configure(config))
{
//TODO: close vpn FD if this fails, either on native side, or here if possible
Log.e(LOG_TAG, "failed to configure daemon");
return START_NOT_STICKY;
}
m_UDPSocket = GetUDPSocket();
if (m_UDPSocket <= 0)
{
Log.e(LOG_TAG, "failed to get proper UDP handle from daemon, aborting.");
return START_NOT_STICKY;
}
protect(m_UDPSocket);
new Thread(() -> {
Mainloop();
}).start();
Log.d(LOG_TAG, "started successfully!");
}
else
{
Log.d(LOG_TAG, "already running");
}
return START_STICKY;
}
}

@ -1,9 +0,0 @@
package network.loki.lokinet;
import android.net.VpnService;
public class LokinetService extends VpnService
{
}

@ -291,7 +291,7 @@ build_external(zmq
${zmq_patch}
CONFIGURE_COMMAND ./configure ${cross_host} --prefix=${DEPS_DESTDIR} --enable-static --disable-shared
--disable-curve-keygen --enable-curve --disable-drafts --disable-libunwind --with-libsodium
--without-pgm --without-norm --without-vmci --without-docs --with-pic --disable-Werror ${zmq_extra}
--without-pgm --without-norm --without-vmci --without-docs --with-pic --disable-Werror --disable-libbsd ${zmq_extra}
"CC=${deps_cc}" "CXX=${deps_cxx}" "CFLAGS=${deps_CFLAGS} -fstack-protector" "CXXFLAGS=${deps_CXXFLAGS} -fstack-protector"
"sodium_CFLAGS=-I${DEPS_DESTDIR}/include" "sodium_LIBS=-L${DEPS_DESTDIR}/lib -lsodium"
)

@ -39,6 +39,11 @@ if [ -e daemon/lokinet.exe ]; then
# zipit up yo
archive="$base.zip"
zip -r "$archive" "$base"
elif [ -e build/outputs/apk/debug/lokinet-debug.apk ] ; then
# android af ngl
cp -av build/outputs/apk/debug/lokinet-debug.apk "$base"
archive="$base.tar.xz"
tar cJvf "$archive" "$base"
else
cp -av daemon/lokinet daemon/lokinet-vpn ../lokinet-bootstrap "$base"
# tar dat shiz up yo

@ -99,6 +99,14 @@ namespace llarp
virtual std::shared_ptr<llarp::vpn::Platform>
makeVPNPlatform();
#ifdef ANDROID
int androidFD = -1;
int
GetUDPSocket();
#endif
protected:
std::shared_ptr<Config> config = nullptr;

@ -1,13 +1,18 @@
#include "network_loki_lokinet_LokinetConfig.h"
#include <llarp.hpp>
#include <config/config.hpp>
#include "lokinet_jni_common.hpp"
extern "C"
{
JNIEXPORT jobject JNICALL
Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv* env, jclass)
Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv* env, jclass, jstring dataDir)
{
auto conf = new llarp::Config();
auto conf = VisitStringAsStringView<llarp::Config*>(
env, dataDir, [](std::string_view val) -> llarp::Config* {
return new llarp::Config{val};
});
if (conf == nullptr)
return nullptr;
return env->NewDirectByteBuffer(conf, sizeof(llarp::Config));
@ -21,15 +26,49 @@ extern "C"
}
JNIEXPORT jboolean JNICALL
Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv* env, jobject self, jstring fname)
Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv* env, jobject self)
{
auto conf = GetImpl<llarp::Config>(env, self);
if (conf == nullptr)
return JNI_FALSE;
if (conf->Load())
{
return JNI_TRUE;
}
return JNI_FALSE;
}
JNIEXPORT jboolean JNICALL
Java_network_loki_lokinet_LokinetConfig_Save(JNIEnv* env, jobject self)
{
auto conf = GetImpl<llarp::Config>(env, self);
if (conf == nullptr)
return JNI_FALSE;
return VisitStringAsStringView<jboolean>(env, fname, [conf](std::string_view val) -> jboolean {
if (conf->Load(val, false, llarp::GetDefaultDataDir()))
return JNI_TRUE;
try
{
conf->Save();
}
catch (...)
{
return JNI_FALSE;
});
}
return JNI_TRUE;
}
JNIEXPORT void JNICALL
Java_network_loki_lokinet_LokinetConfig_AddDefaultValue(
JNIEnv* env, jobject self, jstring section, jstring key, jstring value)
{
auto convert = [](std::string_view str) -> std::string { return std::string{str}; };
const auto sect = VisitStringAsStringView<std::string>(env, section, convert);
const auto k = VisitStringAsStringView<std::string>(env, key, convert);
const auto v = VisitStringAsStringView<std::string>(env, value, convert);
auto conf = GetImpl<llarp::Config>(env, self);
if (conf)
{
conf->AddDefault(sect, k, v);
}
}
}
}

@ -2,6 +2,7 @@
#include "lokinet_jni_common.hpp"
#include "lokinet_jni_vpnio.hpp"
#include <llarp.hpp>
#include <config/config.hpp>
extern "C"
{
@ -31,7 +32,9 @@ extern "C"
try
{
llarp::RuntimeOptions opts{};
ptr->Configure(*config);
// janky make_shared deep copy because jni + shared pointer = scary
ptr->Configure(std::make_shared<llarp::Config>(*config));
ptr->Setup(opts);
}
catch (...)
@ -71,17 +74,30 @@ extern "C"
return ptr->IsUp() ? JNI_FALSE : JNI_TRUE;
}
JNIEXPORT jboolean JNICALL
Java_network_loki_lokinet_LokinetDaemon_InjectVPN(JNIEnv* env, jobject self, jobject vpn)
JNIEXPORT void JNICALL
Java_network_loki_lokinet_LokinetDaemon_InjectVPNFD(JNIEnv* env, jobject self)
{
auto ptr = GetImpl<llarp::Context>(env, self);
auto impl = GetImpl<lokinet_jni_vpnio>(env, vpn);
if (ptr == nullptr || impl == nullptr)
return JNI_FALSE;
if (impl->info.netmask == 0)
return JNI_FALSE;
if (not impl->Init(ptr))
return JNI_FALSE;
return llarp_main_inject_default_vpn(ptr, &impl->io, impl->info) ? JNI_TRUE : JNI_FALSE;
ptr->androidFD = GetObjectMemberAsInt<int>(env, self, "m_FD");
}
JNIEXPORT jint JNICALL
Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv* env, jobject self)
{
auto ptr = GetImpl<llarp::Context>(env, self);
return ptr->GetUDPSocket();
}
JNIEXPORT jstring JNICALL
Java_network_loki_lokinet_LokinetDaemon_DetectFreeRange(JNIEnv* env, jclass)
{
std::string rangestr{};
if (auto maybe = llarp::FindFreeRange())
{
rangestr = maybe->ToString();
}
return env->NewStringUTF(rangestr.c_str());
}
}
}

@ -26,7 +26,7 @@ VisitStringAsStringView(JNIEnv* env, jobject str, V visit)
env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT);
env->DeleteLocalRef(stringJbytes);
return std::move(result);
return result;
}
/// cast jni buffer to T *
@ -45,7 +45,7 @@ static T*
FromObjectMember(JNIEnv* env, jobject self, const char* membername)
{
jclass cl = env->GetObjectClass(self);
jfieldID name = env->GetFieldID(cl, membername, "Ljava/nio/Buffer;");
jfieldID name = env->GetFieldID(cl, membername, "Ljava/nio/ByteBuffer;");
jobject buffer = env->GetObjectField(self, name);
return FromBuffer<T>(env, buffer);
}
@ -79,4 +79,4 @@ GetImpl(JNIEnv* env, jobject self)
return FromObjectMember<T>(env, self, "impl");
}
#endif
#endif

@ -11,15 +11,15 @@ extern "C"
/*
* Class: network_loki_lokinet_LokinetConfig
* Method: Obtain
* Signature: ()Ljava/nio/Buffer;
* Signature: (Ljava/lang/String;)Ljava/nio/ByteBuffer;
*/
JNIEXPORT jobject JNICALL
Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv*, jclass);
Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv*, jclass, jstring);
/*
* Class: network_loki_lokinet_LokinetConfig
* Method: Free
* Signature: (Ljava/nio/Buffer;)V
* Signature: (Ljava/nio/ByteBuffer;)V
*/
JNIEXPORT void JNICALL
Java_network_loki_lokinet_LokinetConfig_Free(JNIEnv*, jclass, jobject);
@ -27,10 +27,27 @@ extern "C"
/*
* Class: network_loki_lokinet_LokinetConfig
* Method: Load
* Signature: (Ljava/lang/String;)Z
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL
Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv*, jobject, jstring);
Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv*, jobject);
/*
* Class: network_loki_lokinet_LokinetConfig
* Method: Save
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL
Java_network_loki_lokinet_LokinetConfig_Save(JNIEnv*, jobject);
/*
* Class: network_loki_lokinet_LokinetConfig
* Method: AddDefaultValue
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_network_loki_lokinet_LokinetConfig_AddDefaultValue(
JNIEnv*, jobject, jstring, jstring, jstring);
#ifdef __cplusplus
}

@ -11,7 +11,7 @@ extern "C"
/*
* Class: network_loki_lokinet_LokinetDaemon
* Method: Obtain
* Signature: ()Ljava/nio/Buffer;
* Signature: ()Ljava/nio/ByteBuffer;
*/
JNIEXPORT jobject JNICALL
Java_network_loki_lokinet_LokinetDaemon_Obtain(JNIEnv*, jclass);
@ -19,7 +19,7 @@ extern "C"
/*
* Class: network_loki_lokinet_LokinetDaemon
* Method: Free
* Signature: (Ljava/nio/Buffer;)V
* Signature: (Ljava/nio/ByteBuffer;)V
*/
JNIEXPORT void JNICALL
Java_network_loki_lokinet_LokinetDaemon_Free(JNIEnv*, jclass, jobject);
@ -58,11 +58,27 @@ extern "C"
/*
* Class: network_loki_lokinet_LokinetDaemon
* Method: InjectVPN
* Signature: (Lnetwork/loki/lokinet/LokinetVPN;)Z
* Method: InjectVPNFD
* Signature: ()V
*/
JNIEXPORT jboolean JNICALL
Java_network_loki_lokinet_LokinetDaemon_InjectVPN(JNIEnv*, jobject, jobject);
JNIEXPORT void JNICALL
Java_network_loki_lokinet_LokinetDaemon_InjectVPNFD(JNIEnv*, jobject);
/*
* Class: network_loki_lokinet_LokinetDaemon
* Method: GetUDPSocket
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv*, jobject);
/*
* Class: network_loki_lokinet_LokinetDaemon
* Method: DetectFreeRange
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL
Java_network_loki_lokinet_LokinetDaemon_DetectFreeRange(JNIEnv*, jclass);
#ifdef __cplusplus
}

@ -59,6 +59,7 @@ add_library(lokinet-platform
net/net_int.cpp
net/route.cpp
net/sock_addr.cpp
vpn/packet_router.cpp
vpn/platform.cpp
)

@ -1020,6 +1020,12 @@ namespace llarp
}
}
void
Config::AddDefault(std::string section, std::string key, std::string val)
{
m_Additional.emplace_back(std::array<std::string, 3>{section, key, val});
}
bool
Config::Load(std::optional<fs::path> fname, bool isRelay)
{
@ -1073,6 +1079,12 @@ namespace llarp
m_Parser.Clear();
LoadOverrides();
/// load additional config options added
for (const auto& [sect, key, val] : m_Additional)
{
conf.addConfigValue(sect, key, val);
}
m_Parser.IterAll([&](std::string_view section, const SectionValues_t& values) {
for (const auto& pair : values)
{

@ -224,6 +224,9 @@ namespace llarp
void
Override(std::string section, std::string key, std::string value);
void
AddDefault(std::string section, std::string key, std::string value);
private:
/// Load (initialize) a default config.
///
@ -242,6 +245,7 @@ namespace llarp
void
LoadOverrides();
std::vector<std::array<std::string, 3>> m_Additional;
ConfigParser m_Parser;
const fs::path m_DataDir;
};

@ -190,6 +190,15 @@ namespace llarp
llarp::LogDebug("free logic");
logic.reset();
}
#if defined(ANDROID)
int
Context::GetUDPSocket()
{
return router->GetOutboundUDPSocket();
}
#endif
} // namespace llarp
extern "C"

@ -5,297 +5,212 @@
#include <array>
#include <utility>
namespace llarp
namespace llarp::dns
{
namespace dns
static std::vector<byte_t>
MessageToBuffer(Message msg)
{
Proxy::Proxy(
llarp_ev_loop_ptr serverLoop,
Logic_ptr serverLogic,
llarp_ev_loop_ptr clientLoop,
Logic_ptr clientLogic,
IQueryHandler* h)
: m_ServerLoop(std::move(serverLoop))
, m_ClientLoop(std::move(clientLoop))
, m_ServerLogic(std::move(serverLogic))
, m_ClientLogic(std::move(clientLogic))
, m_QueryHandler(h)
{
m_Client.user = this;
m_Server.user = this;
m_Client.tick = nullptr;
m_Server.tick = nullptr;
m_Client.recvfrom = &HandleUDPRecv_client;
m_Server.recvfrom = &HandleUDPRecv_server;
}
std::array<byte_t, 1500> tmp = {{0}};
llarp_buffer_t buf{tmp};
if (not msg.Encode(&buf))
throw std::runtime_error("cannot encode dns message");
std::vector<byte_t> pkt;
pkt.resize(buf.cur - buf.base);
std::copy_n(tmp.data(), pkt.size(), pkt.data());
return pkt;
}
PacketHandler::PacketHandler(Logic_ptr logic, IQueryHandler* h)
: m_QueryHandler{h}, m_Logic{logic}
{}
Proxy::Proxy(llarp_ev_loop_ptr loop, Logic_ptr logic, IQueryHandler* h)
: PacketHandler{logic, h}, m_Loop(std::move(loop))
{
m_Server.user = this;
m_Server.tick = nullptr;
m_Server.recvfrom = &HandleUDP;
}
void
Proxy::Stop()
{
if (m_UnboundResolver)
m_UnboundResolver->Stop();
}
void
PacketHandler::Stop()
{
if (m_UnboundResolver)
m_UnboundResolver->Stop();
}
bool
Proxy::Start(const IpAddress& addr, const std::vector<IpAddress>& resolvers)
{
if (resolvers.size())
{
if (not SetupUnboundResolver(resolvers))
{
llarp::LogError("Failed to add upstream resolvers during DNS server setup.");
return false;
}
}
return (llarp_ev_add_udp(m_ServerLoop, &m_Server, addr.createSockAddr()) == 0);
}
bool
Proxy::Start(SockAddr addr, std::vector<IpAddress> resolvers)
{
if (not PacketHandler::Start(addr, std::move(resolvers)))
return false;
return (llarp_ev_add_udp(m_Loop, &m_Server, addr) == 0);
}
static Proxy::Buffer_t
CopyBuffer(const llarp_buffer_t& buf)
{
std::vector<byte_t> msgbuf(buf.sz);
std::copy_n(buf.base, buf.sz, msgbuf.data());
return msgbuf;
}
static Proxy::Buffer_t
CopyBuffer(const llarp_buffer_t& buf)
{
std::vector<byte_t> msgbuf(buf.sz);
std::copy_n(buf.base, buf.sz, msgbuf.data());
return msgbuf;
}
void
Proxy::HandleUDPRecv_server(llarp_udp_io* u, const SockAddr& from, ManagedBuffer buf)
{
Buffer_t msgbuf = CopyBuffer(buf.underlying);
auto self = static_cast<Proxy*>(u->user);
self->HandlePktServer(from, msgbuf);
}
void
Proxy::HandleUDP(llarp_udp_io* u, const SockAddr& from, ManagedBuffer buf)
{
Buffer_t msgbuf = CopyBuffer(buf.underlying);
auto self = static_cast<Proxy*>(u->user);
self->HandlePacket(from, from, std::move(msgbuf));
}
void
Proxy::HandleUDPRecv_client(llarp_udp_io* u, const SockAddr& from, ManagedBuffer buf)
void
PacketHandler::Restart()
{
if (m_UnboundResolver)
{
Buffer_t msgbuf = CopyBuffer(buf.underlying);
auto self = static_cast<Proxy*>(u->user)->shared_from_this();
LogicCall(
self->m_ServerLogic, [self, from, msgbuf]() { self->HandlePktClient(from, msgbuf); });
LogInfo("reset libunbound's internal stuff");
m_UnboundResolver->Init();
}
}
IpAddress
Proxy::PickRandomResolver() const
{
const size_t sz = m_Resolvers.size();
if (sz <= 1)
return m_Resolvers[0];
auto itr = m_Resolvers.begin();
std::advance(itr, llarp::randint() % sz);
return *itr;
}
bool
PacketHandler::Start(SockAddr, std::vector<IpAddress> resolvers)
{
return SetupUnboundResolver(std::move(resolvers));
}
void
Proxy::Restart()
{
if (m_UnboundResolver)
bool
PacketHandler::SetupUnboundResolver(std::vector<IpAddress> resolvers)
{
auto failFunc = [self = weak_from_this()](SockAddr from, SockAddr to, Message msg) {
auto this_ptr = self.lock();
if (this_ptr)
{
LogInfo("reset libunbound's internal stuff");
m_UnboundResolver->Init();
this_ptr->SendServerMessageBufferTo(from, to, MessageToBuffer(std::move(msg)));
}
}
bool
Proxy::SetupUnboundResolver(const std::vector<IpAddress>& resolvers)
{
auto failFunc = [self = weak_from_this()](SockAddr to, Message msg) {
auto this_ptr = self.lock();
if (this_ptr)
{
this_ptr->SendServerMessageTo(to, std::move(msg));
}
};
};
auto replyFunc = [self = weak_from_this()](SockAddr to, std::vector<byte_t> buf) {
auto this_ptr = self.lock();
if (this_ptr)
{
this_ptr->HandleUpstreamResponse(to, std::move(buf));
}
};
auto replyFunc = [self = weak_from_this()](
SockAddr from, SockAddr to, std::vector<byte_t> buf) {
auto this_ptr = self.lock();
if (this_ptr)
{
this_ptr->SendServerMessageBufferTo(from, to, std::move(buf));
}
};
m_UnboundResolver = std::make_shared<UnboundResolver>(
m_ServerLoop, std::move(replyFunc), std::move(failFunc));
if (not m_UnboundResolver->Init())
m_UnboundResolver =
std::make_shared<UnboundResolver>(m_Logic, std::move(replyFunc), std::move(failFunc));
if (not m_UnboundResolver->Init())
{
llarp::LogError("Failed to initialize upstream DNS resolver.");
m_UnboundResolver = nullptr;
return false;
}
for (const auto& resolver : resolvers)
{
if (not m_UnboundResolver->AddUpstreamResolver(resolver.toHost()))
{
llarp::LogError("Failed to initialize upstream DNS resolver.");
llarp::LogError("Failed to add upstream DNS server: ", resolver.toHost());
m_UnboundResolver = nullptr;
return false;
}
for (const auto& resolver : resolvers)
{
if (not m_UnboundResolver->AddUpstreamResolver(resolver.toHost()))
{
llarp::LogError("Failed to add upstream DNS server: ", resolver.toHost());
m_UnboundResolver = nullptr;
return false;
}
}
return true;
m_Resolvers.emplace(resolver);
}
void
Proxy::HandleTick(llarp_udp_io*)
{}
return true;
}
void
Proxy::SendServerMessageBufferTo(const SockAddr& to, const llarp_buffer_t& buf)
void
Proxy::SendServerMessageBufferTo(SockAddr, SockAddr to, Buffer_t buf)
{
if (llarp_ev_udp_sendto(&m_Server, to, buf) < 0)
llarp::LogError("dns reply failed");
}
bool
PacketHandler::ShouldHandlePacket(SockAddr to, SockAddr from, Buffer_t buf) const
{
(void)from;
MessageHeader hdr;
llarp_buffer_t pkt{buf};
if (not hdr.Decode(&pkt))
{
if (llarp_ev_udp_sendto(&m_Server, to, buf) < 0)
llarp::LogError("dns reply failed");
return false;
}
void
Proxy::SendServerMessageTo(const SockAddr& to, Message msg)
Message msg{hdr};
if (not msg.Decode(&pkt))
{
auto self = shared_from_this();
LogicCall(m_ServerLogic, [to, msg = std::move(msg), self]() {
std::array<byte_t, 1500> tmp = {{0}};
llarp_buffer_t buf(tmp);
if (msg.Encode(&buf))
{
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
self->SendServerMessageBufferTo(to, buf);
}
else
llarp::LogWarn("failed to encode dns message when sending");
});
return false;
}
void
Proxy::HandleUpstreamResponse(SockAddr to, std::vector<byte_t> buf)
if (m_QueryHandler and m_QueryHandler->ShouldHookDNSMessage(msg))
return true;
if (m_Resolvers.find(to) != m_Resolvers.end())
{
auto self = shared_from_this();
LogicCall(m_ServerLogic, [to, buffer = std::move(buf), self]() {
llarp_buffer_t buf(buffer);
self->SendServerMessageBufferTo(to, buf);
});
return false;
}
return true;
}
void
Proxy::SendClientMessageTo(const SockAddr& to, Message msg)
void
PacketHandler::HandlePacket(SockAddr resolver, SockAddr from, Buffer_t buf)
{
MessageHeader hdr;
llarp_buffer_t pkt{buf};
if (not hdr.Decode(&pkt))
{
auto self = shared_from_this();
LogicCall(m_ClientLogic, [to, msg, self]() {
std::array<byte_t, 1500> tmp = {{0}};
llarp_buffer_t buf(tmp);
if (msg.Encode(&buf))
{
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
llarp_ev_udp_sendto(&self->m_Client, to, buf);
}
else
llarp::LogWarn("failed to encode dns message when sending");
});
llarp::LogWarn("failed to parse dns header from ", from);
return;
}
void
Proxy::HandlePktClient(const SockAddr& from, Buffer_t buf)
Message msg(hdr);
if (not msg.Decode(&pkt))
{
llarp_buffer_t pkt(buf);
MessageHeader hdr;
if (!hdr.Decode(&pkt))
{
llarp::LogWarn("failed to parse dns header from ", from);
return;
}
TX tx = {hdr.id, from};
auto itr = m_Forwarded.find(tx);
if (itr == m_Forwarded.end())
return;
const auto& requester = itr->second;
auto self = shared_from_this();
Message msg(hdr);
if (msg.Decode(&pkt))
{
if (m_QueryHandler && m_QueryHandler->ShouldHookDNSMessage(msg))
{
msg.hdr_id = itr->first.txid;
if (!m_QueryHandler->HandleHookedDNSMessage(
std::move(msg),
std::bind(
&Proxy::SendServerMessageTo,
self,
requester.createSockAddr(),
std::placeholders::_1)))
{
llarp::LogWarn("failed to handle hooked dns");
}
return;
}
}
LogicCall(m_ServerLogic, [=]() {
// forward reply to requester via server
const llarp_buffer_t tmpbuf(buf);
llarp_ev_udp_sendto(&self->m_Server, requester.createSockAddr(), tmpbuf);
});
// remove pending
m_Forwarded.erase(itr);
llarp::LogWarn("failed to parse dns message from ", from);
return;
}
void
Proxy::HandlePktServer(const SockAddr& from, Buffer_t buf)
// we don't provide a DoH resolver because it requires verified TLS
// TLS needs X509/ASN.1-DER and opting into the Root CA Cabal
// thankfully mozilla added a backdoor that allows ISPs to turn it off
// so we disable DoH for firefox using mozilla's ISP backdoor
// see: https://github.com/loki-project/loki-network/issues/832
for (const auto& q : msg.questions)
{
MessageHeader hdr;
llarp_buffer_t pkt(buf);
if (!hdr.Decode(&pkt))
// is this firefox looking for their backdoor record?
if (q.IsName("use-application-dns.net"))
{
llarp::LogWarn("failed to parse dns header from ", from);
// yea it is, let's turn off DoH because god is dead.
msg.AddNXReply();
// press F to pay respects
SendServerMessageBufferTo(resolver, from, MessageToBuffer(std::move(msg)));
return;
}
}
Message msg(hdr);
if (!msg.Decode(&pkt))
{
llarp::LogWarn("failed to parse dns message from ", from);
return;
}
// we don't provide a DoH resolver because it requires verified TLS
// TLS needs X509/ASN.1-DER and opting into the Root CA Cabal
// thankfully mozilla added a backdoor that allows ISPs to turn it off
// so we disable DoH for firefox using mozilla's ISP backdoor
// see: https://github.com/loki-project/loki-network/issues/832
for (const auto& q : msg.questions)
{
// is this firefox looking for their backdoor record?
if (q.IsName("use-application-dns.net"))
{
// yea it is, let's turn off DoH because god is dead.
msg.AddNXReply();
// press F to pay respects
SendServerMessageTo(from, std::move(msg));
return;
}
}
auto self = shared_from_this();
if (m_QueryHandler && m_QueryHandler->ShouldHookDNSMessage(msg))
{
if (!m_QueryHandler->HandleHookedDNSMessage(
std::move(msg),
std::bind(&Proxy::SendServerMessageTo, self, from, std::placeholders::_1)))
{
llarp::LogWarn("failed to handle hooked dns");
}
}
else if (not m_UnboundResolver)
{
// no upstream resolvers
// let's serv fail it
msg.AddServFail();
SendServerMessageTo(from, std::move(msg));
}
else
if (m_QueryHandler && m_QueryHandler->ShouldHookDNSMessage(msg))
{
auto reply = [self = shared_from_this(), to = from, resolver](dns::Message msg) {
self->SendServerMessageBufferTo(resolver, to, MessageToBuffer(std::move(msg)));
};
if (!m_QueryHandler->HandleHookedDNSMessage(std::move(msg), reply))
{
m_UnboundResolver->Lookup(from, std::move(msg));
llarp::LogWarn("failed to handle hooked dns");
}
}
} // namespace dns
} // namespace llarp
else if (not m_UnboundResolver)
{
// no upstream resolvers
// let's serv fail it
msg.AddServFail();
SendServerMessageBufferTo(resolver, from, MessageToBuffer(std::move(msg)));
}
else
{
m_UnboundResolver->Lookup(resolver, from, std::move(msg));
}
}
} // namespace llarp::dns

@ -27,18 +27,17 @@ namespace llarp
HandleHookedDNSMessage(Message query, std::function<void(Message)> sendReply) = 0;
};
struct Proxy : public std::enable_shared_from_this<Proxy>
struct PacketHandler : public std::enable_shared_from_this<PacketHandler>
{
using Logic_ptr = std::shared_ptr<Logic>;
Proxy(
llarp_ev_loop_ptr serverLoop,
Logic_ptr serverLogic,
llarp_ev_loop_ptr clientLoop,
Logic_ptr clientLogic,
IQueryHandler* handler);
using Buffer_t = std::vector<uint8_t>;
bool
Start(const IpAddress& addr, const std::vector<IpAddress>& resolvers);
explicit PacketHandler(Logic_ptr logic, IQueryHandler* handler);
virtual ~PacketHandler() = default;
virtual bool
Start(SockAddr localaddr, std::vector<IpAddress> upstreamResolvers);
void
Stop();
@ -46,80 +45,50 @@ namespace llarp
void
Restart();
using Buffer_t = std::vector<uint8_t>;
void
HandlePacket(SockAddr resolver, SockAddr from, Buffer_t buf);
private:
/// low level packet handler
static void
HandleUDPRecv_client(llarp_udp_io*, const SockAddr&, ManagedBuffer);
static void
HandleUDPRecv_server(llarp_udp_io*, const SockAddr&, ManagedBuffer);
bool
ShouldHandlePacket(SockAddr to, SockAddr from, Buffer_t buf) const;
/// low level ticker
static void
HandleTick(llarp_udp_io*);
protected:
virtual void
SendServerMessageBufferTo(SockAddr from, SockAddr to, Buffer_t buf) = 0;
private:
void
HandlePktClient(const SockAddr& from, Buffer_t buf);
HandleUpstreamFailure(SockAddr from, SockAddr to, Message msg);
void
HandlePktServer(const SockAddr& from, Buffer_t buf);
bool
SetupUnboundResolver(std::vector<IpAddress> resolvers);
void
SendClientMessageTo(const SockAddr& to, Message msg);
IQueryHandler* const m_QueryHandler;
std::set<IpAddress> m_Resolvers;
std::shared_ptr<UnboundResolver> m_UnboundResolver;
Logic_ptr m_Logic;
};
void
SendServerMessageBufferTo(const SockAddr& to, const llarp_buffer_t& buf);
struct Proxy : public PacketHandler
{
using Logic_ptr = std::shared_ptr<Logic>;
explicit Proxy(llarp_ev_loop_ptr loop, Logic_ptr logic, IQueryHandler* handler);
void
SendServerMessageTo(const SockAddr& to, Message msg);
bool
Start(SockAddr localaddr, std::vector<IpAddress> resolvers) override;
void
HandleUpstreamResponse(SockAddr to, std::vector<byte_t> buf);
using Buffer_t = std::vector<uint8_t>;
protected:
void
HandleUpstreamFailure(const SockAddr& to, Message msg);
IpAddress
PickRandomResolver() const;
SendServerMessageBufferTo(SockAddr from, SockAddr to, Buffer_t buf) override;
bool
SetupUnboundResolver(const std::vector<IpAddress>& resolvers);
private:
static void
HandleUDP(llarp_udp_io*, const SockAddr&, ManagedBuffer);
private:
llarp_udp_io m_Server;
llarp_udp_io m_Client;
llarp_ev_loop_ptr m_ServerLoop;
llarp_ev_loop_ptr m_ClientLoop;
Logic_ptr m_ServerLogic;
Logic_ptr m_ClientLogic;
IQueryHandler* m_QueryHandler;
std::vector<IpAddress> m_Resolvers;
std::shared_ptr<UnboundResolver> m_UnboundResolver;
struct TX
{
MsgID_t txid;
IpAddress from;
bool
operator==(const TX& other) const
{
return txid == other.txid && from == other.from;
}
struct Hash
{
size_t
operator()(const TX& t) const noexcept
{
return t.txid ^ IpAddress::Hash()(t.from);
}
};
};
// maps tx to who to send reply to
std::unordered_map<TX, IpAddress, TX::Hash> m_Forwarded;
llarp_ev_loop_ptr m_Loop;
};
} // namespace dns
} // namespace llarp

@ -10,6 +10,7 @@ namespace llarp::dns
std::weak_ptr<UnboundResolver> resolver;
Message msg;
SockAddr source;
SockAddr replyFrom;
};
void
@ -34,15 +35,15 @@ namespace llarp::dns
unboundContext = nullptr;
}
UnboundResolver::UnboundResolver(llarp_ev_loop_ptr loop, ReplyFunction reply, FailFunction fail)
UnboundResolver::UnboundResolver(
std::shared_ptr<Logic> logic, ReplyFunction reply, FailFunction fail)
: unboundContext(nullptr)
, started(false)
, eventLoop(loop)
, replyFunc([loop, reply](auto source, auto buf) {
loop->call_soon([source, buf, reply]() { reply(source, buf); });
, replyFunc([logic, reply](auto res, auto source, auto buf) {
LogicCall(logic, [source, buf, reply, res]() { reply(res, source, buf); });
})
, failFunc([loop, fail](auto source, auto message) {
loop->call_soon([source, message, fail]() { fail(source, message); });
, failFunc([logic, fail](auto res, auto source, auto message) {
LogicCall(logic, [source, message, res, fail]() { fail(res, source, message); });
})
{}
@ -60,7 +61,7 @@ namespace llarp::dns
{
Message& msg = lookup->msg;
msg.AddServFail();
this_ptr->failFunc(lookup->source, msg);
this_ptr->failFunc(lookup->replyFrom, lookup->source, msg);
ub_resolve_free(result);
return;
}
@ -75,7 +76,7 @@ namespace llarp::dns
buf.cur = buf.base;
hdr.Encode(&buf);
this_ptr->replyFunc(lookup->source, std::move(pkt));
this_ptr->replyFunc(lookup->replyFrom, lookup->source, std::move(pkt));
ub_resolve_free(result);
}
@ -120,17 +121,17 @@ namespace llarp::dns
}
void
UnboundResolver::Lookup(const SockAddr& source, Message msg)
UnboundResolver::Lookup(SockAddr to, SockAddr from, Message msg)
{
if (not unboundContext)
{
msg.AddServFail();
failFunc(source, std::move(msg));
failFunc(to, from, std::move(msg));
return;
}
const auto& q = msg.questions[0];
auto* lookup = new PendingUnboundLookup{weak_from_this(), msg, source};
auto* lookup = new PendingUnboundLookup{weak_from_this(), msg, from, to};
int err = ub_resolve_async(
unboundContext,
q.Name().c_str(),
@ -143,7 +144,7 @@ namespace llarp::dns
if (err != 0)
{
msg.AddServFail();
failFunc(source, std::move(msg));
failFunc(to, from, std::move(msg));
return;
}
}

@ -17,8 +17,9 @@
namespace llarp::dns
{
using ReplyFunction = std::function<void(SockAddr source, std::vector<byte_t> buf)>;
using FailFunction = std::function<void(SockAddr source, Message msg)>;
using ReplyFunction =
std::function<void(SockAddr resolver, SockAddr source, std::vector<byte_t> buf)>;
using FailFunction = std::function<void(SockAddr resolver, SockAddr source, Message msg)>;
class UnboundResolver : public std::enable_shared_from_this<UnboundResolver>
{
@ -28,7 +29,6 @@ namespace llarp::dns
std::atomic<bool> started;
std::unique_ptr<std::thread> runner;
llarp_ev_loop_ptr eventLoop;
ReplyFunction replyFunc;
FailFunction failFunc;
@ -36,7 +36,7 @@ namespace llarp::dns
Reset();
public:
UnboundResolver(llarp_ev_loop_ptr eventLoop, ReplyFunction replyFunc, FailFunction failFunc);
UnboundResolver(std::shared_ptr<Logic> logic, ReplyFunction replyFunc, FailFunction failFunc);
static void
Callback(void* data, int err, ub_result* result);
@ -53,7 +53,7 @@ namespace llarp::dns
AddUpstreamResolver(const std::string& upstreamResolverIP);
void
Lookup(const SockAddr& source, Message msg);
Lookup(SockAddr to, SockAddr from, Message msg);
};
} // namespace llarp::dns

@ -15,8 +15,7 @@ namespace llarp
{
ExitEndpoint::ExitEndpoint(const std::string& name, AbstractRouter* r)
: m_Router(r)
, m_Resolver(std::make_shared<dns::Proxy>(
r->netloop(), r->logic(), r->netloop(), r->logic(), this))
, m_Resolver(std::make_shared<dns::Proxy>(r->netloop(), r->logic(), this))
, m_Name(name)
, m_LocalResolverAddr("127.0.0.1", 53)
, m_InetToNetwork(name + "_exit_rx", r->netloop(), r->netloop())
@ -324,7 +323,7 @@ namespace llarp
loop->add_ticker([&]() { Flush(); });
llarp::LogInfo("Trying to start resolver ", m_LocalResolverAddr.toString());
return m_Resolver->Start(m_LocalResolverAddr, m_UpstreamResolvers);
return m_Resolver->Start(m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers);
}
return true;
}

@ -23,6 +23,7 @@
#include <rpc/endpoint_rpc.hpp>
#include <util/str.hpp>
#include <util/endian.hpp>
#include <dns/srv_data.hpp>
@ -36,13 +37,88 @@ namespace llarp
static constexpr auto FlushInterval = 25ms;
return now >= m_LastFlushAt + FlushInterval;
}
constexpr size_t udp_header_size = 8;
struct DnsHandler : public dns::PacketHandler
{
TunEndpoint* const m_Endpoint;
explicit DnsHandler(AbstractRouter* router, TunEndpoint* ep)
: dns::PacketHandler{router->logic(), ep}, m_Endpoint{ep} {};
void
SendServerMessageBufferTo(SockAddr from, SockAddr to, std::vector<byte_t> buf) override
{
net::IPPacket pkt;
if (buf.size() + 28 > sizeof(pkt.buf))
return;
auto* hdr = pkt.Header();
pkt.buf[1] = 0;
hdr->version = 4;
hdr->ihl = 5;
hdr->tot_len = htons(buf.size() + 28);
hdr->protocol = 0x11; // udp
hdr->ttl = 64;
hdr->frag_off = htons(0b0100000000000000);
hdr->saddr = from.getIPv4();
hdr->daddr = to.getIPv4();
// make udp packet
uint8_t* ptr = pkt.buf + 20;
htobe16buf(ptr, from.getPort());
ptr += 2;
htobe16buf(ptr, to.getPort());
ptr += 2;
htobe16buf(ptr, buf.size() + udp_header_size);
ptr += 2;
htobe16buf(ptr, uint16_t{0}); // checksum
ptr += 2;
std::copy_n(buf.data(), buf.size(), ptr);
/// queue ip packet write
const IpAddress remoteIP{from};
const IpAddress localIP{to};
hdr->check = 0;
hdr->check = net::ipchksum(pkt.buf, 20);
pkt.sz = 28 + buf.size();
m_Endpoint->HandleWriteIPPacket(
pkt.ConstBuffer(), net::ExpandV4(remoteIP.toIP()), net::ExpandV4(localIP.toIP()), 0);
}
};
TunEndpoint::TunEndpoint(AbstractRouter* r, service::Context* parent)
: service::Endpoint(r, parent)
, m_UserToNetworkPktQueue("endpoint_sendq", r->netloop(), r->netloop())
, m_Resolver(std::make_shared<dns::Proxy>(
r->netloop(), r->logic(), r->netloop(), r->logic(), this))
{}
{
m_PacketRouter.reset(
new vpn::PacketRouter{[&](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); }});
#if ANDROID
m_Resolver = std::make_shared<DnsHandler>(r, this);
m_PacketRouter->AddUDPHandler(huint16_t{53}, [&](net::IPPacket pkt) {
const size_t ip_header_size = (pkt.Header()->ihl * 4);
const uint8_t* ptr = pkt.buf + ip_header_size;
const auto dst = ToNet(pkt.dstv4());
const auto src = ToNet(pkt.srcv4());
const SockAddr raddr{src.n, *reinterpret_cast<const uint16_t*>(ptr)};
const SockAddr laddr{dst.n, *reinterpret_cast<const uint16_t*>(ptr + 2)};
std::vector<byte_t> buf;
buf.resize(pkt.sz - (udp_header_size + ip_header_size));
std::copy_n(ptr + udp_header_size, buf.size(), buf.data());
if (m_Resolver->ShouldHandlePacket(laddr, raddr, buf))
m_Resolver->HandlePacket(laddr, raddr, std::move(buf));
else
HandleGotUserPacket(std::move(pkt));
});
#else
m_Resolver = std::make_shared<dns::Proxy>(r->netloop(), r->logic(), this);
#endif
}
util::StatusObject
TunEndpoint::ExtractStatus() const
@ -743,7 +819,7 @@ namespace llarp
auto netloop = Router()->netloop();
if (not netloop->add_network_interface(
m_NetIf, [&](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); }))
m_NetIf, [&](net::IPPacket pkt) { m_PacketRouter->HandleIPPacket(std::move(pkt)); }))
{
LogError(Name(), " failed to add network interface");
return false;
@ -788,7 +864,7 @@ namespace llarp
llarp::LogError(Name(), " failed to set up network interface");
return false;
}
if (!m_Resolver->Start(m_LocalResolverAddr, m_UpstreamResolvers))
if (!m_Resolver->Start(m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers))
{
llarp::LogError(Name(), " failed to start DNS server");
return false;
@ -1008,7 +1084,9 @@ namespace llarp
auto& pkt = write.pkt;
// load
if (!pkt.Load(buf))
{
return false;
}
if (pkt.IsV4())
{
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(dst)));

@ -10,6 +10,7 @@
#include <service/endpoint.hpp>
#include <util/codel.hpp>
#include <util/thread/threading.hpp>
#include <vpn/packet_router.hpp>
#include <future>
#include <queue>
@ -232,7 +233,7 @@ namespace llarp
reply(*query);
}
/// our dns resolver
std::shared_ptr<dns::Proxy> m_Resolver;
std::shared_ptr<dns::PacketHandler> m_Resolver;
/// maps ip address to timestamp last active
std::unordered_map<huint128_t, llarp_time_t> m_IPActivity;
@ -258,6 +259,8 @@ namespace llarp
std::string m_IfName;
std::shared_ptr<vpn::NetworkInterface> m_NetIf;
std::unique_ptr<vpn::PacketRouter> m_PacketRouter;
};
} // namespace handlers

@ -235,6 +235,12 @@ namespace llarp
return m_Pending.size();
}
int
GetUDPSocket() const
{
return m_udp.fd;
}
private:
void
OnTick();

@ -124,8 +124,8 @@ namespace llarp
return ExpandV4Lan(srcv4());
}
static uint16_t
ipchksum(const byte_t* buf, size_t sz, uint32_t sum = 0)
uint16_t
ipchksum(const byte_t* buf, size_t sz, uint32_t sum)
{
while (sz > 1)
{

@ -280,6 +280,9 @@ namespace llarp
MakeICMPUnreachable() const;
};
/// generate ip checksum
uint16_t
ipchksum(const byte_t* buf, size_t sz, uint32_t sum = 0);
} // namespace net
} // namespace llarp

@ -19,6 +19,14 @@
#include <util/logging/logger.hpp>
#include <util/str.hpp>
#if ANDROID
#include <android/ifaddrs.h>
#else
#ifndef _WIN32
#include <ifaddrs.h>
#endif
#endif
#include <cstdio>
#include <list>

@ -26,6 +26,10 @@
#define inet_aton(x, y) inet_pton(AF_INET, x, y)
#endif
#ifndef _WIN32
#include <arpa/inet.h>
#endif
bool
operator==(const sockaddr& a, const sockaddr& b);

@ -11,6 +11,13 @@ namespace llarp
return xntohl(n);
}
template <>
nuint16_t
ToNet(huint16_t h)
{
return xhtons(h);
}
template <>
nuint32_t
ToNet(huint32_t h)

@ -48,6 +48,14 @@ namespace llarp
{
setPort(port);
}
SockAddr::SockAddr(uint32_t ip, uint16_t port)
{
init();
setIPv4(ip);
setPort(ntohs(port));
}
SockAddr::SockAddr(std::string_view addr)
{
init();
@ -292,6 +300,28 @@ namespace llarp
return m_empty;
}
uint32_t
SockAddr::getIPv4() const
{
return m_addr4.sin_addr.s_addr;
}
void
SockAddr::setIPv4(uint32_t ip)
{
m_addr.sin6_family = AF_INET;
uint8_t* ip6 = m_addr.sin6_addr.s6_addr;
llarp::Zero(ip6, sizeof(m_addr.sin6_addr.s6_addr));
applyIPv4MapBytes();
std::memcpy(ip6 + 12, &ip, 4);
m_addr4.sin_addr.s_addr = ip;
m_addr4.sin_family = AF_INET;
m_empty = false;
}
void
SockAddr::setIPv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
{

@ -31,6 +31,7 @@ namespace llarp
SockAddr(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t port);
SockAddr(std::string_view addr);
SockAddr(std::string_view addr, uint16_t port);
SockAddr(uint32_t ip, uint16_t port);
SockAddr(const AddressInfo&);
@ -81,6 +82,9 @@ namespace llarp
setIPv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d);
/// port is in host order
void
setIPv4(uint32_t ip);
void
setPort(uint16_t port);
@ -90,6 +94,9 @@ namespace llarp
huint128_t
asIPv6() const;
/// in network order
uint32_t
getIPv4() const;
huint32_t
asIPv4() const;

@ -318,6 +318,11 @@ namespace llarp
HandleRouterEvent(std::move(event));
}
#if defined(ANDROID)
virtual int
GetOutboundUDPSocket() const = 0;
#endif
protected:
/// Virtual function to handle RouterEvent. HiveRouter overrides this in
/// order to inject the event. The default implementation in Router simply

@ -1345,6 +1345,9 @@ namespace llarp
if (not link->Configure(netloop(), "*", af, m_OutboundPort))
continue;
#if defined(ANDROID)
m_OutboundUDPSocket = link->GetUDPSocket();
#endif
_linkManager.AddLink(std::move(link), false);
return true;
}

@ -519,6 +519,16 @@ namespace llarp
return m_Config;
}
#if defined(ANDROID)
int m_OutboundUDPSocket = -1;
int
GetOutboundUDPSocket() const override
{
return m_OutboundUDPSocket;
}
#endif
private:
std::atomic<bool> _stopping;
std::atomic<bool> _running;

@ -0,0 +1,84 @@
#pragma once
#include <stdio.h>
#include <unistd.h>
#include <ev/vpn.hpp>
#include <vpn/common.hpp>
#include <llarp.hpp>
namespace llarp::vpn
{
class AndroidInterface : public NetworkInterface
{
const int m_fd;
const InterfaceInfo m_Info; // likely 100% ignored on android, at least for now
public:
AndroidInterface(InterfaceInfo info, int fd) : m_fd(fd), m_Info(info)
{
if (m_fd == -1)
throw std::runtime_error(
"Error opening AndroidVPN layer FD: " + std::string{strerror(errno)});
}
virtual ~AndroidInterface()
{
if (m_fd != -1)
::close(m_fd);
}
int
PollFD() const override
{
return m_fd;
}
net::IPPacket
ReadNextPacket() override
{
net::IPPacket pkt;
const auto sz = read(m_fd, pkt.buf, sizeof(pkt.buf));
if (sz >= 0)
pkt.sz = std::min(sz, ssize_t{sizeof(pkt.buf)});
return pkt;
}
bool
WritePacket(net::IPPacket pkt) override
{
const auto sz = write(m_fd, pkt.buf, pkt.sz);
if (sz <= 0)
return false;
return sz == static_cast<ssize_t>(pkt.sz);
}
bool
HasNextPacket() override
{
return false;
}
std::string
IfName() const override
{
return m_Info.ifname;
}
};
class AndroidPlatform : public Platform
{
const int fd;
public:
AndroidPlatform(llarp::Context* ctx) : fd(ctx->androidFD)
{}
std::shared_ptr<NetworkInterface>
ObtainInterface(InterfaceInfo info) override
{
return std::make_shared<AndroidInterface>(std::move(info), fd);
}
};
} // namespace llarp::vpn

@ -0,0 +1,81 @@
#include <vpn/packet_router.hpp>
namespace llarp::vpn
{
struct UDPPacketHandler : public Layer4Handler
{
PacketHandlerFunc m_BaseHandler;
std::unordered_map<nuint16_t, PacketHandlerFunc> m_LocalPorts;
explicit UDPPacketHandler(PacketHandlerFunc baseHandler) : m_BaseHandler{std::move(baseHandler)}
{}
void
AddSubHandler(nuint16_t localport, PacketHandlerFunc handler) override
{
m_LocalPorts.emplace(localport, std::move(handler));
}
void
HandleIPPacket(llarp::net::IPPacket pkt) override
{
const uint8_t* ptr = pkt.buf + (pkt.Header()->ihl * 4) + 2;
const nuint16_t dstPort{*reinterpret_cast<const uint16_t*>(ptr)};
if (auto itr = m_LocalPorts.find(dstPort); itr != m_LocalPorts.end())
{
itr->second(std::move(pkt));
}
else
m_BaseHandler(std::move(pkt));
}
};
struct GenericLayer4Handler : public Layer4Handler
{
PacketHandlerFunc m_BaseHandler;
explicit GenericLayer4Handler(PacketHandlerFunc baseHandler)
: m_BaseHandler{std::move(baseHandler)}
{}
void
HandleIPPacket(llarp::net::IPPacket pkt) override
{
m_BaseHandler(std::move(pkt));
}
};
PacketRouter::PacketRouter(PacketHandlerFunc baseHandler) : m_BaseHandler{std::move(baseHandler)}
{}
void
PacketRouter::HandleIPPacket(llarp::net::IPPacket pkt)
{
const auto proto = pkt.Header()->protocol;
if (const auto itr = m_IPProtoHandler.find(proto); itr != m_IPProtoHandler.end())
{
itr->second->HandleIPPacket(std::move(pkt));
}
else
m_BaseHandler(std::move(pkt));
}
void
PacketRouter::AddUDPHandler(huint16_t localport, PacketHandlerFunc func)
{
constexpr byte_t udp_proto = 0x11;
if (m_IPProtoHandler.find(udp_proto) == m_IPProtoHandler.end())
{
m_IPProtoHandler.emplace(udp_proto, std::make_unique<UDPPacketHandler>(m_BaseHandler));
}
m_IPProtoHandler[udp_proto]->AddSubHandler(ToNet(localport), func);
}
void
PacketRouter::AddIProtoHandler(uint8_t proto, PacketHandlerFunc func)
{
m_IPProtoHandler[proto] = std::make_unique<GenericLayer4Handler>(std::move(func));
}
} // namespace llarp::vpn

@ -0,0 +1,42 @@
#pragma once
#include <net/net_int.hpp>
#include <net/ip_packet.hpp>
#include <functional>
#include <unordered_map>
namespace llarp::vpn
{
using PacketHandlerFunc = std::function<void(llarp::net::IPPacket)>;
struct Layer4Handler
{
virtual ~Layer4Handler() = default;
virtual void
HandleIPPacket(llarp::net::IPPacket pkt) = 0;
virtual void AddSubHandler(nuint16_t, PacketHandlerFunc){};
};
class PacketRouter
{
PacketHandlerFunc m_BaseHandler;
std::unordered_map<uint8_t, std::unique_ptr<Layer4Handler>> m_IPProtoHandler;
public:
/// baseHandler will be called if no other handlers matches a packet
explicit PacketRouter(PacketHandlerFunc baseHandler);
/// feed in an ip packet for handling
void
HandleIPPacket(llarp::net::IPPacket pkt);
/// add a non udp packet handler using ip protocol proto
void
AddIProtoHandler(uint8_t proto, PacketHandlerFunc func);
/// helper that adds a udp packet handler for UDP destinted for localport
void
AddUDPHandler(huint16_t localport, PacketHandlerFunc func);
};
} // namespace llarp::vpn

@ -24,7 +24,7 @@ namespace llarp::vpn
#endif
#ifdef __linux__
#ifdef ANDROID
plat = std::make_shared<vpn::AndroidPlatform>();
plat = std::make_shared<vpn::AndroidPlatform>(ctx);
#else
plat = std::make_shared<vpn::LinuxPlatform>();
#endif

@ -10,6 +10,8 @@
#include <messages/discard.hpp>
#include <util/time.hpp>
#include <net/net_if.hpp>
#undef LOG_TAG
#define LOG_TAG __FILE__

Loading…
Cancel
Save