Browse Source

Merge pull request #1413 from PurpleI2P/openssl

2.28.0
pull/1777/head
orignal 5 years ago committed by GitHub
parent
commit
7866f644d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      ChangeLog
  2. 6
      README.md
  3. 2
      Win32/installer.iss
  4. 4
      android/.gitignore
  5. 1
      android/AndroidManifest.xml
  6. 17
      android/build.gradle
  7. 2
      android/gradle.properties
  8. 4
      android/gradle/wrapper/gradle-wrapper.properties
  9. 2
      android/project.properties
  10. 17
      android/res/menu/options_main.xml
  11. 8
      android/res/values-ru/strings.xml
  12. 8
      android/res/values/strings.xml
  13. 20
      android/src/org/purplei2p/i2pd/ForegroundService.java
  14. 293
      android/src/org/purplei2p/i2pd/I2PDActivity.java
  15. 2
      appveyor.yml
  16. 4
      contrib/docker/Dockerfile
  17. 5
      contrib/rpm/i2pd-git.spec
  18. 5
      contrib/rpm/i2pd.spec
  19. 6
      debian/changelog
  20. 5
      libi2pd/Blinding.cpp
  21. 39
      libi2pd/Datagram.cpp
  22. 20
      libi2pd/Datagram.h
  23. 69
      libi2pd/Destination.cpp
  24. 10
      libi2pd/Destination.h
  25. 95
      libi2pd/LeaseSet.cpp
  26. 26
      libi2pd/LeaseSet.h
  27. 6
      libi2pd/NTCP2.cpp
  28. 8
      libi2pd/NetDb.cpp
  29. 10
      libi2pd/SSUSession.cpp
  30. 25
      libi2pd/Streaming.cpp
  31. 6
      libi2pd/Streaming.h
  32. 4
      libi2pd/version.h
  33. 4
      libi2pd_client/AddressBook.cpp
  34. 19
      libi2pd_client/ClientContext.cpp
  35. 2
      libi2pd_client/ClientContext.h
  36. 73
      libi2pd_client/SAM.cpp
  37. 16
      libi2pd_client/SAM.h
  38. 2
      qt/i2pd_qt/.gitignore
  39. 12
      qt/i2pd_qt/DaemonQT.cpp
  40. 1
      qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml
  41. 7
      qt/i2pd_qt/i2pd_qt.pro
  42. 2
      qt/i2pd_qt/logviewermanager.cpp

12
ChangeLog

@ -1,6 +1,18 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.28.0] - 2019-08-27
### Added
- RAW datagrams in SAM
- Publishing encrypted LeaseSet2 with DH or PSH authentication
- Ability to disable battery optimization for Android
- Transport Network ID Check
### Changed
- Set and handle published encrypted flag for LeaseSet2
### Fixed
- ReceiveID changes in the same stream
- "\r\n" command terminator in SAM
## [2.27.0] - 2019-07-03 ## [2.27.0] - 2019-07-03
### Added ### Added
- Support of PSK and DH authentication for encrypted LeaseSet2 - Support of PSK and DH authentication for encrypted LeaseSet2

6
README.md

