Merge pull request #1681 from oxen-io/dev

v0.9.4
pull/1766/head v0.9.4
Jeff 3 years ago committed by GitHub
commit 4723b532eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

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

18
android/.gitignore vendored

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

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="network.loki.lokinet"
android:installLocation="auto"
android:versionCode="1"
android:versionName="0.8.4">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" /> <!-- normal perm, per https://developer.android.com/guide/topics/permissions/normal-permissions.html -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- normal perm -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light.DarkActionBar"
>
<receiver android:name=".NetworkStateChangeReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
<activity
android:name=".LokiNetActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".LokinetDaemon"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
</application>
</manifest>

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

@ -1,96 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="lokinet" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Insert sdk.dir=... into './local.properties'. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<fail
message="ndk.dir is missing. Insert ndk.dir=... into './local.properties'."
unless="ndk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

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

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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

@ -1,27 +0,0 @@
<LinearLayout android:id="@+id/main_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/vertical_page_margin"
android:paddingLeft="@dimen/horizontal_page_margin"
android:paddingRight="@dimen/horizontal_page_margin"
android:paddingTop="@dimen/vertical_page_margin"
tools:context=".PermsAskerActivity">
<TextView
android:id="@+id/textview_retry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/horizontal_page_margin"
android:visibility="gone"
/>
<Button
android:id="@+id/button_request_write_ext_storage_perms"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Retry requesting VPN"
android:visibility="gone"/>
</LinearLayout>

@ -1,27 +0,0 @@
<LinearLayout android:id="@+id/layout_prompt"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/vertical_page_margin"
android:paddingLeft="@dimen/horizontal_page_margin"
android:paddingRight="@dimen/horizontal_page_margin"
android:paddingTop="@dimen/vertical_page_margin"
tools:context=".PermsAskerActivity">
<TextView
android:id="@+id/textview_explanation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/horizontal_page_margin"
android:text="VPN permissions are required for lokinet usage."
/>
<Button
android:id="@+id/button_ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK"
/>
</LinearLayout>

@ -1,16 +0,0 @@
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".LokiNetActivity">
<item
android:id="@+id/action_start"
android:title="@string/action_start"
android:orderInCategory="98"
/>
<item
android:id="@+id/action_stop"
android:title="@string/action_stop"
android:orderInCategory="99"
/>
</menu>

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">lokinet</string>
<string name="action_start">Start</string>
<string name="action_stop">Stop</string>
<string name="already_stopped">Already stopped</string>
<string name="loaded">lokinet loaded</string>
<string name="starting">lokinet is starting</string>
<string name="jniLibraryLoaded">lokinet: loaded JNI libraries</string>
<string name="startedOkay">lokinet started</string>
<string name="startFailed">lokinet start failed</string>
<string name="stopped">lokinet has stopped</string>
<string name="remaining">remaining</string>
<string name="title_activity_perms_asker_prompt">Prompt</string>
<string name="bootstrap_ok">got bootstrap node info</string>
<string name="bootstrap_fail">failed to bootstrap</string>
<string name="netdb_create_fail">failed to create netdb directory</string>
<string name="vpn_setup_fail">failed to set up vpn tunnel</string>
</resources>

@ -1,16 +0,0 @@
<resources>
<!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
<dimen name="margin_tiny">4dp</dimen>
<dimen name="margin_small">8dp</dimen>
<dimen name="margin_medium">16dp</dimen>
<dimen name="margin_large">32dp</dimen>
<dimen name="margin_huge">64dp</dimen>
<!-- Semantic definitions -->
<dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
<dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
</resources>

@ -1 +0,0 @@
rootProject.name = "lokinet"

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -2,6 +2,7 @@
#include "lokinet_jni_common.hpp"
#include <llarp.hpp>
#include <llarp/config/config.hpp>
#include <llarp/router/abstractrouter.hpp>
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<llarp::Context>(env, self))
{
if (ptr->IsUp())
{
std::promise<std::string> 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());
}
}

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

@ -674,6 +674,22 @@ namespace llarp
m_PathAlignmentTimeout = std::chrono::seconds{val};
});
conf.defineOption<fs::path>(
"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<std::string>("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<fs::path>(
"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<bool>(
"dns",

@ -127,10 +127,7 @@ namespace llarp
std::optional<llarp_time_t> m_PathAlignmentTimeout;
// TODO:
// on-up
// on-down
// on-ready
std::optional<fs::path> m_AddrMapPersistFile;
void
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);
@ -138,8 +135,9 @@ namespace llarp
struct DnsConfig
{
IpAddress m_bind;
std::vector<IpAddress> m_upstreamDNS;
SockAddr m_bind;
std::vector<SockAddr> m_upstreamDNS;
std::vector<fs::path> m_hostfiles;
void
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);

@ -26,9 +26,9 @@ namespace llarp::dns
}
bool
Proxy::Start(SockAddr addr, std::vector<IpAddress> resolvers)
Proxy::Start(SockAddr addr, std::vector<SockAddr> resolvers, std::vector<fs::path> 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<IpAddress> resolvers)
PacketHandler::Start(SockAddr, std::vector<SockAddr> resolvers, std::vector<fs::path> hostfiles)
{
return SetupUnboundResolver(std::move(resolvers));
return SetupUnboundResolver(std::move(resolvers), std::move(hostfiles));
}
bool
PacketHandler::SetupUnboundResolver(std::vector<IpAddress> resolvers)
PacketHandler::SetupUnboundResolver(
std::vector<SockAddr> resolvers, std::vector<fs::path> 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;
}

