From 4c630e0437462374af9e9df6fb2fa990c45fed20 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Tue, 2 Mar 2021 13:18:22 -0500 Subject: [PATCH] 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 --- .drone.jsonnet | 29 +- .gitignore | 3 - android/AndroidManifest.xml | 2 +- android/build.gradle | 4 +- .../network/loki/lokinet/LokiNetActivity.java | 36 +- .../network/loki/lokinet/LokinetConfig.java | 39 ++ .../network/loki/lokinet/LokinetDaemon.java | 167 +++++++ .../network/loki/lokinet/LokinetService.java | 9 - cmake/StaticBuild.cmake | 2 +- contrib/ci/drone-static-upload.sh | 5 + include/llarp.hpp | 8 + jni/lokinet_config.cpp | 55 ++- jni/lokinet_daemon.cpp | 40 +- jni/lokinet_jni_common.hpp | 6 +- jni/network_loki_lokinet_LokinetConfig.h | 27 +- jni/network_loki_lokinet_LokinetDaemon.h | 28 +- llarp/CMakeLists.txt | 1 + llarp/config/config.cpp | 12 + llarp/config/config.hpp | 4 + llarp/context.cpp | 9 + llarp/dns/server.cpp | 413 +++++++----------- llarp/dns/server.hpp | 105 ++--- llarp/dns/unbound_resolver.cpp | 25 +- llarp/dns/unbound_resolver.hpp | 10 +- llarp/handlers/exit.cpp | 5 +- llarp/handlers/tun.cpp | 88 +++- llarp/handlers/tun.hpp | 5 +- llarp/link/server.hpp | 6 + llarp/net/ip_packet.cpp | 4 +- llarp/net/ip_packet.hpp | 3 + llarp/net/net.cpp | 8 + llarp/net/net.hpp | 4 + llarp/net/net_int.cpp | 7 + llarp/net/sock_addr.cpp | 30 ++ llarp/net/sock_addr.hpp | 7 + llarp/router/abstractrouter.hpp | 5 + llarp/router/router.cpp | 3 + llarp/router/router.hpp | 10 + llarp/vpn/android.hpp | 84 ++++ llarp/vpn/packet_router.cpp | 81 ++++ llarp/vpn/packet_router.hpp | 42 ++ llarp/vpn/platform.cpp | 2 +- test/iwp/test_iwp_session.cpp | 2 + 43 files changed, 1033 insertions(+), 402 deletions(-) create mode 100644 android/src/network/loki/lokinet/LokinetConfig.java create mode 100644 android/src/network/loki/lokinet/LokinetDaemon.java delete mode 100644 android/src/network/loki/lokinet/LokinetService.java create mode 100644 llarp/vpn/android.hpp create mode 100644 llarp/vpn/packet_router.cpp create mode 100644 llarp/vpn/packet_router.hpp diff --git a/.drone.jsonnet b/.drone.jsonnet index 5657a8aa2..6a8099167 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -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", diff --git a/.gitignore b/.gitignore index b688b6ad3..2a1d2239a 100644 --- a/.gitignore +++ b/.gitignore @@ -40,9 +40,6 @@ vsproject/ .vs daemon.ini -lokinet-win32.exe -lokinet -lokinet.exe .gradle/ diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 33d5bc1e9..a60e01730 100755 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -33,7 +33,7 @@ - diff --git a/android/build.gradle b/android/build.gradle index d867fc811..eb9d8cd9b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -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' diff --git a/android/src/network/loki/lokinet/LokiNetActivity.java b/android/src/network/loki/lokinet/LokiNetActivity.java index c36fdc7f6..cbe836465 100755 --- a/android/src/network/loki/lokinet/LokiNetActivity.java +++ b/android/src/network/loki/lokinet/LokiNetActivity.java @@ -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 diff --git a/android/src/network/loki/lokinet/LokinetConfig.java b/android/src/network/loki/lokinet/LokinetConfig.java new file mode 100644 index 000000000..209ddfb12 --- /dev/null +++ b/android/src/network/loki/lokinet/LokinetConfig.java @@ -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); + } + } +} diff --git a/android/src/network/loki/lokinet/LokinetDaemon.java b/android/src/network/loki/lokinet/LokinetDaemon.java new file mode 100644 index 000000000..ed808d7a5 --- /dev/null +++ b/android/src/network/loki/lokinet/LokinetDaemon.java @@ -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; + } +} diff --git a/android/src/network/loki/lokinet/LokinetService.java b/android/src/network/loki/lokinet/LokinetService.java deleted file mode 100644 index 7dbae6236..000000000 --- a/android/src/network/loki/lokinet/LokinetService.java +++ /dev/null @@ -1,9 +0,0 @@ -package network.loki.lokinet; - - -import android.net.VpnService; - -public class LokinetService extends VpnService -{ - -} diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake index 285c679f6..ebac3a402 100644 --- a/cmake/StaticBuild.cmake +++ b/cmake/StaticBuild.cmake @@ -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" ) diff --git a/contrib/ci/drone-static-upload.sh b/contrib/ci/drone-static-upload.sh index 6f6647606..12ba90c78 100755 --- a/contrib/ci/drone-static-upload.sh +++ b/contrib/ci/drone-static-upload.sh @@ -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 diff --git a/include/llarp.hpp b/include/llarp.hpp index 5944f7fcc..0e1e01ebd 100644 --- a/include/llarp.hpp +++ b/include/llarp.hpp @@ -99,6 +99,14 @@ namespace llarp virtual std::shared_ptr makeVPNPlatform(); +#ifdef ANDROID + + int androidFD = -1; + + int + GetUDPSocket(); +#endif + protected: std::shared_ptr config = nullptr; diff --git a/jni/lokinet_config.cpp b/jni/lokinet_config.cpp index cc735e740..34fae0fab 100644 --- a/jni/lokinet_config.cpp +++ b/jni/lokinet_config.cpp @@ -1,13 +1,18 @@ #include "network_loki_lokinet_LokinetConfig.h" #include +#include #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( + 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(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(env, self); if (conf == nullptr) return JNI_FALSE; - return VisitStringAsStringView(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(env, section, convert); + const auto k = VisitStringAsStringView(env, key, convert); + const auto v = VisitStringAsStringView(env, value, convert); + + auto conf = GetImpl(env, self); + if (conf) + { + conf->AddDefault(sect, k, v); + } } -} \ No newline at end of file +} diff --git a/jni/lokinet_daemon.cpp b/jni/lokinet_daemon.cpp index f38e5579e..a13cd638f 100644 --- a/jni/lokinet_daemon.cpp +++ b/jni/lokinet_daemon.cpp @@ -2,6 +2,7 @@ #include "lokinet_jni_common.hpp" #include "lokinet_jni_vpnio.hpp" #include +#include 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(*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(env, self); - auto impl = GetImpl(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(env, self, "m_FD"); + } + + JNIEXPORT jint JNICALL + Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv* env, jobject self) + { + auto ptr = GetImpl(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()); } -} \ No newline at end of file +} diff --git a/jni/lokinet_jni_common.hpp b/jni/lokinet_jni_common.hpp index 0b162e54a..76f1e977b 100644 --- a/jni/lokinet_jni_common.hpp +++ b/jni/lokinet_jni_common.hpp @@ -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(env, buffer); } @@ -79,4 +79,4 @@ GetImpl(JNIEnv* env, jobject self) return FromObjectMember(env, self, "impl"); } -#endif \ No newline at end of file +#endif diff --git a/jni/network_loki_lokinet_LokinetConfig.h b/jni/network_loki_lokinet_LokinetConfig.h index 7496155d3..341a64fdd 100644 --- a/jni/network_loki_lokinet_LokinetConfig.h +++ b/jni/network_loki_lokinet_LokinetConfig.h @@ -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 } diff --git a/jni/network_loki_lokinet_LokinetDaemon.h b/jni/network_loki_lokinet_LokinetDaemon.h index 3b23a9f70..78966b989 100644 --- a/jni/network_loki_lokinet_LokinetDaemon.h +++ b/jni/network_loki_lokinet_LokinetDaemon.h @@ -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 } diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 1a7ea79d6..c4d4f0627 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -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 ) diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 35285ba3f..928f73d10 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.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{section, key, val}); + } + bool Config::Load(std::optional 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) { diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 5f561e2cd..86c4b9257 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -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> m_Additional; ConfigParser m_Parser; const fs::path m_DataDir; }; diff --git a/llarp/context.cpp b/llarp/context.cpp index a34d67e9c..b70df293c 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -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" diff --git a/llarp/dns/server.cpp b/llarp/dns/server.cpp index 1451a9766..52f25ddd3 100644 --- a/llarp/dns/server.cpp +++ b/llarp/dns/server.cpp @@ -5,297 +5,212 @@ #include #include -namespace llarp +namespace llarp::dns { - namespace dns + static std::vector + 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 tmp = {{0}}; + llarp_buffer_t buf{tmp}; + if (not msg.Encode(&buf)) + throw std::runtime_error("cannot encode dns message"); + std::vector 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& 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 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 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 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(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(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(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 resolvers) + { + return SetupUnboundResolver(std::move(resolvers)); + } - void - Proxy::Restart() - { - if (m_UnboundResolver) + bool + PacketHandler::SetupUnboundResolver(std::vector 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& 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 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 buf) { + auto this_ptr = self.lock(); + if (this_ptr) + { + this_ptr->SendServerMessageBufferTo(from, to, std::move(buf)); + } + }; - m_UnboundResolver = std::make_shared( - m_ServerLoop, std::move(replyFunc), std::move(failFunc)); - if (not m_UnboundResolver->Init()) + m_UnboundResolver = + std::make_shared(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 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 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 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 diff --git a/llarp/dns/server.hpp b/llarp/dns/server.hpp index a303283ca..9d0a8cc16 100644 --- a/llarp/dns/server.hpp +++ b/llarp/dns/server.hpp @@ -27,18 +27,17 @@ namespace llarp HandleHookedDNSMessage(Message query, std::function sendReply) = 0; }; - struct Proxy : public std::enable_shared_from_this + struct PacketHandler : public std::enable_shared_from_this { using Logic_ptr = std::shared_ptr; - Proxy( - llarp_ev_loop_ptr serverLoop, - Logic_ptr serverLogic, - llarp_ev_loop_ptr clientLoop, - Logic_ptr clientLogic, - IQueryHandler* handler); + using Buffer_t = std::vector; - bool - Start(const IpAddress& addr, const std::vector& resolvers); + explicit PacketHandler(Logic_ptr logic, IQueryHandler* handler); + + virtual ~PacketHandler() = default; + + virtual bool + Start(SockAddr localaddr, std::vector upstreamResolvers); void Stop(); @@ -46,80 +45,50 @@ namespace llarp void Restart(); - using Buffer_t = std::vector; + 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 resolvers); - void - SendClientMessageTo(const SockAddr& to, Message msg); + IQueryHandler* const m_QueryHandler; + std::set m_Resolvers; + std::shared_ptr 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; + 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 resolvers) override; - void - HandleUpstreamResponse(SockAddr to, std::vector buf); + using Buffer_t = std::vector; + 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& 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 m_Resolvers; - std::shared_ptr 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 m_Forwarded; + llarp_ev_loop_ptr m_Loop; }; } // namespace dns } // namespace llarp diff --git a/llarp/dns/unbound_resolver.cpp b/llarp/dns/unbound_resolver.cpp index 9257eaffe..52fa1a335 100644 --- a/llarp/dns/unbound_resolver.cpp +++ b/llarp/dns/unbound_resolver.cpp @@ -10,6 +10,7 @@ namespace llarp::dns std::weak_ptr 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, 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; } } diff --git a/llarp/dns/unbound_resolver.hpp b/llarp/dns/unbound_resolver.hpp index cf0dbc1de..a4fb8c055 100644 --- a/llarp/dns/unbound_resolver.hpp +++ b/llarp/dns/unbound_resolver.hpp @@ -17,8 +17,9 @@ namespace llarp::dns { - using ReplyFunction = std::function buf)>; - using FailFunction = std::function; + using ReplyFunction = + std::function buf)>; + using FailFunction = std::function; class UnboundResolver : public std::enable_shared_from_this { @@ -28,7 +29,6 @@ namespace llarp::dns std::atomic started; std::unique_ptr 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, 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 diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index a6001373a..a610d2978 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -15,8 +15,7 @@ namespace llarp { ExitEndpoint::ExitEndpoint(const std::string& name, AbstractRouter* r) : m_Router(r) - , m_Resolver(std::make_shared( - r->netloop(), r->logic(), r->netloop(), r->logic(), this)) + , m_Resolver(std::make_shared(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; } diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 5de4cfb81..3c3ce2a30 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -23,6 +23,7 @@ #include #include +#include #include @@ -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 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( - 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(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(ptr)}; + const SockAddr laddr{dst.n, *reinterpret_cast(ptr + 2)}; + + std::vector 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(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))); diff --git a/llarp/handlers/tun.hpp b/llarp/handlers/tun.hpp index eb66097a7..78755c5b8 100644 --- a/llarp/handlers/tun.hpp +++ b/llarp/handlers/tun.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -232,7 +233,7 @@ namespace llarp reply(*query); } /// our dns resolver - std::shared_ptr m_Resolver; + std::shared_ptr m_Resolver; /// maps ip address to timestamp last active std::unordered_map m_IPActivity; @@ -258,6 +259,8 @@ namespace llarp std::string m_IfName; std::shared_ptr m_NetIf; + + std::unique_ptr m_PacketRouter; }; } // namespace handlers diff --git a/llarp/link/server.hpp b/llarp/link/server.hpp index a428df0fa..a43d107ca 100644 --- a/llarp/link/server.hpp +++ b/llarp/link/server.hpp @@ -235,6 +235,12 @@ namespace llarp return m_Pending.size(); } + int + GetUDPSocket() const + { + return m_udp.fd; + } + private: void OnTick(); diff --git a/llarp/net/ip_packet.cpp b/llarp/net/ip_packet.cpp index c9bf89f17..c3cb1f239 100644 --- a/llarp/net/ip_packet.cpp +++ b/llarp/net/ip_packet.cpp @@ -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) { diff --git a/llarp/net/ip_packet.hpp b/llarp/net/ip_packet.hpp index ec0faaa0f..b65df40cd 100644 --- a/llarp/net/ip_packet.hpp +++ b/llarp/net/ip_packet.hpp @@ -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 diff --git a/llarp/net/net.cpp b/llarp/net/net.cpp index d795e4d93..aeeeb08a4 100644 --- a/llarp/net/net.cpp +++ b/llarp/net/net.cpp @@ -19,6 +19,14 @@ #include #include +#if ANDROID +#include +#else +#ifndef _WIN32 +#include +#endif +#endif + #include #include diff --git a/llarp/net/net.hpp b/llarp/net/net.hpp index c65243a1e..4cd593fa6 100644 --- a/llarp/net/net.hpp +++ b/llarp/net/net.hpp @@ -26,6 +26,10 @@ #define inet_aton(x, y) inet_pton(AF_INET, x, y) #endif +#ifndef _WIN32 +#include +#endif + bool operator==(const sockaddr& a, const sockaddr& b); diff --git a/llarp/net/net_int.cpp b/llarp/net/net_int.cpp index 5642201ba..72de8ca37 100644 --- a/llarp/net/net_int.cpp +++ b/llarp/net/net_int.cpp @@ -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) diff --git a/llarp/net/sock_addr.cpp b/llarp/net/sock_addr.cpp index 416af3fdb..cb09d6506 100644 --- a/llarp/net/sock_addr.cpp +++ b/llarp/net/sock_addr.cpp @@ -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) { diff --git a/llarp/net/sock_addr.hpp b/llarp/net/sock_addr.hpp index 15696ae30..6e0be7e69 100644 --- a/llarp/net/sock_addr.hpp +++ b/llarp/net/sock_addr.hpp @@ -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; diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index 193d541e7..26625de25 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -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 diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 6e4c237bc..e0641f12b 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -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; } diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 5fe527c57..d5070649a 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -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 _stopping; std::atomic _running; diff --git a/llarp/vpn/android.hpp b/llarp/vpn/android.hpp new file mode 100644 index 000000000..e7917011a --- /dev/null +++ b/llarp/vpn/android.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include +#include + +#include +#include +#include + +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(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 + ObtainInterface(InterfaceInfo info) override + { + return std::make_shared(std::move(info), fd); + } + }; + +} // namespace llarp::vpn diff --git a/llarp/vpn/packet_router.cpp b/llarp/vpn/packet_router.cpp new file mode 100644 index 000000000..aca5a5060 --- /dev/null +++ b/llarp/vpn/packet_router.cpp @@ -0,0 +1,81 @@ +#include + +namespace llarp::vpn +{ + struct UDPPacketHandler : public Layer4Handler + { + PacketHandlerFunc m_BaseHandler; + std::unordered_map 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(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(m_BaseHandler)); + } + m_IPProtoHandler[udp_proto]->AddSubHandler(ToNet(localport), func); + } + + void + PacketRouter::AddIProtoHandler(uint8_t proto, PacketHandlerFunc func) + { + m_IPProtoHandler[proto] = std::make_unique(std::move(func)); + } + +} // namespace llarp::vpn diff --git a/llarp/vpn/packet_router.hpp b/llarp/vpn/packet_router.hpp new file mode 100644 index 000000000..6d5bdf9a1 --- /dev/null +++ b/llarp/vpn/packet_router.hpp @@ -0,0 +1,42 @@ +#pragma once +#include +#include +#include +#include + +namespace llarp::vpn +{ + using PacketHandlerFunc = std::function; + + 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> 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 diff --git a/llarp/vpn/platform.cpp b/llarp/vpn/platform.cpp index 74cfe8840..f4759f92b 100644 --- a/llarp/vpn/platform.cpp +++ b/llarp/vpn/platform.cpp @@ -24,7 +24,7 @@ namespace llarp::vpn #endif #ifdef __linux__ #ifdef ANDROID - plat = std::make_shared(); + plat = std::make_shared(ctx); #else plat = std::make_shared(); #endif diff --git a/test/iwp/test_iwp_session.cpp b/test/iwp/test_iwp_session.cpp index e31113bdf..a4c355aec 100644 --- a/test/iwp/test_iwp_session.cpp +++ b/test/iwp/test_iwp_session.cpp @@ -10,6 +10,8 @@ #include #include +#include + #undef LOG_TAG #define LOG_TAG __FILE__