@ -1,5 +1,6 @@
![GitHub release](https://img.shields.io/github/release/PurpleI2P/i2pd.svg?label=latest%20release) [![GitHub release](https://img.shields.io/github/release/PurpleI2P/i2pd.svg?label=latest%20release)](https://github.com/PurpleI2P/i2pd/releases/latest)
![GitHub](https://img.shields.io/github/license/PurpleI2P/i2pd.svg) [![Snapcraft release](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd)
[![License](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE)
i2pd i2pd
==== ====
@ -66,6 +67,7 @@ Build instructions:
* Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) * Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd)
* CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/) * CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
* Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/) * Docker image - [![Build Status](https://dockerbuildbadges.quelltext.eu/status.svg?organization=meeh&repository=i2pd)](https://hub.docker.com/r/meeh/i2pd/builds/)
* Snap - [![Snap Status](https://build.snapcraft.io/badge/PurpleI2P/i2pd-snap.svg)](https://build.snapcraft.io/user/PurpleI2P/i2pd-snap)
* FreeBSD * FreeBSD
* Android * Android
* iOS * iOS

2
Win32/installer.iss

@ -1,5 +1,5 @@
#define I2Pd_AppName "i2pd" #define I2Pd_AppName "i2pd"
#define I2Pd_ver "2.27.0" #define I2Pd_ver "2.28.0"
#define I2Pd_Publisher "PurpleI2P" #define I2Pd_Publisher "PurpleI2P"
[Setup] [Setup]

4
android/.gitignore vendored

@ -12,5 +12,5 @@ local.properties
build.sh build.sh
android.iml android.iml
build build
*.iml
*.local

1
android/AndroidManifest.xml

@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application <application
android:allowBackup="true" android:allowBackup="true"

17
android/build.gradle

@ -5,7 +5,7 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.3.2' classpath 'com.android.tools.build:gradle:3.4.2'
} }
} }
@ -16,21 +16,22 @@ repositories {
maven { maven {
url 'https://maven.google.com' url 'https://maven.google.com'
} }
google()
} }
dependencies { dependencies {
implementation 'com.android.support:support-compat:28.0.0' implementation 'androidx.core:core:1.0.2'
} }
android { android {
compileSdkVersion 28 compileSdkVersion 29
buildToolsVersion "28.0.3" buildToolsVersion "28.0.3"
defaultConfig { defaultConfig {
applicationId "org.purplei2p.i2pd" applicationId "org.purplei2p.i2pd"
targetSdkVersion 28 targetSdkVersion 29
minSdkVersion 14 minSdkVersion 14
versionCode 2270 versionCode 2280
versionName "2.27.0" versionName "2.28.0"
ndk { ndk {
abiFilters 'armeabi-v7a' abiFilters 'armeabi-v7a'
abiFilters 'x86' abiFilters 'x86'
@ -81,4 +82,8 @@ android {
path './jni/Android.mk' path './jni/Android.mk'
} }
} }
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
} }

2
android/gradle.properties

@ -1 +1,3 @@
android.enableJetifier=true
android.useAndroidX=true
org.gradle.parallel=true org.gradle.parallel=true

4
android/gradle/wrapper/gradle-wrapper.properties vendored

@ -1,6 +1,6 @@
#Thu Mar 14 18:21:08 MSK 2019 #Tue Aug 20 14:39:08 MSK 2019
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

2
android/project.properties

@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target. # Project target.
target=android-28 target=android-29

17
android/res/menu/options_main.xml

@ -3,14 +3,19 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context=".I2PDActivity"> tools:context=".I2PDActivity">
<group android:id="@+id/group_i2pd_control" >
<item
android:id="@+id/action_stop"
android:orderInCategory="99"
android:title="@string/action_stop" />
<item <item
android:id="@+id/action_graceful_stop" android:id="@+id/action_graceful_stop"
android:title="@string/action_graceful_stop"
android:orderInCategory="98" android:orderInCategory="98"
/> android:title="@string/action_graceful_stop" />
</group>
<group android:id="@+id/group_various" >
<item <item
android:id="@+id/action_stop" android:id="@+id/action_battery_otimizations"
android:title="@string/action_stop" android:title="@string/menu_item_battery_optimizations_str" />
android:orderInCategory="99" </group>
/>
</menu> </menu>

8
android/res/values-ru/strings.xml

@ -17,4 +17,12 @@
<string name="remaining">осталось</string> <string name="remaining">осталось</string>
<string name="title_activity_i2_pdperms_asker_prompt">Запрос</string> <string name="title_activity_i2_pdperms_asker_prompt">Запрос</string>
<string name="permDenied">Права для записи на SD карту отклонены, вам необходимо предоставить их для продолжения</string> <string name="permDenied">Права для записи на SD карту отклонены, вам необходимо предоставить их для продолжения</string>
<string name="menu_item_battery_optimizations_str">Оптимизации аккумулятора</string>
<string name="battery_optimizations_enabled">Оптимизации аккумулятора включены</string>
<string name="device_does_not_support_disabling_battery_optimizations">Ваша версия Андроид не поддерживает отключение оптимизаций аккумулятора</string>
<string name="battery_optimizations_enabled_explained">Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\nРекомендуется отключить эти оптимизации.</string>
<string name="battery_optimizations_enabled_dialog" >Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\n\nВам сейчас будет предложено разрешить отключение этих оптимизаций.</string>
<string name="continue_str">Продолжить</string>
<string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Ваша версия Андроид не поддерживает показ диалога об оптимизациях аккумулятора для приложений.</string>
<string name="shutdown_canceled">Плановая остановка отменена</string>
</resources> </resources>

8
android/res/values/strings.xml

@ -17,4 +17,12 @@
<string name="remaining">remaining</string> <string name="remaining">remaining</string>
<string name="title_activity_i2_pdperms_asker_prompt">Prompt</string> <string name="title_activity_i2_pdperms_asker_prompt">Prompt</string>
<string name="permDenied">SD card write permission denied, you need to allow this to continue</string> <string name="permDenied">SD card write permission denied, you need to allow this to continue</string>
<string name="battery_optimizations_enabled">Battery optimizations enabled</string>
<string name="battery_optimizations_enabled_explained">Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\nIt is recommended to allow disabling those battery optimizations.</string>
<string name="battery_optimizations_enabled_dialog" >Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\n\nYou will now be asked to allow to disable those.</string>
<string name="continue_str">Continue</string>
<string name="device_does_not_support_disabling_battery_optimizations">Your Android version does not support opting out of battery optimizations</string>
<string name="menu_item_battery_optimizations_str">Battery Optimizations</string>
<string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Your Android OS version does not support showing the dialog for battery optimizations for applications.</string>
<string name="shutdown_canceled">Planned shutdown canceled</string>
</resources> </resources>

20
android/src/org/purplei2p/i2pd/ForegroundService.java

@ -1,6 +1,5 @@
package org.purplei2p.i2pd; package org.purplei2p.i2pd;
import android.annotation.TargetApi;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
@ -11,10 +10,9 @@ import android.content.Intent;
import android.os.Binder; import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import android.support.v4.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import android.util.Log; import android.util.Log;
import android.widget.Toast;
public class ForegroundService extends Service { public class ForegroundService extends Service {
private static final String TAG="FgService"; private static final String TAG="FgService";
@ -112,14 +110,15 @@ public class ForegroundService extends Service {
// If earlier version channel ID is not used // If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
String channelId = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) ? createNotificationChannel() : ""; String channelId = Build.VERSION.SDK_INT >= 26 ? createNotificationChannel() : "";
// Set the info for the views that show in the notification panel. // Set the info for the views that show in the notification panel.
Notification notification = new NotificationCompat.Builder(this, channelId) NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
.setOngoing(true) .setOngoing(true)
.setSmallIcon(R.drawable.itoopie_notification_icon) // the status icon .setSmallIcon(R.drawable.itoopie_notification_icon); // the status icon
.setPriority(Notification.PRIORITY_DEFAULT) if(Build.VERSION.SDK_INT >= 16) builder = builder.setPriority(Notification.PRIORITY_DEFAULT);
.setCategory(Notification.CATEGORY_SERVICE) if(Build.VERSION.SDK_INT >= 21) builder = builder.setCategory(Notification.CATEGORY_SERVICE);
Notification notification = builder
.setTicker(text) // the status text .setTicker(text) // the status text
.setWhen(System.currentTimeMillis()) // the time stamp .setWhen(System.currentTimeMillis()) // the time stamp
.setContentTitle(getText(R.string.app_name)) // the label of the entry .setContentTitle(getText(R.string.app_name)) // the label of the entry
@ -141,7 +140,8 @@ public class ForegroundService extends Service {
//chan.setLightColor(Color.PURPLE); //chan.setLightColor(Color.PURPLE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager service = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager service = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
service.createNotificationChannel(chan); if(service!=null)service.createNotificationChannel(chan);
else Log.e(TAG, "error: NOTIFICATION_SERVICE is null");
return channelId; return channelId;
} }

293
android/src/org/purplei2p/i2pd/I2PDActivity.java

@ -14,32 +14,44 @@ import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Build; import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.os.IBinder; import android.os.IBinder;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat; import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
// For future package update checking // For future package update checking
import org.purplei2p.i2pd.BuildConfig;
import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS;
public class I2PDActivity extends Activity { public class I2PDActivity extends Activity {
private static final String TAG = "i2pdActvt"; private static final String TAG = "i2pdActvt";
private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1; private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000; public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000;
public static final String PACKAGE_URI_SCHEME = "package:";
private TextView textView; private TextView textView;
private boolean assetsCopied; private boolean assetsCopied;
@ -53,10 +65,7 @@ public class I2PDActivity extends Activity {
public void daemonStateUpdate() public void daemonStateUpdate()
{ {
processAssets(); processAssets();
runOnUiThread(new Runnable(){ runOnUiThread(() -> {
@Override
public void run() {
try { try {
if(textView==null) return; if(textView==null) return;
Throwable tr = daemon.getLastThrowable(); Throwable tr = daemon.getLastThrowable();
@ -65,20 +74,18 @@ public class I2PDActivity extends Activity {
return; return;
} }
DaemonSingleton.State state = daemon.getState(); DaemonSingleton.State state = daemon.getState();
textView.setText( String startResultStr = DaemonSingleton.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : "";
String.valueOf(getText(state.getStatusStringResourceId()))+ String graceStr = DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : "";
(DaemonSingleton.State.startFailed.equals(state) ? ": "+daemon.getDaemonStartResult() : "")+ textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr));
(DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? ": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining) : "")
);
} catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG,"error ignored",tr); Log.e(TAG,"error ignored",tr);
} }
}
}); });
} }
}; };
private static volatile long graceStartedMillis; private static volatile long graceStartedMillis;
private static final Object graceStartedMillis_LOCK=new Object(); private static final Object graceStartedMillis_LOCK=new Object();
private Menu optionsMenu;
private static String formatGraceTimeRemaining() { private static String formatGraceTimeRemaining() {
long remainingSeconds; long remainingSeconds;
@ -92,6 +99,7 @@ public class I2PDActivity extends Activity {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
textView = new TextView(this); textView = new TextView(this);
@ -121,6 +129,8 @@ public class I2PDActivity extends Activity {
} }
rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis); rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis);
} }
openBatteryOptimizationDialogIfNeeded();
} }
@Override @Override
@ -128,7 +138,7 @@ public class I2PDActivity extends Activity {
super.onDestroy(); super.onDestroy();
textView = null; textView = null;
daemon.removeStateChangeListener(daemonStateUpdatedListener); daemon.removeStateChangeListener(daemonStateUpdatedListener);
//cancelGracefulStop(); //cancelGracefulStop0();
try{ try{
doUnbindService(); doUnbindService();
}catch(Throwable tr){ }catch(Throwable tr){
@ -137,24 +147,20 @@ public class I2PDActivity extends Activity {
} }
@Override @Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
switch (requestCode)
{
case MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE:
{ {
if (requestCode == MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
Log.e(TAG, "Memory permission granted"); Log.e(TAG, "WR_EXT_STORAGE perm granted");
else else {
Log.e(TAG, "Memory permission declined"); Log.e(TAG, "WR_EXT_STORAGE perm declined, stopping i2pd");
// TODO: terminate i2pdStop();
return; //TODO must work w/o this perm, ask orignal
} }
default: ;
} }
} }
private static void cancelGracefulStop() { private void cancelGracefulStop0() {
Timer gracefulQuitTimer = getGracefulQuitTimer(); Timer gracefulQuitTimer = getGracefulQuitTimer();
if(gracefulQuitTimer!=null) { if(gracefulQuitTimer!=null) {
gracefulQuitTimer.cancel(); gracefulQuitTimer.cancel();
@ -225,11 +231,17 @@ public class I2PDActivity extends Activity {
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present. // Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.options_main, menu); getMenuInflater().inflate(R.menu.options_main, menu);
menu.findItem(R.id.action_battery_otimizations).setVisible(isBatteryOptimizationsOpenOsDialogApiAvailable());
this.optionsMenu = menu;
return true; return true;
} }
private boolean isBatteryOptimizationsOpenOsDialogApiAvailable() {
return android.os.Build.VERSION.SDK_INT >= 23;
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(@NonNull MenuItem item) {
// Handle action bar item clicks here. The action bar will // Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long // automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml. // as you specify a parent activity in AndroidManifest.xml.
@ -240,36 +252,42 @@ public class I2PDActivity extends Activity {
i2pdStop(); i2pdStop();
return true; return true;
case R.id.action_graceful_stop: case R.id.action_graceful_stop:
synchronized (graceStartedMillis_LOCK) {
if (getGracefulQuitTimer() != null) if (getGracefulQuitTimer() != null)
{ cancelGracefulStop();
item.setTitle(R.string.action_graceful_stop);
i2pdCancelGracefulStop ();
}
else else
{
item.setTitle(R.string.action_cancel_graceful_stop);
i2pdGracefulStop(); i2pdGracefulStop();
} }
return true; return true;
case R.id.action_battery_otimizations:
onActionBatteryOptimizations();
return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
private void i2pdStop() { private void onActionBatteryOptimizations() {
cancelGracefulStop(); if (isBatteryOptimizationsOpenOsDialogApiAvailable()) {
new Thread(new Runnable(){ try {
startActivity(new Intent(ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS));
} catch (ActivityNotFoundException e) {
Log.e(TAG,"BATT_OPTIM_DIALOG_ActvtNotFound", e);
Toast.makeText(this, R.string.os_version_does_not_support_battery_optimizations_show_os_dialog_api, Toast.LENGTH_SHORT).show();
}
}
}
@Override private void i2pdStop() {
public void run() { cancelGracefulStop0();
new Thread(() -> {
Log.d(TAG, "stopping"); Log.d(TAG, "stopping");
try { try {
daemon.stopDaemon(); daemon.stopDaemon();
} catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }
} quit(); //TODO make menu items for starting i2pd. On my Android, I need to reboot the OS to restart i2pd.
},"stop").start(); },"stop").start();
} }
@ -288,10 +306,7 @@ public class I2PDActivity extends Activity {
} }
Toast.makeText(this, R.string.graceful_stop_is_in_progress, Toast.makeText(this, R.string.graceful_stop_is_in_progress,
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
new Thread(new Runnable(){ new Thread(() -> {
@Override
public void run() {
try { try {
Log.d(TAG, "grac stopping"); Log.d(TAG, "grac stopping");
if(daemon.isStartedOkay()) { if(daemon.isStartedOkay()) {
@ -308,25 +323,20 @@ public class I2PDActivity extends Activity {
} catch(Throwable tr) { } catch(Throwable tr) {
Log.e(TAG,"",tr); Log.e(TAG,"",tr);
} }
}
},"gracInit").start(); },"gracInit").start();
} }
private void i2pdCancelGracefulStop() private void cancelGracefulStop()
{
cancelGracefulStop();
Toast.makeText(this, R.string.startedOkay, Toast.LENGTH_SHORT).show();
new Thread(new Runnable()
{
@Override
public void run()
{ {
cancelGracefulStop0();
new Thread(() -> {
try try
{ {
Log.d(TAG, "grac stopping cancel"); Log.d(TAG, "canceling grac stop");
if(daemon.isStartedOkay()) if(daemon.isStartedOkay()) {
daemon.startAcceptingTunnels(); daemon.startAcceptingTunnels();
runOnUiThread(() -> Toast.makeText(this, R.string.shutdown_canceled, Toast.LENGTH_SHORT).show());
}
else else
i2pdStop(); i2pdStop();
} }
@ -334,8 +344,6 @@ public class I2PDActivity extends Activity {
{ {
Log.e(TAG,"",tr); Log.e(TAG,"",tr);
} }
}
},"gracCancel").start(); },"gracCancel").start();
} }
@ -364,8 +372,19 @@ public class I2PDActivity extends Activity {
return gracefulQuitTimer; return gracefulQuitTimer;
} }
private static void setGracefulQuitTimer(Timer gracefulQuitTimer) { private void setGracefulQuitTimer(Timer gracefulQuitTimer) {
I2PDActivity.gracefulQuitTimer = gracefulQuitTimer; I2PDActivity.gracefulQuitTimer = gracefulQuitTimer;
runOnUiThread(()-> {
Menu menu = optionsMenu;
if (menu != null) {
MenuItem item = menu.findItem(R.id.action_graceful_stop);
if (item != null) {
synchronized (graceStartedMillis_LOCK) {
item.setTitle(getGracefulQuitTimer() != null ? R.string.action_cancel_graceful_stop : R.string.action_graceful_stop);
}
}
}
});
} }
/** /**
@ -388,19 +407,22 @@ public class I2PDActivity extends Activity {
// to a file. That doesn't appear to be the case. If the returned array is // to a file. That doesn't appear to be the case. If the returned array is
// null or has 0 length, we assume the path is to a file. This means empty // null or has 0 length, we assume the path is to a file. This means empty
// directories will get turned into files. // directories will get turned into files.
if (contents == null || contents.length == 0) if (contents == null || contents.length == 0) {
throw new IOException(); copyFileAsset(path);
return;
}
// Make the directory. // Make the directory.
File dir = new File(i2pdpath, path); File dir = new File(i2pdpath, path);
dir.mkdirs(); boolean result = dir.mkdirs();
Log.d(TAG, "dir.mkdirs() returned " + result);
// Recurse on the contents. // Recurse on the contents.
for (String entry : contents) { for (String entry : contents) {
copyAsset(path + "/" + entry); copyAsset(path + '/' + entry);
} }
} catch (IOException e) { } catch (IOException e) {
copyFileAsset(path); Log.e(TAG, "ex ignored for path='" + path + "'", e);
} }
} }
@ -413,63 +435,89 @@ public class I2PDActivity extends Activity {
*/ */
private void copyFileAsset(String path) { private void copyFileAsset(String path) {
File file = new File(i2pdpath, path); File file = new File(i2pdpath, path);
if(!file.exists()) try { if(!file.exists()) {
InputStream in = getAssets().open(path); try {
OutputStream out = new FileOutputStream(file); try (InputStream in = getAssets().open(path) ) {
try (OutputStream out = new FileOutputStream(file)) {
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
int read = in.read(buffer); int read = in.read(buffer);
while (read != -1) { while (read != -1) {
out.write(buffer, 0, read); out.write(buffer, 0, read);
read = in.read(buffer); read = in.read(buffer);
} }
out.close(); }
in.close(); }
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "", e); Log.e(TAG, "", e);
} }
} }
}
private void deleteRecursive(File fileOrDirectory) { private void deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) { if (fileOrDirectory.isDirectory()) {
for (File child : fileOrDirectory.listFiles()) { File[] files = fileOrDirectory.listFiles();
if(files!=null) {
for (File child : files) {
deleteRecursive(child); deleteRecursive(child);
} }
} }
fileOrDirectory.delete(); }
boolean deleteResult = fileOrDirectory.delete();
if(!deleteResult)Log.e(TAG, "fileOrDirectory.delete() returned "+deleteResult+", absolute path='"+fileOrDirectory.getAbsolutePath()+"'");
} }
private void processAssets() { private void processAssets() {
if (!assetsCopied) try { if (!assetsCopied) try {
assetsCopied = true; // prevent from running on every state update assetsCopied = true; // prevent from running on every state update
File holderfile = new File(i2pdpath, "assets.ready"); File holderFile = new File(i2pdpath, "assets.ready");
String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX
StringBuilder text = new StringBuilder(); StringBuilder text = new StringBuilder();
if (holderfile.exists()) try { // if holder file exists, read assets version string if (holderFile.exists()) {
BufferedReader br = new BufferedReader(new FileReader(holderfile)); try { // if holder file exists, read assets version string
FileReader fileReader = new FileReader(holderFile);
try {
BufferedReader br = new BufferedReader(fileReader);
try {
String line; String line;
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
text.append(line); text.append(line);
} }
}finally {
try{
br.close(); br.close();
} catch (IOException e) {
Log.e(TAG, "", e);
} }
catch (IOException e) { }
} finally {
try{
fileReader.close();
} catch (IOException e) {
Log.e(TAG, "", e); Log.e(TAG, "", e);
}
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
} }
// if version differs from current app version or null, try to delete certificates folder // if version differs from current app version or null, try to delete certificates folder
if (!text.toString().contains(versionName)) try { if (!text.toString().contains(versionName)) try {
holderfile.delete(); boolean deleteResult = holderFile.delete();
File certpath = new File(i2pdpath, "certificates"); if(!deleteResult)Log.e(TAG, "holderFile.delete() returned "+deleteResult+", absolute path='"+holderFile.getAbsolutePath()+"'");
deleteRecursive(certpath); File certPath = new File(i2pdpath, "certificates");
deleteRecursive(certPath);
} }
catch (Throwable tr) { catch (Throwable tr) {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }
// copy assets. If processed file exists, it won't be overwrited // copy assets. If processed file exists, it won't be overwritten
copyAsset("addressbook"); copyAsset("addressbook");
copyAsset("certificates"); copyAsset("certificates");
copyAsset("tunnels.d"); copyAsset("tunnels.d");
@ -478,14 +526,95 @@ public class I2PDActivity extends Activity {
copyAsset("tunnels.conf"); copyAsset("tunnels.conf");
// update holder file about successful copying // update holder file about successful copying
FileWriter writer = new FileWriter(holderfile); FileWriter writer = new FileWriter(holderFile);
try {
writer.append(versionName); writer.append(versionName);
writer.flush(); } finally {
try{
writer.close(); writer.close();
}catch (IOException e){
Log.e(TAG,"on writer close", e);
}
}
} }
catch (Throwable tr) catch (Throwable tr)
{ {
Log.e(TAG,"copy assets",tr); Log.e(TAG,"on assets copying", tr);
}
}
@SuppressLint("BatteryLife")
private void openBatteryOptimizationDialogIfNeeded() {
boolean questionEnabled = getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true);
Log.i(TAG,"BATT_OPTIM_questionEnabled=="+questionEnabled);
if (!isKnownIgnoringBatteryOptimizations()
&& android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M
&& questionEnabled) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.battery_optimizations_enabled);
builder.setMessage(R.string.battery_optimizations_enabled_dialog);
builder.setPositiveButton(R.string.continue_str, (dialog, which) -> {
try {
startActivity(new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse(PACKAGE_URI_SCHEME + getPackageName())));
} catch (ActivityNotFoundException e) {
Log.e(TAG,"BATT_OPTIM_ActvtNotFound", e);
Toast.makeText(this, R.string.device_does_not_support_disabling_battery_optimizations, Toast.LENGTH_SHORT).show();
}
});
builder.setOnDismissListener(dialog -> setNeverAskForBatteryOptimizationsAgain());
final AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
}
private void setNeverAskForBatteryOptimizationsAgain() {
getPreferences().edit().putBoolean(getBatteryOptimizationPreferenceKey(), false).apply();
}
protected boolean isKnownIgnoringBatteryOptimizations() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (pm == null) {
Log.i(TAG, "BATT_OPTIM: POWER_SERVICE==null");
return false;
}
boolean ignoring = pm.isIgnoringBatteryOptimizations(getPackageName());
Log.i(TAG, "BATT_OPTIM: ignoring==" + ignoring);
return ignoring;
} else {
Log.i(TAG, "BATT_OPTIM: old sdk version=="+Build.VERSION.SDK_INT);
return false;
}
}
protected SharedPreferences getPreferences() {
return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
}
private String getBatteryOptimizationPreferenceKey() {
@SuppressLint("HardwareIds") String device = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
return "show_battery_optimization" + (device == null ? "" : device);
}
private void quit() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
finishAndRemoveTask();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
finishAffinity();
} else {
//moveTaskToBack(true);
finish();
}
}catch (Throwable tr) {
Log.e(TAG, "", tr);
}
try{
daemon.stopDaemon();
}catch (Throwable tr) {
Log.e(TAG, "", tr);
} }
System.exit(0);
} }
} }