@ -35,7 +35,10 @@ namespace llarp
virtual ~PacketHandler() = default;
virtual bool
Start(SockAddr localaddr, std::vector<IpAddress> upstreamResolvers);
Start(
SockAddr localaddr,
std::vector<SockAddr> upstreamResolvers,
std::vector<fs::path> hostfiles);
void
Stop();
@ -58,10 +61,10 @@ namespace llarp
HandleUpstreamFailure(const SockAddr& from, const SockAddr& to, Message msg);
bool
SetupUnboundResolver(std::vector<IpAddress> resolvers);
SetupUnboundResolver(std::vector<SockAddr> resolvers, std::vector<fs::path> hostfiles);
IQueryHandler* const m_QueryHandler;
std::set<IpAddress> m_Resolvers;
std::set<SockAddr> m_Resolvers;
std::shared_ptr<UnboundResolver> 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<IpAddress> resolvers) override;
Start(
SockAddr localaddr,
std::vector<SockAddr> upstreamResolvers,
std::vector<fs::path> hostfiles) override;
protected:
void

@ -2,6 +2,8 @@
#include "server.hpp"
#include <llarp/util/buffer.hpp>
#include <sstream>
#include <llarp/util/str.hpp>
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)
{

@ -7,6 +7,7 @@
#include <queue>
#include <llarp/ev/ev.hpp>
#include <llarp/util/fs.hpp>
#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);

@ -302,7 +302,7 @@ namespace llarp::uv
handle->on<uvw::UDPDataEvent>([this](auto& event, auto& /*handle*/) {
on_recv(
*this,
SockAddr{event.sender.ip, static_cast<uint16_t>(event.sender.port)},
SockAddr{event.sender.ip, huint16_t{static_cast<uint16_t>(event.sender.port)}},
OwnedBuffer{std::move(event.data), event.length});
});
}

@ -21,7 +21,7 @@ namespace llarp
: m_Router(r)
, m_Resolver(std::make_shared<dns::Proxy>(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<quic::TunnelManager>(*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;
}

@ -211,8 +211,8 @@ namespace llarp
std::shared_ptr<vpn::NetworkInterface> m_NetIf;
IpAddress m_LocalResolverAddr;
std::vector<IpAddress> m_UpstreamResolvers;
SockAddr m_LocalResolverAddr;
std::vector<SockAddr> m_UpstreamResolvers;
std::shared_ptr<quic::TunnelManager> m_QUIC;

@ -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<char> data;
if (auto maybe = util::OpenFileStream<fs::ifstream>(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<oxenmq::bt_dict>(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<std::string>(&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<service::Address>(&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<RouterID>(&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<fs::ofstream>(file, std::ios_base::binary))
{
std::map<std::string, std::string> 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

@ -265,7 +265,9 @@ namespace llarp
/// our ip range we are using
llarp::IPRange m_OurRange;
/// upstream dns resolver list
std::vector<IpAddress> m_UpstreamResolvers;
std::vector<SockAddr> m_UpstreamResolvers;
/// dns host files list
std::vector<fs::path> 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<EventLoopWakeup> m_MessageSendWaker;
/// a file to load / store the ephemeral address map to
std::optional<fs::path> m_PersistAddrMapFile;
};
} // namespace handlers

@ -481,10 +481,9 @@ namespace llarp
bool
ILinkLayer::PutSession(const std::shared_ptr<ILinkSession>& 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;

@ -245,7 +245,7 @@ namespace llarp
SecretKey m_SecretKey;
using AuthedLinks = std::unordered_multimap<RouterID, std::shared_ptr<ILinkSession>>;
using Pending = std::unordered_multimap<SockAddr, std::shared_ptr<ILinkSession>>;
using Pending = std::unordered_map<SockAddr, std::shared_ptr<ILinkSession>>;
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));

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

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

@ -485,7 +485,7 @@ namespace llarp::quic
}
auto bound = tcp_tunnel->sock();
saddr = SockAddr{bound.ip, static_cast<uint16_t>(bound.port)};
saddr = SockAddr{bound.ip, huint16_t{static_cast<uint16_t>(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_))

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

@ -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<path::Path_ptr> 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<ProtocolMessage> msg)
path::Path_ptr, const PathID_t from, std::shared_ptr<ProtocolMessage> 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<routing::PathTransferMessage>();
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<ProtocolMessage>(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<ProtocolMessage>(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

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

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

@ -161,7 +161,6 @@ namespace llarp
uint64_t m_UpdateIntrosetTX = 0;
IntroSet currentIntroSet;
Introduction m_NextIntro;
std::unordered_map<Introduction, llarp_time_t> m_BadIntros;
llarp_time_t lastShift = 0s;
uint16_t m_LookupFails = 0;
uint16_t m_BuildFails = 0;

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

@ -58,9 +58,7 @@ namespace llarp
{
if (EnsurePrivateFile(pathname))
return {};
std::string f = pathname.string();
return T{pathname, mode};
return std::make_optional<T>(pathname, mode);
}
using PathVisitor = std::function<bool(const fs::path&)>;

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

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

Loading…
Cancel
Save