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__