2
appveyor.yml

@ -1,4 +1,4 @@
version: 2.27.0.{build} version: 2.28.0.{build}
pull_requests: pull_requests:
do_not_increment_build_number: true do_not_increment_build_number: true
branches: branches:

4
contrib/docker/Dockerfile

@ -36,8 +36,8 @@ RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib
&& cd /usr/local/bin \ && cd /usr/local/bin \
&& strip i2pd \ && strip i2pd \
&& rm -fr /tmp/build && apk --no-cache --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \ && rm -fr /tmp/build && apk --no-cache --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \
boost-python3 python3 gdbm boost-unit_test_framework boost-python linux-headers boost-prg_exec_monitor \ boost-python3 python3 gdbm boost-unit_test_framework linux-headers boost-prg_exec_monitor \
boost-serialization boost-signals boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \ boost-serialization boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \
libtool g++ gcc pkgconfig libtool g++ gcc pkgconfig
# 2. Adding required libraries to run i2pd to ensure it will run. # 2. Adding required libraries to run i2pd to ensure it will run.

5
contrib/rpm/i2pd-git.spec

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.27.0 Version: 2.28.0
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@ -110,6 +110,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Tue Aug 27 2019 orignal <i2porignal@yandex.ru> - 2.28.0
- update to 2.28.0
* Wed Jul 3 2019 orignal <i2porignal@yandex.ru> - 2.27.0 * Wed Jul 3 2019 orignal <i2porignal@yandex.ru> - 2.27.0
- update to 2.27.0 - update to 2.27.0

5
contrib/rpm/i2pd.spec

