diff --git a/.drone.jsonnet b/.drone.jsonnet
index 54951b7b6f..436a039eec 100644
--- a/.drone.jsonnet
+++ b/.drone.jsonnet
@@ -1,6 +1,6 @@
-local default_deps_base='libsystemd-dev python3-dev libuv1-dev libunbound-dev nettle-dev libssl-dev libevent-dev libsqlite3-dev libcurl4-openssl-dev';
+local default_deps_base='libsystemd-dev python3-dev libuv1-dev libunbound-dev nettle-dev libssl-dev libevent-dev libsqlite3-dev libcurl4-openssl-dev make';
local default_deps_nocxx='libsodium-dev ' + default_deps_base; // libsodium-dev needs to be >= 1.0.18
-local default_deps='g++ ' + default_deps_nocxx; // g++ sometimes needs replacement
+local default_deps='g++ ' + default_deps_nocxx;
local default_windows_deps='mingw-w64 zip nsis';
local docker_base = 'registry.oxen.rocks/lokinet-ci-';
@@ -49,20 +49,20 @@ local debian_pipeline(name, image,
] else []
) + [
'eatmydata ' + apt_get_quiet + ' dist-upgrade -y',
- 'eatmydata ' + apt_get_quiet + ' install -y gdb cmake git ninja-build pkg-config ccache ' + deps,
+ 'eatmydata ' + apt_get_quiet + ' install -y gdb cmake git pkg-config ccache ' + deps,
'mkdir build',
'cd build',
- 'cmake .. -G Ninja -DWITH_SETCAP=OFF -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always -DCMAKE_BUILD_TYPE='+build_type+' ' +
+ 'cmake .. -DWITH_SETCAP=OFF -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always -DCMAKE_BUILD_TYPE='+build_type+' ' +
(if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') +
'-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') +
cmake_extra,
- 'ninja -j' + jobs + ' -v',
+ 'VERBOSE=1 make -j' + jobs,
'../contrib/ci/drone-gdb.sh ./test/testAll --use-colour yes',
] + extra_cmds,
}
],
};
-local apk_builder(name, image, extra_cmds=[], allow_fail=false) = {
+local apk_builder(name, image, extra_cmds=[], allow_fail=false, jobs=6) = {
kind: 'pipeline',
type: 'docker',
name: name,
@@ -76,11 +76,7 @@ local apk_builder(name, image, extra_cmds=[], allow_fail=false) = {
[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/${DRONE_STAGE_MACHINE} gradle --no-daemon assembleDebug",
+ 'VERBOSE=1 JOBS='+jobs+' NDK=/usr/lib/android-ndk ./contrib/android.sh'
] + extra_cmds
}
]
@@ -113,17 +109,10 @@ local windows_cross_pipeline(name, image,
'echo "man-db man-db/auto-update boolean false" | debconf-set-selections',
apt_get_quiet + ' update',
apt_get_quiet + ' install -y eatmydata',
- 'eatmydata ' + apt_get_quiet + ' install -y build-essential cmake git ninja-build pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool',
+ 'eatmydata ' + apt_get_quiet + ' install -y build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool',
'update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix',
'update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix',
- 'mkdir build',
- 'cd build',
- 'cmake .. -G Ninja -DCMAKE_EXE_LINKER_FLAGS=-fstack-protector -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always -DCMAKE_TOOLCHAIN_FILE=../contrib/cross/mingw'+toolchain+'.cmake -DCMAKE_BUILD_TYPE='+build_type+' ' +
- (if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') +
- (if lto then '' else '-DWITH_LTO=OFF ') +
- "-DBUILD_STATIC_DEPS=ON -DDOWNLOAD_SODIUM=ON -DBUILD_PACKAGE=ON -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF -DNATIVE_BUILD=OFF -DSTATIC_LINK=ON" +
- cmake_extra,
- 'ninja -j' + jobs + ' -v package',
+ 'VERBOSE=1 JOBS=' + jobs + ' ./contrib/windows.sh'
] + extra_cmds,
}
],
@@ -204,9 +193,9 @@ local mac_builder(name,
'ulimit -n 1024', // because macos sets ulimit to 256 for some reason yeah idk
'mkdir build',
'cd build',
- 'cmake .. -G Ninja -DCMAKE_CXX_FLAGS=-fcolor-diagnostics -DCMAKE_BUILD_TYPE='+build_type+' ' +
+ 'cmake .. -DCMAKE_CXX_FLAGS=-fcolor-diagnostics -DCMAKE_BUILD_TYPE='+build_type+' ' +
(if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + cmake_extra,
- 'ninja -j' + jobs + ' -v',
+ 'VERBOSE=1 make -j' + jobs,
'./test/testAll --use-colour yes',
] + extra_cmds,
}
@@ -254,12 +243,12 @@ local mac_builder(name,
],
jobs=4),
// android apk builder
- apk_builder("android apk", "registry.oxen.rocks/lokinet-ci-android", extra_cmds=['UPLOAD_OS=anrdoid ../contrib/ci/drone-static-upload.sh']),
-
+ apk_builder("android apk", "registry.oxen.rocks/lokinet-ci-android", extra_cmds=['UPLOAD_OS=android ./contrib/ci/drone-static-upload.sh']),
+
// Windows builds (x64)
windows_cross_pipeline("Windows (amd64)", docker_base+'debian-win32-cross',
toolchain='64', extra_cmds=[
- '../contrib/ci/drone-static-upload.sh'
+ './contrib/ci/drone-static-upload.sh'
]),
// Static build (on bionic) which gets uploaded to builds.lokinet.dev:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9ff002b260..ea26515914 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,7 +16,7 @@ if(CCACHE_PROGRAM)
endif()
project(lokinet
- VERSION 0.9.3
+ VERSION 0.9.4
DESCRIPTION "lokinet - IP packet onion router"
LANGUAGES C CXX)
@@ -67,6 +67,11 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif()
+if(BUILD_STATIC_DEPS AND STATIC_LINK)
+ message(STATUS "we are building static deps so we won't build shared libs")
+ set(BUILD_SHARED_LIBS OFF)
+endif()
+
include(CheckCXXSourceCompiles)
include(CheckLibraryExists)
set(CMAKE_CXX_STANDARD 17)
diff --git a/android/.gitignore b/android/.gitignore
deleted file mode 100644
index b8e7e769af..0000000000
--- a/android/.gitignore
+++ /dev/null
@@ -1,18 +0,0 @@
-gen
-tests
-bin
-libs
-log*
-obj
-.gradle
-.idea
-.externalNativeBuild
-ant.properties
-local.properties
-build.sh
-android.iml
-build
-gradle
-gradlew
-gradlew.bat
-gradle.properties
\ No newline at end of file
diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
deleted file mode 100755
index 082852b42e..0000000000
--- a/android/AndroidManifest.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/android/build.gradle b/android/build.gradle
deleted file mode 100644
index 3fd2265396..0000000000
--- a/android/build.gradle
+++ /dev/null
@@ -1,81 +0,0 @@
-buildscript {
- repositories {
- mavenCentral()
- jcenter()
- google()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:4.0.1'
- }
-}
-
-apply plugin: 'com.android.application'
-
-
-repositories {
- jcenter()
- maven {
- url 'https://maven.google.com'
- }
- google()
-}
-
-android {
- compileSdkVersion 28
- defaultConfig {
- applicationId "network.loki.lokinet"
- targetSdkVersion 28
- minSdkVersion 23
- versionCode 1
- versionName '0.8.4'
- 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", "-DANDROID_ARM_MODE=arm", "-DFORCE_OXENMQ_SUBMODULE=ON", "-DBUILD_LIBLOKINET=OFF"
- cppFlags "-std=c++17"
- abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
- // abiFilters 'armeabi-v7a'
- // abiFilters 'arm64-v8a', 'x86_64', 'armeabi-v7a'
- }
- }
-
-
- }
- externalNativeBuild {
- cmake {
- path "../CMakeLists.txt"
- }
- }
- sourceSets {
- main {
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src']
- res.srcDirs = ['res']
- jniLibs.srcDirs = ['libs']
- assets.srcDirs = ['assets']
- }
- }
- signingConfigs {
- jeff {
- storeFile file("jeff-apk.jks")
- keyAlias "jeff-apk"
- }
- }
- buildTypes {
- release {
- minifyEnabled true
- //signingConfig signingConfigs.jeff
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
- debuggable false
- }
- debug {
- // jniDebuggable true
- }
- }
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-}
-
-
diff --git a/android/build.xml b/android/build.xml
deleted file mode 100644
index ffe9081c87..0000000000
--- a/android/build.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/android/proguard-project.txt b/android/proguard-project.txt
deleted file mode 100644
index f2fe1559a2..0000000000
--- a/android/proguard-project.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# To enable ProGuard in your project, edit project.properties
-# to define the proguard.config property as described in that file.
-#
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in ${sdk.dir}/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
diff --git a/android/project.properties b/android/project.properties
deleted file mode 100644
index 919ca9c3b2..0000000000
--- a/android/project.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-28
diff --git a/android/readme.md b/android/readme.md
deleted file mode 100644
index 647032dead..0000000000
--- a/android/readme.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# lokinet android
-
-this directory contains basic stuff for lokinet on android.
-
-## Prerequsites
-
-To build you need the following:
-
-* Gradle (6.x)
-* Android SDK (latest version)
-* Android NDK (latest version)
-
-## Building
-
-Next set up the path to Android SDK and NDK in `local.properties`
-
-```
-sdk.dir=/path/to/android/sdk
-ndk.dir=/path/to/android/ndk
-```
-
-Then build:
-
- $ gradle assemble
-
-This fetches a large amount (several dozen Gigabytes) of files from some
-server somewhere dumping it on your filesystem to make the thing do the
-building, then proceeds to peg all your cores for several dozen minutes
-while it does the required incantations to build 2 apks.
-
-The build outputs apks to to subdirectories in `build/outputs/apk/`
-one called `debug` for debug builds and one called `release` for release builds.
-
diff --git a/android/res/drawable/icon.png b/android/res/drawable/icon.png
deleted file mode 100644
index 30ad9ddcb1..0000000000
Binary files a/android/res/drawable/icon.png and /dev/null differ
diff --git a/android/res/layout/activity_perms_asker.xml b/android/res/layout/activity_perms_asker.xml
deleted file mode 100644
index 4afe5c037a..0000000000
--- a/android/res/layout/activity_perms_asker.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/android/res/layout/activity_perms_explanation.xml b/android/res/layout/activity_perms_explanation.xml
deleted file mode 100644
index 08445664e2..0000000000
--- a/android/res/layout/activity_perms_explanation.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/android/res/menu/options_main.xml b/android/res/menu/options_main.xml
deleted file mode 100644
index dffc63222e..0000000000
--- a/android/res/menu/options_main.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml
deleted file mode 100755
index 16423a9869..0000000000
--- a/android/res/values/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
- lokinet
- Start
- Stop
- Already stopped
- lokinet loaded
- lokinet is starting
- lokinet: loaded JNI libraries
- lokinet started
- lokinet start failed
- lokinet has stopped
- remaining
- Prompt
- got bootstrap node info
- failed to bootstrap
- failed to create netdb directory
- failed to set up vpn tunnel
-
diff --git a/android/res/values/template-dimens.xml b/android/res/values/template-dimens.xml
deleted file mode 100644
index 36847c9708..0000000000
--- a/android/res/values/template-dimens.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
- 4dp
- 8dp
- 16dp
- 32dp
- 64dp
-
-
-
- @dimen/margin_medium
- @dimen/margin_medium
-
-
\ No newline at end of file
diff --git a/android/settings.gradle b/android/settings.gradle
deleted file mode 100644
index b2f9a127ab..0000000000
--- a/android/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-rootProject.name = "lokinet"
diff --git a/android/src/network/loki/lokinet/LokiNetActivity.java b/android/src/network/loki/lokinet/LokiNetActivity.java
deleted file mode 100755
index cbe8364653..0000000000
--- a/android/src/network/loki/lokinet/LokiNetActivity.java
+++ /dev/null
@@ -1,185 +0,0 @@
-package network.loki.lokinet;
-
-
-import java.io.File;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.net.URL;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import android.app.Activity;
-
-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;
-
-import android.os.IBinder;
-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/lokinet.signed";
-
- private AsyncBootstrap bootstrapper;
-
- public static final String LOG_TAG = "LokinetDaemon";
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- textView = new TextView(this);
- setContentView(textView);
- System.loadLibrary("lokinet-android");
- }
-
-
- private static void writeFile(File out, InputStream instream) throws IOException {
- OutputStream outstream = new FileOutputStream(out);
- byte[] buffer = new byte[512];
- int len;
- try {
- do {
- len = instream.read(buffer);
- if (len > 0) {
- outstream.write(buffer, 0, len);
- }
- }
- while (len != -1);
- } finally {
- outstream.close();
- }
- }
-
- public void startLokinet()
- {
- if(bootstrapper != null)
- return;
- bootstrapper = new AsyncBootstrap();
- bootstrapper.execute(DefaultBootstrapURL);
- }
-
- public void runLokinetService()
- {
- 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
- protected void onDestroy() {
- super.onDestroy();
- textView = null;
- }
-
- public File getRootDir()
- {
- return getFilesDir();
- }
-
- private class AsyncBootstrap extends AsyncTask
- {
- public String doInBackground(String ... urls) {
- try
- {
- File bootstrapFile = new File(getRootDir(), "bootstrap.signed");
- URL bootstrapURL = new URL(urls[0]);
- InputStream instream = bootstrapURL.openStream();
- writeFile(bootstrapFile, instream);
- instream.close();
- return getString(R.string.bootstrap_ok);
- }
- catch(Exception thrown)
- {
- return getString(R.string.bootstrap_fail) + ": " + throwableToString(thrown);
- }
- }
- public void onPostExecute(String val) {
- textView.setText(val);
- if(val.equals(getString(R.string.bootstrap_ok)))
- runLokinetService();
- bootstrapDone();
- }
- }
-
- private void bootstrapDone()
- {
- bootstrapper = null;
- }
-
- private CharSequence throwableToString(Throwable tr) {
- StringWriter sw = new StringWriter(8192);
- PrintWriter pw = new PrintWriter(sw);
- tr.printStackTrace(pw);
- pw.close();
- return sw.toString();
- }
-
-
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.options_main, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- // Handle action bar item clicks here. The action bar will
- // automatically handle clicks on the Home/Up button, so long
- // as you specify a parent activity in AndroidManifest.xml.
- int id = item.getItemId();
-
- switch(id){
- case R.id.action_start:
- startLokinet();
- return true;
- case R.id.action_stop:
- return true;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
-}
diff --git a/android/src/network/loki/lokinet/NetworkStateChangeReceiver.java b/android/src/network/loki/lokinet/NetworkStateChangeReceiver.java
deleted file mode 100644
index c4d7764fb7..0000000000
--- a/android/src/network/loki/lokinet/NetworkStateChangeReceiver.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package network.loki.lokinet;
-
-import android.util.Log;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-
-public class NetworkStateChangeReceiver extends BroadcastReceiver {
-
- private static final String TAG = "lokinet";
-
- //api level 1
- @Override
- public void onReceive(final Context context, final Intent intent) {
- Log.d(TAG,"Network state change");
- try {
- ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
- boolean isConnected = activeNetworkInfo!=null && activeNetworkInfo.isConnected();
- // https://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html?hl=ru
- // boolean isWiFi = activeNetworkInfo!=null && (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI);
- } catch (Throwable tr) {
- Log.d(TAG,"",tr);
- }
- }
-}
diff --git a/android/src/network/loki/lokinet/PermsAskerActivity.java b/android/src/network/loki/lokinet/PermsAskerActivity.java
deleted file mode 100644
index f71d0608c3..0000000000
--- a/android/src/network/loki/lokinet/PermsAskerActivity.java
+++ /dev/null
@@ -1,173 +0,0 @@
-package network.loki.lokinet;
-
-import android.Manifest;
-import android.app.Activity;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-import java.lang.reflect.Method;
-
-//dangerous perms, per https://developer.android.com/guide/topics/permissions/normal-permissions.html :
-//android.permission.WRITE_EXTERNAL_STORAGE
-public class PermsAskerActivity extends Activity {
-
- private static final int PERMISSION_VPN = 0;
-
- private Button button_request_write_ext_storage_perms;
- private TextView textview_retry;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- startMainActivity();
- /*
- //if less than Android 6, no runtime perms req system present
- if (android.os.Build.VERSION.SDK_INT < 23) {
-
- return;
- }
-
- setContentView(R.layout.activity_perms_asker);
- button_request_write_ext_storage_perms = (Button) findViewById(R.id.button_request_write_ext_storage_perms);
- textview_retry = (TextView) findViewById(R.id.textview_retry);
-
- button_request_write_ext_storage_perms.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- request_write_ext_storage_perms();
- }
- });
- request_write_ext_storage_perms();
- */
- }
-
- private void request_write_ext_storage_perms() {
-
- textview_retry.setVisibility(TextView.GONE);
- button_request_write_ext_storage_perms.setVisibility(Button.GONE);
-
- Method methodCheckPermission;
- Method method_shouldShowRequestPermissionRationale;
- Method method_requestPermissions;
- try {
- methodCheckPermission = getClass().getMethod("checkSelfPermission", String.class);
- method_shouldShowRequestPermissionRationale =
- getClass().getMethod("shouldShowRequestPermissionRationale", String.class);
- method_requestPermissions =
- getClass().getMethod("requestPermissions", String[].class, int.class);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
- }
- Integer resultObj;
- try {
- resultObj = (Integer) methodCheckPermission.invoke(
- this, Manifest.permission.BIND_VPN_SERVICE);
- } catch (Throwable e) {
- throw new RuntimeException(e);
- }
-
- if (resultObj != PackageManager.PERMISSION_GRANTED) {
-
- // Should we show an explanation?
- Boolean aBoolean;
- try {
- aBoolean = (Boolean) method_shouldShowRequestPermissionRationale.invoke(this,
- Manifest.permission.BIND_VPN_SERVICE);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- if (aBoolean) {
-
- // Show an explanation to the user *asynchronously* -- don't block
- // this thread waiting for the user's response! After the user
- // sees the explanation, try again to request the permission.
-
- showExplanation();
-
- } else {
-
- // No explanation needed, we can request the permission.
-
- try {
- method_requestPermissions.invoke(this,
- new String[]{Manifest.permission.BIND_VPN_SERVICE},
- PERMISSION_VPN);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- } else startMainActivity();
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode,
- String permissions[], int[] grantResults) {
- switch (requestCode) {
- case PERMISSION_VPN: {
- // If request is cancelled, the result arrays are empty.
- if (grantResults.length > 0
- && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-
- // permission was granted, yay! Do the
- // contacts-related task you need to do.
-
- startMainActivity();
-
- } else {
-
- // permission denied, boo! Disable the
- // functionality that depends on this permission.
- textview_retry.setText("you need to allow this to continue");
- textview_retry.setVisibility(TextView.VISIBLE);
- button_request_write_ext_storage_perms.setVisibility(Button.VISIBLE);
- }
- return;
- }
-
- // other 'case' lines to check for other
- // permissions this app might request.
- }
- }
-
- private void startMainActivity() {
- startActivity(new Intent(this, LokiNetActivity.class));
- finish();
- }
-
- private static final int SHOW_EXPLANATION_REQUEST = 1; // The request code
- private void showExplanation() {
- Intent intent = new Intent(this, PermsExplanationActivity.class);
- startActivityForResult(intent, SHOW_EXPLANATION_REQUEST);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- // Check which request we're responding to
- if (requestCode == SHOW_EXPLANATION_REQUEST) {
- // Make sure the request was successful
- if (resultCode == RESULT_OK) {
- // Request the permission
- Method method_requestPermissions;
- try {
- method_requestPermissions =
- getClass().getMethod("requestPermissions", String[].class, int.class);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
- }
- try {
- method_requestPermissions.invoke(this,
- new String[]{Manifest.permission.BIND_VPN_SERVICE},
- PERMISSION_VPN);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- } else {
- finish(); //close the app
- }
- }
- }
-}
diff --git a/android/src/network/loki/lokinet/PermsExplanationActivity.java b/android/src/network/loki/lokinet/PermsExplanationActivity.java
deleted file mode 100644
index bbea360541..0000000000
--- a/android/src/network/loki/lokinet/PermsExplanationActivity.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package network.loki.lokinet;
-
-import android.app.ActionBar;
-import android.content.Intent;
-import android.os.Bundle;
-import android.app.Activity;
-import android.view.View;
-import android.widget.Button;
-
-public class PermsExplanationActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_perms_explanation);
- ActionBar actionBar = getActionBar();
- if(actionBar!=null)actionBar.setHomeButtonEnabled(false);
- Button button_ok = (Button) findViewById(R.id.button_ok);
- button_ok.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- returnFromActivity();
- }
- });
- }
-
- private void returnFromActivity() {
- Intent data = new Intent();
- Activity parent = getParent();
- if (parent == null) {
- setResult(Activity.RESULT_OK, data);
- } else {
- parent.setResult(Activity.RESULT_OK, data);
- }
- finish();
- }
-
-}
diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake
index 264252d030..783ae11d11 100644
--- a/cmake/StaticBuild.cmake
+++ b/cmake/StaticBuild.cmake
@@ -170,6 +170,13 @@ if(APPLE)
endif()
+if("${CMAKE_GENERATOR}" STREQUAL "Unix Makefiles")
+ set(_make $(MAKE))
+else()
+ set(_make make)
+endif()
+
+
# Builds a target; takes the target name (e.g. "readline") and builds it in an external project with
# target name suffixed with `_external`. Its upper-case value is used to get the download details
# (from the variables set above). The following options are supported and passed through to
@@ -178,8 +185,8 @@ set(build_def_DEPENDS "")
set(build_def_PATCH_COMMAND "")
set(build_def_CONFIGURE_COMMAND ./configure ${cross_host} --disable-shared --prefix=${DEPS_DESTDIR} --with-pic
"CC=${deps_cc}" "CXX=${deps_cxx}" "CFLAGS=${deps_CFLAGS}" "CXXFLAGS=${deps_CXXFLAGS}" ${cross_rc})
-set(build_def_BUILD_COMMAND make)
-set(build_def_INSTALL_COMMAND make install)
+set(build_def_BUILD_COMMAND ${_make})
+set(build_def_INSTALL_COMMAND ${_make} install)
set(build_def_BUILD_BYPRODUCTS ${DEPS_DESTDIR}/lib/lib___TARGET___.a ${DEPS_DESTDIR}/include/___TARGET___.h)
function(build_external target)
@@ -247,7 +254,7 @@ build_external(openssl
--prefix=${DEPS_DESTDIR} ${openssl_extra_opts} no-shared no-capieng no-dso no-dtls1 no-ec_nistp_64_gcc_128 no-gost
no-heartbeats no-md2 no-rc5 no-rdrand no-rfc3779 no-sctp no-ssl-trace no-ssl2 no-ssl3
no-static-engine no-tests no-weak-ssl-ciphers no-zlib no-zlib-dynamic "CFLAGS=${deps_CFLAGS}"
- INSTALL_COMMAND make install_sw
+ INSTALL_COMMAND ${_make} install_sw
BUILD_BYPRODUCTS
${DEPS_DESTDIR}/lib/libssl.a ${DEPS_DESTDIR}/lib/libcrypto.a
${DEPS_DESTDIR}/include/openssl/ssl.h ${DEPS_DESTDIR}/include/openssl/crypto.h
@@ -383,7 +390,7 @@ foreach(curl_arch ${curl_arches})
--without-zsh-functions-dir --without-fish-functions-dir
"CC=${deps_cc}" "CFLAGS=${deps_noarch_CFLAGS}${cflags_extra}" ${curl_extra}
BUILD_COMMAND true
- INSTALL_COMMAND make -C lib install && make -C include install
+ INSTALL_COMMAND ${_make} -C lib install && ${_make} -C include install
BUILD_BYPRODUCTS
${curl_prefix}/lib/libcurl.a
${curl_prefix}/include/curl/curl.h
diff --git a/contrib/android.sh b/contrib/android.sh
new file mode 100755
index 0000000000..9dfd64df2a
--- /dev/null
+++ b/contrib/android.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+set -e
+set +x
+
+default_abis="armeabi-v7a arm64-v8a x86 x86_64"
+build_abis=${ABIS:-$default_abis}
+
+test x$NDK = x && echo "NDK env var not set"
+test x$NDK = x && exit 1
+
+echo "building abis: $build_abis"
+
+root="$(readlink -f $(dirname $0)/../)"
+out=$root/lokinet-jni-$(git describe || echo unknown)
+mkdir -p $out
+mkdir -p $root/build-android
+cd $root/build-android
+
+for abi in $build_abis; do
+ mkdir -p build-$abi $out/$abi
+ cd build-$abi
+ cmake \
+ -G 'Unix Makefiles' \
+ -DANDROID=ON \
+ -DANDROID_ABI=$abi \
+ -DANDROID_ARM_MODE=arm \
+ -DANDROID_PLATFORM=android-23 \
+ -DANDROID_STL=c++_static \
+ -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
+ -DBUILD_STATIC_DEPS=ON \
+ -DBUILD_PACKAGE=ON \
+ -DBUILD_SHARED_LIBS=OFF \
+ -DBUILD_TESTING=OFF \
+ -DBUILD_LIBLOKINET=OFF \
+ -DWITH_TESTS=OFF \
+ -DNATIVE_BUILD=OFF \
+ -DSTATIC_LINK=ON \
+ -DWITH_SYSTEMD=OFF \
+ -DFORCE_OXENMQ_SUBMODULE=ON \
+ -DSUBMODULE_CHECK=OFF \
+ -DWITH_LTO=OFF \
+ -DCMAKE_BUILD_TYPE=Release \
+ $@ $root
+ make lokinet-android -j${JOBS:-$(nproc)}
+ cp jni/liblokinet-android.so $out/$abi/liblokinet-android.so
+ cd -
+done
+
+
+echo
+echo "build artifacts outputted to $out"
diff --git a/contrib/bencode-dump.py b/contrib/bencode-dump.py
new file mode 100755
index 0000000000..1c1f554991
--- /dev/null
+++ b/contrib/bencode-dump.py
@@ -0,0 +1,92 @@
+#!/usr/bin/python3
+
+import sys
+import pprint
+
+if len(sys.argv) != 2 or sys.argv[1].startswith('-'):
+ print("Usage: {} FILE -- dumps a bencoded file".format(sys.argv[0]), file=sys.stderr)
+ sys.exit(1)
+
+f = open(sys.argv[1], 'rb')
+
+class HexPrinter():
+ def __init__(self, data):
+ self.data = data
+
+ def __repr__(self):
+ return "hex({} bytes):'{}'".format(len(self.data), self.data.hex())
+
+
+def next_byte():
+ b = f.read(1)
+ assert b is not None and len(b) == 1
+ return b
+
+
+def parse_int():
+ s = b''
+ x = next_byte()
+ while x in b"0123456789":
+ s += x
+ x = next_byte()
+ assert x == b'e' and len(s) > 0, "Invalid integer encoding"
+ return int(s)
+
+
+def parse_string(s):
+ x = next_byte()
+ while x in b"0123456789":
+ s += x
+ x = next_byte()
+ assert x == b':', "Invalid string encoding"
+ s = int(s)
+ data = f.read(s)
+ assert len(data) == s, "Truncated string data"
+ # If the string is ascii then convert to string:
+ if all(0x20 <= b <= 0x7e for b in data):
+ return data.decode()
+ # Otherwise display as hex:
+ return HexPrinter(data)
+
+
+def parse_dict():
+ d = {}
+ last_key = None
+ while True:
+ t = next_byte()
+ if t == b'e':
+ return d
+ assert t in b"0123456789", "Invalid dict: dict keys must be strings"
+ key = parse_string(t)
+ raw_key = key.data if isinstance(key, HexPrinter) else key.encode()
+ if last_key is not None and raw_key <= last_key:
+ print("Warning: found out-of-order dict keys ({} after {})".format(raw_key, last_key), file=sys.stderr)
+ last_key = raw_key
+ t = next_byte()
+ d[key] = parse_thing(t)
+
+
+def parse_list():
+ l = []
+ while True:
+ t = next_byte()
+ if t == b'e':
+ return l
+ l.append(parse_thing(t))
+
+
+def parse_thing(t):
+ if t == b'd':
+ return parse_dict()
+ if t == b'l':
+ return parse_list()
+ if t == b'i':
+ return parse_int()
+ if t in b"0123456789":
+ return parse_string(t)
+ assert False, "Parsing error: encountered invalid type '{}'".format(t)
+
+
+pprint.PrettyPrinter(
+ indent=2
+ ).pprint(parse_thing(next_byte()))
diff --git a/contrib/ci/docker/android.dockerfile b/contrib/ci/docker/android.dockerfile
index 6b069cbcf1..417735ae7d 100644
--- a/contrib/ci/docker/android.dockerfile
+++ b/contrib/ci/docker/android.dockerfile
@@ -1,8 +1,7 @@
FROM debian:testing
RUN /bin/bash -c 'echo "man-db man-db/auto-update boolean false" | debconf-set-selections'
RUN /bin/bash -c 'sed -i "s/main/main contrib/g" /etc/apt/sources.list'
-RUN /bin/bash -c 'apt-get -o=Dpkg::Use-Pty=0 -q update && apt-get -o=Dpkg::Use-Pty=0 -q dist-upgrade -y && apt-get -o=Dpkg::Use-Pty=0 -q install -y android-sdk google-android-ndk-installer'
-RUN /bin/bash -c 'apt-get -o=Dpkg::Use-Pty=0 -q -y install wget git pkg-config'
-RUN /bin/bash -c 'wget https://services.gradle.org/distributions/gradle-6.3-bin.zip -O /tmp/gradle.zip && unzip -d /opt/ /tmp/gradle.zip && ln -s /opt/gradle*/bin/gradle /usr/local/bin/gradle && rm -f /tmp/gradle.zip'
+RUN /bin/bash -c 'apt-get -o=Dpkg::Use-Pty=0 -q update && apt-get -o=Dpkg::Use-Pty=0 -q dist-upgrade -y && apt-get -o=Dpkg::Use-Pty=0 -q install -y android-sdk google-android-ndk-installer wget git pkg-config automake libtool cmake ccache curl zip'
RUN /bin/bash -c 'git clone https://github.com/Shadowstyler/android-sdk-licenses.git /tmp/android-sdk-licenses && cp -a /tmp/android-sdk-licenses/*-license /usr/lib/android-sdk/licenses && rm -rf /tmp/android-sdk-licenses'
-RUN /bin/bash -c 'apt-get -o=Dpkg::Use-Pty=0 -q -y install automake libtool'
\ No newline at end of file
+RUN /bin/bash -c 'wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_2.2.2-stable.tar.xz -O /tmp/flutter.tar.xz && cd /opt && tar -xJvf /tmp/flutter.tar.xz && rm /tmp/flutter.tar.xz && ln -s /opt/flutter/bin/flutter /usr/local/bin/'
+RUN /bin/bash -c 'flutter precache'
\ No newline at end of file
diff --git a/contrib/ci/docker/debian-stable.dockerfile b/contrib/ci/docker/debian-stable.dockerfile
index 406212a131..f5de1e581b 100644
--- a/contrib/ci/docker/debian-stable.dockerfile
+++ b/contrib/ci/docker/debian-stable.dockerfile
@@ -1,3 +1,3 @@
FROM debian:stable
RUN /bin/bash -c 'echo "man-db man-db/auto-update boolean false" | debconf-set-selections'
-RUN /bin/bash -c 'apt-get -o=Dpkg::Use-Pty=0 -q update && apt-get -o=Dpkg::Use-Pty=0 -q dist-upgrade -y && apt-get -o=Dpkg::Use-Pty=0 -q install -y eatmydata gdb cmake git ninja-build pkg-config ccache g++ libsodium-dev libsystemd-dev python3-dev libuv1-dev libunbound-dev nettle-dev libssl-dev libevent-dev libsqlite3-dev libboost-thread-dev liboost-serialization-dev libboost-program-options-dev libgtest-dev libminiupnpc-dev libunwind8-dev libreadline-dev libhidapi-dev libusb-1.0.0-dev qttools5-dev libcurl4-openssl-dev'
+RUN /bin/bash -c 'apt-get -o=Dpkg::Use-Pty=0 -q update && apt-get -o=Dpkg::Use-Pty=0 -q dist-upgrade -y && apt-get -o=Dpkg::Use-Pty=0 -q install -y eatmydata gdb cmake git ninja-build pkg-config ccache g++ libsodium-dev libsystemd-dev python3-dev libuv1-dev libunbound-dev nettle-dev libssl-dev libevent-dev libsqlite3-dev libboost-thread-dev libboost-serialization-dev libboost-program-options-dev libgtest-dev libminiupnpc-dev libunwind8-dev libreadline-dev libhidapi-dev libusb-1.0.0-dev qttools5-dev libcurl4-openssl-dev'
diff --git a/contrib/ci/drone-static-upload.sh b/contrib/ci/drone-static-upload.sh
index 50ae7912f5..bdbd575615 100755
--- a/contrib/ci/drone-static-upload.sh
+++ b/contrib/ci/drone-static-upload.sh
@@ -34,18 +34,18 @@ else
fi
mkdir -v "$base"
-if [ -e daemon/lokinet.exe ]; then
- cp -av lokinet-*.exe "$base"
+if [ -e build-windows ]; then
+ cp -av build-windows/lokinet-*.exe "$base"
# zipit up yo
archive="$base.zip"
zip -r "$archive" "$base"
-elif [ -e build/outputs/apk/debug/lokinet-debug.apk ] ; then
+elif [ -e build-android ] ; then
# android af ngl
- cp -av build/outputs/apk/debug/lokinet-debug.apk "$base"
+ cp -av lokinet-jni-* "$base"
archive="$base.tar.xz"
tar cJvf "$archive" "$base"
else
- cp -av daemon/lokinet daemon/lokinet-vpn ../lokinet-bootstrap "$base"
+ cp -av daemon/lokinet daemon/lokinet-vpn daemon/lokinet-bootstrap "$base"
# tar dat shiz up yo
archive="$base.tar.xz"
tar cJvf "$archive" "$base"
@@ -73,4 +73,3 @@ SFTP
set +o xtrace
echo -e "\n\n\n\n\e[32;1mUploaded to https://${upload_to}/${archive}\e[0m\n\n\n"
-
diff --git a/contrib/py/admin/lokinetmon b/contrib/py/admin/lokinetmon
index 4c36b34e67..2c45b24359 100755
--- a/contrib/py/admin/lokinetmon
+++ b/contrib/py/admin/lokinetmon
@@ -155,7 +155,7 @@ class Monitor:
def time_to(timestamp):
""" return time until timestamp in seconds formatted"""
if timestamp:
- val = int((timestamp - now()) / 1000)
+ val = (timestamp - now()) / 1000.0
if val < 0:
return "{} seconds ago".format(0-val)
else:
@@ -449,7 +449,7 @@ class Monitor:
for intro in context['introset']['intros'] or []:
y_pos = self._display_intro(y_pos, intro, "introset intro", context['paths'])
for path in context['paths']:
- y_pos = self._display_intro(y_pos, path['intro'], "inbound path intro", context['paths'])
+ y_pos = self._display_intro(y_pos, path['intro'], "inbound path [created {}]".format(self.time_to(path['buildStarted'])), context['paths'])
return y_pos
@@ -489,12 +489,8 @@ class Monitor:
for intro in context['currentRemoteIntroset']['intros'] or []:
y_pos = self._display_intro(y_pos, intro, "introset intro", paths)
y_pos += 1
- for intro in context['badIntros'] or []:
- y_pos = self._display_intro(y_pos, intro, "bad intro", paths)
- y_pos += 1
return y_pos
-
def display_data(self):
""" draw main window """
if self.data is not None:
diff --git a/contrib/windows.sh b/contrib/windows.sh
index 99c814590a..c1224afe19 100755
--- a/contrib/windows.sh
+++ b/contrib/windows.sh
@@ -4,7 +4,7 @@ set +x
mkdir -p build-windows
cd build-windows
cmake \
- -G Ninja \
+ -G 'Unix Makefiles' \
-DCMAKE_EXE_LINKER_FLAGS=-fstack-protector \
-DCMAKE_CXX_FLAGS=-fdiagnostics-color=always\
-DCMAKE_TOOLCHAIN_FILE=../contrib/cross/mingw64.cmake\
@@ -22,4 +22,4 @@ cmake \
-DWITH_LTO=OFF \
-DCMAKE_BUILD_TYPE=Release \
$@ ..
-ninja package
+make package -j${JOBS:-$(nproc)}
diff --git a/android/src/network/loki/lokinet/LokinetConfig.java b/jni/java/src/network/loki/lokinet/LokinetConfig.java
similarity index 100%
rename from android/src/network/loki/lokinet/LokinetConfig.java
rename to jni/java/src/network/loki/lokinet/LokinetConfig.java
diff --git a/android/src/network/loki/lokinet/LokinetDaemon.java b/jni/java/src/network/loki/lokinet/LokinetDaemon.java
similarity index 98%
rename from android/src/network/loki/lokinet/LokinetDaemon.java
rename to jni/java/src/network/loki/lokinet/LokinetDaemon.java
index 072d21ab49..b1c4853548 100644
--- a/android/src/network/loki/lokinet/LokinetDaemon.java
+++ b/jni/java/src/network/loki/lokinet/LokinetDaemon.java
@@ -26,6 +26,9 @@ public class LokinetDaemon extends VpnService
private static native String DetectFreeRange();
+ public native String DumpStatus();
+
+
public static final String LOG_TAG = "LokinetDaemon";
ByteBuffer impl = null;
diff --git a/jni/lokinet_daemon.cpp b/jni/lokinet_daemon.cpp
index 4650788510..8de704ae8f 100644
--- a/jni/lokinet_daemon.cpp
+++ b/jni/lokinet_daemon.cpp
@@ -2,6 +2,7 @@
#include "lokinet_jni_common.hpp"
#include
#include
+#include
extern "C"
{
@@ -99,4 +100,23 @@ extern "C"
}
return env->NewStringUTF(rangestr.c_str());
}
+
+ JNIEXPORT jstring JNICALL
+ Java_network_loki_lokinet_LokinetDaemon_DumpStatus(JNIEnv* env, jobject self)
+ {
+ std::string status{};
+ if (auto ptr = GetImpl(env, self))
+ {
+ if (ptr->IsUp())
+ {
+ std::promise result;
+ ptr->CallSafe([&result, router = ptr->router]() {
+ const auto status = router->ExtractStatus();
+ result.set_value(status.dump());
+ });
+ status = result.get_future().get();
+ }
+ }
+ return env->NewStringUTF(status.c_str());
+ }
}
diff --git a/jni/network_loki_lokinet_LokinetDaemon.h b/jni/network_loki_lokinet_LokinetDaemon.h
index 78966b9890..cc4d54fed4 100644
--- a/jni/network_loki_lokinet_LokinetDaemon.h
+++ b/jni/network_loki_lokinet_LokinetDaemon.h
@@ -80,6 +80,14 @@ extern "C"
JNIEXPORT jstring JNICALL
Java_network_loki_lokinet_LokinetDaemon_DetectFreeRange(JNIEnv*, jclass);
+ /*
+ * Class: network_loki_lokinet_LokinetDaemon
+ * Method: DumpStatus
+ * Signature: ()Ljava/lang/String;
+ */
+ JNIEXPORT jstring JNICALL
+ Java_network_loki_lokinet_LokinetDaemon_DumpStatus(JNIEnv*, jobject);
+
#ifdef __cplusplus
}
#endif
diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp
index 0fee001791..bef3e521f4 100644
--- a/llarp/config/config.cpp
+++ b/llarp/config/config.cpp
@@ -674,6 +674,22 @@ namespace llarp
m_PathAlignmentTimeout = std::chrono::seconds{val};
});
+ conf.defineOption(
+ "network",
+ "persist-addrmap-file",
+ ClientOnly,
+ Default{fs::path{params.defaultDataDir / "addrmap.dat"}},
+ Comment{
+ "persist mapped ephemeral addresses to a file",
+ "on restart the mappings will be loaded so that ip addresses will not be mapped to a "
+ "different address",
+ },
+ [this](fs::path arg) {
+ if (arg.empty())
+ throw std::invalid_argument("persist-addrmap-file cannot be empty");
+ m_AddrMapPersistFile = arg;
+ });
+
// Deprecated options:
conf.defineOption("network", "enabled", Deprecated);
}
@@ -713,12 +729,9 @@ namespace llarp
}
if (!arg.empty())
{
- auto& addr = m_upstreamDNS.emplace_back(std::move(arg));
- if (auto p = addr.getPort(); p && *p != 53)
- // unbound doesn't support non-default ports so bail if the user gave one
- throw std::invalid_argument(
- "Invalid [dns] upstream setting: non-default DNS ports are not supported");
- addr.setPort(std::nullopt);
+ auto& entry = m_upstreamDNS.emplace_back(std::move(arg));
+ if (!entry.getPort())
+ entry.setPort(53);
}
});
@@ -730,11 +743,25 @@ namespace llarp
"Address to bind to for handling DNS requests.",
},
[=](std::string arg) {
- m_bind = IpAddress{std::move(arg)};
+ m_bind = SockAddr{std::move(arg)};
if (!m_bind.getPort())
m_bind.setPort(53);
});
+ conf.defineOption(
+ "dns",
+ "add-hosts",
+ ClientOnly,
+ Comment{"Add a hosts file to the dns resolver", "For use with client side dns filtering"},
+ [=](fs::path path) {
+ if (path.empty())
+ return;
+ if (not fs::exists(path))
+ throw std::invalid_argument{
+ stringify("cannot add hosts file ", path, " as it does not seem to exist")};
+ m_hostfiles.emplace_back(std::move(path));
+ });
+
// Ignored option (used by the systemd service file to disable resolvconf configuration).
conf.defineOption(
"dns",
diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp
index 7fb212df43..03f7a000c0 100644
--- a/llarp/config/config.hpp
+++ b/llarp/config/config.hpp
@@ -127,10 +127,7 @@ namespace llarp
std::optional m_PathAlignmentTimeout;
- // TODO:
- // on-up
- // on-down
- // on-ready
+ std::optional m_AddrMapPersistFile;
void
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);
@@ -138,8 +135,9 @@ namespace llarp
struct DnsConfig
{
- IpAddress m_bind;
- std::vector m_upstreamDNS;
+ SockAddr m_bind;
+ std::vector m_upstreamDNS;
+ std::vector m_hostfiles;
void
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);
diff --git a/llarp/dns/server.cpp b/llarp/dns/server.cpp
index d80b797386..22155ec4c0 100644
--- a/llarp/dns/server.cpp
+++ b/llarp/dns/server.cpp
@@ -26,9 +26,9 @@ namespace llarp::dns
}
bool
- Proxy::Start(SockAddr addr, std::vector resolvers)
+ Proxy::Start(SockAddr addr, std::vector resolvers, std::vector hostfiles)
{
- if (not PacketHandler::Start(addr, std::move(resolvers)))
+ if (not PacketHandler::Start(addr, std::move(resolvers), std::move(hostfiles)))
return false;
return m_Server->listen(addr);
}
@@ -44,14 +44,19 @@ namespace llarp::dns
}
bool
- PacketHandler::Start(SockAddr, std::vector resolvers)
+ PacketHandler::Start(SockAddr, std::vector resolvers, std::vector hostfiles)
{
- return SetupUnboundResolver(std::move(resolvers));
+ return SetupUnboundResolver(std::move(resolvers), std::move(hostfiles));
}
bool
- PacketHandler::SetupUnboundResolver(std::vector resolvers)
+ PacketHandler::SetupUnboundResolver(
+ std::vector resolvers, std::vector hostfiles)
{
+ // if we have no resolvers don't set up unbound
+ if (resolvers.empty())
+ return true;
+
auto failFunc = [self = weak_from_this()](
const SockAddr& from, const SockAddr& to, Message msg) {
if (auto this_ptr = self.lock())
@@ -73,14 +78,18 @@ namespace llarp::dns
}
for (const auto& resolver : resolvers)
{
- if (not m_UnboundResolver->AddUpstreamResolver(resolver.toHost()))
+ if (not m_UnboundResolver->AddUpstreamResolver(resolver))
{
- llarp::LogError("Failed to add upstream DNS server: ", resolver.toHost());
+ llarp::LogError("Failed to add upstream DNS server: ", resolver);
m_UnboundResolver = nullptr;
return false;
}
m_Resolvers.emplace(resolver);
}
+ for (const auto& path : hostfiles)
+ {
+ m_UnboundResolver->AddHostsFile(path);
+ }
return true;
}
diff --git a/llarp/dns/server.hpp b/llarp/dns/server.hpp
index 411c57cf23..e778b2e226 100644
--- a/llarp/dns/server.hpp
+++ b/llarp/dns/server.hpp
@@ -35,7 +35,10 @@ namespace llarp
virtual ~PacketHandler() = default;
virtual bool
- Start(SockAddr localaddr, std::vector upstreamResolvers);
+ Start(
+ SockAddr localaddr,
+ std::vector upstreamResolvers,
+ std::vector hostfiles);
void
Stop();
@@ -58,10 +61,10 @@ namespace llarp
HandleUpstreamFailure(const SockAddr& from, const SockAddr& to, Message msg);
bool
- SetupUnboundResolver(std::vector resolvers);
+ SetupUnboundResolver(std::vector resolvers, std::vector hostfiles);
IQueryHandler* const m_QueryHandler;
- std::set m_Resolvers;
+ std::set m_Resolvers;
std::shared_ptr m_UnboundResolver;
EventLoop_ptr m_Loop;
};
@@ -73,7 +76,10 @@ namespace llarp
explicit Proxy(EventLoop_ptr loop, IQueryHandler* handler);
bool
- Start(SockAddr localaddr, std::vector resolvers) override;
+ Start(
+ SockAddr localaddr,
+ std::vector upstreamResolvers,
+ std::vector hostfiles) override;
protected:
void
diff --git a/llarp/dns/unbound_resolver.cpp b/llarp/dns/unbound_resolver.cpp
index c824a6eac1..90ebbbcd39 100644
--- a/llarp/dns/unbound_resolver.cpp
+++ b/llarp/dns/unbound_resolver.cpp
@@ -2,6 +2,8 @@
#include "server.hpp"
#include
+#include
+#include
namespace llarp::dns
{
@@ -105,9 +107,16 @@ namespace llarp::dns
}
bool
- UnboundResolver::AddUpstreamResolver(const std::string& upstreamResolverIP)
+ UnboundResolver::AddUpstreamResolver(const SockAddr& upstreamResolver)
{
- if (ub_ctx_set_fwd(unboundContext, upstreamResolverIP.c_str()) != 0)
+ std::stringstream ss;
+ ss << upstreamResolver.hostString();
+
+ if (const auto port = upstreamResolver.getPort(); port != 53)
+ ss << "@" << port;
+
+ const auto str = ss.str();
+ if (ub_ctx_set_fwd(unboundContext, str.c_str()) != 0)
{
Reset();
return false;
@@ -115,6 +124,21 @@ namespace llarp::dns
return true;
}
+ void
+ UnboundResolver::AddHostsFile(const fs::path& file)
+ {
+ LogDebug("adding hosts file ", file);
+ const auto str = file.u8string();
+ if (auto ret = ub_ctx_hosts(unboundContext, str.c_str()))
+ {
+ throw std::runtime_error{stringify("Failed to add host file ", file, ": ", ub_strerror(ret))};
+ }
+ else
+ {
+ LogInfo("added hosts file ", file);
+ }
+ }
+
void
UnboundResolver::Lookup(SockAddr to, SockAddr from, Message msg)
{
diff --git a/llarp/dns/unbound_resolver.hpp b/llarp/dns/unbound_resolver.hpp
index 215da44448..e1861ab88c 100644
--- a/llarp/dns/unbound_resolver.hpp
+++ b/llarp/dns/unbound_resolver.hpp
@@ -7,6 +7,7 @@
#include
#include
+#include
#include "message.hpp"
@@ -50,7 +51,10 @@ namespace llarp::dns
Init();
bool
- AddUpstreamResolver(const std::string& upstreamResolverIP);
+ AddUpstreamResolver(const SockAddr& upstreamResolverIP);
+
+ void
+ AddHostsFile(const fs::path& file);
void
Lookup(SockAddr to, SockAddr from, Message msg);
diff --git a/llarp/ev/ev_libuv.cpp b/llarp/ev/ev_libuv.cpp
index 9832082d8e..f9ce3c4b4c 100644
--- a/llarp/ev/ev_libuv.cpp
+++ b/llarp/ev/ev_libuv.cpp
@@ -302,7 +302,7 @@ namespace llarp::uv
handle->on([this](auto& event, auto& /*handle*/) {
on_recv(
*this,
- SockAddr{event.sender.ip, static_cast(event.sender.port)},
+ SockAddr{event.sender.ip, huint16_t{static_cast(event.sender.port)}},
OwnedBuffer{std::move(event.data), event.length});
});
}
diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp
index 6f9beb9476..f775bcd3ec 100644
--- a/llarp/handlers/exit.cpp
+++ b/llarp/handlers/exit.cpp
@@ -21,7 +21,7 @@ namespace llarp
: m_Router(r)
, m_Resolver(std::make_shared(r->loop(), this))
, m_Name(std::move(name))
- , m_LocalResolverAddr("127.0.0.1", 53)
+ , m_LocalResolverAddr{"127.0.0.1:53"}
, m_QUIC{std::make_shared(*this)}
, m_InetToNetwork(name + "_exit_rx", r->loop(), r->loop())
@@ -476,8 +476,8 @@ namespace llarp
GetRouter()->loop()->add_ticker([this] { Flush(); });
- llarp::LogInfo("Trying to start resolver ", m_LocalResolverAddr.toString());
- return m_Resolver->Start(m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers);
+ llarp::LogInfo("Trying to start resolver ", m_LocalResolverAddr);
+ return m_Resolver->Start(m_LocalResolverAddr, m_UpstreamResolvers, {});
}
return true;
}
diff --git a/llarp/handlers/exit.hpp b/llarp/handlers/exit.hpp
index 9addb3ab4a..cfea855e9f 100644
--- a/llarp/handlers/exit.hpp
+++ b/llarp/handlers/exit.hpp
@@ -211,8 +211,8 @@ namespace llarp
std::shared_ptr m_NetIf;
- IpAddress m_LocalResolverAddr;
- std::vector m_UpstreamResolvers;
+ SockAddr m_LocalResolverAddr;
+ std::vector m_UpstreamResolvers;
std::shared_ptr m_QUIC;
diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp
index c816297f3f..4a34df6124 100644
--- a/llarp/handlers/tun.cpp
+++ b/llarp/handlers/tun.cpp
@@ -169,6 +169,7 @@ namespace llarp
m_LocalResolverAddr = dnsConf.m_bind;
m_UpstreamResolvers = dnsConf.m_upstreamDNS;
+ m_hostfiles = dnsConf.m_hostfiles;
m_BaseV6Address = conf.m_baseV6Address;
@@ -208,6 +209,110 @@ namespace llarp
m_OurIP = m_OurRange.addr;
m_UseV6 = false;
+ m_PersistAddrMapFile = conf.m_AddrMapPersistFile;
+ if (m_PersistAddrMapFile)
+ {
+ const auto& file = *m_PersistAddrMapFile;
+ if (fs::exists(file))
+ {
+ bool shouldLoadFile = true;
+ {
+ constexpr auto LastModifiedWindow = 1min;
+ const auto lastmodified = fs::last_write_time(file);
+ const auto now = decltype(lastmodified)::clock::now();
+ if (now < lastmodified or now - lastmodified > LastModifiedWindow)
+ {
+ shouldLoadFile = false;
+ }
+ }
+ std::vector data;
+ if (auto maybe = util::OpenFileStream(file, std::ios_base::binary);
+ maybe and shouldLoadFile)
+ {
+ LogInfo(Name(), " loading address map file from ", file);
+ maybe->seekg(0, std::ios_base::end);
+ const size_t len = maybe->tellg();
+ maybe->seekg(0, std::ios_base::beg);
+ data.resize(len);
+ LogInfo(Name(), " reading ", len, " bytes");
+ maybe->read(data.data(), data.size());
+ }
+ else
+ {
+ if (shouldLoadFile)
+ {
+ LogInfo(Name(), " address map file ", file, " does not exist, so we won't load it");
+ }
+ else
+ LogInfo(Name(), " address map file ", file, " not loaded because it's stale");
+ }
+ if (not data.empty())
+ {
+ std::string_view bdata{data.data(), data.size()};
+ LogDebug(Name(), " parsing address map data: ", bdata);
+ const auto parsed = oxenmq::bt_deserialize(bdata);
+ for (const auto& [key, value] : parsed)
+ {
+ huint128_t ip{};
+ if (not ip.FromString(key))
+ {
+ LogWarn(Name(), " malformed IP in addr map data: ", key);
+ continue;
+ }
+ if (m_OurIP == ip)
+ continue;
+ if (not m_OurRange.Contains(ip))
+ {
+ LogWarn(Name(), " out of range IP in addr map data: ", ip);
+ continue;
+ }
+ EndpointBase::AddressVariant_t addr;
+
+ if (const auto* str = std::get_if(&value))
+ {
+ if (auto maybe = service::ParseAddress(*str))
+ {
+ addr = *maybe;
+ }
+ else
+ {
+ LogWarn(Name(), " invalid address in addr map: ", *str);
+ continue;
+ }
+ }
+ else
+ {
+ LogWarn(Name(), " invalid first entry in addr map, not a string");
+ continue;
+ }
+ if (const auto* loki = std::get_if(&addr))
+ {
+ m_IPToAddr.emplace(ip, loki->data());
+ m_AddrToIP.emplace(loki->data(), ip);
+ m_SNodes[*loki] = false;
+ LogInfo(Name(), " remapped ", ip, " to ", *loki);
+ }
+ if (const auto* snode = std::get_if(&addr))
+ {
+ m_IPToAddr.emplace(ip, snode->data());
+ m_AddrToIP.emplace(snode->data(), ip);
+ m_SNodes[*snode] = true;
+ LogInfo(Name(), " remapped ", ip, " to ", *snode);
+ }
+ if (m_NextIP < ip)
+ m_NextIP = ip;
+ // make sure we dont unmap this guy
+ MarkIPActive(ip);
+ }
+ }
+ }
+ else
+ {
+ LogInfo(
+ Name(), " skipping loading addr map at ", file, " as it does not currently exist");
+ }
+ }
+
if (auto* quic = GetQUICTunnel())
{
quic->listen([this](std::string_view, uint16_t port) {
@@ -292,6 +397,14 @@ namespace llarp
service::Address addr, auto msg, bool isV6) -> bool {
using service::Address;
using service::OutboundContext;
+ if (HasInboundConvo(addr))
+ {
+ // if we have an inbound convo to this address don't mark as outbound so we don't have a
+ // state race this codepath is hit when an application verifies that reverse and forward
+ // dns records match for an inbound session
+ SendDNSReply(addr, this, msg, reply, isV6);
+ return true;
+ }
MarkAddressOutbound(addr);
return EnsurePathToService(
addr,
@@ -319,6 +432,7 @@ namespace llarp
service::Address addr, auto msg) -> bool {
using service::Address;
using service::OutboundContext;
+ // TODO: how do we handle SRV record lookups for inbound sessions?
MarkAddressOutbound(addr);
return EnsurePathToService(
addr,
@@ -841,7 +955,8 @@ namespace llarp
llarp::LogError(Name(), " failed to set up network interface");
return false;
}
- if (!m_Resolver->Start(m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers))
+ if (!m_Resolver->Start(
+ m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers, m_hostfiles))
{
llarp::LogError(Name(), " failed to start DNS server");
return false;
@@ -858,6 +973,27 @@ namespace llarp
bool
TunEndpoint::Stop()
{
+ // save address map if applicable
+ if (m_PersistAddrMapFile)
+ {
+ const auto& file = *m_PersistAddrMapFile;
+ LogInfo(Name(), " saving address map to ", file);
+ if (auto maybe = util::OpenFileStream(file, std::ios_base::binary))
+ {
+ std::map addrmap;
+ for (const auto& [ip, addr] : m_IPToAddr)
+ {
+ if (not m_SNodes.at(addr))
+ {
+ const service::Address a{addr.as_array()};
+ if (HasInboundConvo(a))
+ addrmap[ip.ToString()] = a.ToString();
+ }
+ }
+ const auto data = oxenmq::bt_serialize(addrmap);
+ maybe->write(data.data(), data.size());
+ }
+ }
if (m_Resolver)
m_Resolver->Stop();
return llarp::service::Endpoint::Stop();
@@ -973,10 +1109,13 @@ namespace llarp
}
// try sending it on an existing convotag
// this succeds for inbound convos, probably.
- if (SendToOrQueue(to, pkt.ConstBuffer(), type))
+ if (auto maybe = GetBestConvoTagFor(to))
{
- MarkIPActive(dst);
- return;
+ if (SendToOrQueue(*maybe, pkt.ConstBuffer(), type))
+ {
+ MarkIPActive(dst);
+ return;
+ }
}
// try establishing a path to this guy
// will fail if it's an inbound convo
diff --git a/llarp/handlers/tun.hpp b/llarp/handlers/tun.hpp
index 29daf8fccd..4be9ba9091 100644
--- a/llarp/handlers/tun.hpp
+++ b/llarp/handlers/tun.hpp
@@ -265,7 +265,9 @@ namespace llarp
/// our ip range we are using
llarp::IPRange m_OurRange;
/// upstream dns resolver list
- std::vector m_UpstreamResolvers;
+ std::vector m_UpstreamResolvers;
+ /// dns host files list
+ std::vector m_hostfiles;
/// local dns
IpAddress m_LocalResolverAddr;
/// list of strict connect addresses for hooks
@@ -290,6 +292,9 @@ namespace llarp
/// idempotent wakeup for writing messages to network
std::shared_ptr m_MessageSendWaker;
+
+ /// a file to load / store the ephemeral address map to
+ std::optional m_PersistAddrMapFile;
};
} // namespace handlers
diff --git a/llarp/link/server.cpp b/llarp/link/server.cpp
index 6e40b0df44..b8c9f748f9 100644
--- a/llarp/link/server.cpp
+++ b/llarp/link/server.cpp
@@ -481,10 +481,9 @@ namespace llarp
bool
ILinkLayer::PutSession(const std::shared_ptr& s)
{
- static constexpr size_t MaxSessionsPerEndpoint = 5;
Lock_t lock(m_PendingMutex);
const auto address = s->GetRemoteEndpoint();
- if (m_Pending.count(address) >= MaxSessionsPerEndpoint)
+ if (m_Pending.count(address))
return false;
m_Pending.emplace(address, s);
return true;
diff --git a/llarp/link/server.hpp b/llarp/link/server.hpp
index cc3b5e84e5..e58e4da56a 100644
--- a/llarp/link/server.hpp
+++ b/llarp/link/server.hpp
@@ -245,7 +245,7 @@ namespace llarp
SecretKey m_SecretKey;
using AuthedLinks = std::unordered_multimap>;
- using Pending = std::unordered_multimap>;
+ using Pending = std::unordered_map>;
mutable DECLARE_LOCK(Mutex_t, m_AuthedLinksMutex, ACQUIRED_BEFORE(m_PendingMutex));
AuthedLinks m_AuthedLinks GUARDED_BY(m_AuthedLinksMutex);
mutable DECLARE_LOCK(Mutex_t, m_PendingMutex, ACQUIRED_AFTER(m_AuthedLinksMutex));
diff --git a/llarp/net/sock_addr.cpp b/llarp/net/sock_addr.cpp
index 85098d2eab..2fd6ddf5a2 100644
--- a/llarp/net/sock_addr.cpp
+++ b/llarp/net/sock_addr.cpp
@@ -75,10 +75,10 @@ namespace llarp
init();
fromString(addr);
}
- SockAddr::SockAddr(std::string_view addr, uint16_t port)
+ SockAddr::SockAddr(std::string_view addr, huint16_t port)
{
init();
- setPort(huint16_t{port});
+ setPort(port);
fromString(addr, false);
}
@@ -289,7 +289,15 @@ namespace llarp
// TODO: review
if (isEmpty())
return "";
+ std::string str = hostString();
+ str.append(1, ':');
+ str.append(std::to_string(getPort()));
+ return str;
+ }
+ std::string
+ SockAddr::hostString() const
+ {
std::string str;
if (isIPv4())
@@ -313,9 +321,6 @@ namespace llarp
str.append(buf);
str.append("]");
}
-
- str.append(1, ':');
- str.append(std::to_string(getPort()));
return str;
}
diff --git a/llarp/net/sock_addr.hpp b/llarp/net/sock_addr.hpp
index 9078aa0152..2e18451a2b 100644
--- a/llarp/net/sock_addr.hpp
+++ b/llarp/net/sock_addr.hpp
@@ -40,7 +40,7 @@ namespace llarp
// String ctors
SockAddr(std::string_view addr);
- SockAddr(std::string_view addr, uint16_t port); // port is in native (host) order
+ SockAddr(std::string_view addr, huint16_t port); // port is in native (host) order
SockAddr(const AddressInfo&);
@@ -83,6 +83,9 @@ namespace llarp
std::string
toString() const;
+ std::string
+ hostString() const;
+
/// Returns true if this is an empty SockAddr, defined by having no IP address set. An empty IP
/// address with a valid port is still considered empty.
///
diff --git a/llarp/quic/tunnel.cpp b/llarp/quic/tunnel.cpp
index 588dc4efcf..fa5e220298 100644
--- a/llarp/quic/tunnel.cpp
+++ b/llarp/quic/tunnel.cpp
@@ -485,7 +485,7 @@ namespace llarp::quic
}
auto bound = tcp_tunnel->sock();
- saddr = SockAddr{bound.ip, static_cast(bound.port)};
+ saddr = SockAddr{bound.ip, huint16_t{static_cast(bound.port)}};
// Find the first unused psuedo-port value starting from next_pseudo_port_.
if (auto p = find_unused_key(client_tunnels_, next_pseudo_port_))
diff --git a/llarp/router/route_poker.cpp b/llarp/router/route_poker.cpp
index a49eec7f7c..29818e8404 100644
--- a/llarp/router/route_poker.cpp
+++ b/llarp/router/route_poker.cpp
@@ -163,7 +163,7 @@ namespace llarp
systemd_resolved_set_dns(
m_Router->hiddenServiceContext().GetDefault()->GetIfName(),
- m_Router->GetConfig()->dns.m_bind.createSockAddr(),
+ m_Router->GetConfig()->dns.m_bind,
true /* route all DNS */);
}
@@ -178,7 +178,7 @@ namespace llarp
systemd_resolved_set_dns(
m_Router->hiddenServiceContext().GetDefault()->GetIfName(),
- m_Router->GetConfig()->dns.m_bind.createSockAddr(),
+ m_Router->GetConfig()->dns.m_bind,
false /* route DNS only for .loki/.snode */);
}
diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp
index f995a3b842..796d9e5361 100644
--- a/llarp/service/endpoint.cpp
+++ b/llarp/service/endpoint.cpp
@@ -400,7 +400,7 @@ namespace llarp
{
for (const auto& item : Sessions())
{
- if (item.second.remote.Addr() == addr && item.second.inbound)
+ if (item.second.remote.Addr() == addr and item.second.inbound)
return true;
}
return false;
@@ -420,9 +420,24 @@ namespace llarp
void
Endpoint::PutSenderFor(const ConvoTag& tag, const ServiceInfo& info, bool inbound)
{
+ if (info.Addr().IsZero())
+ {
+ LogError(Name(), " cannot put invalid service info ", info, " T=", tag);
+ return;
+ }
auto itr = Sessions().find(tag);
if (itr == Sessions().end())
{
+ if (WantsOutboundSession(info.Addr()) and inbound)
+ {
+ LogWarn(
+ Name(),
+ " not adding sender for ",
+ info.Addr(),
+ " session is inbound and we want outbound T=",
+ tag);
+ return;
+ }
itr = Sessions().emplace(tag, Session{}).first;
itr->second.inbound = inbound;
itr->second.remote = info;
@@ -587,7 +602,10 @@ namespace llarp
bool
Endpoint::PublishIntroSet(const EncryptedIntroSet& introset, AbstractRouter* r)
{
- const auto paths = GetManyPathsWithUniqueEndpoints(this, llarp::dht::IntroSetRelayRedundancy);
+ const auto paths = GetManyPathsWithUniqueEndpoints(
+ this,
+ llarp::dht::IntroSetRelayRedundancy,
+ dht::Key_t{introset.derivedSigningKey.as_array()});
if (paths.size() != llarp::dht::IntroSetRelayRedundancy)
{
@@ -917,6 +935,7 @@ namespace llarp
});
constexpr size_t min_unique_lns_endpoints = 2;
+ constexpr size_t max_unique_lns_endpoints = 7;
// not enough paths
if (paths.size() < min_unique_lns_endpoints)
@@ -951,10 +970,16 @@ namespace llarp
handler(result);
};
+ // pick up to max_unique_lns_endpoints random paths to do lookups from
+ std::vector chosenpaths;
+ chosenpaths.insert(chosenpaths.begin(), paths.begin(), paths.end());
+ std::shuffle(chosenpaths.begin(), chosenpaths.end(), CSRNG{});
+ chosenpaths.resize(std::min(paths.size(), max_unique_lns_endpoints));
+
auto resultHandler =
- m_state->lnsTracker.MakeResultHandler(name, paths.size(), maybeInvalidateCache);
+ m_state->lnsTracker.MakeResultHandler(name, chosenpaths.size(), maybeInvalidateCache);
- for (const auto& path : paths)
+ for (const auto& path : chosenpaths)
{
LogInfo(Name(), " lookup ", name, " from ", path->Endpoint());
auto job = new LookupNameJob{this, GenTXID(), name, resultHandler};
@@ -1075,18 +1100,15 @@ namespace llarp
bool
Endpoint::HandleDataMessage(
- path::Path_ptr path, const PathID_t from, std::shared_ptr msg)
+ path::Path_ptr, const PathID_t from, std::shared_ptr msg)
{
- msg->sender.UpdateAddr();
PutSenderFor(msg->tag, msg->sender, true);
- PutReplyIntroFor(msg->tag, path->intro);
- Introduction intro;
- intro.pathID = from;
- intro.router = PubKey{path->Endpoint()};
- intro.expiresAt = std::min(path->ExpireTime(), msg->introReply.expiresAt);
- intro.latency = path->intro.latency;
- PutIntroFor(msg->tag, intro);
- PutReplyIntroFor(msg->tag, path->intro);
+ Introduction intro = msg->introReply;
+ if (HasInboundConvo(msg->sender.Addr()))
+ {
+ intro.pathID = from;
+ }
+ PutReplyIntroFor(msg->tag, intro);
ConvoTagRX(msg->tag);
return ProcessDataMessage(msg);
}
@@ -1429,7 +1451,7 @@ namespace llarp
if (not m_IntrosetLookupFilter.Insert(remote))
return true;
- const auto paths = GetManyPathsWithUniqueEndpoints(this, NumParallelLookups, remote.ToKey());
+ const auto paths = GetManyPathsWithUniqueEndpoints(this, NumParallelLookups);
using namespace std::placeholders;
const dht::Key_t location = remote.ToKey();
@@ -1453,7 +1475,7 @@ namespace llarp
path->Endpoint(),
order,
GenTXID(),
- timeout + (2 * path->intro.latency));
+ timeout + (2 * path->intro.latency) + IntrosetLookupGraceInterval);
LogInfo(
"doing lookup for ",
remote,
@@ -1791,12 +1813,9 @@ namespace llarp
LogTrace("SendToOrQueue: dropping because data.sz == 0");
return false;
}
-
- // inbound conversation
- const auto now = Now();
-
if (HasInboundConvo(remote))
{
+ // inbound conversation
LogTrace("Have inbound convo");
auto transfer = std::make_shared();
ProtocolFrame& f = transfer->T;
@@ -1805,87 +1824,86 @@ namespace llarp
if (const auto maybe = GetBestConvoTagFor(remote))
{
// the remote guy's intro
- Introduction remoteIntro;
- Introduction replyPath;
+ Introduction replyIntro;
SharedSecret K;
const auto tag = *maybe;
- if (!GetCachedSessionKeyFor(tag, K))
+ if (not GetCachedSessionKeyFor(tag, K))
{
- LogError("no cached key for T=", tag);
+ LogError(Name(), " no cached key for inbound session from ", remote, " T=", tag);
return false;
}
- if (!GetIntroFor(tag, remoteIntro))
+ if (not GetReplyIntroFor(tag, replyIntro))
{
- LogError("no intro for T=", tag);
+ LogError(Name(), "no reply intro for inbound session from ", remote, " T=", tag);
return false;
}
- if (GetReplyIntroFor(tag, replyPath))
+ // get path for intro
+ auto p = GetPathByRouter(replyIntro.router);
+
+ if (not p)
{
- // get path for intro
- ForEachPath([&](path::Path_ptr path) {
- if (path->intro == replyPath)
- {
- p = path;
- return;
- }
- if (p && p->ExpiresSoon(now) && path->IsReady()
- && path->intro.router == replyPath.router)
- {
- p = path;
- }
- });
+ LogWarn(
+ Name(),
+ " has no path for intro router ",
+ RouterID{replyIntro.router},
+ " for inbound convo T=",
+ tag);
+ return false;
}
- else
- p = GetPathByRouter(remoteIntro.router);
- if (p)
+ f.T = tag;
+ // TODO: check expiration of our end
+ auto m = std::make_shared(f.T);
+ m->PutBuffer(data);
+ f.N.Randomize();
+ f.C.Zero();
+ f.R = 0;
+ transfer->Y.Randomize();
+ m->proto = t;
+ m->introReply = p->intro;
+ m->sender = m_Identity.pub;
+ if (auto maybe = GetSeqNoForConvo(f.T))
{
- f.T = tag;
- // TODO: check expiration of our end
- auto m = std::make_shared(f.T);
- m->PutBuffer(data);
- f.N.Randomize();
- f.C.Zero();
- f.R = 0;
- transfer->Y.Randomize();
- m->proto = t;
- m->introReply = p->intro;
- PutReplyIntroFor(f.T, m->introReply);
- m->sender = m_Identity.pub;
- if (auto maybe = GetSeqNoForConvo(f.T))
- {
- m->seqno = *maybe;
- }
- else
- {
- LogWarn(Name(), " no session T=", f.T);
- return false;
- }
- f.S = m->seqno;
- f.F = m->introReply.pathID;
- transfer->P = remoteIntro.pathID;
- auto self = this;
- Router()->QueueWork([transfer, p, m, K, self]() {
- if (not transfer->T.EncryptAndSign(*m, K, self->m_Identity))
- {
- LogError("failed to encrypt and sign");
- return;
- }
- self->m_SendQueue.tryPushBack(SendEvent_t{transfer, p});
- });
- return true;
+ m->seqno = *maybe;
}
else
{
- LogTrace("SendToOrQueue failed to return via inbound: no path");
+ LogWarn(Name(), " could not set sequence number, no session T=", f.T);
+ return false;
}
+ f.S = m->seqno;
+ f.F = p->intro.pathID;
+ transfer->P = replyIntro.pathID;
+ auto self = this;
+ Router()->QueueWork([transfer, p, m, K, self]() {
+ if (not transfer->T.EncryptAndSign(*m, K, self->m_Identity))
+ {
+ LogError("failed to encrypt and sign for sessionn T=", transfer->T.T);
+ return;
+ }
+ self->m_SendQueue.tryPushBack(SendEvent_t{transfer, p});
+ });
+ return true;
}
else
{
- LogWarn("Have inbound convo from ", remote, " but get-best returned none; bug?");
+ LogWarn(
+ Name(),
+ " SendToOrQueue on inbound convo from ",
+ remote,
+ " but get-best returned none; bug?");
}
}
+ if (not WantsOutboundSession(remote))
+ {
+ LogWarn(
+ Name(),
+ " SendToOrQueue on outbound session we did not mark as outbound (remote=",
+ remote,
+ ")");
+ return false;
+ }
// Failed to find a suitable inbound convo, look for outbound
LogTrace("Not an inbound convo");
@@ -1900,34 +1918,28 @@ namespace llarp
return true;
}
}
- // if we want to make an outbound session
- if (WantsOutboundSession(remote))
- {
- LogTrace("Making an outbound session and queuing the data");
- // add pending traffic
- auto& traffic = m_state->m_PendingTraffic;
- traffic[remote].emplace_back(data, t);
- EnsurePathToService(
- remote,
- [self = this](Address addr, OutboundContext* ctx) {
- if (ctx)
- {
- for (auto& pending : self->m_state->m_PendingTraffic[addr])
- {
- ctx->AsyncEncryptAndSendTo(pending.Buffer(), pending.protocol);
- }
- }
- else
+ LogTrace("Making an outbound session and queuing the data");
+ // add pending traffic
+ auto& traffic = m_state->m_PendingTraffic;
+ traffic[remote].emplace_back(data, t);
+ EnsurePathToService(
+ remote,
+ [self = this](Address addr, OutboundContext* ctx) {
+ if (ctx)
+ {
+ for (auto& pending : self->m_state->m_PendingTraffic[addr])
{
- LogWarn("no path made to ", addr);
+ ctx->AsyncEncryptAndSendTo(pending.Buffer(), pending.protocol);
}
- self->m_state->m_PendingTraffic.erase(addr);
- },
- PathAlignmentTimeout());
- return true;
- }
- LogDebug("SendOrQueue failed: no inbound/outbound sessions");
- return false;
+ }
+ else
+ {
+ LogWarn("no path made to ", addr);
+ }
+ self->m_state->m_PendingTraffic.erase(addr);
+ },
+ PathAlignmentTimeout());
+ return true;
}
bool
diff --git a/llarp/service/hidden_service_address_lookup.hpp b/llarp/service/hidden_service_address_lookup.hpp
index 2e656a8853..164b365d28 100644
--- a/llarp/service/hidden_service_address_lookup.hpp
+++ b/llarp/service/hidden_service_address_lookup.hpp
@@ -8,6 +8,9 @@ namespace llarp
{
namespace service
{
+ /// interval for which we will add to lookup timeout interval
+ constexpr auto IntrosetLookupGraceInterval = 20s;
+
struct Endpoint;
struct HiddenServiceAddressLookup : public IServiceLookup
{
diff --git a/llarp/service/outbound_context.cpp b/llarp/service/outbound_context.cpp
index 85ebbb8695..12042b1e21 100644
--- a/llarp/service/outbound_context.cpp
+++ b/llarp/service/outbound_context.cpp
@@ -49,6 +49,7 @@ namespace llarp
ShiftIntroduction(false);
UpdateIntroSet();
SwapIntros();
+ markedBad = remoteIntro.IsExpired(Now());
}
return true;
}
@@ -151,17 +152,17 @@ namespace llarp
return false;
if (remoteIntro.router.IsZero())
return false;
- return IntroSent();
+ return IntroSent() and GetPathByRouter(remoteIntro.router);
}
void
OutboundContext::ShiftIntroRouter(const RouterID r)
{
const auto now = Now();
- Introduction selectedIntro;
+ Introduction selectedIntro{};
for (const auto& intro : currentIntroSet.intros)
{
- if (intro.expiresAt > selectedIntro.expiresAt && intro.router != r)
+ if (intro.expiresAt > selectedIntro.expiresAt and intro.router != r)
{
selectedIntro = intro;
}
@@ -194,17 +195,26 @@ namespace llarp
OutboundContext::HandlePathBuilt(path::Path_ptr p)
{
path::Builder::HandlePathBuilt(p);
- /// don't use it if we are marked bad
+ p->SetDataHandler([self = weak_from_this()](auto path, auto frame) {
+ if (auto ptr = self.lock())
+ return ptr->HandleHiddenServiceFrame(path, frame);
+ return false;
+ });
+ p->SetDropHandler([self = weak_from_this()](auto path, auto id, auto seqno) {
+ if (auto ptr = self.lock())
+ return ptr->HandleDataDrop(path, id, seqno);
+ return false;
+ });
if (markedBad)
- return;
- p->SetDataHandler(util::memFn(&OutboundContext::HandleHiddenServiceFrame, this));
- p->SetDropHandler(util::memFn(&OutboundContext::HandleDataDrop, this));
- // we now have a path to the next intro, swap intros
- if (p->Endpoint() == m_NextIntro.router)
- SwapIntros();
- else
{
- LogInfo(Name(), " built to non aligned router: ", p->Endpoint());
+ // ignore new path if we are marked dead
+ LogInfo(Name(), " marked bad, ignoring new path");
+ p->EnterState(path::ePathIgnore, Now());
+ }
+ else if (p->Endpoint() == m_NextIntro.router)
+ {
+ // we now have a path to the next intro, swap intros
+ SwapIntros();
}
}
@@ -289,7 +299,7 @@ namespace llarp
path->Endpoint(),
relayOrder,
m_Endpoint->GenTXID(),
- (IntrosetUpdateInterval / 2) + (2 * path->intro.latency));
+ (IntrosetUpdateInterval / 2) + (2 * path->intro.latency) + IntrosetLookupGraceInterval);
relayOrder++;
if (job->SendRequestViaPath(path, m_Endpoint->Router()))
updatingIntroSet = true;
@@ -314,11 +324,6 @@ namespace llarp
obj["currentRemoteIntroset"] = currentIntroSet.ExtractStatus();
obj["nextIntro"] = m_NextIntro.ExtractStatus();
obj["readyToSend"] = ReadyToSend();
- std::transform(
- m_BadIntros.begin(),
- m_BadIntros.end(),
- std::back_inserter(obj["badIntros"]),
- [](const auto& item) -> util::StatusObject { return item.first.ExtractStatus(); });
return obj;
}
@@ -337,10 +342,14 @@ namespace llarp
{
SwapIntros();
}
-
- if ((remoteIntro.router.IsZero() or m_BadIntros.count(remoteIntro))
- and GetPathByRouter(m_NextIntro.router))
- SwapIntros();
+ if (ReadyToSend())
+ {
+ // if we dont have a cached session key after sending intro we are in a fugged state so
+ // expunge
+ SharedSecret discardme;
+ if (not m_DataHandler->GetCachedSessionKeyFor(currentConvoTag, discardme))
+ return true;
+ }
if (m_GotInboundTraffic and m_LastInboundTraffic + sendTimeout <= now)
{
@@ -351,22 +360,36 @@ namespace llarp
}
// check for stale intros
// update the introset if we think we need to
- if (currentIntroSet.HasStaleIntros(now, path::intro_path_spread))
+ if (currentIntroSet.HasStaleIntros(now, path::intro_path_spread)
+ or remoteIntro.ExpiresSoon(now, path::intro_path_spread))
{
UpdateIntroSet();
+ ShiftIntroduction(false);
+ }
+
+ if (ReadyToSend())
+ {
+ if (not remoteIntro.router.IsZero() and not GetPathByRouter(remoteIntro.router))
+ {
+ // pick another good intro if we have no path on our current intro
+ std::vector otherIntros;
+ ForEachPath([now, router = remoteIntro.router, &otherIntros](auto path) {
+ if (path and path->IsReady() and path->Endpoint() != router
+ and not path->ExpiresSoon(now, path::intro_path_spread))
+ {
+ otherIntros.emplace_back(path->intro);
+ }
+ });
+ if (not otherIntros.empty())
+ {
+ std::shuffle(otherIntros.begin(), otherIntros.end(), CSRNG{});
+ remoteIntro = otherIntros[0];
+ }
+ }
}
// lookup router in intro if set and unknown
if (not m_NextIntro.router.IsZero())
m_Endpoint->EnsureRouterIsKnown(m_NextIntro.router);
- // expire bad intros
- auto itr = m_BadIntros.begin();
- while (itr != m_BadIntros.end())
- {
- if (now > itr->second && now - itr->second > path::default_lifetime)
- itr = m_BadIntros.erase(itr);
- else
- ++itr;
- }
if (ReadyToSend() and not m_ReadyHooks.empty())
{
@@ -386,6 +409,8 @@ namespace llarp
{
// send a keep alive to keep this session alive
KeepAlive();
+ if (markedBad)
+ return true;
}
// if we are dead return true so we are removed
return timeout > 0s ? (now >= timeout && now - timeout > sendTimeout)
@@ -433,13 +458,18 @@ namespace llarp
return false;
size_t numValidPaths = 0;
- ForEachPath([now, &numValidPaths](path::Path_ptr path) {
+ bool havePathToNextIntro = false;
+ ForEachPath([now, this, &havePathToNextIntro, &numValidPaths](path::Path_ptr path) {
if (not path->IsReady())
return;
if (not path->intro.ExpiresSoon(now, path::default_lifetime - path::intro_path_spread))
+ {
numValidPaths++;
+ if (path->intro.router == m_NextIntro.router)
+ havePathToNextIntro = true;
+ }
});
- return numValidPaths < numDesiredPaths;
+ return numValidPaths < numDesiredPaths or not havePathToNextIntro;
}
void
@@ -449,11 +479,8 @@ namespace llarp
}
void
- OutboundContext::MarkIntroBad(const Introduction& intro, llarp_time_t now)
- {
- // insert bad intro
- m_BadIntros[intro] = now;
- }
+ OutboundContext::MarkIntroBad(const Introduction&, llarp_time_t)
+ {}
bool
OutboundContext::IntroSent() const
@@ -488,13 +515,12 @@ namespace llarp
continue;
if (m_Endpoint->SnodeBlacklist().count(intro.router))
continue;
- if (m_BadIntros.find(intro) == m_BadIntros.end() && remoteIntro.router == intro.router)
+ if (remoteIntro.router == intro.router)
{
if (intro.expiresAt > m_NextIntro.expiresAt)
{
success = true;
m_NextIntro = intro;
- return true;
}
}
}
@@ -508,7 +534,7 @@ namespace llarp
m_Endpoint->EnsureRouterIsKnown(intro.router);
if (intro.ExpiresSoon(now))
continue;
- if (m_BadIntros.find(intro) == m_BadIntros.end() && m_NextIntro != intro)
+ if (m_NextIntro != intro)
{
if (intro.expiresAt > m_NextIntro.expiresAt)
{
diff --git a/llarp/service/outbound_context.hpp b/llarp/service/outbound_context.hpp
index 4242e85bcb..8e27500e61 100644
--- a/llarp/service/outbound_context.hpp
+++ b/llarp/service/outbound_context.hpp
@@ -161,7 +161,6 @@ namespace llarp
uint64_t m_UpdateIntrosetTX = 0;
IntroSet currentIntroSet;
Introduction m_NextIntro;
- std::unordered_map m_BadIntros;
llarp_time_t lastShift = 0s;
uint16_t m_LookupFails = 0;
uint16_t m_BuildFails = 0;
diff --git a/llarp/service/protocol.cpp b/llarp/service/protocol.cpp
index 89c6a09aa2..8ecf32ac8c 100644
--- a/llarp/service/protocol.cpp
+++ b/llarp/service/protocol.cpp
@@ -367,9 +367,15 @@ namespace llarp
AuthResult result) {
if (result.code == AuthResultCode::eAuthAccepted)
{
- handler->PutSenderFor(msg->tag, msg->sender, true);
- handler->PutIntroFor(msg->tag, msg->introReply);
- handler->PutReplyIntroFor(msg->tag, fromIntro);
+ if (handler->WantsOutboundSession(msg->sender.Addr()))
+ {
+ handler->PutSenderFor(msg->tag, msg->sender, false);
+ }
+ else
+ {
+ handler->PutSenderFor(msg->tag, msg->sender, true);
+ }
+ handler->PutReplyIntroFor(msg->tag, msg->introReply);
handler->PutCachedSessionKeyFor(msg->tag, sharedKey);
handler->SendAuthResult(path, from, msg->tag, result);
LogInfo("auth okay for T=", msg->tag, " from ", msg->sender.Addr());
diff --git a/llarp/util/fs.hpp b/llarp/util/fs.hpp
index be68057d67..3973f35401 100644
--- a/llarp/util/fs.hpp
+++ b/llarp/util/fs.hpp
@@ -58,9 +58,7 @@ namespace llarp
{
if (EnsurePrivateFile(pathname))
return {};
-
- std::string f = pathname.string();
- return T{pathname, mode};
+ return std::make_optional(pathname, mode);
}
using PathVisitor = std::function;
diff --git a/readme.md b/readme.md
index dc6a7d2860..a1620ac89f 100644
--- a/readme.md
+++ b/readme.md
@@ -40,9 +40,9 @@ You can install these using:
$ sudo apt install lokinet
-if you want to build a dev build you can do the following:
+If you are not on a platform supported by the debian packages or if you want to build a dev build, this is the most "portable" way to do it:
- $ sudo apt install build-essential cmake git libcap-dev libcurl4-openssl-dev libuv1-dev libsodium-dev pkg-config
+ $ sudo apt install build-essential cmake git libcap-dev pkg-config automake libtool
$ git clone --recursive https://github.com/loki-project/loki-network
$ cd loki-network
$ mkdir build
diff --git a/test/net/test_sock_addr.cpp b/test/net/test_sock_addr.cpp
index 48fd2ff392..9102a96edf 100644
--- a/test/net/test_sock_addr.cpp
+++ b/test/net/test_sock_addr.cpp
@@ -31,7 +31,7 @@ TEST_CASE("SockAddr fromString", "[SockAddr]")
CHECK(llarp::SockAddr("255.255.255.255").toString() == "255.255.255.255:0");
CHECK(llarp::SockAddr("255.255.255.255:255").toString() == "255.255.255.255:255");
CHECK(llarp::SockAddr("255.255.255.255:65535").toString() == "255.255.255.255:65535");
- CHECK(llarp::SockAddr("5.6.7.8", 5678).toString() == "5.6.7.8:5678");
+ CHECK(llarp::SockAddr("5.6.7.8", llarp::huint16_t{5678}).toString() == "5.6.7.8:5678");
CHECK_THROWS_WITH(llarp::SockAddr("abcd"), "abcd is not a valid IPv4 address");
@@ -66,7 +66,7 @@ TEST_CASE("SockAddr fromString", "[SockAddr]")
CHECK_THROWS_WITH(llarp::SockAddr("1.2.3.4:1a"), "1a is not a valid port");
- CHECK_THROWS_WITH(llarp::SockAddr("5.6.7.8:1234", 5678), "invalid ip address (port not allowed here): 5.6.7.8:1234");
+ CHECK_THROWS_WITH(llarp::SockAddr("5.6.7.8:1234", llarp::huint16_t{5678}), "invalid ip address (port not allowed here): 5.6.7.8:1234");
}
TEST_CASE("SockAddr from sockaddr_in", "[SockAddr]")