Skip to content

Commit

Permalink
Large collection of changes to make android work
Browse files Browse the repository at this point in the history
- Previous android java and jni code updated to work, but with much love
  still needed to make it work nicely, e.g. handling when the VPN is
  turned off.

- DNS handling refactored to allow android to intercept and handle DNS
  requests as we can't set the system DNS to use a high port
  (and apparently Chrome ignores system DNS settings anyway)

- add packet router structure to allow separate handling of specific
  intercepted traffic, e.g. UDP traffic to port 53 gets handled by our
  DNS handler rather than being naively forwarded as exit traffic.

- For now, android lokinet is exit-only and hard-coded to use exit.loki
  as its exit.  The exit will be configurable before release, but
  allowing to not use exit-only mode is more of a challenge.

- some old gitignore remnants which were matching to things we don't
  want them to (and are no longer relevant) removed

- some minor changes to CI configuration
  • Loading branch information
tewinget committed Mar 2, 2021
1 parent f2f0486 commit 4c630e0
Show file tree
Hide file tree
Showing 43 changed files with 1,033 additions and 402 deletions.
29 changes: 27 additions & 2 deletions .drone.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -156,6 +178,7 @@ local deb_builder(image, distro, distro_branch, arch='amd64', loki_repo=true) =
]
};


// Macos build
local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra_cmds=[], allow_fail=false) = {
kind: 'pipeline',
Expand Down Expand Up @@ -224,6 +247,8 @@ local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra
'../contrib/ci/drone-check-static-libs.sh',
'UPLOAD_OS=linux-armhf ../contrib/ci/drone-static-upload.sh'
]),
// android apk builder
apk_builder("android apk", "registry.oxen.rocks/lokinet-ci-android", extra_cmds=['UPLOAD_OS=anrdoid ../contrib/ci/drone-static-upload.sh']),

// Windows builds (x64)
windows_cross_pipeline("Windows (amd64)", "debian:testing",
Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ vsproject/
.vs

daemon.ini
lokinet-win32.exe
lokinet
lokinet.exe


.gradle/
Expand Down
2 changes: 1 addition & 1 deletion android/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
4 changes: 2 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
36 changes: 32 additions & 4 deletions android/src/network/loki/lokinet/LokiNetActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@

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;
Expand All @@ -29,14 +31,18 @@
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);
Expand Down Expand Up @@ -71,11 +77,33 @@ public void startLokinet()
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
Expand Down
39 changes: 39 additions & 0 deletions android/src/network/loki/lokinet/LokinetConfig.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
167 changes: 167 additions & 0 deletions android/src/network/loki/lokinet/LokinetDaemon.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
9 changes: 0 additions & 9 deletions android/src/network/loki/lokinet/LokinetService.java

This file was deleted.

2 changes: 1 addition & 1 deletion cmake/StaticBuild.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down
5 changes: 5 additions & 0 deletions contrib/ci/drone-static-upload.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 4c630e0

Please sign in to comment.