@ -1,5 +1,5 @@
Name: i2pd Name: i2pd
Version: 2.27.0 Version: 2.28.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@ -108,6 +108,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Tue Aug 27 2019 orignal <i2porignal@yandex.ru> - 2.28.0
- update to 2.28.0
* Wed Jul 3 2019 orignal <i2porignal@yandex.ru> - 2.27.0 * Wed Jul 3 2019 orignal <i2porignal@yandex.ru> - 2.27.0
- update to 2.27.0 - update to 2.27.0

6
debian/changelog vendored

@ -1,3 +1,9 @@
i2pd (2.28.0-1) unstable; urgency=medium
* updated to version 2.28.0/0.9.42
-- orignal <orignal@i2pmail.org> Tue, 27 Aug 2019 16:00:00 +0000
i2pd (2.27.0-1) unstable; urgency=medium i2pd (2.27.0-1) unstable; urgency=medium
* updated to version 2.27.0/0.9.41 * updated to version 2.27.0/0.9.41

5
libi2pd/Blinding.cpp

@ -139,6 +139,11 @@ namespace data
{ {
uint8_t addr[40]; // TODO: define length from b33 uint8_t addr[40]; // TODO: define length from b33
size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40); size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40);
if (l < 32)
{
LogPrint (eLogError, "Blinding: malformed b33 ", b33);
return;
}
uint32_t checksum = crc32 (0, addr + 3, l - 3); uint32_t checksum = crc32 (0, addr + 3, l - 3);
// checksum is Little Endian // checksum is Little Endian
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16); addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);

39
libi2pd/Datagram.cpp

@ -12,10 +12,8 @@ namespace i2p
namespace datagram namespace datagram
{ {
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner): DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
m_Owner (owner.get()), m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr)
m_Receiver (nullptr)
{ {
m_Identity.FromBase64 (owner->GetIdentity()->ToBase64());
} }
DatagramDestination::~DatagramDestination () DatagramDestination::~DatagramDestination ()
@ -28,14 +26,15 @@ namespace datagram
auto owner = m_Owner; auto owner = m_Owner;
std::vector<uint8_t> v(MAX_DATAGRAM_SIZE); std::vector<uint8_t> v(MAX_DATAGRAM_SIZE);
uint8_t * buf = v.data(); uint8_t * buf = v.data();
auto identityLen = m_Identity.ToBuffer (buf, MAX_DATAGRAM_SIZE); auto localIdentity = m_Owner->GetIdentity ();
auto identityLen = localIdentity->ToBuffer (buf, MAX_DATAGRAM_SIZE);
uint8_t * signature = buf + identityLen; uint8_t * signature = buf + identityLen;
auto signatureLen = m_Identity.GetSignatureLen (); auto signatureLen = localIdentity->GetSignatureLen ();
uint8_t * buf1 = signature + signatureLen; uint8_t * buf1 = signature + signatureLen;
size_t headerLen = identityLen + signatureLen; size_t headerLen = identityLen + signatureLen;
memcpy (buf1, payload, len); memcpy (buf1, payload, len);
if (m_Identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) if (localIdentity->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{ {
uint8_t hash[32]; uint8_t hash[32];
SHA256(buf1, len, hash); SHA256(buf1, len, hash);
@ -49,6 +48,12 @@ namespace datagram
session->SendMsg(msg); session->SendMsg(msg);
} }
void DatagramDestination::SendRawDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
{
auto msg = CreateDataMessage (payload, len, fromPort, toPort, true); // raw
auto session = ObtainSession(identity);
session->SendMsg(msg);
}
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len) void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len)
{ {
@ -82,6 +87,14 @@ namespace datagram
LogPrint (eLogWarning, "Datagram signature verification failed"); LogPrint (eLogWarning, "Datagram signature verification failed");
} }
void DatagramDestination::HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if (m_RawReceiver)
m_RawReceiver (fromPort, toPort, buf, len);
else
LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram");
}
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port) DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port)
{ {
std::lock_guard<std::mutex> lock(m_ReceiversMutex); std::lock_guard<std::mutex> lock(m_ReceiversMutex);
@ -92,18 +105,24 @@ namespace datagram
return r; return r;
} }
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw)
{ {
// unzip it // unzip it
uint8_t uncompressed[MAX_DATAGRAM_SIZE]; uint8_t uncompressed[MAX_DATAGRAM_SIZE];
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE); size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
if (uncompressedLen) if (uncompressedLen)
{
if (isRaw)
HandleRawDatagram (fromPort, toPort, uncompressed, uncompressedLen);
else
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen); HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
}
else else
LogPrint (eLogWarning, "Datagram: decompression failed"); LogPrint (eLogWarning, "Datagram: decompression failed");
} }
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort, bool isRaw)
{ {
auto msg = NewI2NPMessage (); auto msg = NewI2NPMessage ();
uint8_t * buf = msg->GetPayload (); uint8_t * buf = msg->GetPayload ();
@ -114,7 +133,7 @@ namespace datagram
htobe32buf (msg->GetPayload (), size); // length htobe32buf (msg->GetPayload (), size); // length
htobe16buf (buf + 4, fromPort); // source port htobe16buf (buf + 4, fromPort); // source port
htobe16buf (buf + 6, toPort); // destination port htobe16buf (buf + 6, toPort); // destination port
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol
msg->len += size + 4; msg->len += size + 4;
msg->FillI2NPMessageHeader (eI2NPData); msg->FillI2NPMessageHeader (eI2NPData);
} }
@ -170,7 +189,7 @@ namespace datagram
return nullptr; return nullptr;
} }
DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination, DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
const i2p::data::IdentHash & remoteIdent) : const i2p::data::IdentHash & remoteIdent) :
m_LocalDestination(localDestination), m_LocalDestination(localDestination),
m_RemoteIdent(remoteIdent), m_RemoteIdent(remoteIdent),

20
libi2pd/Datagram.h

@ -37,7 +37,7 @@ namespace datagram
class DatagramSession : public std::enable_shared_from_this<DatagramSession> class DatagramSession : public std::enable_shared_from_this<DatagramSession>
{ {
public: public:
DatagramSession(i2p::client::ClientDestination * localDestination, const i2p::data::IdentHash & remoteIdent); DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination, const i2p::data::IdentHash & remoteIdent);
void Start (); void Start ();
void Stop (); void Stop ();
@ -81,7 +81,7 @@ namespace datagram
void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls); void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls);
private: private:
i2p::client::ClientDestination * m_LocalDestination; std::shared_ptr<i2p::client::ClientDestination> m_LocalDestination;
i2p::data::IdentHash m_RemoteIdent; i2p::data::IdentHash m_RemoteIdent;
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet; std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession; std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
@ -99,6 +99,8 @@ namespace datagram
class DatagramDestination class DatagramDestination
{ {
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver; typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
typedef std::function<void (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> RawReceiver;
public: public:
@ -107,7 +109,8 @@ namespace datagram
~DatagramDestination (); ~DatagramDestination ();
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false);
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
void ResetReceiver () { m_Receiver = nullptr; }; void ResetReceiver () { m_Receiver = nullptr; };
@ -115,6 +118,9 @@ namespace datagram
void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; }; void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; };
void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); }; void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); };
void SetRawReceiver (const RawReceiver& receiver) { m_RawReceiver = receiver; };
void ResetRawReceiver () { m_RawReceiver = nullptr; };
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote); std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote);
// clean up stale sessions // clean up stale sessions
@ -124,17 +130,19 @@ namespace datagram
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident); std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort, bool isRaw = false);
void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len); void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len);
void HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
/** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */ /** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */
Receiver FindReceiver(uint16_t port); Receiver FindReceiver(uint16_t port);
private: private:
i2p::client::ClientDestination * m_Owner;
i2p::data::IdentityEx m_Identity; std::shared_ptr<i2p::client::ClientDestination> m_Owner;
Receiver m_Receiver; // default Receiver m_Receiver; // default
RawReceiver m_RawReceiver; // default
std::mutex m_SessionsMutex; std::mutex m_SessionsMutex;
std::map<i2p::data::IdentHash, DatagramSession_ptr > m_Sessions; std::map<i2p::data::IdentHash, DatagramSession_ptr > m_Sessions;
std::mutex m_ReceiversMutex; std::mutex m_ReceiversMutex;

69
libi2pd/Destination.cpp

@ -846,7 +846,7 @@ namespace client
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params): ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (isPublic, params), m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), LeaseSetDestination (isPublic, params), m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
m_DatagramDestination (nullptr), m_RefCounter (0), m_DatagramDestination (nullptr), m_RefCounter (0),
m_ReadyChecker(GetService()) m_ReadyChecker(GetService()), m_AuthType (i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE)
{ {
if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only
@ -868,12 +868,46 @@ namespace client
if (isPublic) if (isPublic)
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created"); LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
// extract streaming params try
{
if (params) if (params)
{ {
// extract streaming params
auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY); auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
if (it != params->end ()) if (it != params->end ())
m_StreamingAckDelay = std::stoi(it->second); m_StreamingAckDelay = std::stoi(it->second);
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
{
// authentication for encrypted LeaseSet
it = params->find (I2CP_PARAM_LEASESET_AUTH_TYPE);
m_AuthType = std::stoi (it->second);
if (m_AuthType > 0)
{
m_AuthKeys = std::make_shared<std::vector<i2p::data::AuthPublicKey> >();
if (m_AuthType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_DH)
ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_DH, params);
else if (m_AuthType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_PSK, params);
else
{
LogPrint (eLogError, "Destination: Unexpected auth type ", m_AuthType);
m_AuthType = 0;
}
if (m_AuthKeys->size ())
LogPrint (eLogInfo, "Destination: ", m_AuthKeys->size (), " auth keys read");
else
{
LogPrint (eLogError, "Destination: No auth keys read for auth type ", m_AuthType);
m_AuthKeys = nullptr;
}
}
}
}
}
catch (std::exception & ex)
{
LogPrint(eLogError, "Destination: unable to parse parameters for destination: ", ex.what());
} }
} }
@ -977,6 +1011,13 @@ namespace client
else else
LogPrint (eLogError, "Destination: Missing datagram destination"); LogPrint (eLogError, "Destination: Missing datagram destination");
break; break;
case PROTOCOL_TYPE_RAW:
// raw datagram
if (m_DatagramDestination)
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length, true);
else
LogPrint (eLogError, "Destination: Missing raw datagram destination");
break;
default: default:
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]); LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
} }
@ -1139,10 +1180,11 @@ namespace client
{ {
// standard LS2 (type 3) first // standard LS2 (type 3) first
auto keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256; auto keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256;
bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2, auto ls2 = std::make_shared<i2p::data::LocalLeaseSet2> (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2,
m_Keys, m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels); m_Keys, m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels, IsPublic (), isPublishedEncrypted);
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) // encrypt if type 5 if (isPublishedEncrypted) // encrypt if type 5
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys); ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys, m_AuthType, m_AuthKeys);
leaseSet = ls2; leaseSet = ls2;
} }
SetLeaseSet (leaseSet); SetLeaseSet (leaseSet);
@ -1161,5 +1203,22 @@ namespace client
LogPrint (eLogError, "Destinations: decryptor is not set"); LogPrint (eLogError, "Destinations: decryptor is not set");
return false; return false;
} }
void ClientDestination::ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params)
{
for (auto it: *params)
if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group))
{
auto pos = it.second.find (':');
if (pos != std::string::npos)
{
i2p::data::AuthPublicKey pubKey;
if (pubKey.FromBase64 (it.second.substr (pos+1)))
m_AuthKeys->push_back (pubKey);
else
LogPrint (eLogError, "Destination: Unexpected auth key ", it.second.substr (pos+1));
}
}
}
} }
} }

