Merge pull request #1537 from majestrate/android-works-now-2021-02-11

Android
This commit is contained in:
Jeff 2021-03-02 14:08:40 -05:00 committed by GitHub
commit f685d615bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1055 additions and 424 deletions

View File

@ -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',

3
.gitignore vendored
View File

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

View File

@ -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">

View File

@ -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'

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

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

View File

@ -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"
)

View File

@ -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

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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());
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
)

View File

@ -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)
{

View File

@ -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;
};

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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)));

View File

@ -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

View File

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

View File

@ -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)
{

View File

@ -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

View File

@ -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>

View File

@ -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);

View File

@ -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)

View File

@ -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)
{

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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;

84
llarp/vpn/android.hpp Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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