mirror of https://github.com/oxen-io/lokinet
Merge pull request #1537 from majestrate/android-works-now-2021-02-11
Android
This commit is contained in:
commit
f685d615bf
|
@ -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',
|
||||
|
@ -155,6 +177,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',
|
||||
|
@ -223,6 +246,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)", docker_base+'debian-win32-cross',
|
||||
|
|
|
@ -40,9 +40,6 @@ vsproject/
|
|||
.vs
|
||||
|
||||
daemon.ini
|
||||
lokinet-win32.exe
|
||||
lokinet
|
||||
lokinet.exe
|
||||
|
||||
|
||||
.gradle/
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service android:name=".LokinetService"
|
||||
<service android:name=".LokinetDaemon"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||
|
|
|
@ -27,11 +27,11 @@ android {
|
|||
targetSdkVersion 28
|
||||
minSdkVersion 23
|
||||
versionCode 1
|
||||
versionName '0.8.0'
|
||||
versionName '0.8.3'
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
// targets "lokinet-android"
|
||||
arguments "-DWITH_LTO=OFF", "-DCXXOPTS_BUILD_TESTS=OFF","-DWITH_TESTS=OFF", "-DCMAKE_CROSSCOMPILING=ON", "-DNATIVE_BUILD=OFF", "-DANDROID=ON", "-DANDROID_STL=c++_static", "-DBUILD_STATIC_DEPS=ON", "-DBUILD_SHARED_LIBS=OFF", "-DSTATIC_LINK=ON", "-DDOWNLOAD_UV=FORCE", "-DANDROID_ARM_MODE=arm"
|
||||
arguments "-DWITH_LTO=OFF", "-DCXXOPTS_BUILD_TESTS=OFF","-DWITH_TESTS=OFF", "-DCMAKE_CROSSCOMPILING=ON", "-DNATIVE_BUILD=OFF", "-DANDROID=ON", "-DANDROID_STL=c++_static", "-DBUILD_STATIC_DEPS=ON", "-DBUILD_SHARED_LIBS=OFF", "-DSTATIC_LINK=ON", "-DANDROID_ARM_MODE=arm", "-DFORCE_OXENMQ_SUBMODULE=ON"
|
||||
cppFlags "-std=c++17"
|
||||
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||
// abiFilters 'armeabi-v7a'
|
||||
|
|
|
@ -19,7 +19,9 @@ import android.content.Context;
|
|||
|
||||
import android.content.ComponentName;
|
||||
import android.content.ServiceConnection;
|
||||
import android.Manifest;
|
||||
|
||||
import android.net.VpnService;
|
||||
import android.os.AsyncTask;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
@ -29,14 +31,18 @@ import android.view.Menu;
|
|||
import android.view.MenuItem;
|
||||
import android.widget.TextView;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
public class LokiNetActivity extends Activity {
|
||||
private static final String TAG = "lokinet-activity";
|
||||
private TextView textView;
|
||||
private static final String DefaultBootstrapURL = "https://seed.lokinet.org/bootstrap.signed";
|
||||
private static final String DefaultBootstrapURL = "https://seed.lokinet.org/lokinet.signed";
|
||||
|
||||
private AsyncBootstrap bootstrapper;
|
||||
|
||||
public static final String LOG_TAG = "LokinetDaemon";
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -71,11 +77,33 @@ public class LokiNetActivity extends Activity {
|
|||
bootstrapper.execute(DefaultBootstrapURL);
|
||||
}
|
||||
|
||||
|
||||
public void runLokinetService()
|
||||
{
|
||||
startService(new Intent(LokiNetActivity.this,
|
||||
LokinetService.class));
|
||||
Intent intent = VpnService.prepare(getApplicationContext());
|
||||
if (intent != null)
|
||||
{
|
||||
Log.d(LOG_TAG, "VpnService.prepare() returned an Intent, so launch that intent.");
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.w(LOG_TAG, "VpnService.prepare() returned null, not running.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if (resultCode == RESULT_OK)
|
||||
{
|
||||
Log.d(LOG_TAG, "VpnService prepared intent RESULT_OK, launching LokinetDaemon Service");
|
||||
startService(new Intent(LokiNetActivity.this,
|
||||
LokinetDaemon.class));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.d(LOG_TAG, "VpnService prepared intent NOT RESULT_OK, shit.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package network.loki.lokinet;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class LokinetConfig
|
||||
{
|
||||
static {
|
||||
System.loadLibrary("lokinet-android");
|
||||
}
|
||||
|
||||
private static native ByteBuffer Obtain(String dataDir);
|
||||
private static native void Free(ByteBuffer buf);
|
||||
|
||||
/*** load config file from disk */
|
||||
public native boolean Load();
|
||||
/*** save chages to disk */
|
||||
public native boolean Save();
|
||||
|
||||
|
||||
/** override default config value before loading from config file */
|
||||
public native void AddDefaultValue(String section, String key, String value);
|
||||
|
||||
private final ByteBuffer impl;
|
||||
|
||||
public LokinetConfig(String dataDir)
|
||||
{
|
||||
impl = Obtain(dataDir);
|
||||
if(impl == null)
|
||||
throw new RuntimeException("cannot obtain config from "+dataDir);
|
||||
}
|
||||
|
||||
public void finalize()
|
||||
{
|
||||
if (impl != null)
|
||||
{
|
||||
Free(impl);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
package network.loki.lokinet;
|
||||
|
||||
import java.lang.Thread;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.io.File;
|
||||
|
||||
import android.net.VpnService;
|
||||
import android.util.Log;
|
||||
import android.content.Intent;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
public class LokinetDaemon extends VpnService
|
||||
{
|
||||
static {
|
||||
System.loadLibrary("lokinet-android");
|
||||
}
|
||||
|
||||
private static native ByteBuffer Obtain();
|
||||
private static native void Free(ByteBuffer buf);
|
||||
public native boolean Configure(LokinetConfig config);
|
||||
public native int Mainloop();
|
||||
public native boolean IsRunning();
|
||||
public native boolean Stop();
|
||||
public native void InjectVPNFD();
|
||||
public native int GetUDPSocket();
|
||||
|
||||
private static native String DetectFreeRange();
|
||||
|
||||
public static final String LOG_TAG = "LokinetDaemon";
|
||||
|
||||
ByteBuffer impl = null;
|
||||
ParcelFileDescriptor iface;
|
||||
int m_FD = -1;
|
||||
int m_UDPSocket = -1;
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
|
||||
if (IsRunning())
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
if (impl != null)
|
||||
{
|
||||
Free(impl);
|
||||
impl = null;
|
||||
}
|
||||
}
|
||||
|
||||
public int onStartCommand(Intent intent, int flags, int startID)
|
||||
{
|
||||
Log.d(LOG_TAG, "onStartCommand()");
|
||||
|
||||
if (!IsRunning())
|
||||
{
|
||||
if (impl != null)
|
||||
{
|
||||
Free(impl);
|
||||
impl = null;
|
||||
}
|
||||
impl = Obtain();
|
||||
if (impl == null)
|
||||
{
|
||||
Log.e(LOG_TAG, "got nullptr when creating llarp::Context in jni");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
String dataDir = getFilesDir().toString();
|
||||
LokinetConfig config;
|
||||
try
|
||||
{
|
||||
config = new LokinetConfig(dataDir);
|
||||
}
|
||||
catch(RuntimeException ex)
|
||||
{
|
||||
Log.e(LOG_TAG, ex.toString());
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
// FIXME: make these configurable
|
||||
String exitNode = "exit.loki";
|
||||
String upstreamDNS = "1.1.1.1";
|
||||
String ourRange = DetectFreeRange();
|
||||
|
||||
if(ourRange.isEmpty())
|
||||
{
|
||||
Log.e(LOG_TAG, "cannot detect free range");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
|
||||
// set up config values
|
||||
config.AddDefaultValue("network", "exit-node", exitNode);
|
||||
config.AddDefaultValue("network", "ifaddr", ourRange);
|
||||
config.AddDefaultValue("dns", "upstream", upstreamDNS);
|
||||
|
||||
|
||||
if (!config.Load())
|
||||
{
|
||||
Log.e(LOG_TAG, "failed to load (or create) config file at: " + dataDir + "/lokinet.ini");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
VpnService.Builder builder = new VpnService.Builder();
|
||||
|
||||
builder.setMtu(1500);
|
||||
|
||||
String[] parts = ourRange.split("/");
|
||||
String ourIP = parts[0];
|
||||
int ourMask = Integer.parseInt(parts[1]);
|
||||
|
||||
builder.addAddress(ourIP, ourMask);
|
||||
builder.addRoute("0.0.0.0", 0);
|
||||
builder.addDnsServer(upstreamDNS);
|
||||
builder.setSession("Lokinet");
|
||||
builder.setConfigureIntent(null);
|
||||
|
||||
iface = builder.establish();
|
||||
if (iface == null)
|
||||
{
|
||||
Log.e(LOG_TAG, "VPN Interface from builder.establish() came back null");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
m_FD = iface.detachFd();
|
||||
|
||||
InjectVPNFD();
|
||||
|
||||
if (!Configure(config))
|
||||
{
|
||||
//TODO: close vpn FD if this fails, either on native side, or here if possible
|
||||
Log.e(LOG_TAG, "failed to configure daemon");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
m_UDPSocket = GetUDPSocket();
|
||||
|
||||
if (m_UDPSocket <= 0)
|
||||
{
|
||||
Log.e(LOG_TAG, "failed to get proper UDP handle from daemon, aborting.");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
protect(m_UDPSocket);
|
||||
|
||||
new Thread(() -> {
|
||||
Mainloop();
|
||||
}).start();
|
||||
|
||||
Log.d(LOG_TAG, "started successfully!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.d(LOG_TAG, "already running");
|
||||
}
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package network.loki.lokinet;
|
||||
|
||||
|
||||
import android.net.VpnService;
|
||||
|
||||
public class LokinetService extends VpnService
|
||||
{
|
||||
|
||||
}
|
|
@ -291,7 +291,7 @@ build_external(zmq
|
|||
${zmq_patch}
|
||||
CONFIGURE_COMMAND ./configure ${cross_host} --prefix=${DEPS_DESTDIR} --enable-static --disable-shared
|
||||
--disable-curve-keygen --enable-curve --disable-drafts --disable-libunwind --with-libsodium
|
||||
--without-pgm --without-norm --without-vmci --without-docs --with-pic --disable-Werror ${zmq_extra}
|
||||
--without-pgm --without-norm --without-vmci --without-docs --with-pic --disable-Werror --disable-libbsd ${zmq_extra}
|
||||
"CC=${deps_cc}" "CXX=${deps_cxx}" "CFLAGS=${deps_CFLAGS} -fstack-protector" "CXXFLAGS=${deps_CXXFLAGS} -fstack-protector"
|
||||
"sodium_CFLAGS=-I${DEPS_DESTDIR}/include" "sodium_LIBS=-L${DEPS_DESTDIR}/lib -lsodium"
|
||||
)
|
||||
|
|
|
@ -39,6 +39,11 @@ if [ -e daemon/lokinet.exe ]; then
|
|||
# zipit up yo
|
||||
archive="$base.zip"
|
||||
zip -r "$archive" "$base"
|
||||
elif [ -e build/outputs/apk/debug/lokinet-debug.apk ] ; then
|
||||
# android af ngl
|
||||
cp -av build/outputs/apk/debug/lokinet-debug.apk "$base"
|
||||
archive="$base.tar.xz"
|
||||
tar cJvf "$archive" "$base"
|
||||
else
|
||||
cp -av daemon/lokinet daemon/lokinet-vpn ../lokinet-bootstrap "$base"
|
||||
# tar dat shiz up yo
|
||||
|
|
|
@ -99,6 +99,14 @@ namespace llarp
|
|||
virtual std::shared_ptr<llarp::vpn::Platform>
|
||||
makeVPNPlatform();
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
int androidFD = -1;
|
||||
|
||||
int
|
||||
GetUDPSocket();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Config> config = nullptr;
|
||||
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
#include "network_loki_lokinet_LokinetConfig.h"
|
||||
#include <llarp.hpp>
|
||||
#include <config/config.hpp>
|
||||
#include "lokinet_jni_common.hpp"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv* env, jclass)
|
||||
Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv* env, jclass, jstring dataDir)
|
||||
{
|
||||
auto conf = new llarp::Config();
|
||||
auto conf = VisitStringAsStringView<llarp::Config*>(
|
||||
env, dataDir, [](std::string_view val) -> llarp::Config* {
|
||||
return new llarp::Config{val};
|
||||
});
|
||||
|
||||
if (conf == nullptr)
|
||||
return nullptr;
|
||||
return env->NewDirectByteBuffer(conf, sizeof(llarp::Config));
|
||||
|
@ -21,15 +26,49 @@ extern "C"
|
|||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv* env, jobject self, jstring fname)
|
||||
Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv* env, jobject self)
|
||||
{
|
||||
auto conf = GetImpl<llarp::Config>(env, self);
|
||||
if (conf == nullptr)
|
||||
return JNI_FALSE;
|
||||
return VisitStringAsStringView<jboolean>(env, fname, [conf](std::string_view val) -> jboolean {
|
||||
if (conf->Load(val, false, llarp::GetDefaultDataDir()))
|
||||
return JNI_TRUE;
|
||||
return JNI_FALSE;
|
||||
});
|
||||
if (conf->Load())
|
||||
{
|
||||
return JNI_TRUE;
|
||||
}
|
||||
return JNI_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_network_loki_lokinet_LokinetConfig_Save(JNIEnv* env, jobject self)
|
||||
{
|
||||
auto conf = GetImpl<llarp::Config>(env, self);
|
||||
if (conf == nullptr)
|
||||
return JNI_FALSE;
|
||||
try
|
||||
{
|
||||
conf->Save();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return JNI_FALSE;
|
||||
}
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_network_loki_lokinet_LokinetConfig_AddDefaultValue(
|
||||
JNIEnv* env, jobject self, jstring section, jstring key, jstring value)
|
||||
{
|
||||
auto convert = [](std::string_view str) -> std::string { return std::string{str}; };
|
||||
|
||||
const auto sect = VisitStringAsStringView<std::string>(env, section, convert);
|
||||
const auto k = VisitStringAsStringView<std::string>(env, key, convert);
|
||||
const auto v = VisitStringAsStringView<std::string>(env, value, convert);
|
||||
|
||||
auto conf = GetImpl<llarp::Config>(env, self);
|
||||
if (conf)
|
||||
{
|
||||
conf->AddDefault(sect, k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "lokinet_jni_common.hpp"
|
||||
#include "lokinet_jni_vpnio.hpp"
|
||||
#include <llarp.hpp>
|
||||
#include <config/config.hpp>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
@ -31,7 +32,9 @@ extern "C"
|
|||
try
|
||||
{
|
||||
llarp::RuntimeOptions opts{};
|
||||
ptr->Configure(*config);
|
||||
|
||||
// janky make_shared deep copy because jni + shared pointer = scary
|
||||
ptr->Configure(std::make_shared<llarp::Config>(*config));
|
||||
ptr->Setup(opts);
|
||||
}
|
||||
catch (...)
|
||||
|
@ -71,17 +74,30 @@ extern "C"
|
|||
return ptr->IsUp() ? JNI_FALSE : JNI_TRUE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_network_loki_lokinet_LokinetDaemon_InjectVPN(JNIEnv* env, jobject self, jobject vpn)
|
||||
JNIEXPORT void JNICALL
|
||||
Java_network_loki_lokinet_LokinetDaemon_InjectVPNFD(JNIEnv* env, jobject self)
|
||||
{
|
||||
auto ptr = GetImpl<llarp::Context>(env, self);
|
||||
auto impl = GetImpl<lokinet_jni_vpnio>(env, vpn);
|
||||
if (ptr == nullptr || impl == nullptr)
|
||||
return JNI_FALSE;
|
||||
if (impl->info.netmask == 0)
|
||||
return JNI_FALSE;
|
||||
if (not impl->Init(ptr))
|
||||
return JNI_FALSE;
|
||||
return llarp_main_inject_default_vpn(ptr, &impl->io, impl->info) ? JNI_TRUE : JNI_FALSE;
|
||||
|
||||
ptr->androidFD = GetObjectMemberAsInt<int>(env, self, "m_FD");
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv* env, jobject self)
|
||||
{
|
||||
auto ptr = GetImpl<llarp::Context>(env, self);
|
||||
|
||||
return ptr->GetUDPSocket();
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_network_loki_lokinet_LokinetDaemon_DetectFreeRange(JNIEnv* env, jclass)
|
||||
{
|
||||
std::string rangestr{};
|
||||
if (auto maybe = llarp::FindFreeRange())
|
||||
{
|
||||
rangestr = maybe->ToString();
|
||||
}
|
||||
return env->NewStringUTF(rangestr.c_str());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ VisitStringAsStringView(JNIEnv* env, jobject str, V visit)
|
|||
env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT);
|
||||
env->DeleteLocalRef(stringJbytes);
|
||||
|
||||
return std::move(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// cast jni buffer to T *
|
||||
|
@ -45,7 +45,7 @@ static T*
|
|||
FromObjectMember(JNIEnv* env, jobject self, const char* membername)
|
||||
{
|
||||
jclass cl = env->GetObjectClass(self);
|
||||
jfieldID name = env->GetFieldID(cl, membername, "Ljava/nio/Buffer;");
|
||||
jfieldID name = env->GetFieldID(cl, membername, "Ljava/nio/ByteBuffer;");
|
||||
jobject buffer = env->GetObjectField(self, name);
|
||||
return FromBuffer<T>(env, buffer);
|
||||
}
|
||||
|
@ -79,4 +79,4 @@ GetImpl(JNIEnv* env, jobject self)
|
|||
return FromObjectMember<T>(env, self, "impl");
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -11,15 +11,15 @@ extern "C"
|
|||
/*
|
||||
* Class: network_loki_lokinet_LokinetConfig
|
||||
* Method: Obtain
|
||||
* Signature: ()Ljava/nio/Buffer;
|
||||
* Signature: (Ljava/lang/String;)Ljava/nio/ByteBuffer;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv*, jclass);
|
||||
Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv*, jclass, jstring);
|
||||
|
||||
/*
|
||||
* Class: network_loki_lokinet_LokinetConfig
|
||||
* Method: Free
|
||||
* Signature: (Ljava/nio/Buffer;)V
|
||||
* Signature: (Ljava/nio/ByteBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_network_loki_lokinet_LokinetConfig_Free(JNIEnv*, jclass, jobject);
|
||||
|
@ -27,10 +27,27 @@ extern "C"
|
|||
/*
|
||||
* Class: network_loki_lokinet_LokinetConfig
|
||||
* Method: Load
|
||||
* Signature: (Ljava/lang/String;)Z
|
||||
* Signature: ()Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv*, jobject, jstring);
|
||||
Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv*, jobject);
|
||||
|
||||
/*
|
||||
* Class: network_loki_lokinet_LokinetConfig
|
||||
* Method: Save
|
||||
* Signature: ()Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_network_loki_lokinet_LokinetConfig_Save(JNIEnv*, jobject);
|
||||
|
||||
/*
|
||||
* Class: network_loki_lokinet_LokinetConfig
|
||||
* Method: AddDefaultValue
|
||||
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_network_loki_lokinet_LokinetConfig_AddDefaultValue(
|
||||
JNIEnv*, jobject, jstring, jstring, jstring);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ extern "C"
|
|||
/*
|
||||
* Class: network_loki_lokinet_LokinetDaemon
|
||||
* Method: Obtain
|
||||
* Signature: ()Ljava/nio/Buffer;
|
||||
* Signature: ()Ljava/nio/ByteBuffer;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_network_loki_lokinet_LokinetDaemon_Obtain(JNIEnv*, jclass);
|
||||
|
@ -19,7 +19,7 @@ extern "C"
|
|||
/*
|
||||
* Class: network_loki_lokinet_LokinetDaemon
|
||||
* Method: Free
|
||||
* Signature: (Ljava/nio/Buffer;)V
|
||||
* Signature: (Ljava/nio/ByteBuffer;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_network_loki_lokinet_LokinetDaemon_Free(JNIEnv*, jclass, jobject);
|
||||
|
@ -58,11 +58,27 @@ extern "C"
|
|||
|
||||
/*
|
||||
* Class: network_loki_lokinet_LokinetDaemon
|
||||
* Method: InjectVPN
|
||||
* Signature: (Lnetwork/loki/lokinet/LokinetVPN;)Z
|
||||
* Method: InjectVPNFD
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_network_loki_lokinet_LokinetDaemon_InjectVPN(JNIEnv*, jobject, jobject);
|
||||
JNIEXPORT void JNICALL
|
||||
Java_network_loki_lokinet_LokinetDaemon_InjectVPNFD(JNIEnv*, jobject);
|
||||
|
||||
/*
|
||||
* Class: network_loki_lokinet_LokinetDaemon
|
||||
* Method: GetUDPSocket
|
||||
* Signature: ()I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv*, jobject);
|
||||
|
||||
/*
|
||||
* Class: network_loki_lokinet_LokinetDaemon
|
||||
* Method: DetectFreeRange
|
||||
* Signature: ()Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_network_loki_lokinet_LokinetDaemon_DetectFreeRange(JNIEnv*, jclass);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ add_library(lokinet-platform
|
|||
net/net_int.cpp
|
||||
net/route.cpp
|
||||
net/sock_addr.cpp
|
||||
vpn/packet_router.cpp
|
||||
vpn/platform.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -1020,6 +1020,12 @@ namespace llarp
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::AddDefault(std::string section, std::string key, std::string val)
|
||||
{
|
||||
m_Additional.emplace_back(std::array<std::string, 3>{section, key, val});
|
||||
}
|
||||
|
||||
bool
|
||||
Config::Load(std::optional<fs::path> fname, bool isRelay)
|
||||
{
|
||||
|
@ -1073,6 +1079,12 @@ namespace llarp
|
|||
m_Parser.Clear();
|
||||
LoadOverrides();
|
||||
|
||||
/// load additional config options added
|
||||
for (const auto& [sect, key, val] : m_Additional)
|
||||
{
|
||||
conf.addConfigValue(sect, key, val);
|
||||
}
|
||||
|
||||
m_Parser.IterAll([&](std::string_view section, const SectionValues_t& values) {
|
||||
for (const auto& pair : values)
|
||||
{
|
||||
|
|
|
@ -224,6 +224,9 @@ namespace llarp
|
|||
void
|
||||
Override(std::string section, std::string key, std::string value);
|
||||
|
||||
void
|
||||
AddDefault(std::string section, std::string key, std::string value);
|
||||
|
||||
private:
|
||||
/// Load (initialize) a default config.
|
||||
///
|
||||
|
@ -242,6 +245,7 @@ namespace llarp
|
|||
void
|
||||
LoadOverrides();
|
||||
|
||||
std::vector<std::array<std::string, 3>> m_Additional;
|
||||
ConfigParser m_Parser;
|
||||
const fs::path m_DataDir;
|
||||
};
|
||||
|
|
|
@ -190,6 +190,15 @@ namespace llarp
|
|||
llarp::LogDebug("free logic");
|
||||
logic.reset();
|
||||
}
|
||||
|
||||
#if defined(ANDROID)
|
||||
int
|
||||
Context::GetUDPSocket()
|
||||
{
|
||||
return router->GetOutboundUDPSocket();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace llarp
|
||||
|
||||
extern "C"
|
||||
|
|
|
@ -5,297 +5,212 @@
|
|||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace dns
|
||||
static std::vector<byte_t>
|
||||
MessageToBuffer(Message msg)
|
||||
{
|
||||
Proxy::Proxy(
|
||||
llarp_ev_loop_ptr serverLoop,
|
||||
Logic_ptr serverLogic,
|
||||
llarp_ev_loop_ptr clientLoop,
|
||||
Logic_ptr clientLogic,
|
||||
IQueryHandler* h)
|
||||
: m_ServerLoop(std::move(serverLoop))
|
||||
, m_ClientLoop(std::move(clientLoop))
|
||||
, m_ServerLogic(std::move(serverLogic))
|
||||
, m_ClientLogic(std::move(clientLogic))
|
||||
, m_QueryHandler(h)
|
||||
{
|
||||
m_Client.user = this;
|
||||
m_Server.user = this;
|
||||
m_Client.tick = nullptr;
|
||||
m_Server.tick = nullptr;
|
||||
m_Client.recvfrom = &HandleUDPRecv_client;
|
||||
m_Server.recvfrom = &HandleUDPRecv_server;
|
||||
}
|
||||
std::array<byte_t, 1500> tmp = {{0}};
|
||||
llarp_buffer_t buf{tmp};
|
||||
if (not msg.Encode(&buf))
|
||||
throw std::runtime_error("cannot encode dns message");
|
||||
std::vector<byte_t> pkt;
|
||||
pkt.resize(buf.cur - buf.base);
|
||||
std::copy_n(tmp.data(), pkt.size(), pkt.data());
|
||||
return pkt;
|
||||
}
|
||||
PacketHandler::PacketHandler(Logic_ptr logic, IQueryHandler* h)
|
||||
: m_QueryHandler{h}, m_Logic{logic}
|
||||
{}
|
||||
|
||||
void
|
||||
Proxy::Stop()
|
||||
{
|
||||
if (m_UnboundResolver)
|
||||
m_UnboundResolver->Stop();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::Start(const IpAddress& addr, const std::vector<IpAddress>& resolvers)
|
||||
void
|
||||
PacketHandler::Stop()
|
||||
{
|
||||
if (m_UnboundResolver)
|
||||
m_UnboundResolver->Stop();
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::Start(SockAddr addr, std::vector<IpAddress> resolvers)
|
||||
{
|
||||
if (not PacketHandler::Start(addr, std::move(resolvers)))
|
||||
return false;
|
||||
return (llarp_ev_add_udp(m_Loop, &m_Server, addr) == 0);
|
||||
}
|
||||
|
||||
static Proxy::Buffer_t
|
||||
CopyBuffer(const llarp_buffer_t& buf)
|
||||
{
|
||||
std::vector<byte_t> msgbuf(buf.sz);
|
||||
std::copy_n(buf.base, buf.sz, msgbuf.data());
|
||||
return msgbuf;
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::HandleUDP(llarp_udp_io* u, const SockAddr& from, ManagedBuffer buf)
|
||||
{
|
||||
Buffer_t msgbuf = CopyBuffer(buf.underlying);
|
||||
auto self = static_cast<Proxy*>(u->user);
|
||||
self->HandlePacket(from, from, std::move(msgbuf));
|
||||
}
|
||||
|
||||
void
|
||||
PacketHandler::Restart()
|
||||
{
|
||||
if (m_UnboundResolver)
|
||||
{
|
||||
if (resolvers.size())
|
||||
LogInfo("reset libunbound's internal stuff");
|
||||
m_UnboundResolver->Init();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PacketHandler::Start(SockAddr, std::vector<IpAddress> resolvers)
|
||||
{
|
||||
return SetupUnboundResolver(std::move(resolvers));
|
||||
}
|
||||
|
||||
bool
|
||||
PacketHandler::SetupUnboundResolver(std::vector<IpAddress> resolvers)
|
||||
{
|
||||
auto failFunc = [self = weak_from_this()](SockAddr from, SockAddr to, Message msg) {
|
||||
auto this_ptr = self.lock();
|
||||
if (this_ptr)
|
||||
{
|
||||
if (not SetupUnboundResolver(resolvers))
|
||||
{
|
||||
llarp::LogError("Failed to add upstream resolvers during DNS server setup.");
|
||||
return false;
|
||||
}
|
||||
this_ptr->SendServerMessageBufferTo(from, to, MessageToBuffer(std::move(msg)));
|
||||
}
|
||||
return (llarp_ev_add_udp(m_ServerLoop, &m_Server, addr.createSockAddr()) == 0);
|
||||
}
|
||||
};
|
||||
|
||||
static Proxy::Buffer_t
|
||||
CopyBuffer(const llarp_buffer_t& buf)
|
||||
{
|
||||
std::vector<byte_t> msgbuf(buf.sz);
|
||||
std::copy_n(buf.base, buf.sz, msgbuf.data());
|
||||
return msgbuf;
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::HandleUDPRecv_server(llarp_udp_io* u, const SockAddr& from, ManagedBuffer buf)
|
||||
{
|
||||
Buffer_t msgbuf = CopyBuffer(buf.underlying);
|
||||
auto self = static_cast<Proxy*>(u->user);
|
||||
self->HandlePktServer(from, msgbuf);
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::HandleUDPRecv_client(llarp_udp_io* u, const SockAddr& from, ManagedBuffer buf)
|
||||
{
|
||||
Buffer_t msgbuf = CopyBuffer(buf.underlying);
|
||||
auto self = static_cast<Proxy*>(u->user)->shared_from_this();
|
||||
LogicCall(
|
||||
self->m_ServerLogic, [self, from, msgbuf]() { self->HandlePktClient(from, msgbuf); });
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::Restart()
|
||||
{
|
||||
if (m_UnboundResolver)
|
||||
auto replyFunc = [self = weak_from_this()](
|
||||
SockAddr from, SockAddr to, std::vector<byte_t> buf) {
|
||||
auto this_ptr = self.lock();
|
||||
if (this_ptr)
|
||||
{
|
||||
LogInfo("reset libunbound's internal stuff");
|
||||
m_UnboundResolver->Init();
|
||||
this_ptr->SendServerMessageBufferTo(from, to, std::move(buf));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
Proxy::SetupUnboundResolver(const std::vector<IpAddress>& resolvers)
|
||||
m_UnboundResolver =
|
||||
std::make_shared<UnboundResolver>(m_Logic, std::move(replyFunc), std::move(failFunc));
|
||||
if (not m_UnboundResolver->Init())
|
||||
{
|
||||
auto failFunc = [self = weak_from_this()](SockAddr to, Message msg) {
|
||||
auto this_ptr = self.lock();
|
||||
if (this_ptr)
|
||||
{
|
||||
this_ptr->SendServerMessageTo(to, std::move(msg));
|
||||
}
|
||||
};
|
||||
|
||||
auto replyFunc = [self = weak_from_this()](SockAddr to, std::vector<byte_t> buf) {
|
||||
auto this_ptr = self.lock();
|
||||
if (this_ptr)
|
||||
{
|
||||
this_ptr->HandleUpstreamResponse(to, std::move(buf));
|
||||
}
|
||||
};
|
||||
|
||||
m_UnboundResolver = std::make_shared<UnboundResolver>(
|
||||
m_ServerLoop, 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;
|
||||
}
|
||||
}
|
||||
m_Resolvers.emplace(resolver);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Message msg{hdr};
|
||||
if (not msg.Decode(&pkt))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_QueryHandler and m_QueryHandler->ShouldHookDNSMessage(msg))
|
||||
return true;
|
||||
|
||||
if (m_Resolvers.find(to) != m_Resolvers.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PacketHandler::HandlePacket(SockAddr resolver, SockAddr from, Buffer_t buf)
|
||||
{
|
||||
MessageHeader hdr;
|
||||
llarp_buffer_t pkt{buf};
|
||||
if (not hdr.Decode(&pkt))
|
||||
{
|
||||
llarp::LogWarn("failed to parse dns header from ", from);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::HandleTick(llarp_udp_io*)
|
||||
{}
|
||||
|
||||
void
|
||||
Proxy::SendServerMessageBufferTo(const SockAddr& to, const llarp_buffer_t& buf)
|
||||
Message msg(hdr);
|
||||
if (not msg.Decode(&pkt))
|
||||
{
|
||||
if (llarp_ev_udp_sendto(&m_Server, to, buf) < 0)
|
||||
llarp::LogError("dns reply failed");
|
||||
llarp::LogWarn("failed to parse dns message from ", from);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::SendServerMessageTo(const SockAddr& to, Message msg)
|
||||
// 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)
|
||||
{
|
||||
auto self = shared_from_this();
|
||||
LogicCall(m_ServerLogic, [to, msg = std::move(msg), self]() {
|
||||
std::array<byte_t, 1500> tmp = {{0}};
|
||||
llarp_buffer_t buf(tmp);
|
||||
if (msg.Encode(&buf))
|
||||
{
|
||||
buf.sz = buf.cur - buf.base;
|
||||
buf.cur = buf.base;
|
||||
self->SendServerMessageBufferTo(to, buf);
|
||||
}
|
||||
else
|
||||
llarp::LogWarn("failed to encode dns message when sending");
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::HandleUpstreamResponse(SockAddr to, std::vector<byte_t> buf)
|
||||
{
|
||||
auto self = shared_from_this();
|
||||
LogicCall(m_ServerLogic, [to, buffer = std::move(buf), self]() {
|
||||
llarp_buffer_t buf(buffer);
|
||||
self->SendServerMessageBufferTo(to, buf);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::SendClientMessageTo(const SockAddr& to, Message msg)
|
||||
{
|
||||
auto self = shared_from_this();
|
||||
LogicCall(m_ClientLogic, [to, msg, self]() {
|
||||
std::array<byte_t, 1500> tmp = {{0}};
|
||||
llarp_buffer_t buf(tmp);
|
||||
if (msg.Encode(&buf))
|
||||
{
|
||||
buf.sz = buf.cur - buf.base;
|
||||
buf.cur = buf.base;
|
||||
llarp_ev_udp_sendto(&self->m_Client, to, buf);
|
||||
}
|
||||
else
|
||||
llarp::LogWarn("failed to encode dns message when sending");
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::HandlePktClient(const SockAddr& from, Buffer_t buf)
|
||||
{
|
||||
llarp_buffer_t pkt(buf);
|
||||
MessageHeader hdr;
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::HandlePktServer(const SockAddr& from, Buffer_t buf)
|
||||
if (m_QueryHandler && m_QueryHandler->ShouldHookDNSMessage(msg))
|
||||
{
|
||||
MessageHeader hdr;
|
||||
llarp_buffer_t pkt(buf);
|
||||
if (!hdr.Decode(&pkt))
|
||||
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))
|
||||
{
|
||||
llarp::LogWarn("failed to parse dns header from ", from);
|
||||
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
|
||||
{
|
||||
m_UnboundResolver->Lookup(from, std::move(msg));
|
||||
llarp::LogWarn("failed to handle hooked dns");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
else if (not m_UnboundResolver)
|
||||
{
|
||||
// no upstream resolvers
|
||||
// let's serv fail it
|
||||
msg.AddServFail();
|
||||
SendServerMessageBufferTo(resolver, from, MessageToBuffer(std::move(msg)));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_UnboundResolver->Lookup(resolver, from, std::move(msg));
|
||||
}
|
||||
}
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -27,18 +27,17 @@ namespace llarp
|
|||
HandleHookedDNSMessage(Message query, std::function<void(Message)> sendReply) = 0;
|
||||
};
|
||||
|
||||
struct Proxy : public std::enable_shared_from_this<Proxy>
|
||||
struct PacketHandler : public std::enable_shared_from_this<PacketHandler>
|
||||
{
|
||||
using Logic_ptr = std::shared_ptr<Logic>;
|
||||
Proxy(
|
||||
llarp_ev_loop_ptr serverLoop,
|
||||
Logic_ptr serverLogic,
|
||||
llarp_ev_loop_ptr clientLoop,
|
||||
Logic_ptr clientLogic,
|
||||
IQueryHandler* handler);
|
||||
using Buffer_t = std::vector<uint8_t>;
|
||||
|
||||
bool
|
||||
Start(const IpAddress& addr, const std::vector<IpAddress>& resolvers);
|
||||
explicit PacketHandler(Logic_ptr logic, IQueryHandler* handler);
|
||||
|
||||
virtual ~PacketHandler() = default;
|
||||
|
||||
virtual bool
|
||||
Start(SockAddr localaddr, std::vector<IpAddress> upstreamResolvers);
|
||||
|
||||
void
|
||||
Stop();
|
||||
|
@ -46,80 +45,50 @@ namespace llarp
|
|||
void
|
||||
Restart();
|
||||
|
||||
using Buffer_t = std::vector<uint8_t>;
|
||||
|
||||
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);
|
||||
|
||||
/// low level ticker
|
||||
static void
|
||||
HandleTick(llarp_udp_io*);
|
||||
|
||||
void
|
||||
HandlePktClient(const SockAddr& from, Buffer_t buf);
|
||||
|
||||
void
|
||||
HandlePktServer(const SockAddr& from, Buffer_t buf);
|
||||
|
||||
void
|
||||
SendClientMessageTo(const SockAddr& to, Message msg);
|
||||
|
||||
void
|
||||
SendServerMessageBufferTo(const SockAddr& to, const llarp_buffer_t& buf);
|
||||
|
||||
void
|
||||
SendServerMessageTo(const SockAddr& to, Message msg);
|
||||
|
||||
void
|
||||
HandleUpstreamResponse(SockAddr to, std::vector<byte_t> buf);
|
||||
|
||||
void
|
||||
HandleUpstreamFailure(const SockAddr& to, Message msg);
|
||||
|
||||
IpAddress
|
||||
PickRandomResolver() const;
|
||||
HandlePacket(SockAddr resolver, SockAddr from, Buffer_t buf);
|
||||
|
||||
bool
|
||||
SetupUnboundResolver(const std::vector<IpAddress>& resolvers);
|
||||
ShouldHandlePacket(SockAddr to, SockAddr from, Buffer_t buf) const;
|
||||
|
||||
protected:
|
||||
virtual void
|
||||
SendServerMessageBufferTo(SockAddr from, SockAddr to, Buffer_t buf) = 0;
|
||||
|
||||
private:
|
||||
void
|
||||
HandleUpstreamFailure(SockAddr from, SockAddr to, Message msg);
|
||||
|
||||
bool
|
||||
SetupUnboundResolver(std::vector<IpAddress> resolvers);
|
||||
|
||||
IQueryHandler* const m_QueryHandler;
|
||||
std::set<IpAddress> m_Resolvers;
|
||||
std::shared_ptr<UnboundResolver> m_UnboundResolver;
|
||||
Logic_ptr m_Logic;
|
||||
};
|
||||
|
||||
struct Proxy : public PacketHandler
|
||||
{
|
||||
using Logic_ptr = std::shared_ptr<Logic>;
|
||||
explicit Proxy(llarp_ev_loop_ptr loop, Logic_ptr logic, IQueryHandler* handler);
|
||||
|
||||
bool
|
||||
Start(SockAddr localaddr, std::vector<IpAddress> resolvers) override;
|
||||
|
||||
using Buffer_t = std::vector<uint8_t>;
|
||||
|
||||
protected:
|
||||
void
|
||||
SendServerMessageBufferTo(SockAddr from, SockAddr to, Buffer_t buf) override;
|
||||
|
||||
private:
|
||||
static void
|
||||
HandleUDP(llarp_udp_io*, const SockAddr&, ManagedBuffer);
|
||||
|
||||
private:
|
||||
llarp_udp_io m_Server;
|
||||
llarp_udp_io m_Client;
|
||||
llarp_ev_loop_ptr m_ServerLoop;
|
||||
llarp_ev_loop_ptr m_ClientLoop;
|
||||
Logic_ptr m_ServerLogic;
|
||||
Logic_ptr m_ClientLogic;
|
||||
IQueryHandler* m_QueryHandler;
|
||||
std::vector<IpAddress> m_Resolvers;
|
||||
std::shared_ptr<UnboundResolver> m_UnboundResolver;
|
||||
|
||||
struct TX
|
||||
{
|
||||
MsgID_t txid;
|
||||
IpAddress from;
|
||||
|
||||
bool
|
||||
operator==(const TX& other) const
|
||||
{
|
||||
return txid == other.txid && from == other.from;
|
||||
}
|
||||
|
||||
struct Hash
|
||||
{
|
||||
size_t
|
||||
operator()(const TX& t) const noexcept
|
||||
{
|
||||
return t.txid ^ IpAddress::Hash()(t.from);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// maps tx to who to send reply to
|
||||
std::unordered_map<TX, IpAddress, TX::Hash> m_Forwarded;
|
||||
llarp_ev_loop_ptr m_Loop;
|
||||
};
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace llarp::dns
|
|||
std::weak_ptr<UnboundResolver> resolver;
|
||||
Message msg;
|
||||
SockAddr source;
|
||||
SockAddr replyFrom;
|
||||
};
|
||||
|
||||
void
|
||||
|
@ -34,15 +35,15 @@ namespace llarp::dns
|
|||
unboundContext = nullptr;
|
||||
}
|
||||
|
||||
UnboundResolver::UnboundResolver(llarp_ev_loop_ptr loop, ReplyFunction reply, FailFunction fail)
|
||||
UnboundResolver::UnboundResolver(
|
||||
std::shared_ptr<Logic> logic, ReplyFunction reply, FailFunction fail)
|
||||
: unboundContext(nullptr)
|
||||
, started(false)
|
||||
, eventLoop(loop)
|
||||
, replyFunc([loop, reply](auto source, auto buf) {
|
||||
loop->call_soon([source, buf, reply]() { reply(source, buf); });
|
||||
, replyFunc([logic, reply](auto res, auto source, auto buf) {
|
||||
LogicCall(logic, [source, buf, reply, res]() { reply(res, source, buf); });
|
||||
})
|
||||
, failFunc([loop, fail](auto source, auto message) {
|
||||
loop->call_soon([source, message, fail]() { fail(source, message); });
|
||||
, failFunc([logic, fail](auto res, auto source, auto message) {
|
||||
LogicCall(logic, [source, message, res, fail]() { fail(res, source, message); });
|
||||
})
|
||||
{}
|
||||
|
||||
|
@ -60,7 +61,7 @@ namespace llarp::dns
|
|||
{
|
||||
Message& msg = lookup->msg;
|
||||
msg.AddServFail();
|
||||
this_ptr->failFunc(lookup->source, msg);
|
||||
this_ptr->failFunc(lookup->replyFrom, lookup->source, msg);
|
||||
ub_resolve_free(result);
|
||||
return;
|
||||
}
|
||||
|
@ -75,7 +76,7 @@ namespace llarp::dns
|
|||
buf.cur = buf.base;
|
||||
hdr.Encode(&buf);
|
||||
|
||||
this_ptr->replyFunc(lookup->source, std::move(pkt));
|
||||
this_ptr->replyFunc(lookup->replyFrom, lookup->source, std::move(pkt));
|
||||
|
||||
ub_resolve_free(result);
|
||||
}
|
||||
|
@ -120,17 +121,17 @@ namespace llarp::dns
|
|||
}
|
||||
|
||||
void
|
||||
UnboundResolver::Lookup(const SockAddr& source, Message msg)
|
||||
UnboundResolver::Lookup(SockAddr to, SockAddr from, Message msg)
|
||||
{
|
||||
if (not unboundContext)
|
||||
{
|
||||
msg.AddServFail();
|
||||
failFunc(source, std::move(msg));
|
||||
failFunc(to, from, std::move(msg));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& q = msg.questions[0];
|
||||
auto* lookup = new PendingUnboundLookup{weak_from_this(), msg, source};
|
||||
auto* lookup = new PendingUnboundLookup{weak_from_this(), msg, from, to};
|
||||
int err = ub_resolve_async(
|
||||
unboundContext,
|
||||
q.Name().c_str(),
|
||||
|
@ -143,7 +144,7 @@ namespace llarp::dns
|
|||
if (err != 0)
|
||||
{
|
||||
msg.AddServFail();
|
||||
failFunc(source, std::move(msg));
|
||||
failFunc(to, from, std::move(msg));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
|
||||
namespace llarp::dns
|
||||
{
|
||||
using ReplyFunction = std::function<void(SockAddr source, std::vector<byte_t> buf)>;
|
||||
using FailFunction = std::function<void(SockAddr source, Message msg)>;
|
||||
using ReplyFunction =
|
||||
std::function<void(SockAddr resolver, SockAddr source, std::vector<byte_t> buf)>;
|
||||
using FailFunction = std::function<void(SockAddr resolver, SockAddr source, Message msg)>;
|
||||
|
||||
class UnboundResolver : public std::enable_shared_from_this<UnboundResolver>
|
||||
{
|
||||
|
@ -28,7 +29,6 @@ namespace llarp::dns
|
|||
std::atomic<bool> started;
|
||||
std::unique_ptr<std::thread> runner;
|
||||
|
||||
llarp_ev_loop_ptr eventLoop;
|
||||
ReplyFunction replyFunc;
|
||||
FailFunction failFunc;
|
||||
|
||||
|
@ -36,7 +36,7 @@ namespace llarp::dns
|
|||
Reset();
|
||||
|
||||
public:
|
||||
UnboundResolver(llarp_ev_loop_ptr eventLoop, ReplyFunction replyFunc, FailFunction failFunc);
|
||||
UnboundResolver(std::shared_ptr<Logic> logic, ReplyFunction replyFunc, FailFunction failFunc);
|
||||
|
||||
static void
|
||||
Callback(void* data, int err, ub_result* result);
|
||||
|
@ -53,7 +53,7 @@ namespace llarp::dns
|
|||
AddUpstreamResolver(const std::string& upstreamResolverIP);
|
||||
|
||||
void
|
||||
Lookup(const SockAddr& source, Message msg);
|
||||
Lookup(SockAddr to, SockAddr from, Message msg);
|
||||
};
|
||||
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -15,8 +15,7 @@ namespace llarp
|
|||
{
|
||||
ExitEndpoint::ExitEndpoint(const std::string& name, AbstractRouter* r)
|
||||
: m_Router(r)
|
||||
, m_Resolver(std::make_shared<dns::Proxy>(
|
||||
r->netloop(), r->logic(), r->netloop(), r->logic(), this))
|
||||
, m_Resolver(std::make_shared<dns::Proxy>(r->netloop(), r->logic(), this))
|
||||
, m_Name(name)
|
||||
, m_LocalResolverAddr("127.0.0.1", 53)
|
||||
, m_InetToNetwork(name + "_exit_rx", r->netloop(), r->netloop())
|
||||
|
@ -324,7 +323,7 @@ namespace llarp
|
|||
loop->add_ticker([&]() { Flush(); });
|
||||
|
||||
llarp::LogInfo("Trying to start resolver ", m_LocalResolverAddr.toString());
|
||||
return m_Resolver->Start(m_LocalResolverAddr, m_UpstreamResolvers);
|
||||
return m_Resolver->Start(m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <rpc/endpoint_rpc.hpp>
|
||||
|
||||
#include <util/str.hpp>
|
||||
#include <util/endian.hpp>
|
||||
|
||||
#include <dns/srv_data.hpp>
|
||||
|
||||
|
@ -36,13 +37,88 @@ namespace llarp
|
|||
static constexpr auto FlushInterval = 25ms;
|
||||
return now >= m_LastFlushAt + FlushInterval;
|
||||
}
|
||||
constexpr size_t udp_header_size = 8;
|
||||
|
||||
struct DnsHandler : public dns::PacketHandler
|
||||
{
|
||||
TunEndpoint* const m_Endpoint;
|
||||
|
||||
explicit DnsHandler(AbstractRouter* router, TunEndpoint* ep)
|
||||
: dns::PacketHandler{router->logic(), ep}, m_Endpoint{ep} {};
|
||||
|
||||
void
|
||||
SendServerMessageBufferTo(SockAddr from, SockAddr to, std::vector<byte_t> buf) override
|
||||
{
|
||||
net::IPPacket pkt;
|
||||
|
||||
if (buf.size() + 28 > sizeof(pkt.buf))
|
||||
return;
|
||||
|
||||
auto* hdr = pkt.Header();
|
||||
pkt.buf[1] = 0;
|
||||
hdr->version = 4;
|
||||
hdr->ihl = 5;
|
||||
hdr->tot_len = htons(buf.size() + 28);
|
||||
hdr->protocol = 0x11; // udp
|
||||
hdr->ttl = 64;
|
||||
hdr->frag_off = htons(0b0100000000000000);
|
||||
|
||||
hdr->saddr = from.getIPv4();
|
||||
hdr->daddr = to.getIPv4();
|
||||
|
||||
// make udp packet
|
||||
uint8_t* ptr = pkt.buf + 20;
|
||||
htobe16buf(ptr, from.getPort());
|
||||
ptr += 2;
|
||||
htobe16buf(ptr, to.getPort());
|
||||
ptr += 2;
|
||||
htobe16buf(ptr, buf.size() + udp_header_size);
|
||||
ptr += 2;
|
||||
htobe16buf(ptr, uint16_t{0}); // checksum
|
||||
ptr += 2;
|
||||
std::copy_n(buf.data(), buf.size(), ptr);
|
||||
|
||||
/// queue ip packet write
|
||||
const IpAddress remoteIP{from};
|
||||
const IpAddress localIP{to};
|
||||
|
||||
hdr->check = 0;
|
||||
hdr->check = net::ipchksum(pkt.buf, 20);
|
||||
pkt.sz = 28 + buf.size();
|
||||
m_Endpoint->HandleWriteIPPacket(
|
||||
pkt.ConstBuffer(), net::ExpandV4(remoteIP.toIP()), net::ExpandV4(localIP.toIP()), 0);
|
||||
}
|
||||
};
|
||||
|
||||
TunEndpoint::TunEndpoint(AbstractRouter* r, service::Context* parent)
|
||||
: service::Endpoint(r, parent)
|
||||
, m_UserToNetworkPktQueue("endpoint_sendq", r->netloop(), r->netloop())
|
||||
, m_Resolver(std::make_shared<dns::Proxy>(
|
||||
r->netloop(), r->logic(), r->netloop(), r->logic(), this))
|
||||
{}
|
||||
{
|
||||
m_PacketRouter.reset(
|
||||
new vpn::PacketRouter{[&](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); }});
|
||||
#if ANDROID
|
||||
m_Resolver = std::make_shared<DnsHandler>(r, this);
|
||||
m_PacketRouter->AddUDPHandler(huint16_t{53}, [&](net::IPPacket pkt) {
|
||||
const size_t ip_header_size = (pkt.Header()->ihl * 4);
|
||||
|
||||
const uint8_t* ptr = pkt.buf + ip_header_size;
|
||||
const auto dst = ToNet(pkt.dstv4());
|
||||
const auto src = ToNet(pkt.srcv4());
|
||||
const SockAddr raddr{src.n, *reinterpret_cast<const uint16_t*>(ptr)};
|
||||
const SockAddr laddr{dst.n, *reinterpret_cast<const uint16_t*>(ptr + 2)};
|
||||
|
||||
std::vector<byte_t> buf;
|
||||
buf.resize(pkt.sz - (udp_header_size + ip_header_size));
|
||||
std::copy_n(ptr + udp_header_size, buf.size(), buf.data());
|
||||
if (m_Resolver->ShouldHandlePacket(laddr, raddr, buf))
|
||||
m_Resolver->HandlePacket(laddr, raddr, std::move(buf));
|
||||
else
|
||||
HandleGotUserPacket(std::move(pkt));
|
||||
});
|
||||
#else
|
||||
m_Resolver = std::make_shared<dns::Proxy>(r->netloop(), r->logic(), this);
|
||||
#endif
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
TunEndpoint::ExtractStatus() const
|
||||
|
@ -743,7 +819,7 @@ namespace llarp
|
|||
|
||||
auto netloop = Router()->netloop();
|
||||
if (not netloop->add_network_interface(
|
||||
m_NetIf, [&](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); }))
|
||||
m_NetIf, [&](net::IPPacket pkt) { m_PacketRouter->HandleIPPacket(std::move(pkt)); }))
|
||||
{
|
||||
LogError(Name(), " failed to add network interface");
|
||||
return false;
|
||||
|
@ -788,7 +864,7 @@ namespace llarp
|
|||
llarp::LogError(Name(), " failed to set up network interface");
|
||||
return false;
|
||||
}
|
||||
if (!m_Resolver->Start(m_LocalResolverAddr, m_UpstreamResolvers))
|
||||
if (!m_Resolver->Start(m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers))
|
||||
{
|
||||
llarp::LogError(Name(), " failed to start DNS server");
|
||||
return false;
|
||||
|
@ -1008,7 +1084,9 @@ namespace llarp
|
|||
auto& pkt = write.pkt;
|
||||
// load
|
||||
if (!pkt.Load(buf))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (pkt.IsV4())
|
||||
{
|
||||
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(dst)));
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <service/endpoint.hpp>
|
||||
#include <util/codel.hpp>
|
||||
#include <util/thread/threading.hpp>
|
||||
#include <vpn/packet_router.hpp>
|
||||
|
||||
#include <future>
|
||||
#include <queue>
|
||||
|
@ -232,7 +233,7 @@ namespace llarp
|
|||
reply(*query);
|
||||
}
|
||||
/// our dns resolver
|
||||
std::shared_ptr<dns::Proxy> m_Resolver;
|
||||
std::shared_ptr<dns::PacketHandler> m_Resolver;
|
||||
|
||||
/// maps ip address to timestamp last active
|
||||
std::unordered_map<huint128_t, llarp_time_t> m_IPActivity;
|
||||
|
@ -258,6 +259,8 @@ namespace llarp
|
|||
std::string m_IfName;
|
||||
|
||||
std::shared_ptr<vpn::NetworkInterface> m_NetIf;
|
||||
|
||||
std::unique_ptr<vpn::PacketRouter> m_PacketRouter;
|
||||
};
|
||||
|
||||
} // namespace handlers
|
||||
|
|
|
@ -235,6 +235,12 @@ namespace llarp
|
|||
return m_Pending.size();
|
||||
}
|
||||
|
||||
int
|
||||
GetUDPSocket() const
|
||||
{
|
||||
return m_udp.fd;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
OnTick();
|
||||
|
|
|
@ -124,8 +124,8 @@ namespace llarp
|
|||
return ExpandV4Lan(srcv4());
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
ipchksum(const byte_t* buf, size_t sz, uint32_t sum = 0)
|
||||
uint16_t
|
||||
ipchksum(const byte_t* buf, size_t sz, uint32_t sum)
|
||||
{
|
||||
while (sz > 1)
|
||||
{
|
||||
|
|
|
@ -280,6 +280,9 @@ namespace llarp
|
|||
MakeICMPUnreachable() const;
|
||||
};
|
||||
|
||||
/// generate ip checksum
|
||||
uint16_t
|
||||
ipchksum(const byte_t* buf, size_t sz, uint32_t sum = 0);
|
||||
} // namespace net
|
||||
} // namespace llarp
|
||||
|
||||
|
|
|
@ -19,6 +19,14 @@
|
|||
#include <util/logging/logger.hpp>
|
||||
#include <util/str.hpp>
|
||||
|
||||
#if ANDROID
|
||||
#include <android/ifaddrs.h>
|
||||
#else
|
||||
#ifndef _WIN32
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
#define inet_aton(x, y) inet_pton(AF_INET, x, y)
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
bool
|
||||
operator==(const sockaddr& a, const sockaddr& b);
|
||||
|
||||
|
|
|
@ -11,6 +11,13 @@ namespace llarp
|
|||
return xntohl(n);
|
||||
}
|
||||
|
||||
template <>
|
||||
nuint16_t
|
||||
ToNet(huint16_t h)
|
||||
{
|
||||
return xhtons(h);
|
||||
}
|
||||
|
||||
template <>
|
||||
nuint32_t
|
||||
ToNet(huint32_t h)
|
||||
|
|
|
@ -48,6 +48,14 @@ namespace llarp
|
|||
{
|
||||
setPort(port);
|
||||
}
|
||||
|
||||
SockAddr::SockAddr(uint32_t ip, uint16_t port)
|
||||
{
|
||||
init();
|
||||
setIPv4(ip);
|
||||
setPort(ntohs(port));
|
||||
}
|
||||
|
||||
SockAddr::SockAddr(std::string_view addr)
|
||||
{
|
||||
init();
|
||||
|
@ -292,6 +300,28 @@ namespace llarp
|
|||
return m_empty;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SockAddr::getIPv4() const
|
||||
{
|
||||
return m_addr4.sin_addr.s_addr;
|
||||
}
|
||||
|
||||
void
|
||||
SockAddr::setIPv4(uint32_t ip)
|
||||
{
|
||||
m_addr.sin6_family = AF_INET;
|
||||
|
||||
uint8_t* ip6 = m_addr.sin6_addr.s6_addr;
|
||||
llarp::Zero(ip6, sizeof(m_addr.sin6_addr.s6_addr));
|
||||
|
||||
applyIPv4MapBytes();
|
||||
|
||||
std::memcpy(ip6 + 12, &ip, 4);
|
||||
m_addr4.sin_addr.s_addr = ip;
|
||||
m_addr4.sin_family = AF_INET;
|
||||
m_empty = false;
|
||||
}
|
||||
|
||||
void
|
||||
SockAddr::setIPv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
|
||||
{
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace llarp
|
|||
SockAddr(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t port);
|
||||
SockAddr(std::string_view addr);
|
||||
SockAddr(std::string_view addr, uint16_t port);
|
||||
SockAddr(uint32_t ip, uint16_t port);
|
||||
|
||||
SockAddr(const AddressInfo&);
|
||||
|
||||
|
@ -81,6 +82,9 @@ namespace llarp
|
|||
setIPv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d);
|
||||
|
||||
/// port is in host order
|
||||
void
|
||||
setIPv4(uint32_t ip);
|
||||
|
||||
void
|
||||
setPort(uint16_t port);
|
||||
|
||||
|
@ -90,6 +94,9 @@ namespace llarp
|
|||
|
||||
huint128_t
|
||||
asIPv6() const;
|
||||
/// in network order
|
||||
uint32_t
|
||||
getIPv4() const;
|
||||
|
||||
huint32_t
|
||||
asIPv4() const;
|
||||
|
|
|
@ -318,6 +318,11 @@ namespace llarp
|
|||
HandleRouterEvent(std::move(event));
|
||||
}
|
||||
|
||||
#if defined(ANDROID)
|
||||
virtual int
|
||||
GetOutboundUDPSocket() const = 0;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/// Virtual function to handle RouterEvent. HiveRouter overrides this in
|
||||
/// order to inject the event. The default implementation in Router simply
|
||||
|
|
|
@ -1345,6 +1345,9 @@ namespace llarp
|
|||
if (not link->Configure(netloop(), "*", af, m_OutboundPort))
|
||||
continue;
|
||||
|
||||
#if defined(ANDROID)
|
||||
m_OutboundUDPSocket = link->GetUDPSocket();
|
||||
#endif
|
||||
_linkManager.AddLink(std::move(link), false);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -519,6 +519,16 @@ namespace llarp
|
|||
return m_Config;
|
||||
}
|
||||
|
||||
#if defined(ANDROID)
|
||||
int m_OutboundUDPSocket = -1;
|
||||
|
||||
int
|
||||
GetOutboundUDPSocket() const override
|
||||
{
|
||||
return m_OutboundUDPSocket;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
std::atomic<bool> _stopping;
|
||||
std::atomic<bool> _running;
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <ev/vpn.hpp>
|
||||
#include <vpn/common.hpp>
|
||||
#include <llarp.hpp>
|
||||
|
||||
namespace llarp::vpn
|
||||
{
|
||||
class AndroidInterface : public NetworkInterface
|
||||
{
|
||||
const int m_fd;
|
||||
const InterfaceInfo m_Info; // likely 100% ignored on android, at least for now
|
||||
|
||||
public:
|
||||
AndroidInterface(InterfaceInfo info, int fd) : m_fd(fd), m_Info(info)
|
||||
{
|
||||
if (m_fd == -1)
|
||||
throw std::runtime_error(
|
||||
"Error opening AndroidVPN layer FD: " + std::string{strerror(errno)});
|
||||
}
|
||||
|
||||
virtual ~AndroidInterface()
|
||||
{
|
||||
if (m_fd != -1)
|
||||
::close(m_fd);
|
||||
}
|
||||
|
||||
int
|
||||
PollFD() const override
|
||||
{
|
||||
return m_fd;
|
||||
}
|
||||
|
||||
net::IPPacket
|
||||
ReadNextPacket() override
|
||||
{
|
||||
net::IPPacket pkt;
|
||||
const auto sz = read(m_fd, pkt.buf, sizeof(pkt.buf));
|
||||
if (sz >= 0)
|
||||
pkt.sz = std::min(sz, ssize_t{sizeof(pkt.buf)});
|
||||
return pkt;
|
||||
}
|
||||
|
||||
bool
|
||||
WritePacket(net::IPPacket pkt) override
|
||||
{
|
||||
const auto sz = write(m_fd, pkt.buf, pkt.sz);
|
||||
if (sz <= 0)
|
||||
return false;
|
||||
return sz == static_cast<ssize_t>(pkt.sz);
|
||||
}
|
||||
|
||||
bool
|
||||
HasNextPacket() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string
|
||||
IfName() const override
|
||||
{
|
||||
return m_Info.ifname;
|
||||
}
|
||||
};
|
||||
|
||||
class AndroidPlatform : public Platform
|
||||
{
|
||||
const int fd;
|
||||
|
||||
public:
|
||||
AndroidPlatform(llarp::Context* ctx) : fd(ctx->androidFD)
|
||||
{}
|
||||
|
||||
std::shared_ptr<NetworkInterface>
|
||||
ObtainInterface(InterfaceInfo info) override
|
||||
{
|
||||
return std::make_shared<AndroidInterface>(std::move(info), fd);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace llarp::vpn
|
|
@ -0,0 +1,81 @@
|
|||
#include <vpn/packet_router.hpp>
|
||||
|
||||
namespace llarp::vpn
|
||||
{
|
||||
struct UDPPacketHandler : public Layer4Handler
|
||||
{
|
||||
PacketHandlerFunc m_BaseHandler;
|
||||
std::unordered_map<nuint16_t, PacketHandlerFunc> m_LocalPorts;
|
||||
|
||||
explicit UDPPacketHandler(PacketHandlerFunc baseHandler) : m_BaseHandler{std::move(baseHandler)}
|
||||
{}
|
||||
|
||||
void
|
||||
AddSubHandler(nuint16_t localport, PacketHandlerFunc handler) override
|
||||
{
|
||||
m_LocalPorts.emplace(localport, std::move(handler));
|
||||
}
|
||||
|
||||
void
|
||||
HandleIPPacket(llarp::net::IPPacket pkt) override
|
||||
{
|
||||
const uint8_t* ptr = pkt.buf + (pkt.Header()->ihl * 4) + 2;
|
||||
const nuint16_t dstPort{*reinterpret_cast<const uint16_t*>(ptr)};
|
||||
if (auto itr = m_LocalPorts.find(dstPort); itr != m_LocalPorts.end())
|
||||
{
|
||||
itr->second(std::move(pkt));
|
||||
}
|
||||
else
|
||||
m_BaseHandler(std::move(pkt));
|
||||
}
|
||||
};
|
||||
|
||||
struct GenericLayer4Handler : public Layer4Handler
|
||||
{
|
||||
PacketHandlerFunc m_BaseHandler;
|
||||
|
||||
explicit GenericLayer4Handler(PacketHandlerFunc baseHandler)
|
||||
: m_BaseHandler{std::move(baseHandler)}
|
||||
{}
|
||||
|
||||
void
|
||||
HandleIPPacket(llarp::net::IPPacket pkt) override
|
||||
{
|
||||
m_BaseHandler(std::move(pkt));
|
||||
}
|
||||
};
|
||||
|
||||
PacketRouter::PacketRouter(PacketHandlerFunc baseHandler) : m_BaseHandler{std::move(baseHandler)}
|
||||
{}
|
||||
|
||||
void
|
||||
PacketRouter::HandleIPPacket(llarp::net::IPPacket pkt)
|
||||
{
|
||||
const auto proto = pkt.Header()->protocol;
|
||||
if (const auto itr = m_IPProtoHandler.find(proto); itr != m_IPProtoHandler.end())
|
||||
{
|
||||
itr->second->HandleIPPacket(std::move(pkt));
|
||||
}
|
||||
else
|
||||
m_BaseHandler(std::move(pkt));
|
||||
}
|
||||
|
||||
void
|
||||
PacketRouter::AddUDPHandler(huint16_t localport, PacketHandlerFunc func)
|
||||
{
|
||||
constexpr byte_t udp_proto = 0x11;
|
||||
|
||||
if (m_IPProtoHandler.find(udp_proto) == m_IPProtoHandler.end())
|
||||
{
|
||||
m_IPProtoHandler.emplace(udp_proto, std::make_unique<UDPPacketHandler>(m_BaseHandler));
|
||||
}
|
||||
m_IPProtoHandler[udp_proto]->AddSubHandler(ToNet(localport), func);
|
||||
}
|
||||
|
||||
void
|
||||
PacketRouter::AddIProtoHandler(uint8_t proto, PacketHandlerFunc func)
|
||||
{
|
||||
m_IPProtoHandler[proto] = std::make_unique<GenericLayer4Handler>(std::move(func));
|
||||
}
|
||||
|
||||
} // namespace llarp::vpn
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
#include <net/net_int.hpp>
|
||||
#include <net/ip_packet.hpp>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace llarp::vpn
|
||||
{
|
||||
using PacketHandlerFunc = std::function<void(llarp::net::IPPacket)>;
|
||||
|
||||
struct Layer4Handler
|
||||
{
|
||||
virtual ~Layer4Handler() = default;
|
||||
|
||||
virtual void
|
||||
HandleIPPacket(llarp::net::IPPacket pkt) = 0;
|
||||
|
||||
virtual void AddSubHandler(nuint16_t, PacketHandlerFunc){};
|
||||
};
|
||||
|
||||
class PacketRouter
|
||||
{
|
||||
PacketHandlerFunc m_BaseHandler;
|
||||
std::unordered_map<uint8_t, std::unique_ptr<Layer4Handler>> m_IPProtoHandler;
|
||||
|
||||
public:
|
||||
/// baseHandler will be called if no other handlers matches a packet
|
||||
explicit PacketRouter(PacketHandlerFunc baseHandler);
|
||||
|
||||
/// feed in an ip packet for handling
|
||||
void
|
||||
HandleIPPacket(llarp::net::IPPacket pkt);
|
||||
|
||||
/// add a non udp packet handler using ip protocol proto
|
||||
void
|
||||
AddIProtoHandler(uint8_t proto, PacketHandlerFunc func);
|
||||
|
||||
/// helper that adds a udp packet handler for UDP destinted for localport
|
||||
void
|
||||
AddUDPHandler(huint16_t localport, PacketHandlerFunc func);
|
||||
};
|
||||
} // namespace llarp::vpn
|
|
@ -24,7 +24,7 @@ namespace llarp::vpn
|
|||
#endif
|
||||
#ifdef __linux__
|
||||
#ifdef ANDROID
|
||||
plat = std::make_shared<vpn::AndroidPlatform>();
|
||||
plat = std::make_shared<vpn::AndroidPlatform>(ctx);
|
||||
#else
|
||||
plat = std::make_shared<vpn::LinuxPlatform>();
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <messages/discard.hpp>
|
||||
#include <util/time.hpp>
|
||||
|
||||
#include <net/net_if.hpp>
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG __FILE__
|
||||
|
||||
|
|
Loading…
Reference in New Issue