10
libi2pd/Destination.h

@ -56,6 +56,9 @@ namespace client
const int DEFAULT_LEASESET_TYPE = 1; const int DEFAULT_LEASESET_TYPE = 1;
const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType"; const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType";
const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64 const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64
const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType";
const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn
const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn
// latency // latency
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min"; const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
@ -131,6 +134,7 @@ namespace client
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet); void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
int GetLeaseSetType () const { return m_LeaseSetType; }; int GetLeaseSetType () const { return m_LeaseSetType; };
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; }; void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
bool IsPublic () const { return m_IsPublic; };
virtual void CleanupDestination () {}; // additional clean up in derived classes virtual void CleanupDestination () {}; // additional clean up in derived classes
// I2CP // I2CP
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0; virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
@ -248,6 +252,9 @@ namespace client
void ScheduleCheckForReady(ReadyPromise * p); void ScheduleCheckForReady(ReadyPromise * p);
void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p); void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p);
#endif #endif
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
private: private:
i2p::data::PrivateKeys m_Keys; i2p::data::PrivateKeys m_Keys;
@ -263,6 +270,9 @@ namespace client
boost::asio::deadline_timer m_ReadyChecker; boost::asio::deadline_timer m_ReadyChecker;
int m_AuthType;
std::shared_ptr<std::vector<i2p::data::AuthPublicKey> > m_AuthKeys;
public: public:
// for HTTP only // for HTTP only

95
libi2pd/LeaseSet.cpp

@ -252,7 +252,7 @@ namespace data
} }
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases): LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases):
LeaseSet (storeLeases), m_StoreType (storeType), m_OrigStoreType (storeType) LeaseSet (storeLeases), m_StoreType (storeType)
{ {
SetBuffer (buf, len); SetBuffer (buf, len);
if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
@ -262,7 +262,7 @@ namespace data
} }
LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret): LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret):
LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_OrigStoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
{ {
ReadFromBufferEncrypted (buf, len, key, secret); ReadFromBufferEncrypted (buf, len, key, secret);
} }
@ -302,6 +302,12 @@ namespace data
return; return;
} }
} }
if (flags & LEASESET2_FLAG_UNPUBLISHED_LEASESET) m_IsPublic = false;
if (flags & LEASESET2_FLAG_PUBLISHED_ENCRYPTED)
{
m_IsPublishedEncrypted = true;
m_IsPublic = true;
}
// type specific part // type specific part
size_t s = 0; size_t s = 0;
switch (m_StoreType) switch (m_StoreType)
@ -741,7 +747,8 @@ namespace data
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey, uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels): std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
bool isPublic, bool isPublishedEncrypted):
LocalLeaseSet (keys.GetPublic (), nullptr, 0) LocalLeaseSet (keys.GetPublic (), nullptr, 0)
{ {
auto identity = keys.GetPublic (); auto identity = keys.GetPublic ();
@ -756,6 +763,12 @@ namespace data
flags |= LEASESET2_FLAG_OFFLINE_KEYS; flags |= LEASESET2_FLAG_OFFLINE_KEYS;
m_BufferLen += keys.GetOfflineSignature ().size (); m_BufferLen += keys.GetOfflineSignature ().size ();
} }
if (isPublishedEncrypted)
{
flags |= LEASESET2_FLAG_PUBLISHED_ENCRYPTED;
isPublic = true;
}
if (!isPublic) flags |= LEASESET2_FLAG_UNPUBLISHED_LEASESET;
m_Buffer = new uint8_t[m_BufferLen + 1]; m_Buffer = new uint8_t[m_BufferLen + 1];
m_Buffer[0] = storeType; m_Buffer[0] = storeType;
@ -809,11 +822,21 @@ namespace data
m_Buffer[0] = storeType; m_Buffer[0] = storeType;
} }
LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType): LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys,
int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys):
LocalLeaseSet2 (ls->GetIdentity ()), m_InnerLeaseSet (ls) LocalLeaseSet2 (ls->GetIdentity ()), m_InnerLeaseSet (ls)
{ {
size_t lenInnerPlaintext = ls->GetBufferLen () + 1, lenOuterPlaintext = lenInnerPlaintext + 32 + 1, size_t lenInnerPlaintext = ls->GetBufferLen () + 1, lenOuterPlaintext = lenInnerPlaintext + 32 + 1;
lenOuterCiphertext = lenOuterPlaintext + 32; uint8_t layer1Flags = 0;
if (authKeys)
{
if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH) layer1Flags |= 0x01; // DH, authentication scheme 0, auth bit 1
else if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_PSK) layer1Flags |= 0x03; // PSK, authentication scheme 1, auth bit 1
if (layer1Flags)
lenOuterPlaintext += 32 + 2 + authKeys->size ()*40; // auth data len
}
size_t lenOuterCiphertext = lenOuterPlaintext + 32;
m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/; m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/;
m_Buffer = new uint8_t[m_BufferLen + 1]; m_Buffer = new uint8_t[m_BufferLen + 1];
m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
@ -823,9 +846,9 @@ namespace data
i2p::util::GetDateString (timestamp, date); i2p::util::GetDateString (timestamp, date);
uint8_t blindedPriv[64], blindedPub[128]; // 64 and 128 max uint8_t blindedPriv[64], blindedPub[128]; // 64 and 128 max
size_t publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub); size_t publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub);
std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKeyType, blindedPriv)); std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv));
auto offset = 1; auto offset = 1;
htobe16buf (m_Buffer + offset, blindedKeyType); offset += 2; // Blinded Public Key Sig Type htobe16buf (m_Buffer + offset, blindedKey.GetBlindedSigType ()); offset += 2; // Blinded Public Key Sig Type
memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key
htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds) htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds)
auto nextMidnight = (timestamp/86400LL + 1)*86400LL; // 86400 = 24*3600 seconds auto nextMidnight = (timestamp/86400LL + 1)*86400LL; // 86400 = 24*3600 seconds
@ -847,12 +870,26 @@ namespace data
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L1K", keys1); i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L1K", keys1);
offset += 32; // outerSalt offset += 32; // outerSalt
uint8_t * outerPlainText = m_Buffer + offset; uint8_t * outerPlainText = m_Buffer + offset;
m_Buffer[offset] = 0; offset++; // flag m_Buffer[offset] = layer1Flags; offset++; // layer 1 flags
// auth data
uint8_t innerInput[68]; // authCookie || subcredential || publishedTimestamp
if (layer1Flags)
{
RAND_bytes (innerInput, 32); // authCookie
CreateClientAuthData (subcredential, authType, authKeys, innerInput, m_Buffer + offset);
offset += 32 + 2 + authKeys->size ()*40; // auth clients
}
// Layer 2 // Layer 2
// keys = HKDF(outerSalt, outerInput, "ELS2_L2K", 44) // keys = HKDF(outerSalt, outerInput, "ELS2_L2K", 44)
uint8_t keys2[64]; // 44 bytes actual data uint8_t keys2[64]; // 44 bytes actual data
RAND_bytes (m_Buffer + offset, 32); // innerSalt = CSRNG(32) RAND_bytes (m_Buffer + offset, 32); // innerSalt = CSRNG(32)
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2); if (layer1Flags)
{
memcpy (innerInput + 32, subcredential, 36); // + subcredential || publishedTimestamp
i2p::crypto::HKDF (m_Buffer + offset, innerInput, 68, "ELS2_L2K", keys2);
}
else
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2); // no authCookie
offset += 32; // innerSalt offset += 32; // innerSalt
m_Buffer[offset] = ls->GetStoreType (); m_Buffer[offset] = ls->GetStoreType ();
memcpy (m_Buffer + offset + 1, ls->GetBuffer (), ls->GetBufferLen ()); memcpy (m_Buffer + offset + 1, ls->GetBuffer (), ls->GetBufferLen ());
@ -880,5 +917,43 @@ namespace data
LogPrint (eLogError, "LeaseSet2: couldn't extract inner layer"); LogPrint (eLogError, "LeaseSet2: couldn't extract inner layer");
} }
void LocalEncryptedLeaseSet2::CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t * authCookie, uint8_t * authData) const
{
if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH)
{
i2p::crypto::X25519Keys ek;
ek.GenerateKeys (); // esk and epk
memcpy (authData, ek.GetPublicKey (), 32); authData += 32; // epk
htobe16buf (authData, authKeys->size ()); authData += 2; // num clients
uint8_t authInput[100]; // sharedSecret || cpk_i || subcredential || publishedTimestamp
memcpy (authInput + 64, subcredential, 36);
for (auto& it: *authKeys)
{
ek.Agree (it, authInput); // sharedSecret = DH(esk, cpk_i)
memcpy (authInput + 32, it, 32);
uint8_t okm[64]; // 52 actual data
i2p::crypto::HKDF (ek.GetPublicKey (), authInput, 100, "ELS2_XCA", okm);
memcpy (authData, okm + 44, 8); authData += 8; // clientID_i
i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i
}
}
else // assume PSK
{
uint8_t authSalt[32];
RAND_bytes (authSalt, 32);
memcpy (authData, authSalt, 32); authData += 32; // authSalt
htobe16buf (authData, authKeys->size ()); authData += 2; // num clients
uint8_t authInput[68]; // authInput = psk_i || subcredential || publishedTimestamp
memcpy (authInput + 32, subcredential, 36);
for (auto& it: *authKeys)
{
memcpy (authInput, it, 32);
uint8_t okm[64]; // 52 actual data
i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm);
memcpy (authData, okm + 44, 8); authData += 8; // clientID_i
i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i
}
}
}
} }
} }

26
libi2pd/LeaseSet.h

@ -79,9 +79,9 @@ namespace data
bool operator== (const LeaseSet& other) const bool operator== (const LeaseSet& other) const
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); }; { return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };
virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; }; virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
virtual uint8_t GetOrigStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
virtual uint32_t GetPublishedTimestamp () const { return 0; }; // should be set for LeaseSet2 only virtual uint32_t GetPublishedTimestamp () const { return 0; }; // should be set for LeaseSet2 only
virtual std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return nullptr; }; virtual std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return nullptr; };
virtual bool IsPublishedEncrypted () const { return false; };
// implements RoutingDestination // implements RoutingDestination
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; }; std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
@ -129,6 +129,8 @@ namespace data
const uint8_t NETDB_STORE_TYPE_META_LEASESET2 = 7; const uint8_t NETDB_STORE_TYPE_META_LEASESET2 = 7;
const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001; const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001;
const uint16_t LEASESET2_FLAG_UNPUBLISHED_LEASESET = 0x0002;
const uint16_t LEASESET2_FLAG_PUBLISHED_ENCRYPTED = 0x0004;
class LeaseSet2: public LeaseSet class LeaseSet2: public LeaseSet
{ {
@ -137,8 +139,9 @@ namespace data
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true); LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true);
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr); // store type 5, called from local netdb only LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr); // store type 5, called from local netdb only
uint8_t GetStoreType () const { return m_StoreType; }; uint8_t GetStoreType () const { return m_StoreType; };
uint8_t GetOrigStoreType () const { return m_OrigStoreType; };
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; }; uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; };
bool IsPublic () const { return m_IsPublic; };
bool IsPublishedEncrypted () const { return m_IsPublishedEncrypted; };
std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return m_TransientVerifier; }; std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return m_TransientVerifier; };
void Update (const uint8_t * buf, size_t len, bool verifySignature); void Update (const uint8_t * buf, size_t len, bool verifySignature);
@ -160,8 +163,9 @@ namespace data
private: private:
uint8_t m_StoreType, m_OrigStoreType; uint8_t m_StoreType;
uint32_t m_PublishedTimestamp = 0; uint32_t m_PublishedTimestamp = 0;
bool m_IsPublic = true, m_IsPublishedEncrypted = false;
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier; std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2 std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2
}; };
@ -227,7 +231,8 @@ namespace data
LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey, uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey,
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels); std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
bool isPublic, bool isPublishedEncrypted = false);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
virtual ~LocalLeaseSet2 () { delete[] m_Buffer; }; virtual ~LocalLeaseSet2 () { delete[] m_Buffer; };
@ -247,17 +252,28 @@ namespace data
size_t m_BufferLen; size_t m_BufferLen;
}; };
const int ENCRYPTED_LEASESET_AUTH_TYPE_NONE = 0;
const int ENCRYPTED_LEASESET_AUTH_TYPE_DH = 1;
const int ENCRYPTED_LEASESET_AUTH_TYPE_PSK = 2;
typedef i2p::data::Tag<32> AuthPublicKey;
class LocalEncryptedLeaseSet2: public LocalLeaseSet2 class LocalEncryptedLeaseSet2: public LocalLeaseSet2
{ {
public: public:
LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519); LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE, std::shared_ptr<std::vector<AuthPublicKey> > authKeys = nullptr);
LocalEncryptedLeaseSet2 (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP LocalEncryptedLeaseSet2 (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
const IdentHash& GetStoreHash () const { return m_StoreHash; }; const IdentHash& GetStoreHash () const { return m_StoreHash; };
std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return m_InnerLeaseSet; }; std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return m_InnerLeaseSet; };
private:
void CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t * authCookie, uint8_t * authData) const;
private: private:
IdentHash m_StoreHash; IdentHash m_StoreHash;

6
libi2pd/NTCP2.cpp

@ -161,6 +161,7 @@ namespace transport
// fill options // fill options
uint8_t options[32]; // actual options size is 16 bytes uint8_t options[32]; // actual options size is 16 bytes
memset (options, 0, 16); memset (options, 0, 16);
options[0] = i2p::context.GetNetID (); // network ID
options[1] = 2; // ver options[1] = 2; // ver
htobe16buf (options + 2, paddingLength); // padLen htobe16buf (options + 2, paddingLength); // padLen
// m3p2Len // m3p2Len
@ -248,6 +249,11 @@ namespace transport
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, m_H, 32, m_K, nonce, options, 16, false)) // decrypt if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionRequestBuffer + 32, 16, m_H, 32, m_K, nonce, options, 16, false)) // decrypt
{ {
// options // options
if (options[0] && options[0] != i2p::context.GetNetID ())
{
LogPrint (eLogWarning, "NTCP2: SessionRequest networkID ", (int)options[0], " mismatch. Expected ", i2p::context.GetNetID ());
return false;
}
if (options[1] == 2) // ver is always 2 if (options[1] == 2) // ver is always 2
{ {
paddingLen = bufbe16toh (options + 2); paddingLen = bufbe16toh (options + 2);

8
libi2pd/NetDb.cpp

@ -306,12 +306,20 @@ namespace data
auto it = m_LeaseSets.find(ident); auto it = m_LeaseSets.find(ident);
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType || if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ()) leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
{
if (leaseSet->IsPublic ())
{ {
// TODO: implement actual update // TODO: implement actual update
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32()); LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
m_LeaseSets[ident] = leaseSet; m_LeaseSets[ident] = leaseSet;
return true; return true;
} }
else
{
LogPrint (eLogWarning, "NetDb: Unpublished LeaseSet2 received: ", ident.ToBase32());
m_LeaseSets.erase (ident);
}
}
} }
else else
LogPrint (eLogError, "NetDb: new LeaseSet2 validation failed: ", ident.ToBase32()); LogPrint (eLogError, "NetDb: new LeaseSet2 validation failed: ", ident.ToBase32());

10
libi2pd/SSUSession.cpp

@ -1,4 +1,5 @@
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include "version.h"
#include "Crypto.h" #include "Crypto.h"
#include "Log.h" #include "Log.h"
#include "Timestamp.h" #include "Timestamp.h"
@ -729,7 +730,8 @@ namespace transport
encryption.Encrypt (encrypted, encryptedLen, encrypted); encryption.Encrypt (encrypted, encryptedLen, encrypted);
// assume actual buffer size is 18 (16 + 2) bytes more // assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, iv, 16); memcpy (buf + len, iv, 16);
htobe16buf (buf + len + 16, encryptedLen); uint16_t netid = i2p::context.GetNetID ();
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac); i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac);
} }
@ -750,7 +752,8 @@ namespace transport
m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted); m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted);
// assume actual buffer size is 18 (16 + 2) bytes more // assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, header->iv, 16); memcpy (buf + len, header->iv, 16);
htobe16buf (buf + len + 16, encryptedLen); uint16_t netid = i2p::context.GetNetID ();
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac); i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac);
} }
@ -799,7 +802,8 @@ namespace transport
uint16_t encryptedLen = len - (encrypted - buf); uint16_t encryptedLen = len - (encrypted - buf);
// assume actual buffer size is 18 (16 + 2) bytes more // assume actual buffer size is 18 (16 + 2) bytes more
memcpy (buf + len, header->iv, 16); memcpy (buf + len, header->iv, 16);
htobe16buf (buf + len + 16, encryptedLen); uint16_t netid = i2p::context.GetNetID ();
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
uint8_t digest[16]; uint8_t digest[16];
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest); i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest);
return !memcmp (header->mac, digest, 16); return !memcmp (header->mac, digest, 16);

25
libi2pd/Streaming.cpp

@ -918,7 +918,7 @@ namespace stream
{ {
expired = false; expired = false;
// time to request // time to request
if (m_RemoteLeaseSet->GetOrigStoreType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) if (m_RemoteLeaseSet->IsPublishedEncrypted ())
m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet ( m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet (
std::make_shared<i2p::data::BlindedPublicKey>(m_RemoteIdentity)); std::make_shared<i2p::data::BlindedPublicKey>(m_RemoteIdentity));
else else
@ -964,7 +964,6 @@ namespace stream
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip): StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
m_LastIncomingReceiveStreamID (0),
m_PendingIncomingTimer (m_Owner->GetService ()) m_PendingIncomingTimer (m_Owner->GetService ())
{ {
} }
@ -991,6 +990,7 @@ namespace stream
{ {
std::unique_lock<std::mutex> l(m_StreamsMutex); std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams.clear (); m_Streams.clear ();
m_IncomingStreams.clear ();
} }
} }
@ -1013,19 +1013,18 @@ namespace stream
if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
{ {
uint32_t receiveStreamID = packet->GetReceiveStreamID (); uint32_t receiveStreamID = packet->GetReceiveStreamID ();
if (receiveStreamID == m_LastIncomingReceiveStreamID) auto it1 = m_IncomingStreams.find (receiveStreamID);
if (it1 != m_IncomingStreams.end ())
{ {
// already pending // already pending
LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists"); LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists");
DeletePacket (packet); // drop it, because previous should be connected DeletePacket (packet); // drop it, because previous should be connected
return; return;
} }
auto incomingStream = CreateNewIncomingStream (); auto incomingStream = CreateNewIncomingStream (receiveStreamID);
incomingStream->HandleNextPacket (packet); // SYN incomingStream->HandleNextPacket (packet); // SYN
auto ident = incomingStream->GetRemoteIdentity(); auto ident = incomingStream->GetRemoteIdentity();
m_LastIncomingReceiveStreamID = receiveStreamID;
// handle saved packets if any // handle saved packets if any
{ {
auto it = m_SavedPackets.find (receiveStreamID); auto it = m_SavedPackets.find (receiveStreamID);
@ -1062,11 +1061,11 @@ namespace stream
else // follow on packet without SYN else // follow on packet without SYN
{ {
uint32_t receiveStreamID = packet->GetReceiveStreamID (); uint32_t receiveStreamID = packet->GetReceiveStreamID ();
for (auto& it: m_Streams) auto it1 = m_IncomingStreams.find (receiveStreamID);
if (it.second->GetSendStreamID () == receiveStreamID) if (it1 != m_IncomingStreams.end ())
{ {
// found // found
it.second->HandleNextPacket (packet); it1->second->HandleNextPacket (packet);
return; return;
} }
// save follow on packet // save follow on packet
@ -1105,11 +1104,12 @@ namespace stream
return s; return s;
} }
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream () std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream (uint32_t receiveStreamID)
{ {
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this); auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
std::unique_lock<std::mutex> l(m_StreamsMutex); std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s; m_Streams[s->GetRecvStreamID ()] = s;
m_IncomingStreams[receiveStreamID] = s;
return s; return s;
} }
@ -1118,9 +1118,8 @@ namespace stream
if (stream) if (stream)
{ {
std::unique_lock<std::mutex> l(m_StreamsMutex); std::unique_lock<std::mutex> l(m_StreamsMutex);
auto it = m_Streams.find (stream->GetRecvStreamID ()); m_Streams.erase (stream->GetRecvStreamID ());
if (it != m_Streams.end ()) m_IncomingStreams.erase (stream->GetSendStreamID ());
m_Streams.erase (it);
} }
} }

6
libi2pd/Streaming.h

@ -269,8 +269,10 @@ namespace stream
void AcceptOnceAcceptor (std::shared_ptr<Stream> stream, Acceptor acceptor, Acceptor prev); void AcceptOnceAcceptor (std::shared_ptr<Stream> stream, Acceptor acceptor, Acceptor prev);
private:
void HandleNextPacket (Packet * packet); void HandleNextPacket (Packet * packet);
std::shared_ptr<Stream> CreateNewIncomingStream (); std::shared_ptr<Stream> CreateNewIncomingStream (uint32_t receiveStreamID);
void HandlePendingIncomingTimer (const boost::system::error_code& ecode); void HandlePendingIncomingTimer (const boost::system::error_code& ecode);
private: private:
@ -280,8 +282,8 @@ namespace stream
bool m_Gzip; // gzip compression of data messages bool m_Gzip; // gzip compression of data messages
std::mutex m_StreamsMutex; std::mutex m_StreamsMutex;
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
std::map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
Acceptor m_Acceptor; Acceptor m_Acceptor;
uint32_t m_LastIncomingReceiveStreamID;
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams; std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
boost::asio::deadline_timer m_PendingIncomingTimer; boost::asio::deadline_timer m_PendingIncomingTimer;
std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN

4
libi2pd/version.h

@ -7,7 +7,7 @@
#define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) #define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 27 #define I2PD_VERSION_MINOR 28
#define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
@ -21,7 +21,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 41 #define I2P_VERSION_MICRO 42
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

4
libi2pd_client/AddressBook.cpp

@ -422,9 +422,9 @@ namespace client
std::string name = s.substr(0, pos++); std::string name = s.substr(0, pos++);
std::string addr = s.substr(pos); std::string addr = s.substr(pos);
size_t pos = s.find('#'); size_t pos = addr.find('#');
if (pos != std::string::npos) if (pos != std::string::npos)
addr = addr.substr(pos); // remove comments addr = addr.substr(0, pos); // remove comments
auto ident = std::make_shared<i2p::data::IdentityEx> (); auto ident = std::make_shared<i2p::data::IdentityEx> ();
if (!ident->FromBase64(addr)) { if (!ident->FromBase64(addr)) {

19
libi2pd_client/ClientContext.cpp

@ -381,6 +381,16 @@ namespace client
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), value); return section.second.get (boost::property_tree::ptree::path_type (name, '/'), value);
} }
template<typename Section>
void ClientContext::ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map<std::string, std::string>& options) const
{
for (auto it: section.second)
{
if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group))
options[it.first] = it.second.get_value ("");
}
}
template<typename Section> template<typename Section>
void ClientContext::ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const void ClientContext::ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const
{ {
@ -397,6 +407,15 @@ namespace client
if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType; if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType;
std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, ""); std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, "");
if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey; if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey;
auto authType = GetI2CPOption(section, I2CP_PARAM_LEASESET_AUTH_TYPE, 0);
if (authType != "0") // auth is set
{
options[I2CP_PARAM_LEASESET_AUTH_TYPE] = authType;
if (authType == "1") // DH
ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_DH, options);
else if (authType == "2") // PSK
ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_PSK, options);
}
} }
void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const

2
libi2pd_client/ClientContext.h

@ -95,6 +95,8 @@ namespace client
template<typename Section> template<typename Section>
std::string GetI2CPStringOption (const Section& section, const std::string& name, const std::string& value) const; // GetI2CPOption with string default value std::string GetI2CPStringOption (const Section& section, const std::string& name, const std::string& value) const; // GetI2CPOption with string default value
template<typename Section> template<typename Section>
void ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map<std::string, std::string>& options) const;
template<typename Section>
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const; // for tunnels void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const; // for tunnels
void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const; // for HTTP and SOCKS proxy void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const; // for HTTP and SOCKS proxy

73
libi2pd_client/SAM.cpp

@ -239,6 +239,7 @@ namespace client
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred); char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
if (eol) if (eol)
{ {
if (eol > m_Buffer && eol[-1] == '\r') eol--;
*eol = 0; *eol = 0;
char * separator = strchr (m_Buffer, ' '); char * separator = strchr (m_Buffer, ' ');
if (separator) if (separator)
@ -259,7 +260,7 @@ namespace client
ProcessDestGenerate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); ProcessDestGenerate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP)) else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND)) else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND) || !strcmp (m_Buffer, SAM_RAW_SEND))
{ {
size_t len = bytes_transferred - (separator - m_Buffer) - 1; size_t len = bytes_transferred - (separator - m_Buffer) - 1;
size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1); size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1);
@ -337,8 +338,20 @@ namespace client
return; return;
} }
SAMSessionType type = eSAMSessionTypeUnknown;
if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream;
else if (style == SAM_VALUE_DATAGRAM) type = eSAMSessionTypeDatagram;
else if (style == SAM_VALUE_RAW) type = eSAMSessionTypeRaw;
if (type == eSAMSessionTypeUnknown)
{
// unknown style
SendI2PError("Unknown STYLE");
return;
}
std::shared_ptr<boost::asio::ip::udp::endpoint> forward = nullptr; std::shared_ptr<boost::asio::ip::udp::endpoint> forward = nullptr;
if (style == SAM_VALUE_DATAGRAM && params.find(SAM_VALUE_HOST) != params.end() && params.find(SAM_VALUE_PORT) != params.end()) if ((type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) &&
params.find(SAM_VALUE_HOST) != params.end() && params.find(SAM_VALUE_PORT) != params.end())
{ {
// udp forward selected // udp forward selected
boost::system::error_code e; boost::system::error_code e;
@ -379,16 +392,20 @@ namespace client
} }
// create destination // create destination
auto session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, &params); auto session = m_Owner.CreateSession (id, type, destination == SAM_VALUE_TRANSIENT ? "" : destination, &params);
if (session) if (session)
{ {
m_SocketType = eSAMSocketTypeSession; m_SocketType = eSAMSocketTypeSession;
if (style == SAM_VALUE_DATAGRAM) if (type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw)
{ {
session->UDPEndpoint = forward; session->UDPEndpoint = forward;
auto dest = session->localDestination->CreateDatagramDestination (); auto dest = session->localDestination->CreateDatagramDestination ();
if (type == eSAMSessionTypeDatagram)
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
else // raw
dest->SetRawReceiver (std::bind (&SAMSocket::HandleI2PRawDatagramReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
} }
if (session->localDestination->IsReady ()) if (session->localDestination->IsReady ())
@ -550,7 +567,10 @@ namespace client
{ {
i2p::data::IdentityEx dest; i2p::data::IdentityEx dest;
dest.FromBase64 (params[SAM_PARAM_DESTINATION]); dest.FromBase64 (params[SAM_PARAM_DESTINATION]);
if (session->Type == eSAMSessionTypeDatagram)
d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
else // raw
d->SendRawDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
} }
else else
LogPrint (eLogError, "SAM: missing datagram destination"); LogPrint (eLogError, "SAM: missing datagram destination");
@ -926,16 +946,44 @@ namespace client
} }
} }
void SAMSocket::HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: raw datagram received ", len);
auto session = m_Owner.FindSession(m_ID);
if(session)
{
auto ep = session->UDPEndpoint;
if (ep)
// udp forward enabled
m_Owner.SendTo(buf, len, ep);
else
{
#ifdef _MSC_VER
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len);
#else
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len);
#endif
if (len < SAM_SOCKET_BUFFER_SIZE - l)
{
memcpy (m_StreamBuffer + l, buf, len);
WriteI2PData(len + l);
}
else
LogPrint (eLogWarning, "SAM: received raw datagram size ", len," exceeds buffer");
}
}
}
void SAMSocket::HandleStreamSend(const boost::system::error_code & ec) void SAMSocket::HandleStreamSend(const boost::system::error_code & ec)
{ {
m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this())); m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this()));
} }
SAMSession::SAMSession (SAMBridge & parent, const std::string & id, std::shared_ptr<ClientDestination> dest): SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type, std::shared_ptr<ClientDestination> dest):
m_Bridge(parent), m_Bridge(parent),
localDestination (dest), localDestination (dest),
UDPEndpoint(nullptr), UDPEndpoint(nullptr),
Name(id) Name(id), Type (type)
{ {
} }
@ -964,7 +1012,8 @@ namespace client
{"ECDSA_SHA256_P521", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521}, {"ECDSA_SHA256_P521", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521},
{"EdDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519}, {"EdDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519},
{"GOST_GOSTR3411256_GOSTR3410CRYPTOPROA", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256}, {"GOST_GOSTR3411256_GOSTR3410CRYPTOPROA", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256},
{"GOST_GOSTR3411512_GOSTR3410TC26A512", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512} {"GOST_GOSTR3411512_GOSTR3410TC26A512", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512},
{"RedDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519},
} }
{ {
} }
@ -1061,8 +1110,8 @@ namespace client
Accept (); Accept ();
} }
std::shared_ptr<SAMSession> SAMBridge::CreateSession (const std::string& id, const std::string& destination, std::shared_ptr<SAMSession> SAMBridge::CreateSession (const std::string& id, SAMSessionType type,
const std::map<std::string, std::string> * params) const std::string& destination, const std::map<std::string, std::string> * params)
{ {
std::shared_ptr<ClientDestination> localDestination = nullptr; std::shared_ptr<ClientDestination> localDestination = nullptr;
if (destination != "") if (destination != "")
@ -1102,7 +1151,7 @@ namespace client
if (localDestination) if (localDestination)
{ {
localDestination->Acquire (); localDestination->Acquire ();
auto session = std::make_shared<SAMSession>(*this, id, localDestination); auto session = std::make_shared<SAMSession>(*this, id, type, localDestination);
std::unique_lock<std::mutex> l(m_SessionsMutex); std::unique_lock<std::mutex> l(m_SessionsMutex);
auto ret = m_Sessions.insert (std::make_pair(id, session)); auto ret = m_Sessions.insert (std::make_pair(id, session));
if (!ret.second) if (!ret.second)
@ -1193,8 +1242,12 @@ namespace client
{ {
i2p::data::IdentityEx dest; i2p::data::IdentityEx dest;
dest.FromBase64 (destination); dest.FromBase64 (destination);
if (session->Type == eSAMSessionTypeDatagram)
session->localDestination->GetDatagramDestination ()-> session->localDestination->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
else // raw
session->localDestination->GetDatagramDestination ()->
SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
} }
else else
LogPrint (eLogError, "SAM: Session ", sessionID, " not found"); LogPrint (eLogError, "SAM: Session ", sessionID, " not found");

16
libi2pd_client/SAM.h

@ -40,12 +40,14 @@ namespace client
const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n"; const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n";
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND"; const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND";
const char SAM_RAW_SEND[] = "RAW SEND";
const char SAM_DEST_GENERATE[] = "DEST GENERATE"; const char SAM_DEST_GENERATE[] = "DEST GENERATE";
const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n"; const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n";
const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n"; const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n";
const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP"; const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP";
const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n"; const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n";
const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n"; const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n";
const char SAM_RAW_RECEIVED[] = "RAW RECEIVED SIZE=%lu\n";
const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n"; const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n";
const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=KEY_NOT_FOUND NAME=%s\n"; const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=KEY_NOT_FOUND NAME=%s\n";
const char SAM_PARAM_MIN[] = "MIN"; const char SAM_PARAM_MIN[] = "MIN";
@ -111,6 +113,7 @@ namespace client
void HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream); void HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream);
void HandleWriteI2PData (const boost::system::error_code& ecode, size_t sz); void HandleWriteI2PData (const boost::system::error_code& ecode, size_t sz);
void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void ProcessSessionCreate (char * buf, size_t len); void ProcessSessionCreate (char * buf, size_t len);
void ProcessStreamConnect (char * buf, size_t len, size_t rem); void ProcessStreamConnect (char * buf, size_t len, size_t rem);
@ -149,14 +152,23 @@ namespace client
std::shared_ptr<i2p::stream::Stream> m_Stream; std::shared_ptr<i2p::stream::Stream> m_Stream;
}; };
enum SAMSessionType
{
eSAMSessionTypeUnknown,
eSAMSessionTypeStream,
eSAMSessionTypeDatagram,
eSAMSessionTypeRaw
};
struct SAMSession struct SAMSession
{ {
SAMBridge & m_Bridge; SAMBridge & m_Bridge;
std::shared_ptr<ClientDestination> localDestination; std::shared_ptr<ClientDestination> localDestination;
std::shared_ptr<boost::asio::ip::udp::endpoint> UDPEndpoint; std::shared_ptr<boost::asio::ip::udp::endpoint> UDPEndpoint;
std::string Name; std::string Name;
SAMSessionType Type;
SAMSession (SAMBridge & parent, const std::string & name, std::shared_ptr<ClientDestination> dest); SAMSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr<ClientDestination> dest);
~SAMSession (); ~SAMSession ();
void CloseStreams (); void CloseStreams ();
@ -173,7 +185,7 @@ namespace client
void Stop (); void Stop ();
boost::asio::io_service& GetService () { return m_Service; }; boost::asio::io_service& GetService () { return m_Service; };
std::shared_ptr<SAMSession> CreateSession (const std::string& id, const std::string& destination, // empty string means transient std::shared_ptr<SAMSession> CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient
const std::map<std::string, std::string> * params); const std::map<std::string, std::string> * params);
void CloseSession (const std::string& id); void CloseSession (const std::string& id);
std::shared_ptr<SAMSession> FindSession (const std::string& id) const; std::shared_ptr<SAMSession> FindSession (const std::string& id) const;

2
qt/i2pd_qt/.gitignore vendored

@ -7,3 +7,5 @@ Makefile*
*.stash *.stash
object_script.* object_script.*
i2pd_qt_plugin_import.cpp i2pd_qt_plugin_import.cpp
i2pd_qt.pro.autosave*

12
qt/i2pd_qt/DaemonQT.cpp

@ -11,6 +11,8 @@
#include <QMutexLocker> #include <QMutexLocker>
#include <QThread> #include <QThread>
//#define DEBUG_WITH_DEFAULT_LOGGING (1)
namespace i2p namespace i2p
{ {
namespace qt namespace qt
@ -151,10 +153,16 @@ namespace qt
int result; int result;
{ {
std::shared_ptr<std::iostream> logstreamptr=std::make_shared<std::stringstream>(); std::shared_ptr<std::iostream> logstreamptr=
#ifdef DEBUG_WITH_DEFAULT_LOGGING
nullptr
#else
std::make_shared<std::stringstream>()
#endif
;
//TODO move daemon init deinit to a bg thread //TODO move daemon init deinit to a bg thread
DaemonQTImpl daemon; DaemonQTImpl daemon;
(*logstreamptr) << "Initialising the daemon..." << std::endl; if(logstreamptr) (*logstreamptr) << "Initialising the daemon..." << std::endl;
bool daemonInitSuccess = daemon.init(argc, argv, logstreamptr); bool daemonInitSuccess = daemon.init(argc, argv, logstreamptr);
if(!daemonInitSuccess) if(!daemonInitSuccess)
{ {

1
qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml

@ -35,6 +35,7 @@
<translation type="qt" /> <translation type="qt" />
<releases> <releases>
<release version="2.28.0" date="2019-08-27" />
<release version="2.27.0" date="2019-07-03" /> <release version="2.27.0" date="2019-07-03" />
<release version="2.26.0" date="2019-06-07" /> <release version="2.26.0" date="2019-06-07" />
<release version="2.25.0" date="2019-05-09" /> <release version="2.25.0" date="2019-05-09" />

7
qt/i2pd_qt/i2pd_qt.pro

@ -5,7 +5,12 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = i2pd_qt TARGET = i2pd_qt
TEMPLATE = app TEMPLATE = app
QMAKE_CXXFLAGS *= -std=c++11 -Wno-unused-parameter -Wno-maybe-uninitialized QMAKE_CXXFLAGS *= -std=c++11 -Wno-unused-parameter -Wno-maybe-uninitialized
DEFINES += USE_UPNP
# For now, disable UPnP which currently crashes on Stop() -- https://github.com/PurpleI2P/i2pd/issues/1387
#DEFINES += USE_UPNP
DEFINES -= USE_UPNP
debug: DEFINES += DEBUG_WITH_DEFAULT_LOGGING
SOURCES += DaemonQT.cpp mainwindow.cpp \ SOURCES += DaemonQT.cpp mainwindow.cpp \
../../libi2pd/api.cpp \ ../../libi2pd/api.cpp \

2
qt/i2pd_qt/logviewermanager.cpp

@ -18,7 +18,7 @@ namespace logviewer {
QString Worker::pollAndShootATimerForInfiniteRetries() { QString Worker::pollAndShootATimerForInfiniteRetries() {
std::shared_ptr<std::iostream> logStream=logViewerManager.getLogStream(); std::shared_ptr<std::iostream> logStream=logViewerManager.getLogStream();
assert(logStream!=nullptr); if(!logStream)return "";
std::streamsize MAX_SZ=64*1024; std::streamsize MAX_SZ=64*1024;
char*buf=(char*)malloc(MAX_SZ*sizeof(char)); char*buf=(char*)malloc(MAX_SZ*sizeof(char));
if(buf==nullptr)return ""; if(buf==nullptr)return "";

Loading…
Cancel
Save