diff -Nru i2pd-2.29.0/android/AndroidManifest.xml i2pd-2.31.0/android/AndroidManifest.xml --- i2pd-2.29.0/android/AndroidManifest.xml 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/android/AndroidManifest.xml 2020-04-10 17:33:54.000000000 +0000 @@ -16,6 +16,8 @@ android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@android:style/Theme.Holo.Light.DarkActionBar" + android:requestLegacyExternalStorage="true" + android:usesCleartextTraffic="true" > diff -Nru i2pd-2.29.0/android/assets/certificates/reseed/backup_at_mail.i2p.crt i2pd-2.31.0/android/assets/certificates/reseed/backup_at_mail.i2p.crt --- i2pd-2.29.0/android/assets/certificates/reseed/backup_at_mail.i2p.crt 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/android/assets/certificates/reseed/backup_at_mail.i2p.crt 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFfTCCA2WgAwIBAgIEOprmhjANBgkqhkiG9w0BAQ0FADBvMQswCQYDVQQGEwJY -WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt -b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEYMBYGA1UEAwwPYmFja3VwQG1haWwu -aTJwMB4XDTEzMTAxMzEzNDQ1NVoXDTIzMTAxMzEzNDQ1NVowbzELMAkGA1UEBhMC -WFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFub255 -bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGDAWBgNVBAMMD2JhY2t1cEBtYWls -LmkycDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIoAkobXwk/Enf1d -roHyqCyvcJfZJVTwb/LgYWAvCBMCr+RGqlSgtk3g69Y3I0xU08fD2kGt3r5Pwsbr -omXIbJAcccyLqmQ5QX6QgL+X9VpMDp9C4h2RogCrqLBAWw4cuZ4RS9VCpP1Yis7H -uejYqENP86p7BsRnuW/4cYnfunAdMpss4LpRGQXt1nTX+kfgCYgnKFbFqwAHt7yV -Ds+Pe6FuBHPlp+sc1amKRcUnSvhXLsv43VicnT7xYL/kUsN83wrtHA3B4aGDx3aA -3/EzuRmIXQB0BlTZILMEyYwG/nc4OsW82QYrvEZ9BIg9A4lF/wS/KZCICPxLF2zo -dGjnmlgkiA4s8eO+va/ElHyELjckVXqmG1eXHhSkEsDvOQJy01IUuwLinvq7cUbJ -HfJBZJllEg+sLDCv3FkEqN+XjBNFfQN4oNew4w6IPY6YH1INVB9LL0Cmdu4DudLv -TY8OcI8eSfez3hmm+pYQ23PJRYYnvRDnRECyIWBegkckWRh8U/WvZUYUvETK6EDl -/0KpTtfzX6MqHA5D6bTAB8Y3ijGMLrZ/B5vj5yCoZbLiGme9X2moR2k1LEhdhtzV -exsqezCpg6dn48FTX7mHjvR5/r4kz2jqBGmdPUWIIxnjFUzDUK3llVQiHihleHpe -jL4LqnhBGKWFRTaVwaIkBG4zAfIzAgMBAAGjITAfMB0GA1UdDgQWBBQNkfW7bSMl -1/4KDbgwrkf9x1Zu/TANBgkqhkiG9w0BAQ0FAAOCAgEAGg3a3rTf0EznQocmio0T -5gCoL0n8h6yKW/PyPAIELrd9wiYjhJFcWvMTcJJJnVqmAL5vpvhaAFVtAfx70MGa -0DZ7FvytK5hEfF4IqOFDyEEVGJR5rIpVK4MeI1nmwEsxdbW+FhODjtRzgYO8XBME -Xj4aY1FWg9vxc3reUj6PSFsZtsB0aLiRgL9JDovJIiRw0Uqr1v2wXBte5yVCxDge -vTREZtpK4cKetoOa68pwSXI32JwKE18j6bfdKVBCcYQKlKP/3gHGduaDrQv3w32S -DRym5s6MREeTUOtAw4wq46KpdOX8yyAqJPrCfMwS6ORd3t+egqOw0PUnsqb97w4O -lUtrRYvb2cOj60SmRx4vJvItyuHbKqIK7o2e1RcUZPXYoAVx2ww4XB2Wk4D7LSAs -cS7nLj8yAqzJ2qqtBzxu+zILJtkVa12dKF0xmS0BxBp4sCYiBtmAVE8AWQqEuSHA -FrMWqoXcjcfdvvyX487FFWWUE7ZBIn0hee2sK9J9+SPtqczJaN7TF3K3nzo65WJG -1epltmq2Ugjb67Gz7v4y7H23DJ/qhm8yLtCHTj69HTta5I08j6Kut924WLZaiMO/ -4YoEL5AE63X0sxYibKFQiq7FW5nUJA280GRlY3xSMFzlB2ggazrUV3YAWVDhfdnI -flpzWXkFM2D36OUaubfe9YY= ------END CERTIFICATE----- diff -Nru i2pd-2.29.0/android/assets/certificates/reseed/reseedi2pnetin_at_mail.i2p.crt i2pd-2.31.0/android/assets/certificates/reseed/reseedi2pnetin_at_mail.i2p.crt --- i2pd-2.29.0/android/assets/certificates/reseed/reseedi2pnetin_at_mail.i2p.crt 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/android/assets/certificates/reseed/reseedi2pnetin_at_mail.i2p.crt 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF3DCCA8SgAwIBAgIQPxUlcrbHX/xdyJ09E36rJzANBgkqhkiG9w0BAQsFADB3 -MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK -ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEgMB4GA1UEAwwX -cmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwHhcNMTgxMjA3MTYzNDIxWhcNMjgxMjA3 -MTYzNDIxWjB3MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhY -MR4wHAYDVQQKExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEg -MB4GA1UEAwwXcmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQC912NDk6x85uqB4gyQQcded0RVrbWehWOanDRv7kC3 -92jeziPbeMtqrLsfU1MdDtQiGijpNkQ/IIitPw+6vJAIh82gyOUZvsn2XOyb/Fz0 -Fu8OrDghwl39yK8kwtqCFw3VAgafgKxz2oRge9mxFBECi50vYEPIBwNhr4yc/opu -wWUmzmRyX4gD7vKmRU6ZTwX4LXnwdl+5VbW3updcZKsDuTnKvC9FGhDRR9kIk2G9 -43sLN263nCYPykP7DaB1cUdi1vDEMw5dot+eu16qTIbuypEvYNvbB/9FyCQllm1h -vBbSku3IYpcnRPmoeyhoR/MmCySRbK5R4SrSsVD1YBpwxgn0Q4+fzEgFzT9P4oez -HkDGKVP2HdgmXx9j36fEqqvjqzRleWDwEWwIZVRLCFO+hhhT3JAjnNGJTWv1SQGB -8tz9nyYTJuhvyHE/CO5owFeCdeOGMq2KPge9w34T+mvewTEEhGU8yRAt8Xp8s5Y9 -RCUGvuQ79+edRtj7FJg7yVB8pAQ+VB9msNQvzrTnPYC9Wo7chJhBiraMiIabzIhC -f34Gg9lkX1N0dVND5rnZWwzBM6JhNG1iZZCRHVPnXdZRixUlqmFpCP/eekshksj/ -6UP/WeGA6X4HyEsC6QEf7eMhcHYjyyTzYagKrwCHg77fmIjF8rmpP2LqWSQW8bDD -uQIDAQABo2QwYjAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIG -CCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wIAYDVR0OBBkEF3Jlc2VlZGkycG5l -dGluQG1haWwuaTJwMA0GCSqGSIb3DQEBCwUAA4ICAQCWpXs6iuTy/w2R7q7Ua6vl -JYZwbQ+czk5ydzkBgcNkMMMNRT7sZR9xYvV+ftiL4bFQP/3ZJyo7cYz2Q6+M3oAm -YDcZWBkLUVihSlMxhWwmeFTKV2EL+bzwY1V/cy7wgukKnFIes75dLP/v25jgjdlw -Xe6R+fQM0EoHeVzzrWk/qYp6oEwtQXfZnUu/Bf45hRnnHBzzh1wCql41vbEs3Niq -+SVwY1wLT0yC1L8HqjCLX1/L5PAXxbvEGzwnXSkLKK4bPxdmVDZvS9uzXrWmTbNi -HpKIFnOif16zSgyeaOM7HETIJuVzgooUMtt+Vsr1VGdtm6K7I9J5C+rX/ckU8oaX -UjmzhWXudN0VTslogsKUCV6xG2CskeE3wnuT8HYXz9NMw6c/kIGH4hY7LcfU8Teu -QjSy2RRvy6InmZNV5sY9lzzO6romEycSoUlpCa3Ltb/5KKoYZFTsXr8suqJk89lC -e+TVMHqOZdLK/usqQDcafLypHpw9SH2Tg4jrzV/zLqacbjx6bZD5IrpY0Gf7BXg/ -pikwyA9c490G6ZcWrSEP8bzh6LL2rA2AwxaeJJNVyLHCSLrn/7DezM5J/qhd90Qg -kcZGJrUOCSWl6mDvUZn5XiQ955XwOnZQ+wsM85B3CVX22x5bp0SYWHCQBPnthPwP -Q5DD3jExbpwG5n35HEcHYw== ------END CERTIFICATE----- diff -Nru i2pd-2.29.0/android/build.gradle i2pd-2.31.0/android/build.gradle --- i2pd-2.29.0/android/build.gradle 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/android/build.gradle 2020-04-10 17:33:54.000000000 +0000 @@ -30,8 +30,9 @@ applicationId "org.purplei2p.i2pd" targetSdkVersion 29 minSdkVersion 14 - versionCode 2290 - versionName "2.29.0" + versionCode 2310 + versionName "2.31.0" + setProperty("archivesBaseName", archivesBaseName + "-" + versionName) ndk { abiFilters 'armeabi-v7a' abiFilters 'x86' @@ -56,9 +57,10 @@ splits { abi { // change that to true if you need splitted apk - enable false + enable true reset() - include "armeabi-v7a", "arm64-v8a", "x86", "x86_64" + //include "armeabi-v7a", "arm64-v8a", "x86", "x86_64" + include "armeabi-v7a", "x86" universalApk true } } @@ -72,7 +74,7 @@ } buildTypes { release { - minifyEnabled true + minifyEnabled false signingConfig signingConfigs.orignal proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt' } @@ -87,3 +89,16 @@ targetCompatibility = '1.8' } } + +ext.abiCodes = ['armeabi-v7a':1, 'x86':2, 'arm64-v8a':3, 'x86_64':4] +import com.android.build.OutputFile + +android.applicationVariants.all { variant -> + variant.outputs.each { output -> + def baseAbiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI)) + + if (baseAbiVersionCode != null) { + output.versionCodeOverride = baseAbiVersionCode + variant.versionCode + } + } +} diff -Nru i2pd-2.29.0/android/jni/Android.mk i2pd-2.31.0/android/jni/Android.mk --- i2pd-2.29.0/android/jni/Android.mk 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/android/jni/Android.mk 2020-04-10 17:33:54.000000000 +0000 @@ -25,29 +25,29 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_system -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_system.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_system.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_date_time -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_filesystem -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_program_options -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) diff -Nru i2pd-2.29.0/android/jni/Application.mk i2pd-2.31.0/android/jni/Application.mk --- i2pd-2.29.0/android/jni/Application.mk 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/android/jni/Application.mk 2020-04-10 17:33:54.000000000 +0000 @@ -9,15 +9,15 @@ #APP_STL := c++_shared APP_STL := c++_static -# Enable c++11 extensions in source code -APP_CPPFLAGS += -std=c++11 -fexceptions -frtti +# Enable c++17 extensions in source code +APP_CPPFLAGS += -std=c++17 -fexceptions -frtti APP_CPPFLAGS += -DANDROID -D__ANDROID__ -DUSE_UPNP ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) APP_CPPFLAGS += -DANDROID_ARM7A endif -# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -b boost-1_72_0 # git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git # git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git # git clone https://github.com/PurpleI2P/android-ifaddrs.git diff -Nru i2pd-2.29.0/android/README.md i2pd-2.31.0/android/README.md --- i2pd-2.29.0/android/README.md 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/android/README.md 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,19 @@ +# how to compile? +## Install the gradle + NDK or use android-studio +[https://gradle.org/install/](https://gradle.org/install/) + +## Install the depencies +``` +git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -b boost-1_72_0 +git clone https://github.com/PurpleI2P/android-ifaddrs.git +git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git +git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git +``` +## Set libs in jni/Application.mk on 24 line: +``` +# change to your own +I2PD_LIBS_PATH = /home/user/i2pd/android/ +``` + +## compile apk file +gradle clean assembleRelease diff -Nru i2pd-2.29.0/android/res/layout/webview.xml i2pd-2.31.0/android/res/layout/webview.xml --- i2pd-2.29.0/android/res/layout/webview.xml 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/android/res/layout/webview.xml 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,13 @@ + + + + + diff -Nru i2pd-2.29.0/android/res/menu/options_main.xml i2pd-2.31.0/android/res/menu/options_main.xml --- i2pd-2.29.0/android/res/menu/options_main.xml 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/android/res/menu/options_main.xml 2020-04-10 17:33:54.000000000 +0000 @@ -12,6 +12,10 @@ android:id="@+id/action_graceful_stop" android:orderInCategory="98" android:title="@string/action_graceful_stop" /> + Battery Optimizations Your Android OS version does not support showing the dialog for battery optimizations for applications. Planned shutdown canceled + Start webview diff -Nru i2pd-2.29.0/android/src/org/purplei2p/i2pd/I2PDActivity.java i2pd-2.31.0/android/src/org/purplei2p/i2pd/I2PDActivity.java --- i2pd-2.29.0/android/src/org/purplei2p/i2pd/I2PDActivity.java 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/android/src/org/purplei2p/i2pd/I2PDActivity.java 2020-04-10 17:33:54.000000000 +0000 @@ -39,15 +39,23 @@ import android.widget.TextView; import android.widget.Toast; + import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; // For future package update checking +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + + import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS; public class I2PDActivity extends Activity { + private WebView webView; + private static final String TAG = "i2pdActvt"; private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1; public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000; @@ -56,6 +64,7 @@ private TextView textView; private boolean assetsCopied; private String i2pdpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/"; + //private ConfigParser parser = new ConfigParser(i2pdpath); // TODO: private static final DaemonSingleton daemon = DaemonSingleton.getInstance(); @@ -262,6 +271,16 @@ case R.id.action_battery_otimizations: onActionBatteryOptimizations(); return true; + case R.id.action_start_webview: + setContentView(R.layout.webview); + this.webView = (WebView) findViewById(R.id.webview1); + this.webView.setWebViewClient(new WebViewClient()); + + WebSettings webSettings = this.webView.getSettings(); + webSettings.setBuiltInZoomControls(true); + webSettings.setJavaScriptEnabled(false); + this.webView.loadUrl("http://127.0.0.1:7070"); // TODO: instead 7070 I2Pd....HttpPort + break; } return super.onOptionsItemSelected(item); diff -Nru i2pd-2.29.0/android_binary_only/jni/Android.mk i2pd-2.31.0/android_binary_only/jni/Android.mk --- i2pd-2.29.0/android_binary_only/jni/Android.mk 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/android_binary_only/jni/Android.mk 2020-04-10 17:33:54.000000000 +0000 @@ -26,29 +26,29 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_system -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_system.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_system.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_date_time -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_filesystem -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := boost_program_options -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_68_0-clang/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_68_0-clang/include +LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) diff -Nru i2pd-2.29.0/android_binary_only/jni/Application.mk i2pd-2.31.0/android_binary_only/jni/Application.mk --- i2pd-2.29.0/android_binary_only/jni/Application.mk 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/android_binary_only/jni/Application.mk 2020-04-10 17:33:54.000000000 +0000 @@ -9,8 +9,8 @@ NDK_TOOLCHAIN_VERSION := clang APP_STL := c++_static -# Enable c++11 extensions in source code -APP_CPPFLAGS += -std=c++11 -fvisibility=default -fPIE +# Enable c++17 extensions in source code +APP_CPPFLAGS += -std=c++17 -fvisibility=default -fPIE APP_CPPFLAGS += -DANDROID_BINARY -DANDROID -D__ANDROID__ -DUSE_UPNP APP_LDFLAGS += -rdynamic -fPIE -pie @@ -21,7 +21,7 @@ # Forcing debug optimization. Use `ndk-build NDK_DEBUG=1` instead. #APP_OPTIM := debug -# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -b boost-1_72_0 # git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git # git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git # git clone https://github.com/PurpleI2P/android-ifaddrs.git diff -Nru i2pd-2.29.0/appveyor.yml i2pd-2.31.0/appveyor.yml --- i2pd-2.29.0/appveyor.yml 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/appveyor.yml 2020-04-10 17:33:54.000000000 +0000 @@ -1,4 +1,4 @@ -version: 2.29.0.{build} +version: 2.31.0.{build} pull_requests: do_not_increment_build_number: true branches: @@ -18,9 +18,9 @@ install: - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc mingw-w64-{i686,x86_64}-gcc-ada mingw-w64-{i686,x86_64}-gcc-objc" -- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu --force" +- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu" -- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu --force" +- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu" - if "%MSYSTEM%" == "MINGW64" ( c:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-boost mingw-w64-x86_64-miniupnpc" diff -Nru i2pd-2.29.0/build/build_mingw.cmd i2pd-2.31.0/build/build_mingw.cmd --- i2pd-2.29.0/build/build_mingw.cmd 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/build/build_mingw.cmd 2020-04-10 17:33:54.000000000 +0000 @@ -54,6 +54,14 @@ call :BUILDING echo. +REM building for WinXP +set "WD=C:\msys64-xp\usr\bin\" +set MSYSTEM=MINGW32 +set bitness=32 +set "xSH=%WD%bash -lc" +call :BUILDING_XP +echo. + del README.txt >> nul echo Build complete... @@ -71,5 +79,11 @@ %xSH% "make DEBUG=no USE_UPNP=yes USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_aesni.zip %FILELIST% && make clean" > build/build_win%bitness%_aesni_%tag%.log 2>&1 echo Build without extensions... %xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST% && make clean" > build/build_win%bitness%_%tag%.log 2>&1 +goto EOF + +:BUILDING_XP +%xSH% "make clean" >> nul +echo Building i2pd %tag% for winxp... +%xSH% "make DEBUG=no USE_UPNP=yes USE_WINXP_FLAGS=yes -j%threads% && zip -r9 build/i2pd_%tag%_winxp_mingw.zip %FILELIST% && make clean" > build/build_winxp_%tag%.log 2>&1 :EOF \ No newline at end of file diff -Nru i2pd-2.29.0/build/CMakeLists.txt i2pd-2.31.0/build/CMakeLists.txt --- i2pd-2.29.0/build/CMakeLists.txt 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/build/CMakeLists.txt 2020-04-10 17:33:54.000000000 +0000 @@ -19,8 +19,6 @@ option(WITH_MESHNET "Build for cjdns test network" OFF) option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF) option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF) -option(WITH_I2LUA "Build for i2lua" OFF) -option(WITH_WEBSOCKETS "Build with websocket ui" OFF) # paths set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" ) @@ -75,28 +73,20 @@ "${LIBI2PD_SRC_DIR}/Signature.cpp" "${LIBI2PD_SRC_DIR}/Timestamp.cpp" "${LIBI2PD_SRC_DIR}/api.cpp" - "${LIBI2PD_SRC_DIR}/Event.cpp" "${LIBI2PD_SRC_DIR}/Gost.cpp" "${LIBI2PD_SRC_DIR}/ChaCha20.cpp" "${LIBI2PD_SRC_DIR}/Poly1305.cpp" "${LIBI2PD_SRC_DIR}/Ed25519.cpp" "${LIBI2PD_SRC_DIR}/NTCP2.cpp" "${LIBI2PD_SRC_DIR}/Blinding.cpp" + "${LIBI2PD_SRC_DIR}/Elligator.cpp" + "${LIBI2PD_SRC_DIR}/ECIESX25519AEADRatchetSession.cpp" ) -if (WITH_WEBSOCKETS) - add_definitions(-DWITH_EVENTS) - find_package(websocketpp REQUIRED) -endif () - if (WIN32 OR MSYS) list (APPEND LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/I2PEndian.cpp") endif () -if (WITH_I2LUA) - add_definitions(-DI2LUA) -endif() - add_library(libi2pd ${LIBI2PD_SRC}) set_target_properties(libi2pd PROPERTIES PREFIX "") @@ -122,13 +112,8 @@ "${LIBI2PD_CLIENT_SRC_DIR}/SOCKS.cpp" "${LIBI2PD_CLIENT_SRC_DIR}/HTTPProxy.cpp" "${LIBI2PD_CLIENT_SRC_DIR}/I2CP.cpp" - "${LIBI2PD_CLIENT_SRC_DIR}/WebSocks.cpp" ) -if(WITH_WEBSOCKETS) - list (APPEND CLIENT_SRC "${LIBI2PD_CLIENT_SRC_DIR}/Websocket.cpp") -endif () - add_library(libi2pdclient ${CLIENT_SRC}) set_target_properties(libi2pdclient PROPERTIES PREFIX "") @@ -182,17 +167,17 @@ set( CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections" ) # -flto is added from above endif () -# check for c++11 support +# check for c++17 & c++11 support include(CheckCXXCompilerFlag) +CHECK_CXX_COMPILER_FLAG("-std=c++17" CXX17_SUPPORTED) CHECK_CXX_COMPILER_FLAG("-std=c++11" CXX11_SUPPORTED) -CHECK_CXX_COMPILER_FLAG("-std=c++0x" CXX0X_SUPPORTED) -if (CXX11_SUPPORTED) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" ) -elseif (CXX0X_SUPPORTED) # gcc 4.6 - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x" ) -elseif (NOT MSVC) - message(SEND_ERROR "C++11 standard not seems to be supported by compiler. Too old version?") -endif () +if(CXX17_SUPPORTED) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") +elseif(CXX11_SUPPORTED) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +else() + message(SEND_ERROR "C++17 nor C++11 standard not seems to be supported by compiler. Too old version?") +endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pipe") @@ -424,8 +409,6 @@ message(STATUS " MESHNET : ${WITH_MESHNET}") message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}") message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}") -message(STATUS " I2LUA : ${WITH_I2LUA}") -message(STATUS " WEBSOCKETS : ${WITH_WEBSOCKETS}") message(STATUS "---------------------------------------") #Handle paths nicely diff -Nru i2pd-2.29.0/ChangeLog i2pd-2.31.0/ChangeLog --- i2pd-2.29.0/ChangeLog 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/ChangeLog 2020-04-10 17:33:54.000000000 +0000 @@ -1,6 +1,43 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog +## [2.31.0] - 2020-04-10 +### Added +- NTCP2 through HTTP proxy +- Publish LeaseSet2 for I2CP destinations +- Show status page on main activity for android +- Handle ECIESFlag in DatabaseLookup at floodfill +- C++17 features for eligible compilers +### Changed +- Droped Websockets and Lua support +- Send DeliveryStatusMsg for LeaseSet for ECIES-X25519-AEAD-Ratchet +- Keep sending new session reply until established for ECIES-X25519-AEAD-Ratchet +- Updated SSU log messages +- Reopen SSU socket on exception +- Security hardening headers in web console +- Various web console changes +- Various QT changes +### Fixed +- NTCP2 socket descriptors leak +- Race condition with router's identity in transport sessions +- Not terminated streams remain forever + +## [2.30.0] - 2020-02-25 +### Added +- Single threaded SAM +- Experimental support of ECIES-X25519-AEAD-Ratchet crypto type +### Changed +- Minimal MTU size is 1280 for ipv6 +- Use unordered_map instead map for destination's sessions and tags list +- Use std::shuffle instead std::random_shuffle +- SAM is single threaded by default +- Reseeds list +### Fixed +- Correct termination of streaming destination +- Extra ',' in RouterInfo response in I2PControl +- SAM crash on session termination +- Storage for Android 10 + ## [2.29.0] - 2019-10-21 ### Added - Client auth flag for b33 address diff -Nru i2pd-2.29.0/contrib/certificates/reseed/backup_at_mail.i2p.crt i2pd-2.31.0/contrib/certificates/reseed/backup_at_mail.i2p.crt --- i2pd-2.29.0/contrib/certificates/reseed/backup_at_mail.i2p.crt 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/contrib/certificates/reseed/backup_at_mail.i2p.crt 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFfTCCA2WgAwIBAgIEOprmhjANBgkqhkiG9w0BAQ0FADBvMQswCQYDVQQGEwJY -WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt -b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEYMBYGA1UEAwwPYmFja3VwQG1haWwu -aTJwMB4XDTEzMTAxMzEzNDQ1NVoXDTIzMTAxMzEzNDQ1NVowbzELMAkGA1UEBhMC -WFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFub255 -bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGDAWBgNVBAMMD2JhY2t1cEBtYWls -LmkycDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIoAkobXwk/Enf1d -roHyqCyvcJfZJVTwb/LgYWAvCBMCr+RGqlSgtk3g69Y3I0xU08fD2kGt3r5Pwsbr -omXIbJAcccyLqmQ5QX6QgL+X9VpMDp9C4h2RogCrqLBAWw4cuZ4RS9VCpP1Yis7H -uejYqENP86p7BsRnuW/4cYnfunAdMpss4LpRGQXt1nTX+kfgCYgnKFbFqwAHt7yV -Ds+Pe6FuBHPlp+sc1amKRcUnSvhXLsv43VicnT7xYL/kUsN83wrtHA3B4aGDx3aA -3/EzuRmIXQB0BlTZILMEyYwG/nc4OsW82QYrvEZ9BIg9A4lF/wS/KZCICPxLF2zo -dGjnmlgkiA4s8eO+va/ElHyELjckVXqmG1eXHhSkEsDvOQJy01IUuwLinvq7cUbJ -HfJBZJllEg+sLDCv3FkEqN+XjBNFfQN4oNew4w6IPY6YH1INVB9LL0Cmdu4DudLv -TY8OcI8eSfez3hmm+pYQ23PJRYYnvRDnRECyIWBegkckWRh8U/WvZUYUvETK6EDl -/0KpTtfzX6MqHA5D6bTAB8Y3ijGMLrZ/B5vj5yCoZbLiGme9X2moR2k1LEhdhtzV -exsqezCpg6dn48FTX7mHjvR5/r4kz2jqBGmdPUWIIxnjFUzDUK3llVQiHihleHpe -jL4LqnhBGKWFRTaVwaIkBG4zAfIzAgMBAAGjITAfMB0GA1UdDgQWBBQNkfW7bSMl -1/4KDbgwrkf9x1Zu/TANBgkqhkiG9w0BAQ0FAAOCAgEAGg3a3rTf0EznQocmio0T -5gCoL0n8h6yKW/PyPAIELrd9wiYjhJFcWvMTcJJJnVqmAL5vpvhaAFVtAfx70MGa -0DZ7FvytK5hEfF4IqOFDyEEVGJR5rIpVK4MeI1nmwEsxdbW+FhODjtRzgYO8XBME -Xj4aY1FWg9vxc3reUj6PSFsZtsB0aLiRgL9JDovJIiRw0Uqr1v2wXBte5yVCxDge -vTREZtpK4cKetoOa68pwSXI32JwKE18j6bfdKVBCcYQKlKP/3gHGduaDrQv3w32S -DRym5s6MREeTUOtAw4wq46KpdOX8yyAqJPrCfMwS6ORd3t+egqOw0PUnsqb97w4O -lUtrRYvb2cOj60SmRx4vJvItyuHbKqIK7o2e1RcUZPXYoAVx2ww4XB2Wk4D7LSAs -cS7nLj8yAqzJ2qqtBzxu+zILJtkVa12dKF0xmS0BxBp4sCYiBtmAVE8AWQqEuSHA -FrMWqoXcjcfdvvyX487FFWWUE7ZBIn0hee2sK9J9+SPtqczJaN7TF3K3nzo65WJG -1epltmq2Ugjb67Gz7v4y7H23DJ/qhm8yLtCHTj69HTta5I08j6Kut924WLZaiMO/ -4YoEL5AE63X0sxYibKFQiq7FW5nUJA280GRlY3xSMFzlB2ggazrUV3YAWVDhfdnI -flpzWXkFM2D36OUaubfe9YY= ------END CERTIFICATE----- diff -Nru i2pd-2.29.0/contrib/certificates/reseed/reseedi2pnetin_at_mail.i2p.crt i2pd-2.31.0/contrib/certificates/reseed/reseedi2pnetin_at_mail.i2p.crt --- i2pd-2.29.0/contrib/certificates/reseed/reseedi2pnetin_at_mail.i2p.crt 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/contrib/certificates/reseed/reseedi2pnetin_at_mail.i2p.crt 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF3DCCA8SgAwIBAgIQPxUlcrbHX/xdyJ09E36rJzANBgkqhkiG9w0BAQsFADB3 -MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK -ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEgMB4GA1UEAwwX -cmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwHhcNMTgxMjA3MTYzNDIxWhcNMjgxMjA3 -MTYzNDIxWjB3MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhY -MR4wHAYDVQQKExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEg -MB4GA1UEAwwXcmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQC912NDk6x85uqB4gyQQcded0RVrbWehWOanDRv7kC3 -92jeziPbeMtqrLsfU1MdDtQiGijpNkQ/IIitPw+6vJAIh82gyOUZvsn2XOyb/Fz0 -Fu8OrDghwl39yK8kwtqCFw3VAgafgKxz2oRge9mxFBECi50vYEPIBwNhr4yc/opu -wWUmzmRyX4gD7vKmRU6ZTwX4LXnwdl+5VbW3updcZKsDuTnKvC9FGhDRR9kIk2G9 -43sLN263nCYPykP7DaB1cUdi1vDEMw5dot+eu16qTIbuypEvYNvbB/9FyCQllm1h -vBbSku3IYpcnRPmoeyhoR/MmCySRbK5R4SrSsVD1YBpwxgn0Q4+fzEgFzT9P4oez -HkDGKVP2HdgmXx9j36fEqqvjqzRleWDwEWwIZVRLCFO+hhhT3JAjnNGJTWv1SQGB -8tz9nyYTJuhvyHE/CO5owFeCdeOGMq2KPge9w34T+mvewTEEhGU8yRAt8Xp8s5Y9 -RCUGvuQ79+edRtj7FJg7yVB8pAQ+VB9msNQvzrTnPYC9Wo7chJhBiraMiIabzIhC -f34Gg9lkX1N0dVND5rnZWwzBM6JhNG1iZZCRHVPnXdZRixUlqmFpCP/eekshksj/ -6UP/WeGA6X4HyEsC6QEf7eMhcHYjyyTzYagKrwCHg77fmIjF8rmpP2LqWSQW8bDD -uQIDAQABo2QwYjAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIG -CCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wIAYDVR0OBBkEF3Jlc2VlZGkycG5l -dGluQG1haWwuaTJwMA0GCSqGSIb3DQEBCwUAA4ICAQCWpXs6iuTy/w2R7q7Ua6vl -JYZwbQ+czk5ydzkBgcNkMMMNRT7sZR9xYvV+ftiL4bFQP/3ZJyo7cYz2Q6+M3oAm -YDcZWBkLUVihSlMxhWwmeFTKV2EL+bzwY1V/cy7wgukKnFIes75dLP/v25jgjdlw -Xe6R+fQM0EoHeVzzrWk/qYp6oEwtQXfZnUu/Bf45hRnnHBzzh1wCql41vbEs3Niq -+SVwY1wLT0yC1L8HqjCLX1/L5PAXxbvEGzwnXSkLKK4bPxdmVDZvS9uzXrWmTbNi -HpKIFnOif16zSgyeaOM7HETIJuVzgooUMtt+Vsr1VGdtm6K7I9J5C+rX/ckU8oaX -UjmzhWXudN0VTslogsKUCV6xG2CskeE3wnuT8HYXz9NMw6c/kIGH4hY7LcfU8Teu -QjSy2RRvy6InmZNV5sY9lzzO6romEycSoUlpCa3Ltb/5KKoYZFTsXr8suqJk89lC -e+TVMHqOZdLK/usqQDcafLypHpw9SH2Tg4jrzV/zLqacbjx6bZD5IrpY0Gf7BXg/ -pikwyA9c490G6ZcWrSEP8bzh6LL2rA2AwxaeJJNVyLHCSLrn/7DezM5J/qhd90Qg -kcZGJrUOCSWl6mDvUZn5XiQ955XwOnZQ+wsM85B3CVX22x5bp0SYWHCQBPnthPwP -Q5DD3jExbpwG5n35HEcHYw== ------END CERTIFICATE----- diff -Nru i2pd-2.29.0/contrib/rpm/i2pd-git.spec i2pd-2.31.0/contrib/rpm/i2pd-git.spec --- i2pd-2.29.0/contrib/rpm/i2pd-git.spec 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/contrib/rpm/i2pd-git.spec 2020-04-10 17:33:54.000000000 +0000 @@ -1,7 +1,7 @@ %define git_hash %(git rev-parse HEAD | cut -c -7) Name: i2pd-git -Version: 2.29.0 +Version: 2.31.0 Release: git%{git_hash}%{?dist} Summary: I2P router written in C++ Conflicts: i2pd @@ -55,14 +55,22 @@ %endif %endif +%if 0%{?mageia} > 7 +pushd build make %{?_smp_mflags} +popd +%else +make %{?_smp_mflags} +%endif %install -cd build +pushd build + %if 0%{?mageia} -cd build +pushd build %endif + chrpath -d i2pd %{__install} -D -m 755 i2pd %{buildroot}%{_sbindir}/i2pd %{__install} -D -m 755 %{_builddir}/%{name}-%{version}/contrib/i2pd.conf %{buildroot}%{_sysconfdir}/i2pd/i2pd.conf @@ -110,6 +118,12 @@ %changelog +* Fri Apr 10 2020 orignal - 2.31.0 +- update to 2.31.0 + +* Tue Feb 25 2020 orignal - 2.30.0 +- update to 2.30.0 + * Mon Oct 21 2019 orignal - 2.29.0 - update to 2.29.0 diff -Nru i2pd-2.29.0/contrib/rpm/i2pd.spec i2pd-2.31.0/contrib/rpm/i2pd.spec --- i2pd-2.29.0/contrib/rpm/i2pd.spec 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/contrib/rpm/i2pd.spec 2020-04-10 17:33:54.000000000 +0000 @@ -1,5 +1,5 @@ Name: i2pd -Version: 2.29.0 +Version: 2.31.0 Release: 1%{?dist} Summary: I2P router written in C++ Conflicts: i2pd-git @@ -53,14 +53,22 @@ %endif %endif +%if 0%{?mageia} > 7 +pushd build make %{?_smp_mflags} +popd +%else +make %{?_smp_mflags} +%endif %install -cd build +pushd build + %if 0%{?mageia} -cd build +pushd build %endif + chrpath -d i2pd install -D -m 755 i2pd %{buildroot}%{_sbindir}/i2pd install -D -m 755 %{_builddir}/%{name}-%{version}/contrib/i2pd.conf %{buildroot}%{_sysconfdir}/i2pd/i2pd.conf @@ -108,6 +116,12 @@ %changelog +* Fri Apr 10 2020 orignal - 2.31.0 +- update to 2.31.0 + +* Tue Feb 25 2020 orignal - 2.30.0 +- update to 2.30.0 + * Mon Oct 21 2019 orignal - 2.29.0 - update to 2.29.0 diff -Nru i2pd-2.29.0/daemon/Daemon.cpp i2pd-2.31.0/daemon/Daemon.cpp --- i2pd-2.29.0/daemon/Daemon.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/daemon/Daemon.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -26,9 +26,6 @@ #include "Timestamp.h" #include "util.h" -#include "Event.h" -#include "Websocket.h" - namespace i2p { namespace util @@ -43,9 +40,6 @@ std::unique_ptr m_I2PControlService; std::unique_ptr UPnP; std::unique_ptr m_NTPSync; -#ifdef WITH_EVENTS - std::unique_ptr m_WebsocketServer; -#endif }; Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {} @@ -62,12 +56,12 @@ return service; } - bool Daemon_Singleton::init(int argc, char* argv[]) { - return init(argc, argv, nullptr); - } + bool Daemon_Singleton::init(int argc, char* argv[]) { + return init(argc, argv, nullptr); + } - bool Daemon_Singleton::init(int argc, char* argv[], std::shared_ptr logstream) - { + bool Daemon_Singleton::init(int argc, char* argv[], std::shared_ptr logstream) + { i2p::config::Init(); i2p::config::ParseCmdline(argc, argv); @@ -110,10 +104,10 @@ logs = "file"; i2p::log::Logger().SetLogLevel(loglevel); - if (logstream) { - LogPrint(eLogInfo, "Log: will send messages to std::ostream"); - i2p::log::Logger().SendTo (logstream); - } else if (logs == "file") { + if (logstream) { + LogPrint(eLogInfo, "Log: will send messages to std::ostream"); + i2p::log::Logger().SendTo (logstream); + } else if (logs == "file") { if (logfile == "") logfile = i2p::fs::DataDirPath("i2pd.log"); LogPrint(eLogInfo, "Log: will send messages to ", logfile); @@ -127,7 +121,7 @@ // use stdout -- default } - LogPrint(eLogInfo, "i2pd v", VERSION, " starting"); + LogPrint(eLogInfo, "i2pd v", VERSION, " starting"); LogPrint(eLogDebug, "FS: main config file: ", config); LogPrint(eLogDebug, "FS: data directory: ", datadir); @@ -151,8 +145,8 @@ LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port); i2p::context.UpdatePort (port); } - i2p::context.SetSupportsV6 (ipv6); - i2p::context.SetSupportsV4 (ipv4); + i2p::context.SetSupportsV6 (ipv6); + i2p::context.SetSupportsV4 (ipv4); bool ntcp; i2p::config::GetOption("ntcp", ntcp); i2p::context.PublishNTCPAddress (ntcp, !ipv6); @@ -233,15 +227,15 @@ if (family.length () > 0) LogPrint(eLogInfo, "Daemon: family set to ", family); - bool trust; i2p::config::GetOption("trust.enabled", trust); - if (trust) - { - LogPrint(eLogInfo, "Daemon: explicit trust enabled"); - std::string fam; i2p::config::GetOption("trust.family", fam); + bool trust; i2p::config::GetOption("trust.enabled", trust); + if (trust) + { + LogPrint(eLogInfo, "Daemon: explicit trust enabled"); + std::string fam; i2p::config::GetOption("trust.family", fam); std::string routers; i2p::config::GetOption("trust.routers", routers); bool restricted = false; - if (fam.length() > 0) - { + if (fam.length() > 0) + { std::set fams; size_t pos = 0, comma; do @@ -253,7 +247,7 @@ while (comma != std::string::npos); i2p::transport::transports.RestrictRoutesToFamilies(fams); restricted = fams.size() > 0; - } + } if (routers.length() > 0) { std::set idents; size_t pos = 0, comma; @@ -272,14 +266,15 @@ } if(!restricted) LogPrint(eLogError, "Daemon: no trusted routers of families specififed"); - } - bool hidden; i2p::config::GetOption("trust.hidden", hidden); - if (hidden) - { - LogPrint(eLogInfo, "Daemon: using hidden mode"); - i2p::data::netdb.SetHidden(true); - } - return true; + } + + bool hidden; i2p::config::GetOption("trust.hidden", hidden); + if (hidden) + { + LogPrint(eLogInfo, "Daemon: using hidden mode"); + i2p::data::netdb.SetHidden(true); + } + return true; } bool Daemon_Singleton::start() @@ -322,7 +317,7 @@ bool http; i2p::config::GetOption("http.enabled", http); if (http) { std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); - uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); + uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); LogPrint(eLogInfo, "Daemon: starting HTTP Server at ", httpAddr, ":", httpPort); d.httpServer = std::unique_ptr(new i2p::http::HTTPServer(httpAddr, httpPort)); d.httpServer->Start(); @@ -344,26 +339,11 @@ d.m_I2PControlService = std::unique_ptr(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort)); d.m_I2PControlService->Start (); } -#ifdef WITH_EVENTS - - bool websocket; i2p::config::GetOption("websockets.enabled", websocket); - if(websocket) { - std::string websocketAddr; i2p::config::GetOption("websockets.address", websocketAddr); - uint16_t websocketPort; i2p::config::GetOption("websockets.port", websocketPort); - LogPrint(eLogInfo, "Daemon: starting Websocket server at ", websocketAddr, ":", websocketPort); - d.m_WebsocketServer = std::unique_ptr(new i2p::event::WebsocketServer (websocketAddr, websocketPort)); - d.m_WebsocketServer->Start(); - i2p::event::core.SetListener(d.m_WebsocketServer->ToListener()); - } -#endif return true; } bool Daemon_Singleton::stop() { -#ifdef WITH_EVENTS - i2p::event::core.SetListener(nullptr); -#endif LogPrint(eLogInfo, "Daemon: shutting down"); LogPrint(eLogInfo, "Daemon: stopping Client"); i2p::client::context.Stop(); @@ -397,13 +377,6 @@ d.m_I2PControlService->Stop (); d.m_I2PControlService = nullptr; } -#ifdef WITH_EVENTS - if (d.m_WebsocketServer) { - LogPrint(eLogInfo, "Daemon: stopping Websocket server"); - d.m_WebsocketServer->Stop(); - d.m_WebsocketServer = nullptr; - } -#endif i2p::crypto::TerminateCrypto (); i2p::log::Logger().Stop(); diff -Nru i2pd-2.29.0/daemon/HTTPServer.cpp i2pd-2.31.0/daemon/HTTPServer.cpp --- i2pd-2.29.0/daemon/HTTPServer.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/daemon/HTTPServer.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -54,6 +54,7 @@ " body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n" " a, .slide label { text-decoration: none; color: #894C84; }\r\n" " a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n" + " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none; color: initial; width: 1.5em;}\r\n" " .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n" " .wrapper { margin: 0 auto; padding: 1em; max-width: 60em; }\r\n" " .left { float: left; position: absolute; }\r\n" @@ -63,9 +64,11 @@ " .tunnel.failed { color: #D33F3F; }\r\n" " .tunnel.building { color: #434343; }\r\n" " caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n" - " table { width: 100%; border-collapse: collapse; text-align: center; }\r\n" - " .slide p, .slide [type='checkbox']{ display:none; }\r\n" - " .slide [type='checkbox']:checked ~ p { display:block; margin-top: 0; padding: 0; }\r\n" + " table { display: table; border-collapse: collapse; text-align: center; }\r\n" + " table.extaddr { text-align: left; }\r\n table.services { width: 100%; }" + " .streamdest { width: 120px; max-width: 240px; overflow: hidden; text-overflow: ellipsis;}\r\n" + " .slide div.content, .slide [type='checkbox'] { display: none; }\r\n" + " .slide [type='checkbox']:checked ~ div.content { display: block; margin-top: 0; padding: 0; }\r\n" " .disabled:after { color: #D33F3F; content: \"Disabled\" }\r\n" " .enabled:after { color: #56B734; content: \"Enabled\" }\r\n" "\r\n"; @@ -89,10 +92,12 @@ const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test"; const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config"; const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel"; + const char HTTP_COMMAND_KILLSTREAM[] = "closestream"; const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; const char HTTP_PARAM_ADDRESS[] = "address"; static std::string ConvertTime (uint64_t time); + std::map HTTPConnection::m_Tokens; static void ShowUptime (std::stringstream& s, int seconds) { @@ -177,8 +182,10 @@ "
\r\n" " Main page
\r\n
\r\n" " Router commands
\r\n" - " Local destinations
\r\n" - " LeaseSets
\r\n" + " Local destinations
\r\n"; + if (i2p::context.IsFloodfill ()) + s << " LeaseSets
\r\n"; + s << " Tunnels
\r\n" " Transit tunnels
\r\n" " Transports
\r\n" @@ -203,10 +210,7 @@ s << "ERROR: " << string << "
\r\n"; } - void ShowStatus ( - std::stringstream& s, - bool includeHiddenContent, - i2p::http::OutputFormatEnum outputFormat) + void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat) { s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); @@ -233,11 +237,8 @@ } s << "
\r\n"; #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) - if (auto remains = Daemon.gracefulShutdownInterval) { - s << "Stopping in: "; - s << remains << " seconds"; - s << "
\r\n"; - } + if (auto remains = Daemon.gracefulShutdownInterval) + s << "Stopping in: " << remains << " seconds
\r\n"; #endif auto family = i2p::context.GetFamily (); if (family.length () > 0) @@ -253,51 +254,56 @@ ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)
\r\n"; s << "Data path: " << i2p::fs::GetDataDir() << "
\r\n"; - s << "
"; - if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) { - s << "\r\n\r\n

\r\n"; - } - if(includeHiddenContent) { - s << "Router Ident: " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; - s << "Router Family: " << i2p::context.GetRouterInfo().GetProperty("family") << "
\r\n"; + s << "

"; + if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) { + s << "\r\n\r\n
\r\n"; + } + if(includeHiddenContent) { + s << "Router Ident: " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; + if (!i2p::context.GetRouterInfo().GetProperty("family").empty()) + s << "Router Family: " << i2p::context.GetRouterInfo().GetProperty("family") << "
\r\n"; s << "Router Caps: " << i2p::context.GetRouterInfo().GetProperty("caps") << "
\r\n"; - s << "Our external address:" << "
\r\n" ; + s << "Our external address:" << "
\r\n\r\n"; for (const auto& address : i2p::context.GetRouterInfo().GetAddresses()) { + s << "\r\n"; if (address->IsNTCP2 () && !address->IsPublishedNTCP2 ()) { - s << "NTCP2"; + s << "\r\n\r\n"; continue; } switch (address->transportStyle) { case i2p::data::RouterInfo::eTransportNTCP: { - s << "NTCP"; + s << "\r\n"; break; } case i2p::data::RouterInfo::eTransportSSU: + { + s << "\r\n"; + break; + } default: - s << "Unknown  "; + s << "\r\n"; } - s << address->host.to_string() << ":" << address->port << "
\r\n"; + s << "\r\n\r\n"; } - } - s << "

\r\n\r\n"; - if(outputFormat==OutputFormatEnum::forQtUi) { - s << "
"; - } - s << "Routers: " << i2p::data::netdb.GetNumRouters () << " "; + s << "
NTCP2"; if (address->host.is_v6 ()) s << "v6"; - s << "   supported
\r\n"; + s << "
supported
NTCP"; if (address->IsPublishedNTCP2 ()) s << "2"; if (address->host.is_v6 ()) s << "v6"; - s << "  "; + s << "SSU"; if (address->host.is_v6 ()) - s << "SSUv6     "; - else - s << "SSU     "; - break; + s << "v6"; + s << "Unknown" << address->host.to_string() << ":" << address->port << "
\r\n"; + } + s << "
\r\n
\r\n"; + if(outputFormat==OutputFormatEnum::forQtUi) { + s << "
"; + } + s << "Routers: " << i2p::data::netdb.GetNumRouters () << " "; s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " "; s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "
\r\n"; @@ -308,17 +314,17 @@ s << "Client Tunnels: " << std::to_string(clientTunnelCount) << " "; s << "Transit Tunnels: " << std::to_string(transitTunnelCount) << "
\r\n
\r\n"; - if(outputFormat==OutputFormatEnum::forWebConsole) { - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); - s << "\r\n"; - s << "
Services
ServiceState
" << "HTTP Proxy" << "
" << "SOCKS Proxy" << "
" << "BOB" << "
" << "SAM" << "
" << "I2CP" << "
" << "I2PControl" << "
\r\n"; - } + if(outputFormat==OutputFormatEnum::forWebConsole) { + bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "
Services
" << "HTTP Proxy" << "
" << "SOCKS Proxy" << "
" << "BOB" << "
" << "SAM" << "
" << "I2CP" << "
" << "I2PControl" << "
\r\n"; + } } void ShowLocalDestinations (std::stringstream& s) @@ -352,24 +358,25 @@ static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr dest) { - s << "Base64:
\r\n
\r\n
\r\n"; if (dest->IsEncryptedLeaseSet ()) { i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ()); - s << "
\r\n\r\n

\r\n"; + s << "

\r\n\r\n
\r\n"; s << blinded.ToB33 () << ".b32.i2p
\r\n"; - s << "

\r\n
\r\n"; + s << "
\r\n
\r\n"; } if(dest->GetNumRemoteLeaseSets()) { - s << "
\r\n\r\n

\r\n"; + s << "

\r\n\r\n
\r\n"; for(auto& it: dest->GetLeaseSets ()) - s << it.first.ToBase32 () << " " << (int)it.second->GetStoreType () << "
\r\n"; - s << "

\r\n\r\n"; + s << "\r\n"; + s << "
AddressTypeEncType
" << it.first.ToBase32 () << "" << (int)it.second->GetStoreType () << "" << (int)it.second->GetEncryptionType () <<"
\r\n
\r\n
\r\n
\r\n"; } else - s << "LeaseSets: 0
\r\n"; + s << "LeaseSets: 0
\r\n
\r\n"; auto pool = dest->GetTunnelPool (); if (pool) { @@ -394,28 +401,31 @@ if (!dest->GetSessions ().empty ()) { std::stringstream tmp_s; uint32_t out_tags = 0; for (const auto& it: dest->GetSessions ()) { - tmp_s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " " << it.second->GetNumOutgoingTags () << "
\r\n"; + tmp_s << "" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "" << it.second->GetNumOutgoingTags () << "\r\n"; out_tags = out_tags + it.second->GetNumOutgoingTags (); } - s << "
\r\n\r\n

\r\n" << tmp_s.str () << "

\r\n
\r\n"; + s << "
\r\n\r\n" + << "
\r\n\r\n" << tmp_s.str () << "
DestinationAmount
\r\n
\r\n
\r\n"; } else s << "Outgoing: 0
\r\n"; s << "
\r\n"; } - void ShowLocalDestination (std::stringstream& s, const std::string& b32) + void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token) { s << "Local Destination:
\r\n
\r\n"; i2p::data::IdentHash ident; ident.FromBase32 (b32); auto dest = i2p::client::context.FindLocalDestination (ident); + if (dest) { ShowLeaseSetDestination (s, dest); // show streams - s << "\r\n"; - s << ""; - s << ""; + s << "
Streams
StreamIDDestination
\r\n\r\n\r\n"; + s << ""; + s << ""; s << ""; s << ""; s << ""; @@ -424,13 +434,20 @@ s << ""; s << ""; s << ""; - s << "\r\n"; + s << "\r\n\r\n\r\n"; for (const auto& it: dest->GetAllStreams ()) { + auto streamDest = i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()); s << ""; - s << ""; - s << ""; + s << ""; + if (it->GetRecvStreamID ()) { + s << ""; + } else { + s << ""; s << ""; s << ""; s << ""; @@ -441,7 +458,7 @@ s << ""; s << "\r\n"; } - s << "
Streams
StreamID"; // Stream closing button column + s << "DestinationSentReceivedOutRTTWindowStatus
" << it->GetSendStreamID () << "" << i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()) << "" << it->GetRecvStreamID () << ""; + } + s << "" << streamDest << "" << it->GetNumSentBytes () << "" << it->GetNumReceivedBytes () << "" << it->GetSendQueueSize () << "" << (int)it->GetStatus () << "
"; + s << "\r\n"; } } @@ -463,46 +480,57 @@ void ShowLeasesSets(std::stringstream& s) { - s << "LeaseSets:
\r\n
\r\n"; - int counter = 1; - // for each lease set - i2p::data::netdb.VisitLeaseSets( - [&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr leaseSet) - { - // create copy of lease set so we extract leases - auto storeType = leaseSet->GetStoreType (); - std::unique_ptr ls; - if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET) - ls.reset (new i2p::data::LeaseSet (leaseSet->GetBuffer(), leaseSet->GetBufferLen())); - else - ls.reset (new i2p::data::LeaseSet2 (storeType, leaseSet->GetBuffer(), leaseSet->GetBufferLen())); - if (!ls) return; - s << "
\r\n"; - if (!ls->IsValid()) - s << "
!! Invalid !!
\r\n"; - s << "
\r\n"; - s << "\r\n

\r\n"; - s << "Store type: " << (int)storeType << "
\r\n"; - s << "Expires: " << ConvertTime(ls->GetExpirationTime()) << "
\r\n"; - if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2) - { - // leases information is available - auto leases = ls->GetNonExpiredLeases(); - s << "Non Expired Leases: " << leases.size() << "
\r\n"; - for ( auto & l : leases ) + if (i2p::data::netdb.GetNumLeaseSets ()) + { + s << "LeaseSets:
\r\n
\r\n"; + int counter = 1; + // for each lease set + i2p::data::netdb.VisitLeaseSets( + [&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr leaseSet) + { + // create copy of lease set so we extract leases + auto storeType = leaseSet->GetStoreType (); + std::unique_ptr ls; + if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET) + ls.reset (new i2p::data::LeaseSet (leaseSet->GetBuffer(), leaseSet->GetBufferLen())); + else + ls.reset (new i2p::data::LeaseSet2 (storeType, leaseSet->GetBuffer(), leaseSet->GetBufferLen())); + if (!ls) return; + s << "

\r\n"; + if (!ls->IsValid()) + s << "
!! Invalid !!
\r\n"; + s << "
\r\n"; + s << "\r\n
\r\n"; + s << "Store type: " << (int)storeType << "
\r\n"; + s << "Expires: " << ConvertTime(ls->GetExpirationTime()) << "
\r\n"; + if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2) { - s << "Gateway: " << l->tunnelGateway.ToBase64() << "
\r\n"; - s << "TunnelID: " << l->tunnelID << "
\r\n"; - s << "EndDate: " << ConvertTime(l->endDate) << "
\r\n"; + // leases information is available + auto leases = ls->GetNonExpiredLeases(); + s << "Non Expired Leases: " << leases.size() << "
\r\n"; + for ( auto & l : leases ) + { + s << "Gateway: " << l->tunnelGateway.ToBase64() << "
\r\n"; + s << "TunnelID: " << l->tunnelID << "
\r\n"; + s << "EndDate: " << ConvertTime(l->endDate) << "
\r\n"; + } } - } - s << "

\r\n
\r\n
\r\n"; - } - ); - // end for each lease set + s << "
\r\n
\r\n
\r\n"; + } + ); + // end for each lease set + } + else if (!i2p::context.IsFloodfill ()) + { + s << "LeaseSets: not floodfill.
\r\n"; + } + else + { + s << "LeaseSets: 0
\r\n"; + } } void ShowTunnels (std::stringstream& s) @@ -564,16 +592,23 @@ void ShowTransitTunnels (std::stringstream& s) { - s << "Transit tunnels:
\r\n
\r\n"; - for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) + if(i2p::tunnel::tunnels.CountTransitTunnels()) { - if (std::dynamic_pointer_cast(it)) - s << it->GetTunnelID () << " ⇒ "; - else if (std::dynamic_pointer_cast(it)) - s << " ⇒ " << it->GetTunnelID (); - else - s << " ⇒ " << it->GetTunnelID () << " ⇒ "; - s << " " << it->GetNumTransmittedBytes () << "
\r\n"; + s << "Transit tunnels:
\r\n
\r\n"; + for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) + { + if (std::dynamic_pointer_cast(it)) + s << it->GetTunnelID () << " ⇒ "; + else if (std::dynamic_pointer_cast(it)) + s << " ⇒ " << it->GetTunnelID (); + else + s << " ⇒ " << it->GetTunnelID () << " ⇒ "; + s << " " << it->GetNumTransmittedBytes () << "
\r\n"; + } + } + else + { + s << "Transit tunnels: no transit tunnels currently built.
\r\n"; } } @@ -607,13 +642,15 @@ } if (!tmp_s.str ().empty ()) { - s << "
\r\n\r\n

"; - s << tmp_s.str () << "

\r\n
\r\n"; + s << "
\r\n\r\n
" + << tmp_s.str () << "
\r\n
\r\n"; } if (!tmp_s6.str ().empty ()) { - s << "
\r\n\r\n

"; - s << tmp_s6.str () << "

\r\n
\r\n"; + s << "
\r\n\r\n
" + << tmp_s6.str () << "
\r\n
\r\n"; } } @@ -640,7 +677,7 @@ auto sessions = ssuServer->GetSessions (); if (!sessions.empty ()) { - s << "
\r\n\r\n

"; + s << "

\r\n\r\n
"; for (const auto& it: sessions) { auto endpoint = it.second->GetRemoteEndpoint (); @@ -652,12 +689,12 @@ s << " [itag:" << it.second->GetRelayTag () << "]"; s << "
\r\n" << std::endl; } - s << "

\r\n
\r\n"; + s << "
\r\n
\r\n"; } auto sessions6 = ssuServer->GetSessionsV6 (); if (!sessions6.empty ()) { - s << "
\r\n\r\n

"; + s << "

\r\n\r\n
"; for (const auto& it: sessions6) { auto endpoint = it.second->GetRemoteEndpoint (); @@ -669,7 +706,7 @@ s << " [itag:" << it.second->GetRelayTag () << "]"; s << "
\r\n" << std::endl; } - s << "

\r\n
\r\n"; + s << "
\r\n
\r\n"; } } } @@ -678,17 +715,24 @@ { std::string webroot; i2p::config::GetOption("http.webroot", webroot); auto sam = i2p::client::context.GetSAMBridge (); - if (!sam) { + if (!sam) + { ShowError(s, "SAM disabled"); return; } - s << "SAM Sessions:
\r\n
\r\n"; - for (auto& it: sam->GetSessions ()) + + if(sam->GetSessions ().size ()) { - auto& name = it.second->localDestination->GetNickname (); - s << ""; - s << name << " (" << it.first << ")
\r\n" << std::endl; + s << "SAM Sessions:
\r\n
\r\n"; + for (auto& it: sam->GetSessions ()) + { + auto& name = it.second->localDestination->GetNickname (); + s << ""; + s << name << " (" << it.first << ")
\r\n" << std::endl; + } } + else + s << "SAM Sessions: no sessions currently running.
\r\n"; } static void ShowSAMSession (std::stringstream& s, const std::string& id) @@ -858,7 +902,8 @@ m_Socket->close (); } - bool HTTPConnection::CheckAuth (const HTTPReq & req) { + bool HTTPConnection::CheckAuth (const HTTPReq & req) + { /* method #1: http://user:pass@127.0.0.1:7070/ */ if (req.uri.find('@') != std::string::npos) { URL url; @@ -920,7 +965,7 @@ } else if (req.uri.find("cmd=") != std::string::npos) { HandleCommand (req, res, s); } else { - ShowStatus (s, true, i2p::http::OutputFormatEnum::forWebConsole); + ShowStatus (s, true, i2p::http::OutputFormatEnum::forWebConsole); res.add_header("Refresh", "10"); } ShowPageTail (s); @@ -930,7 +975,23 @@ SendReply (res, content); } - std::map HTTPConnection::m_Tokens; + uint32_t HTTPConnection::CreateToken () + { + uint32_t token; + RAND_bytes ((uint8_t *)&token, 4); + token &= 0x7FFFFFFF; // clear first bit + auto ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_Tokens.begin (); it != m_Tokens.end (); ) + { + if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT) + it = m_Tokens.erase (it); + else + ++it; + } + m_Tokens[token] = ts; + return token; + } + void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s) { std::map params; @@ -947,18 +1008,7 @@ ShowTunnels (s); else if (page == HTTP_PAGE_COMMANDS) { - uint32_t token; - RAND_bytes ((uint8_t *)&token, 4); - token &= 0x7FFFFFFF; // clear first bit - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_Tokens.begin (); it != m_Tokens.end (); ) - { - if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT) - it = m_Tokens.erase (it); - else - ++it; - } - m_Tokens[token] = ts; + uint32_t token = CreateToken (); ShowCommands (s, token); } else if (page == HTTP_PAGE_TRANSIT_TUNNELS) @@ -966,7 +1016,10 @@ else if (page == HTTP_PAGE_LOCAL_DESTINATIONS) ShowLocalDestinations (s); else if (page == HTTP_PAGE_LOCAL_DESTINATION) - ShowLocalDestination (s, params["b32"]); + { + uint32_t token = CreateToken (); + ShowLocalDestination (s, params["b32"], token); + } else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION) ShowI2CPLocalDestination (s, params["i2cp_id"]); else if (page == HTTP_PAGE_SAM_SESSIONS) @@ -992,7 +1045,10 @@ url.parse(req.uri); url.parse_query(params); + std::string webroot; i2p::config::GetOption("http.webroot", webroot); + std::string redirect = "5; url=" + webroot + "?page=commands"; std::string token = params["token"]; + if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ()) { ShowError(s, "Invalid token"); @@ -1008,36 +1064,74 @@ i2p::context.SetAcceptsTunnels (true); else if (cmd == HTTP_COMMAND_DISABLE_TRANSIT) i2p::context.SetAcceptsTunnels (false); - else if (cmd == HTTP_COMMAND_SHUTDOWN_START) { + else if (cmd == HTTP_COMMAND_SHUTDOWN_START) + { i2p::context.SetAcceptsTunnels (false); #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) Daemon.gracefulShutdownInterval = 10*60; #elif defined(WIN32_APP) i2p::win32::GracefulShutdown (); #endif - } else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) { + } + else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) + { i2p::context.SetAcceptsTunnels (true); #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) Daemon.gracefulShutdownInterval = 0; #elif defined(WIN32_APP) i2p::win32::StopGracefulShutdown (); #endif - } else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) { + } + else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) + { #ifndef WIN32_APP Daemon.running = false; #else i2p::win32::StopWin32App (); #endif - } else if (cmd == HTTP_COMMAND_LOGLEVEL){ + } + else if (cmd == HTTP_COMMAND_LOGLEVEL) + { std::string level = params["level"]; SetLogLevel (level); - } else { + } + else if (cmd == HTTP_COMMAND_KILLSTREAM) + { + std::string b32 = params["b32"]; + uint32_t streamID = std::stoul(params["streamID"], nullptr); + + i2p::data::IdentHash ident; + ident.FromBase32 (b32); + auto dest = i2p::client::context.FindLocalDestination (ident); + + if (streamID) + { + if (dest) + { + if(dest->DeleteStream (streamID)) + s << "SUCCESS: Stream closed

\r\n"; + else + s << "ERROR: Stream not found or already was closed

\r\n"; + } + else + s << "ERROR: Destination not found

\r\n"; + } + else + s << "ERROR: StreamID can be null

\r\n"; + + s << "Return to destination page
\r\n"; + s << "

You will be redirected back in 5 seconds"; + redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32; + res.add_header("Refresh", redirect.c_str()); + return; + } + else + { res.code = 400; ShowError(s, "Unknown command: " + cmd); return; } - std::string webroot; i2p::config::GetOption("http.webroot", webroot); - std::string redirect = "5; url=" + webroot + "?page=commands"; + s << "SUCCESS: Command accepted

\r\n"; s << "Back to commands list
\r\n"; s << "

You will be redirected in 5 seconds"; @@ -1047,6 +1141,8 @@ void HTTPConnection::SendReply (HTTPRes& reply, std::string& content) { reply.add_header("X-Frame-Options", "SAMEORIGIN"); + reply.add_header("X-Content-Type-Options", "nosniff"); + reply.add_header("X-XSS-Protection", "1; mode=block"); reply.add_header("Content-Type", "text/html"); reply.body = content; diff -Nru i2pd-2.29.0/daemon/HTTPServer.h i2pd-2.31.0/daemon/HTTPServer.h --- i2pd-2.29.0/daemon/HTTPServer.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/daemon/HTTPServer.h 2020-04-10 17:33:54.000000000 +0000 @@ -35,6 +35,7 @@ void HandlePage (const HTTPReq & req, HTTPRes & res, std::stringstream& data); void HandleCommand (const HTTPReq & req, HTTPRes & res, std::stringstream& data); void SendReply (HTTPRes & res, std::string & content); + uint32_t CreateToken (); private: @@ -88,7 +89,7 @@ void ShowTransports (std::stringstream& s); void ShowSAMSessions (std::stringstream& s); void ShowI2PTunnels (std::stringstream& s); - void ShowLocalDestination (std::stringstream& s, const std::string& b32); + void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token); } // http } // i2p diff -Nru i2pd-2.29.0/daemon/I2PControl.cpp i2pd-2.31.0/daemon/I2PControl.cpp --- i2pd-2.29.0/daemon/I2PControl.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/daemon/I2PControl.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -394,13 +394,15 @@ void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { + bool first = true; for (auto it = params.begin (); it != params.end (); it++) { LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first); auto it1 = m_RouterInfoHandlers.find (it->first); if (it1 != m_RouterInfoHandlers.end ()) { - if (it != params.begin ()) results << ","; + if (!first) results << ","; + else first = false; (this->*(it1->second))(results); } else diff -Nru i2pd-2.29.0/debian/changelog i2pd-2.31.0/debian/changelog --- i2pd-2.29.0/debian/changelog 2020-02-03 20:35:27.000000000 +0000 +++ i2pd-2.31.0/debian/changelog 2020-04-20 03:25:01.000000000 +0000 @@ -1,8 +1,10 @@ -i2pd (2.29.0-1build1) focal; urgency=medium +i2pd (2.31.0-1) unstable; urgency=medium - * No change rebuild against new boost1.71 ABI + * New upstream release + * Bump Standards-Version to 4.5.0 + * Add upstream metadata - -- Dimitri John Ledkov Mon, 03 Feb 2020 20:35:27 +0000 + -- Yangfl Mon, 20 Apr 2020 11:25:01 +0800 i2pd (2.29.0-1) unstable; urgency=medium diff -Nru i2pd-2.29.0/debian/control i2pd-2.31.0/debian/control --- i2pd-2.29.0/debian/control 2020-02-03 20:35:27.000000000 +0000 +++ i2pd-2.31.0/debian/control 2020-04-20 03:25:01.000000000 +0000 @@ -1,10 +1,10 @@ Source: i2pd Section: net Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Yangfl +Maintainer: Yangfl Build-Depends: debhelper-compat (= 12), libboost-system-dev (>= 1.46), libboost-date-time-dev, libboost-filesystem-dev, libboost-program-options-dev, libminiupnpc-dev, libssl-dev, zlib1g-dev, cmake, libwebsocketpp-dev, -Standards-Version: 4.4.1 +Rules-Requires-Root: no +Standards-Version: 4.5.0 Homepage: https://i2pd.website Vcs-Git: https://salsa.debian.org/yangfl-guest/i2pd.git Vcs-Browser: https://salsa.debian.org/yangfl-guest/i2pd diff -Nru i2pd-2.29.0/debian/gbp.conf i2pd-2.31.0/debian/gbp.conf --- i2pd-2.29.0/debian/gbp.conf 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/debian/gbp.conf 2020-04-20 03:25:01.000000000 +0000 @@ -0,0 +1,4 @@ +[DEFAULT] +upstream-branch = upstream +debian-branch = master +pristine-tar = True diff -Nru i2pd-2.29.0/debian/gitlab-ci.yml i2pd-2.31.0/debian/gitlab-ci.yml --- i2pd-2.29.0/debian/gitlab-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/debian/gitlab-ci.yml 2020-04-20 03:25:01.000000000 +0000 @@ -0,0 +1,3 @@ +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff -Nru i2pd-2.29.0/debian/upstream/metadata i2pd-2.31.0/debian/upstream/metadata --- i2pd-2.29.0/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/debian/upstream/metadata 2020-04-20 03:25:01.000000000 +0000 @@ -0,0 +1,3 @@ +Bug-Submit: https://github.com/PurpleI2P/i2pd/issues +Repository: https://github.com/PurpleI2P/i2pd.git +Repository-Browse: https://github.com/PurpleI2P/i2pd diff -Nru i2pd-2.29.0/filelist.mk i2pd-2.31.0/filelist.mk --- i2pd-2.29.0/filelist.mk 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/filelist.mk 2020-04-10 17:33:54.000000000 +0000 @@ -5,13 +5,13 @@ # SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \ # Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp TunnelGateway.cpp \ # Destination.cpp Base.cpp I2PEndian.cpp FS.cpp Config.cpp Family.cpp \ -# Config.cpp HTTP.cpp Timestamp.cpp util.cpp api.cpp Event.cpp Gost.cpp +# Config.cpp HTTP.cpp Timestamp.cpp util.cpp api.cpp Gost.cpp LIB_SRC = $(wildcard $(LIB_SRC_DIR)/*.cpp) #LIB_CLIENT_SRC = \ # AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp MatchedDestination.cpp \ -# SAM.cpp SOCKS.cpp HTTPProxy.cpp I2CP.cpp WebSocks.cpp +# SAM.cpp SOCKS.cpp HTTPProxy.cpp I2CP.cpp LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_SRC_DIR)/*.cpp) diff -Nru i2pd-2.29.0/.gitignore i2pd-2.31.0/.gitignore --- i2pd-2.29.0/.gitignore 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/.gitignore 2020-04-10 17:33:54.000000000 +0000 @@ -258,9 +258,15 @@ # qt -qt/i2pd_qt/*.ui.autosave +qt/i2pd_qt/*.autosave qt/i2pd_qt/*.ui.bk* qt/i2pd_qt/*.ui_* #unknown android stuff android/libs/ + +#various logs +*LOGS/ + +qt/build-*.sh* + diff -Nru i2pd-2.29.0/libi2pd/api.cpp i2pd-2.31.0/libi2pd/api.cpp --- i2pd-2.29.0/libi2pd/api.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/api.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -77,7 +77,7 @@ std::shared_ptr CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params) { - auto localDestination = std::make_shared (keys, isPublic, params); + auto localDestination = std::make_shared (keys, isPublic, params); localDestination->Start (); return localDestination; } @@ -86,7 +86,7 @@ const std::map * params) { i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); - auto localDestination = std::make_shared (keys, isPublic, params); + auto localDestination = std::make_shared (keys, isPublic, params); localDestination->Start (); return localDestination; } diff -Nru i2pd-2.29.0/libi2pd/Config.cpp i2pd-2.31.0/libi2pd/Config.cpp --- i2pd-2.29.0/libi2pd/Config.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Config.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -60,7 +60,7 @@ ("share", value()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") ("ntcp", value()->default_value(false), "Enable NTCP transport (default: disabled)") ("ssu", value()->default_value(true), "Enable SSU transport (default: enabled)") - ("ntcpproxy", value()->default_value(""), "Proxy URL for NTCP transport") + ("ntcpproxy", value()->default_value(""), "Proxy URL for NTCP transport") #ifdef _WIN32 ("svcctl", value()->default_value(""), "Windows service management ('install' or 'remove')") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") @@ -131,6 +131,7 @@ ("sam.enabled", value()->default_value(true), "Enable or disable SAM Application bridge") ("sam.address", value()->default_value("127.0.0.1"), "SAM listen address") ("sam.port", value()->default_value(7656), "SAM listen port") + ("sam.singlethread", value()->default_value(true), "Sessions run in the SAM bridge's thread") ; options_description bob("BOB options"); @@ -190,10 +191,10 @@ "https://reseed.i2p-projekt.de/," "https://i2p.mooo.com/netDb/," "https://netdb.i2p2.no/," + "https://reseed.i2p2.no/," + "https://reseed2.i2p2.no/," // "https://us.reseed.i2p2.no:444/," // mamoth's shit // "https://uk.reseed.i2p2.no:444/," // mamoth's shit - "https://reseed.i2p.net.in/," - "https://download.xxlspeed.com/," "https://reseed-fr.i2pd.xyz/," "https://reseed.memcpy.io/," "https://reseed.onion.im/," @@ -217,13 +218,6 @@ ("trust.hidden", value()->default_value(false), "Should we hide our router from other routers?") ; - options_description websocket("Websocket Options"); - websocket.add_options() - ("websockets.enabled", value()->default_value(false), "Enable websocket server") - ("websockets.address", value()->default_value("127.0.0.1"), "Address to bind websocket server on") - ("websockets.port", value()->default_value(7666), "Port to bind websocket server on") - ; - options_description exploratory("Exploratory Options"); exploratory.add_options() ("exploratory.inbound.length", value()->default_value(2), "Exploratory inbound tunnel length") @@ -238,6 +232,7 @@ ("ntcp2.published", value()->default_value(true), "Publish NTCP2 (default: enabled)") ("ntcp2.port", value()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)") ("ntcp2.addressv6", value()->default_value("::"), "Address to bind NTCP2 on") + ("ntcp2.proxy", value()->default_value(""), "Proxy URL for NTCP2 transport") ; options_description nettime("Time sync options"); @@ -273,7 +268,6 @@ .add(reseed) .add(addressbook) .add(trust) - .add(websocket) .add(exploratory) .add(ntcp2) .add(nettime) @@ -304,16 +298,16 @@ std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl; std::cout << m_OptionsDesc; exit(EXIT_SUCCESS); - } + } else if (m_Options.count("version")) { std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl; - std::cout << "Boost version " + std::cout << "Boost version " << BOOST_VERSION / 100000 << "." // maj. version << BOOST_VERSION / 100 % 1000 << "." // min. version << BOOST_VERSION % 100 // patch version << std::endl; -#if defined(OPENSSL_VERSION_TEXT) +#if defined(OPENSSL_VERSION_TEXT) std::cout << OPENSSL_VERSION_TEXT << std::endl; #endif #if defined(LIBRESSL_VERSION_TEXT) diff -Nru i2pd-2.29.0/libi2pd/Crypto.cpp i2pd-2.31.0/libi2pd/Crypto.cpp --- i2pd-2.29.0/libi2pd/Crypto.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Crypto.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -367,6 +367,18 @@ #endif } + void X25519Keys::SetPrivateKey (const uint8_t * priv) + { +#if OPENSSL_X25519 + if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx); + if (m_Pkey) EVP_PKEY_free (m_Pkey); + m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32); + m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); +#else + memcpy (m_PrivateKey, priv, 32); +#endif + } + // ElGamal void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) { @@ -1259,18 +1271,29 @@ #endif } - void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out) + void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, + uint8_t * out, size_t outLen) { #if OPENSSL_HKDF - EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, NULL); + EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, nullptr); EVP_PKEY_derive_init (pctx); EVP_PKEY_CTX_set_hkdf_md (pctx, EVP_sha256()); - EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32); - EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen); + if (key && keyLen) + { + EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32); + EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen); + } + else + { + // zerolen + EVP_PKEY_CTX_hkdf_mode (pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY); + uint8_t tempKey[32]; unsigned int len; + HMAC(EVP_sha256(), salt, 32, nullptr, 0, tempKey, &len); + EVP_PKEY_CTX_set1_hkdf_key (pctx, tempKey, len); + } if (info.length () > 0) EVP_PKEY_CTX_add1_hkdf_info (pctx, info.c_str (), info.length ()); - size_t outlen = 64; - EVP_PKEY_derive (pctx, out, &outlen); + EVP_PKEY_derive (pctx, out, &outLen); EVP_PKEY_CTX_free (pctx); #else uint8_t prk[32]; unsigned int len; @@ -1278,8 +1301,11 @@ auto l = info.length (); memcpy (out, info.c_str (), l); out[l] = 0x01; HMAC(EVP_sha256(), prk, 32, out, l + 1, out, &len); - memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02; - HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len); + if (outLen > 32) // 64 + { + memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02; + HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len); + } #endif } diff -Nru i2pd-2.29.0/libi2pd/Crypto.h i2pd-2.31.0/libi2pd/Crypto.h --- i2pd-2.29.0/libi2pd/Crypto.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Crypto.h 2020-04-10 17:33:54.000000000 +0000 @@ -27,8 +27,8 @@ # define X509_getm_notAfter X509_get_notAfter #else # define LEGACY_OPENSSL 0 -# define OPENSSL_HKDF 1 # if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1 +# define OPENSSL_HKDF 1 # define OPENSSL_EDDSA 1 # define OPENSSL_X25519 1 # define OPENSSL_SIPHASH 1 @@ -80,6 +80,7 @@ void GenerateKeys (); const uint8_t * GetPublicKey () const { return m_PublicKey; }; void GetPrivateKey (uint8_t * priv) const; + void SetPrivateKey (const uint8_t * priv); // wihout calculating public void Agree (const uint8_t * pub, uint8_t * shared); private: @@ -296,7 +297,7 @@ // HKDF - void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out); // salt - 32, out - 64, info <= 32 + void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32 // init and terminate void InitCrypto (bool precomputation); diff -Nru i2pd-2.29.0/libi2pd/CryptoKey.cpp i2pd-2.31.0/libi2pd/CryptoKey.cpp --- i2pd-2.29.0/libi2pd/CryptoKey.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/CryptoKey.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -14,6 +14,7 @@ void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) { + if (!ctx) return; ElGamalEncrypt (m_PublicKey, data, encrypted, ctx, zeroPadding); } @@ -24,6 +25,7 @@ bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) { + if (!ctx) return false; return ElGamalDecrypt (m_PrivateKey, encrypted, data, ctx, zeroPadding); } @@ -146,6 +148,34 @@ BN_free (x); BN_free (y); } + ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor (const uint8_t * pub) + { + memcpy (m_PublicKey, pub, 32); + } + + void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool) + { + memcpy (pub, m_PublicKey, 32); + } + + ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv) + { + m_StaticKeys.SetPrivateKey (priv); + } + + bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding) + { + m_StaticKeys.Agree (epub, sharedSecret); + return true; + } + + void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub) + { + X25519Keys k; + k.GenerateKeys (); + k.GetPrivateKey (priv); + memcpy (pub, k.GetPublicKey (), 32); + } } } diff -Nru i2pd-2.29.0/libi2pd/CryptoKey.h i2pd-2.31.0/libi2pd/CryptoKey.h --- i2pd-2.29.0/libi2pd/CryptoKey.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/CryptoKey.h 2020-04-10 17:33:54.000000000 +0000 @@ -116,6 +116,39 @@ }; void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub); + +// ECIES-X25519-AEAD-Ratchet + + class ECIESX25519AEADRatchetEncryptor: public CryptoKeyEncryptor + { + public: + + ECIESX25519AEADRatchetEncryptor (const uint8_t * pub); + ~ECIESX25519AEADRatchetEncryptor () {}; + void Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool); + // copies m_PublicKey to pub + + private: + + uint8_t m_PublicKey[32]; + }; + + class ECIESX25519AEADRatchetDecryptor: public CryptoKeyDecryptor + { + public: + + ECIESX25519AEADRatchetDecryptor (const uint8_t * priv); + ~ECIESX25519AEADRatchetDecryptor () {}; + bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding); + // agree with static and return in sharedSecret (32 bytes) + size_t GetPublicKeyLen () const { return 32; }; + + private: + + X25519Keys m_StaticKeys; + }; + + void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub); } } diff -Nru i2pd-2.29.0/libi2pd/Destination.cpp i2pd-2.31.0/libi2pd/Destination.cpp --- i2pd-2.29.0/libi2pd/Destination.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Destination.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -7,15 +7,15 @@ #include "Timestamp.h" #include "NetDb.hpp" #include "Destination.h" -#include "util.h" namespace i2p { namespace client { - LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map * params): - m_IsRunning (false), m_Thread (nullptr), m_IsPublic (isPublic), - m_PublishReplyToken (0), m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service), + LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service, + bool isPublic, const std::map * params): + m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0), + m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service), m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service), m_LeaseSetType (DEFAULT_LEASESET_TYPE), m_AuthType (i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE) { @@ -123,77 +123,36 @@ LeaseSetDestination::~LeaseSetDestination () { - if (m_IsRunning) - Stop (); if (m_Pool) i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool); for (auto& it: m_LeaseSetRequests) it.second->Complete (nullptr); } - void LeaseSetDestination::Run () + void LeaseSetDestination::Start () { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Destination: runtime exception: ", ex.what ()); - } - } - } - - bool LeaseSetDestination::Start () - { - if (!m_IsRunning) - { - if (m_Nickname.empty ()) - m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname - LoadTags (); - m_IsRunning = true; - m_Pool->SetLocalDestination (shared_from_this ()); - m_Pool->SetActive (true); - m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); - m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, - shared_from_this (), std::placeholders::_1)); - m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ())); - - return true; - } - else - return false; + if (m_Nickname.empty ()) + m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname + LoadTags (); + m_Pool->SetLocalDestination (shared_from_this ()); + m_Pool->SetActive (true); + m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT)); + m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer, + shared_from_this (), std::placeholders::_1)); } - bool LeaseSetDestination::Stop () + void LeaseSetDestination::Stop () { - if (m_IsRunning) + m_CleanupTimer.cancel (); + m_PublishConfirmationTimer.cancel (); + m_PublishVerificationTimer.cancel (); + if (m_Pool) { - m_CleanupTimer.cancel (); - m_PublishConfirmationTimer.cancel (); - m_PublishVerificationTimer.cancel (); - - m_IsRunning = false; - if (m_Pool) - { - m_Pool->SetLocalDestination (nullptr); - i2p::tunnel::tunnels.StopTunnelPool (m_Pool); - } - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = 0; - } - SaveTags (); - CleanUp (); // GarlicDestination - return true; + m_Pool->SetLocalDestination (nullptr); + i2p::tunnel::tunnels.StopTunnelPool (m_Pool); } - else - return false; + SaveTags (); + CleanUp (); // GarlicDestination } bool LeaseSetDestination::Reconfigure(std::map params) @@ -353,30 +312,38 @@ void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr msg) { - m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msg)); + uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); + m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID)); + } + + void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len) + { + I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]); + LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); } - void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from) + bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) { - uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET]; switch (typeID) { case eI2NPData: - HandleDataMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); + HandleDataMessage (payload, len); break; case eI2NPDeliveryStatus: // we assume tunnel tests non-encrypted - HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from)); + HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET)); break; case eI2NPDatabaseStore: - HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); + HandleDatabaseStoreMessage (payload, len); break; case eI2NPDatabaseSearchReply: - HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); + HandleDatabaseSearchReplyMessage (payload, len); break; default: - i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from)); + LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID); + return false; } + return true; } void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len) @@ -421,7 +388,7 @@ if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET) leaseSet = std::make_shared (buf + offset, len - offset); // LeaseSet else - leaseSet = std::make_shared (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset); // LeaseSet2 + leaseSet = std::make_shared (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetPreferredCryptoType () ); // LeaseSet2 if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key) { if (leaseSet->GetIdentHash () != GetIdentHash ()) @@ -445,7 +412,7 @@ auto it2 = m_LeaseSetRequests.find (key); if (it2 != m_LeaseSetRequests.end () && it2->second->requestedBlindedKey) { - auto ls2 = std::make_shared (buf + offset, len - offset, it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? *m_LeaseSetPrivKey : nullptr); + auto ls2 = std::make_shared (buf + offset, len - offset, it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? *m_LeaseSetPrivKey : nullptr, GetPreferredCryptoType ()); if (ls2->IsValid ()) { m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key @@ -511,9 +478,8 @@ LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found"); } - void LeaseSetDestination::HandleDeliveryStatusMessage (std::shared_ptr msg) + void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID) { - uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); if (msgID == m_PublishReplyToken) { LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32()); @@ -525,7 +491,7 @@ shared_from_this (), std::placeholders::_1)); } else - i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg); + i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID); } void LeaseSetDestination::SetLeaseSetUpdated () @@ -856,10 +822,19 @@ } } - ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params): - LeaseSetDestination (isPublic, params), m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), + i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType () const + { + if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET)) + return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET; + return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; + } + + ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, + bool isPublic, const std::map * params): + LeaseSetDestination (service, isPublic, params), + m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), m_DatagramDestination (nullptr), m_RefCounter (0), - m_ReadyChecker(GetService()) + m_ReadyChecker(service) { 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 @@ -872,11 +847,14 @@ if (it != params->end ()) m_EncryptionKeyType = std::stoi(it->second); } - - if (isPublic && m_EncryptionKeyType == GetIdentity ()->GetCryptoKeyType ()) // TODO: presist key type + + memset (m_EncryptionPrivateKey, 0, 256); + memset (m_EncryptionPublicKey, 0, 256); + if (isPublic) PersistTemporaryKeys (); else i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey); + m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_EncryptionKeyType, m_EncryptionPrivateKey); if (isPublic) LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created"); @@ -924,70 +902,34 @@ { } - bool ClientDestination::Start () + void ClientDestination::Start () { - if (LeaseSetDestination::Start ()) - { - m_StreamingDestination = std::make_shared (GetSharedFromThis ()); // TODO: - m_StreamingDestination->Start (); - for (auto& it: m_StreamingDestinationsByPorts) - it.second->Start (); - return true; - } - else - return false; + LeaseSetDestination::Start (); + m_StreamingDestination = std::make_shared (GetSharedFromThis ()); // TODO: + m_StreamingDestination->Start (); + for (auto& it: m_StreamingDestinationsByPorts) + it.second->Start (); } - bool ClientDestination::Stop () + void ClientDestination::Stop () { - if (LeaseSetDestination::Stop ()) + LeaseSetDestination::Stop (); + m_ReadyChecker.cancel(); + m_StreamingDestination->Stop (); + //m_StreamingDestination->SetOwner (nullptr); + m_StreamingDestination = nullptr; + for (auto& it: m_StreamingDestinationsByPorts) { - m_ReadyChecker.cancel(); - m_StreamingDestination->Stop (); - //m_StreamingDestination->SetOwner (nullptr); - m_StreamingDestination = nullptr; - for (auto& it: m_StreamingDestinationsByPorts) - { - it.second->Stop (); - //it.second->SetOwner (nullptr); - } - m_StreamingDestinationsByPorts.clear (); - if (m_DatagramDestination) - { - delete m_DatagramDestination; - m_DatagramDestination = nullptr; - } - return true; + it.second->Stop (); + //it.second->SetOwner (nullptr); + } + m_StreamingDestinationsByPorts.clear (); + if (m_DatagramDestination) + { + delete m_DatagramDestination; + m_DatagramDestination = nullptr; } - else - return false; - } - -#ifdef I2LUA - void ClientDestination::Ready(ReadyPromise & p) - { - ScheduleCheckForReady(&p); - } - - void ClientDestination::ScheduleCheckForReady(ReadyPromise * p) - { - // tick every 100ms - m_ReadyChecker.expires_from_now(boost::posix_time::milliseconds(100)); - m_ReadyChecker.async_wait([&, p] (const boost::system::error_code & ecode) { - HandleCheckForReady(ecode, p); - }); - } - - void ClientDestination::HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p) - { - if(ecode) // error happened - p->set_value(nullptr); - else if(IsReady()) // we are ready - p->set_value(std::shared_ptr(this)); - else // we are not ready - ScheduleCheckForReady(p); } -#endif void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len) { @@ -1165,8 +1107,8 @@ LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p"); memset (m_EncryptionPrivateKey, 0, 256); memset (m_EncryptionPublicKey, 0, 256); - i2p::data::PrivateKeys::GenerateCryptoKeyPair (GetIdentity ()->GetCryptoKeyType (), m_EncryptionPrivateKey, m_EncryptionPublicKey); - + i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey); + // TODO:: persist crypto key type std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out); if (f1) { f1.write ((char *)m_EncryptionPublicKey, 256); @@ -1188,10 +1130,11 @@ else { // standard LS2 (type 3) first - auto keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256; + uint16_t keyLen = m_Decryptor ? m_Decryptor->GetPublicKeyLen () : 256; bool isPublishedEncrypted = GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; auto ls2 = std::make_shared (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2, - m_Keys, m_EncryptionKeyType, keyLen, m_EncryptionPublicKey, tunnels, IsPublic (), isPublishedEncrypted); + m_Keys, i2p::data::LocalLeaseSet2::KeySections { {m_EncryptionKeyType, keyLen, m_EncryptionPublicKey} }, + tunnels, IsPublic (), isPublishedEncrypted); if (isPublishedEncrypted) // encrypt if type 5 ls2 = std::make_shared (ls2, m_Keys, GetAuthType (), m_AuthKeys); leaseSet = ls2; @@ -1204,7 +1147,7 @@ if (m_DatagramDestination) m_DatagramDestination->CleanUp (); } - bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const + bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const { if (m_Decryptor) return m_Decryptor->Decrypt (encrypted, data, ctx, true); @@ -1229,5 +1172,46 @@ } } } + + bool ClientDestination::DeleteStream (uint32_t recvStreamID) + { + if (m_StreamingDestination->DeleteStream (recvStreamID)) + return true; + for (auto it: m_StreamingDestinationsByPorts) + if (it.second->DeleteStream (recvStreamID)) + return true; + return false; + } + + RunnableClientDestination::RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params): + RunnableService ("Destination"), + ClientDestination (GetIOService (), keys, isPublic, params) + { + } + + RunnableClientDestination::~RunnableClientDestination () + { + if (IsRunning ()) + Stop (); + } + + void RunnableClientDestination::Start () + { + if (!IsRunning ()) + { + ClientDestination::Start (); + StartIOService (); + } + } + + void RunnableClientDestination::Stop () + { + if (IsRunning ()) + { + ClientDestination::Stop (); + StopIOService (); + } + } + } } diff -Nru i2pd-2.29.0/libi2pd/Destination.h i2pd-2.31.0/libi2pd/Destination.h --- i2pd-2.29.0/libi2pd/Destination.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Destination.h 2020-04-10 17:33:54.000000000 +0000 @@ -8,9 +8,6 @@ #include #include #include -#ifdef I2LUA -#include -#endif #include #include "Identity.h" #include "TunnelPool.h" @@ -20,6 +17,7 @@ #include "NetDb.hpp" #include "Streaming.h" #include "Datagram.h" +#include "util.h" namespace i2p { @@ -98,18 +96,17 @@ public: - LeaseSetDestination (bool isPublic, const std::map * params = nullptr); + LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map * params = nullptr); ~LeaseSetDestination (); const std::string& GetNickname () const { return m_Nickname; }; + boost::asio::io_service& GetService () { return m_Service; }; - virtual bool Start (); - virtual bool Stop (); + virtual void Start (); + virtual void Stop (); /** i2cp reconfigure */ virtual bool Reconfigure(std::map i2cpOpts); - bool IsRunning () const { return m_IsRunning; }; - boost::asio::io_service& GetService () { return m_Service; }; std::shared_ptr GetTunnelPool () { return m_Pool; }; bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; }; std::shared_ptr FindLeaseSet (const i2p::data::IdentHash& ident); @@ -121,7 +118,6 @@ // implements GarlicDestination std::shared_ptr GetLeaseSet (); std::shared_ptr GetTunnelPool () const { return m_Pool; } - void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from); // override GarlicDestination bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); @@ -131,6 +127,10 @@ protected: + // implements GarlicDestination + void HandleI2NPMessage (const uint8_t * buf, size_t len); + bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len); + void SetLeaseSet (std::shared_ptr newLeaseSet); int GetLeaseSetType () const { return m_LeaseSetType; }; void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; }; @@ -143,7 +143,6 @@ private: - void Run (); void UpdateLeaseSet (); std::shared_ptr GetLeaseSetMt (); void Publish (); @@ -152,19 +151,18 @@ void HandlePublishDelayTimer (const boost::system::error_code& ecode); void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len); void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len); - void HandleDeliveryStatusMessage (std::shared_ptr msg); + void HandleDeliveryStatusMessage (uint32_t msgID); void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr requestedBlindedKey = nullptr); bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, std::shared_ptr request); void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); void HandleCleanupTimer (const boost::system::error_code& ecode); void CleanupRemoteLeaseSets (); + i2p::data::CryptoKeyType GetPreferredCryptoType () const; private: - volatile bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; + boost::asio::io_service& m_Service; mutable std::mutex m_RemoteLeaseSetsMutex; std::map > m_RemoteLeaseSets; std::map > m_LeaseSetRequests; @@ -195,19 +193,13 @@ class ClientDestination: public LeaseSetDestination { public: -#ifdef I2LUA - // type for informing that a client destination is ready - typedef std::promise > ReadyPromise; - // informs promise with shared_from_this() when this destination is ready to use - // if cancelled before ready, informs promise with nullptr - void Ready(ReadyPromise & p); -#endif - ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); + ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, + bool isPublic, const std::map * params = nullptr); ~ClientDestination (); - virtual bool Start (); - virtual bool Stop (); + void Start (); + void Stop (); const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; @@ -231,12 +223,14 @@ int GetStreamingAckDelay () const { return m_StreamingAckDelay; } // datagram - i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; - i2p::datagram::DatagramDestination * CreateDatagramDestination (); + i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; + i2p::datagram::DatagramDestination * CreateDatagramDestination (); // implements LocalDestination - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const; + bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; + bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const { return m_EncryptionKeyType == keyType; }; + const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const { return m_EncryptionPublicKey; }; protected: @@ -247,14 +241,10 @@ private: - std::shared_ptr GetSharedFromThis () - { return std::static_pointer_cast(shared_from_this ()); } + std::shared_ptr GetSharedFromThis () { + return std::static_pointer_cast(shared_from_this ()); + } void PersistTemporaryKeys (); -#ifdef I2LUA - void ScheduleCheckForReady(ReadyPromise * p); - void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p); -#endif - void ReadAuthKey (const std::string& group, const std::map * params); private: @@ -278,7 +268,20 @@ // for HTTP only std::vector > GetAllStreams () const; + bool DeleteStream (uint32_t recvStreamID); }; + + class RunnableClientDestination: private i2p::util::RunnableService, public ClientDestination + { + public: + + RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); + ~RunnableClientDestination (); + + void Start (); + void Stop (); + }; + } } diff -Nru i2pd-2.29.0/libi2pd/ECIESX25519AEADRatchetSession.cpp i2pd-2.31.0/libi2pd/ECIESX25519AEADRatchetSession.cpp --- i2pd-2.29.0/libi2pd/ECIESX25519AEADRatchetSession.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/libi2pd/ECIESX25519AEADRatchetSession.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,664 @@ +#include +#include +#include "Log.h" +#include "Crypto.h" +#include "Elligator.h" +#include "Tag.h" +#include "I2PEndian.h" +#include "Timestamp.h" +#include "Tunnel.h" +#include "TunnelPool.h" +#include "ECIESX25519AEADRatchetSession.h" + +namespace i2p +{ +namespace garlic +{ + + void RatchetTagSet::DHInitialize (const uint8_t * rootKey, const uint8_t * k) + { + // DH_INITIALIZE(rootKey, k) + uint8_t keydata[64]; + i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64) + // nextRootKey = keydata[0:31] + i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_KeyData.buf); + // [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64) + memcpy (m_SymmKeyCK, m_KeyData.buf + 32, 32); + m_NextSymmKeyIndex = 0; + } + + void RatchetTagSet::NextSessionTagRatchet () + { + i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), nullptr, 0, "STInitialization", m_KeyData.buf); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64) + memcpy (m_SessTagConstant, m_KeyData.GetSessTagConstant (), 32); + m_NextIndex = 0; + } + + uint64_t RatchetTagSet::GetNextSessionTag () + { + i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64) + m_NextIndex++; + if (m_NextIndex >= 65535) m_NextIndex = 0; // TODO: dirty hack, should create new tagset + return m_KeyData.GetTag (); + } + + void RatchetTagSet::GetSymmKey (int index, uint8_t * key) + { + if (m_NextSymmKeyIndex > 0 && index >= m_NextSymmKeyIndex) + { + auto num = index + 1 - m_NextSymmKeyIndex; + for (int i = 0; i < num; i++) + i2p::crypto::HKDF (m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); + m_NextSymmKeyIndex += num; + memcpy (key, m_CurrentSymmKeyCK + 32, 32); + } + else + CalculateSymmKeyCK (index, key); + } + + void RatchetTagSet::CalculateSymmKeyCK (int index, uint8_t * key) + { + // TODO: store intermediate keys + uint8_t currentSymmKeyCK[64]; + i2p::crypto::HKDF (m_SymmKeyCK, nullptr, 0, "SymmetricRatchet", currentSymmKeyCK); // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64) + for (int i = 0; i < index; i++) + i2p::crypto::HKDF (currentSymmKeyCK, nullptr, 0, "SymmetricRatchet", currentSymmKeyCK); // keydata_n = HKDF(symmKey_chainKey_(n-1), SYMMKEY_CONSTANT, "SymmetricRatchet", 64) + memcpy (key, currentSymmKeyCK + 32, 32); + } + + ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner): + GarlicRoutingSession (owner, true) + { + ResetKeys (); + } + + ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession () + { + } + + void ECIESX25519AEADRatchetSession::ResetKeys () + { + // TODO : use precalculated hashes + static const char protocolName[41] = "Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"; // 40 bytes + SHA256 ((const uint8_t *)protocolName, 40, m_H); + memcpy (m_CK, m_H, 32); + SHA256 (m_H, 32, m_H); + } + + void ECIESX25519AEADRatchetSession::MixHash (const uint8_t * buf, size_t len) + { + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + SHA256_Update (&ctx, buf, len); + SHA256_Final (m_H, &ctx); + } + + void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce) + { + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); + } + + bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf) + { + for (int i = 0; i < 10; i++) + { + m_EphemeralKeys.GenerateKeys (); + if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys.GetPublicKey (), buf)) + return true; // success + } + return false; + } + + uint64_t ECIESX25519AEADRatchetSession::CreateNewSessionTag () const + { + uint8_t tagsetKey[32]; + i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32) + // Session Tag Ratchet + RatchetTagSet tagsetNsr; + tagsetNsr.DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey) + tagsetNsr.NextSessionTagRatchet (); + return tagsetNsr.GetNextSessionTag (); + } + + bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len) + { + if (!GetOwner ()) return false; + // we are Bob + // KDF1 + MixHash (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET), 32); // h = SHA256(h || bpk) + + if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) + { + LogPrint (eLogError, "Garlic: Can't decode elligator"); + return false; + } + buf += 32; len -= 32; + MixHash (m_Aepk, 32); // h = SHA256(h || aepk) + + uint8_t sharedSecret[32]; + GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519(bsk, aepk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + + // decrypt flags/static + uint8_t nonce[12], fs[32]; + CreateNonce (0, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt + { + LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed "); + return false; + } + MixHash (buf, 48); // h = SHA256(h || ciphertext) + buf += 48; len -= 48; // 32 data + 16 poly + + // decrypt payload + std::vector payload (len - 16); + // KDF2 for payload + bool isStatic = !i2p::data::Tag<32> (fs).IsZero (); + if (isStatic) + { + // static key, fs is apk + memcpy (m_RemoteStaticKey, fs, 32); + GetOwner ()->Decrypt (fs, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519(bsk, apk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + } + else // all zeros flags + CreateNonce (1, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed"); + return false; + } + if (isStatic) MixHash (buf, len); // h = SHA256(h || ciphertext) + m_State = eSessionStateNewSessionReceived; + GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); + + HandlePayload (payload.data (), len - 16); + + return true; + } + + void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len, int index) + { + size_t offset = 0; + while (offset < len) + { + uint8_t blk = buf[offset]; + offset++; + auto size = bufbe16toh (buf + offset); + offset += 2; + LogPrint (eLogDebug, "Garlic: Block type ", (int)blk, " of size ", size); + if (size > len) + { + LogPrint (eLogError, "Garlic: Unexpected block length ", size); + break; + } + switch (blk) + { + case eECIESx25519BlkGalicClove: + GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size); + break; + case eECIESx25519BlkDateTime: + LogPrint (eLogDebug, "Garlic: datetime"); + break; + case eECIESx25519BlkOptions: + LogPrint (eLogDebug, "Garlic: options"); + break; + case eECIESx25519BlkPadding: + LogPrint (eLogDebug, "Garlic: padding"); + break; + case eECIESx25519BlkAckRequest: + { + LogPrint (eLogDebug, "Garlic: ack request"); + m_AckRequests.push_back ({0, index}); // TODO: use actual tagsetid + break; + } + default: + LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk); + } + offset += size; + } + } + + bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) + { + ResetKeys (); + // we are Alice, bpk is m_RemoteStaticKey + size_t offset = 0; + if (!GenerateEphemeralKeysAndEncode (out + offset)) + { + LogPrint (eLogError, "Garlic: Can't encode elligator"); + return false; + } + offset += 32; + + // KDF1 + MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk) + MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || aepk) + uint8_t sharedSecret[32]; + m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + // encrypt static key section + uint8_t nonce[12]; + CreateNonce (0, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: Static section AEAD encryption failed "); + return false; + } + MixHash (out + offset, 48); // h = SHA256(h || ciphertext) + offset += 48; + // KDF2 + GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519 (ask, bpk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + // encrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); + return false; + } + MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext) + + m_State = eSessionStateNewSessionSent; + if (GetOwner ()) + GetOwner ()->AddECIESx25519SessionTag (0, CreateNewSessionTag (), shared_from_this ()); + + return true; + } + + bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) + { + // we are Bob + uint64_t tag = CreateNewSessionTag (); + + size_t offset = 0; + memcpy (out + offset, &tag, 8); + offset += 8; + if (!GenerateEphemeralKeysAndEncode (out + offset)) // bepk + { + LogPrint (eLogError, "Garlic: Can't encode elligator"); + return false; + } + offset += 32; + // KDF for Reply Key Section + MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag) + MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk) + uint8_t sharedSecret[32]; + m_EphemeralKeys.Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32) + m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + uint8_t nonce[12]; + CreateNonce (0, nonce); + // calulate hash for zero length + if (!i2p::crypto::AEADChaCha20Poly1305 (sharedSecret /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad) + { + LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed"); + return false; + } + MixHash (out + offset, 16); // h = SHA256(h || ciphertext) + offset += 16; + memcpy (m_NSRHeader, out, 56); // for possible next NSR + // KDF for payload + uint8_t keydata[64]; + i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) + // k_ab = keydata[0:31], k_ba = keydata[32:63] + m_ReceiveTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) + m_ReceiveTagset.NextSessionTagRatchet (); + m_SendTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) + m_SendTagset.NextSessionTagRatchet (); + GenerateMoreReceiveTags (GetOwner ()->GetNumTags ()); + i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) + // encrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed"); + return false; + } + m_State = eSessionStateNewSessionReplySent; + + return true; + } + + bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) + { + // we are Bob and sent NSR already + memcpy (out, m_NSRHeader, 56); + uint8_t nonce[12]; + CreateNonce (0, nonce); + // encrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed"); + return false; + } + return true; + } + + bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len) + { + // we are Alice + LogPrint (eLogDebug, "Garlic: reply received"); + const uint8_t * tag = buf; + buf += 8; len -= 8; // tag + uint8_t bepk[32]; // Bob's ephemeral key + if (!i2p::crypto::GetElligator ()->Decode (buf, bepk)) + { + LogPrint (eLogError, "Garlic: Can't decode elligator"); + return false; + } + buf += 32; len -= 32; + // KDF for Reply Key Section + MixHash (tag, 8); // h = SHA256(h || tag) + MixHash (bepk, 32); // h = SHA256(h || bepk) + uint8_t sharedSecret[32]; + m_EphemeralKeys.Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32) + GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519 (ask, bepk) + i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) + uint8_t nonce[12]; + CreateNonce (0, nonce); + // calulate hash for zero length + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anyting */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only + { + LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed"); + return false; + } + MixHash (buf, 16); // h = SHA256(h || ciphertext) + buf += 16; len -= 16; + // KDF for payload + uint8_t keydata[64]; + i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) + // k_ab = keydata[0:31], k_ba = keydata[32:63] + m_SendTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) + m_SendTagset.NextSessionTagRatchet (); + m_ReceiveTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba) + m_ReceiveTagset.NextSessionTagRatchet (); + GenerateMoreReceiveTags (GetOwner ()->GetNumTags ()); + i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32) + // decrypt payload + std::vector payload (len - 16); + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, payload.data (), len - 16, false)) // decrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); + return false; + } + + m_State = eSessionStateEstablished; + GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); + HandlePayload (payload.data (), len - 16); + + return true; + } + + bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) + { + uint8_t nonce[12]; + auto index = m_SendTagset.GetNextIndex (); + CreateNonce (index, nonce); // tag's index + uint64_t tag = m_SendTagset.GetNextSessionTag (); + memcpy (out, &tag, 8); + // ad = The session tag, 8 bytes + // ciphertext = ENCRYPT(k, n, payload, ad) + uint8_t key[32]; + m_SendTagset.GetSymmKey (index, key); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, key, nonce, out + 8, outLen - 8, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); + return false; + } + return true; + } + + bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index) + { + uint8_t nonce[12]; + CreateNonce (index, nonce); // tag's index + len -= 8; // tag + std::vector payload (len - 16); + uint8_t key[32]; + m_ReceiveTagset.GetSymmKey (index, key); + if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 8, len - 16, buf, 8, key, nonce, payload.data (), len - 16, false)) // decrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed"); + return false; + } + HandlePayload (payload.data (), len - 16, index); + if (m_ReceiveTagset.GetNextIndex () - index <= GetOwner ()->GetNumTags ()*2/3) + GenerateMoreReceiveTags (GetOwner ()->GetNumTags ()); + return true; + } + + bool ECIESX25519AEADRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len, int index) + { + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + switch (m_State) + { + case eSessionStateNewSessionReplySent: + m_State = eSessionStateEstablished; +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif + case eSessionStateEstablished: + return HandleExistingSessionMessage (buf, len, index); + case eSessionStateNew: + return HandleNewIncomingSession (buf, len); + case eSessionStateNewSessionSent: + return HandleNewOutgoingSessionReply (buf, len); + default: + return false; + } + return true; + } + + std::shared_ptr ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr msg) + { + auto m = NewI2NPMessage (); + m->Align (12); // in order to get buf aligned to 16 (12 + 4) + uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length + auto payload = CreatePayload (msg); + size_t len = payload.size (); + + switch (m_State) + { + case eSessionStateEstablished: + if (!NewExistingSessionMessage (payload.data (), payload.size (), buf, m->maxLen)) + return nullptr; + len += 24; + break; + case eSessionStateNew: + if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen)) + return nullptr; + len += 96; + break; + case eSessionStateNewSessionReceived: + if (!NewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen)) + return nullptr; + len += 72; + break; + case eSessionStateNewSessionReplySent: + if (!NextNewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen)) + return nullptr; + len += 72; + break; + default: + return nullptr; + } + + htobe32buf (m->GetPayload (), len); + m->len += len + 4; + m->FillI2NPMessageHeader (eI2NPGarlic); + return m; + } + + std::vector ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr msg) + { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + size_t payloadLen = 7; // datatime + if (msg && m_Destination) + payloadLen += msg->GetPayloadLength () + 13 + 32; + auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) ? CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ()) : nullptr; + std::shared_ptr deliveryStatus; + if (leaseSet) + { + payloadLen += leaseSet->GetPayloadLength () + 13; + deliveryStatus = CreateEncryptedDeliveryStatusMsg (leaseSet->GetMsgID ()); + payloadLen += deliveryStatus->GetPayloadLength () + 49; + if (GetLeaseSetUpdateMsgID ()) GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); // remove previous + SetLeaseSetUpdateStatus (eLeaseSetSubmitted); + SetLeaseSetUpdateMsgID (leaseSet->GetMsgID ()); + SetLeaseSetSubmissionTime (ts); + GetOwner ()->DeliveryStatusSent (shared_from_this (), leaseSet->GetMsgID ()); + } + if (m_AckRequests.size () > 0) + payloadLen += m_AckRequests.size ()*4 + 3; + uint8_t paddingSize; + RAND_bytes (&paddingSize, 1); + paddingSize &= 0x0F; paddingSize++; // 1 - 16 + payloadLen += paddingSize + 3; + std::vector v(payloadLen); + size_t offset = 0; + // DateTime + v[offset] = eECIESx25519BlkDateTime; offset++; + htobe16buf (v.data () + offset, 4); offset += 2; + htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds + // LeaseSet + if (leaseSet) + offset += CreateGarlicClove (leaseSet, v.data () + offset, payloadLen - offset); + // DeliveryStatus + if (deliveryStatus) + offset += CreateDeliveryStatusClove (deliveryStatus, v.data () + offset, payloadLen - offset); + // msg + if (msg && m_Destination) + offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset, true); + // ack + if (m_AckRequests.size () > 0) + { + v[offset] = eECIESx25519BlkAck; offset++; + htobe16buf (v.data () + offset, m_AckRequests.size ()*4); offset += 2; + for (auto& it: m_AckRequests) + { + htobe16buf (v.data () + offset, it.first); offset += 2; + htobe16buf (v.data () + offset, it.second); offset += 2; + } + m_AckRequests.clear (); + } + // padding + v[offset] = eECIESx25519BlkPadding; offset++; + htobe16buf (v.data () + offset, paddingSize); offset += 2; + memset (v.data () + offset, 0, paddingSize); offset += paddingSize; + return v; + } + + size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len, bool isDestination) + { + if (!msg) return 0; + uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; + if (isDestination) cloveSize += 32; + if ((int)len < cloveSize + 3) return 0; + buf[0] = eECIESx25519BlkGalicClove; // clove type + htobe16buf (buf + 1, cloveSize); // size + buf += 3; + if (isDestination) + { + *buf = (eGarlicDeliveryTypeDestination << 5); + memcpy (buf + 1, *m_Destination, 32); buf += 32; + } + else + *buf = 0; + buf++; // flag and delivery instructions + *buf = msg->GetTypeID (); // I2NP msg type + htobe32buf (buf + 1, msg->GetMsgID ()); // msgID + htobe32buf (buf + 5, msg->GetExpiration ()/1000); // expiration in seconds + memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ()); + return cloveSize + 3; + } + + size_t ECIESX25519AEADRatchetSession::CreateDeliveryStatusClove (std::shared_ptr msg, uint8_t * buf, size_t len) + { + uint16_t cloveSize = msg->GetPayloadLength () + 9 + 37 /* delivery instruction */; + if ((int)len < cloveSize + 3) return 0; + buf[0] = eECIESx25519BlkGalicClove; // clove type + htobe16buf (buf + 1, cloveSize); // size + buf += 3; + if (GetOwner ()) + { + auto inboundTunnel = GetOwner ()->GetTunnelPool ()->GetNextInboundTunnel (); + if (inboundTunnel) + { + // delivery instructions + *buf = eGarlicDeliveryTypeTunnel << 5; buf++; // delivery instructions flag tunnel + // hash and tunnelID sequence is reversed for Garlic + memcpy (buf, inboundTunnel->GetNextIdentHash (), 32); buf += 32;// To Hash + htobe32buf (buf, inboundTunnel->GetNextTunnelID ()); buf += 4;// tunnelID + } + else + { + LogPrint (eLogError, "Garlic: No inbound tunnels in the pool for DeliveryStatus"); + return 0; + } + *buf = msg->GetTypeID (); // I2NP msg type + htobe32buf (buf + 1, msg->GetMsgID ()); // msgID + htobe32buf (buf + 5, msg->GetExpiration ()/1000); // expiration in seconds + memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ()); + } + else + return 0; + return cloveSize + 3; + } + + void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (int numTags) + { + for (int i = 0; i < numTags; i++) + { + auto index = m_ReceiveTagset.GetNextIndex (); + uint64_t tag = m_ReceiveTagset.GetNextSessionTag (); + GetOwner ()->AddECIESx25519SessionTag (index, tag, shared_from_this ()); + } + } + + bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts) + { + CleanupUnconfirmedLeaseSet (ts); + return ts > m_LastActivityTimestamp + ECIESX25519_EXPIRATION_TIMEOUT; + } + + std::shared_ptr WrapECIESX25519AEADRatchetMessage (std::shared_ptr msg, const uint8_t * key, uint64_t tag) + { + auto m = NewI2NPMessage (); + m->Align (12); // in order to get buf aligned to 16 (12 + 4) + uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length + uint8_t nonce[12]; + memset (nonce, 0, 12); // n = 0 + size_t offset = 0; + memcpy (buf + offset, &tag, 8); offset += 8; + auto payload = buf + offset; + uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; + size_t len = cloveSize + 3; + payload[0] = eECIESx25519BlkGalicClove; // clove type + htobe16buf (payload + 1, cloveSize); // size + payload += 3; + *payload = 0; payload++; // flag and delivery instructions + *payload = msg->GetTypeID (); // I2NP msg type + htobe32buf (payload + 1, msg->GetMsgID ()); // msgID + htobe32buf (payload + 5, msg->GetExpiration ()/1000); // expiration in seconds + memcpy (payload + 9, msg->GetPayload (), msg->GetPayloadLength ()); + + if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len, buf, 8, key, nonce, buf + offset, len + 16, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); + return nullptr; + } + offset += len + 16; + + htobe32buf (m->GetPayload (), offset); + m->len += offset + 4; + m->FillI2NPMessageHeader (eI2NPGarlic); + return m; + } + +} +} + + diff -Nru i2pd-2.29.0/libi2pd/ECIESX25519AEADRatchetSession.h i2pd-2.31.0/libi2pd/ECIESX25519AEADRatchetSession.h --- i2pd-2.29.0/libi2pd/ECIESX25519AEADRatchetSession.h 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/libi2pd/ECIESX25519AEADRatchetSession.h 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,136 @@ +#ifndef ECIES_X25519_AEAD_RATCHET_SESSION_H__ +#define ECIES_X25519_AEAD_RATCHET_SESSION_H__ + +#include +#include +#include +#include +#include +#include +#include "Identity.h" +#include "Crypto.h" +#include "Garlic.h" + +namespace i2p +{ +namespace garlic +{ + class RatchetTagSet + { + public: + + void DHInitialize (const uint8_t * rootKey, const uint8_t * k); + void NextSessionTagRatchet (); + uint64_t GetNextSessionTag (); + int GetNextIndex () const { return m_NextIndex; }; + void GetSymmKey (int index, uint8_t * key); + + private: + + void CalculateSymmKeyCK (int index, uint8_t * key); + + private: + + union + { + uint64_t ll[8]; + uint8_t buf[64]; + + const uint8_t * GetSessTagCK () const { return buf; }; // sessTag_chainKey = keydata[0:31] + const uint8_t * GetSessTagConstant () const { return buf + 32; }; // SESSTAG_CONSTANT = keydata[32:63] + uint64_t GetTag () const { return ll[4]; }; // tag = keydata[32:39] + + } m_KeyData; + uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64]; + int m_NextIndex, m_NextSymmKeyIndex; + }; + + enum ECIESx25519BlockType + { + eECIESx25519BlkDateTime = 0, + eECIESx25519BlkSessionID = 1, + eECIESx25519BlkTermination = 4, + eECIESx25519BlkOptions = 5, + eECIESx25519BlkNextSessionKey = 7, + eECIESx25519BlkAck = 8, + eECIESx25519BlkAckRequest = 9, + eECIESx25519BlkGalicClove = 11, + eECIESx25519BlkPadding = 254 + }; + + + const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second of inactivity we should restart after + const int ECIESX25519_EXPIRATION_TIMEOUT = 600; // in seconds + + class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, public std::enable_shared_from_this + { + enum SessionState + { + eSessionStateNew =0, + eSessionStateNewSessionReceived, + eSessionStateNewSessionSent, + eSessionStateNewSessionReplySent, + eSessionStateEstablished + }; + + public: + + ECIESX25519AEADRatchetSession (GarlicDestination * owner); + ~ECIESX25519AEADRatchetSession (); + + bool HandleNextMessage (const uint8_t * buf, size_t len, int index = 0); + std::shared_ptr WrapSingleMessage (std::shared_ptr msg); + + const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } + void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } + + void SetDestination (const i2p::data::IdentHash& dest) // TODO: + { + if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest)); + } + + bool CheckExpired (uint64_t ts); // true is expired + bool CanBeRestarted (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_RESTART_TIMEOUT; } + + private: + + void ResetKeys (); + void MixHash (const uint8_t * buf, size_t len); + void CreateNonce (uint64_t seqn, uint8_t * nonce); + bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes + uint64_t CreateNewSessionTag () const; + + bool HandleNewIncomingSession (const uint8_t * buf, size_t len); + bool HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len); + bool HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index); + void HandlePayload (const uint8_t * buf, size_t len, int index = 0); + + bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); + bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); + bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); + bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); + + std::vector CreatePayload (std::shared_ptr msg); + size_t CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len, bool isDestination = false); + size_t CreateDeliveryStatusClove (std::shared_ptr msg, uint8_t * buf, size_t len); + + void GenerateMoreReceiveTags (int numTags); + + private: + + uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32]; + uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only + uint8_t m_NSRHeader[56], m_NSRKey[32]; // new session reply, for incoming only + i2p::crypto::X25519Keys m_EphemeralKeys; + SessionState m_State = eSessionStateNew; + uint64_t m_LastActivityTimestamp = 0; // incoming + RatchetTagSet m_SendTagset, m_ReceiveTagset; + std::unique_ptr m_Destination;// TODO: might not need it + std::list > m_AckRequests; // (tagsetid, index) + }; + + std::shared_ptr WrapECIESX25519AEADRatchetMessage (std::shared_ptr msg, const uint8_t * key, uint64_t tag); +} +} + +#endif diff -Nru i2pd-2.29.0/libi2pd/Ed25519.cpp i2pd-2.31.0/libi2pd/Ed25519.cpp --- i2pd-2.29.0/libi2pd/Ed25519.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Ed25519.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -31,7 +31,7 @@ BN_mod_inverse (tmp, tmp, q, ctx); BN_set_word (d, 121665); BN_set_negative (d, 1); - BN_mul (d, d, tmp, ctx); + BN_mod_mul (d, d, tmp, q, ctx); // 2^((q-1)/4) I = BN_new (); diff -Nru i2pd-2.29.0/libi2pd/Elligator.cpp i2pd-2.31.0/libi2pd/Elligator.cpp --- i2pd-2.29.0/libi2pd/Elligator.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Elligator.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,207 @@ +#include +#include "Crypto.h" +#include "Elligator.h" + +namespace i2p +{ +namespace crypto +{ + + Elligator2::Elligator2 () + { + // TODO: share with Ed22519 + p = BN_new (); + // 2^255-19 + BN_set_bit (p, 255); // 2^255 + BN_sub_word (p, 19); + p38 = BN_dup (p); BN_add_word (p38, 3); BN_div_word (p38, 8); // (p+3)/8 + p12 = BN_dup (p); BN_sub_word (p12, 1); BN_div_word (p12, 2); // (p-1)/2 + p14 = BN_dup (p); BN_sub_word (p14, 1); BN_div_word (p14, 4); // (p-1)/4 + + A = BN_new (); BN_set_word (A, 486662); + nA = BN_new (); BN_sub (nA, p, A); + + BN_CTX * ctx = BN_CTX_new (); + // calculate sqrt(-1) + sqrtn1 = BN_new (); + BN_set_word (sqrtn1, 2); + BN_mod_exp (sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4 + + u = BN_new (); BN_set_word (u, 2); + iu = BN_new (); BN_mod_inverse (iu, u, p, ctx); + + BN_CTX_free (ctx); + } + + Elligator2::~Elligator2 () + { + BN_free (p); BN_free (p38); BN_free (p12); BN_free (p14); + BN_free (sqrtn1); BN_free (A); BN_free (nA); + BN_free (u); BN_free (iu); + } + + bool Elligator2::Encode (const uint8_t * key, uint8_t * encoded, bool highY, bool random) const + { + bool ret = true; + BN_CTX * ctx = BN_CTX_new (); + BN_CTX_start (ctx); + + uint8_t key1[32]; + for (size_t i = 0; i < 16; i++) // from Little Endian + { + key1[i] = key[31 - i]; + key1[31 - i] = key[i]; + } + + BIGNUM * x = BN_CTX_get (ctx); BN_bin2bn (key1, 32, x); + BIGNUM * xA = BN_CTX_get (ctx); BN_add (xA, x, A); // x + A + BN_sub (xA, p, xA); // p - (x + A) + + BIGNUM * uxxA = BN_CTX_get (ctx); // u*x*xA + BN_mod_mul (uxxA, u, x, p, ctx); + BN_mod_mul (uxxA, uxxA, xA, p, ctx); + + if (Legendre (uxxA, ctx) != -1) + { + uint8_t randByte = 0; // random highest bits and high y + if (random) + { + RAND_bytes (&randByte, 1); + highY = randByte & 0x01; + } + + BIGNUM * r = BN_CTX_get (ctx); + if (highY) + { + BN_mod_inverse (r, x, p, ctx); + BN_mod_mul (r, r, xA, p, ctx); + } + else + { + BN_mod_inverse (r, xA, p, ctx); + BN_mod_mul (r, r, x, p, ctx); + } + BN_mod_mul (r, r, iu, p, ctx); + + SquareRoot (r, r, ctx); + bn2buf (r, encoded, 32); + + if (random) + encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte + for (size_t i = 0; i < 16; i++) // To Little Endian + { + uint8_t tmp = encoded[i]; + encoded[i] = encoded[31 - i]; + encoded[31 - i] = tmp; + } + } + else + ret = false; + + BN_CTX_end (ctx); + BN_CTX_free (ctx); + return ret; + } + + bool Elligator2::Decode (const uint8_t * encoded, uint8_t * key) const + { + bool ret = true; + BN_CTX * ctx = BN_CTX_new (); + BN_CTX_start (ctx); + + uint8_t encoded1[32]; + for (size_t i = 0; i < 16; i++) // from Little Endian + { + encoded1[i] = encoded[31 - i]; + encoded1[31 - i] = encoded[i]; + } + encoded1[0] &= 0x3F; // drop two highest bits + + BIGNUM * r = BN_CTX_get (ctx); BN_bin2bn (encoded1, 32, r); + + if (BN_cmp (r, p12) <= 0) // r < (p-1)/2 + { + // v = -A/(1+u*r^2) + BIGNUM * v = BN_CTX_get (ctx); BN_mod_sqr (v, r, p, ctx); + BN_mod_mul (v, v, u, p, ctx); + BN_add_word (v, 1); + BN_mod_inverse (v, v, p, ctx); + BN_mod_mul (v, v, nA, p, ctx); + + BIGNUM * vpA = BN_CTX_get (ctx); + BN_add (vpA, v, A); // v + A + // t = v^3+A*v^2+v = v^2*(v+A)+v + BIGNUM * t = BN_CTX_get (ctx); BN_mod_sqr (t, v, p, ctx); + BN_mod_mul (t, t, vpA, p, ctx); + BN_mod_add (t, t, v, p, ctx); + + int legendre = Legendre (t, ctx); + BIGNUM * x = BN_CTX_get (ctx); + if (legendre == 1) + BN_copy (x, v); + else + { + BN_sub (x, p, v); + BN_mod_sub (x, x, A, p, ctx); + } + + bn2buf (x, key, 32); + for (size_t i = 0; i < 16; i++) // To Little Endian + { + uint8_t tmp = key[i]; + key[i] = key[31 - i]; + key[31 - i] = tmp; + } + } + else + ret = false; + + BN_CTX_end (ctx); + BN_CTX_free (ctx); + + return ret; + } + + void Elligator2::SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const + { + BIGNUM * t = BN_CTX_get (ctx); + BN_mod_exp (t, x, p14, p, ctx); // t = x^((p-1)/4) + BN_mod_exp (r, x, p38, p, ctx); // r = x^((p+3)/8) + BN_add_word (t, 1); + + if (!BN_cmp (t, p)) + BN_mod_mul (r, r, sqrtn1, p, ctx); + + if (BN_cmp (r, p12) > 0) // r > (p-1)/2 + BN_sub (r, p, r); + } + + int Elligator2::Legendre (const BIGNUM * a, BN_CTX * ctx) const + { + // assume a < p, so don't check for a % p = 0, but a = 0 only + if (BN_is_zero(a)) return 0; + BIGNUM * r = BN_CTX_get (ctx); + BN_mod_exp (r, a, p12, p, ctx); // r = a^((p-1)/2) mod p + if (BN_is_word(r, 1)) + return 1; + else if (BN_is_zero(r)) + return 0; + return -1; + } + + static std::unique_ptr g_Elligator; + std::unique_ptr& GetElligator () + { + if (!g_Elligator) + { + auto el = new Elligator2(); + if (!g_Elligator) // make sure it was not created already + g_Elligator.reset (el); + else + delete el; + } + return g_Elligator; + } +} +} + diff -Nru i2pd-2.29.0/libi2pd/Elligator.h i2pd-2.31.0/libi2pd/Elligator.h --- i2pd-2.29.0/libi2pd/Elligator.h 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Elligator.h 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,39 @@ +#ifndef ELLIGATOR_H__ +#define ELLIGATOR_H__ + +#include +#include +#include + +namespace i2p +{ +namespace crypto +{ + + class Elligator2 + { + public: + + Elligator2 (); + ~Elligator2 (); + + bool Encode (const uint8_t * key, uint8_t * encoded, bool highY = false, bool random = true) const; + bool Decode (const uint8_t * encoded, uint8_t * key) const; + + private: + + void SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const; + int Legendre (const BIGNUM * a, BN_CTX * ctx) const; // a/p + + private: + + BIGNUM * p, * p38, * p12, * p14, * sqrtn1, * A, * nA, * u, * iu; + }; + + std::unique_ptr& GetElligator (); +} +} + +#endif + + diff -Nru i2pd-2.29.0/libi2pd/Event.cpp i2pd-2.31.0/libi2pd/Event.cpp --- i2pd-2.29.0/libi2pd/Event.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Event.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -#include "Event.h" -#include "Log.h" - -namespace i2p -{ - namespace event - { -#ifdef WITH_EVENTS - EventCore core; -#endif - - void EventCore::SetListener(EventListener * l) - { - m_listener = l; - LogPrint(eLogInfo, "Event: listener set"); - } - - void EventCore::QueueEvent(const EventType & ev) - { - if(m_listener) m_listener->HandleEvent(ev); - } - - void EventCore::CollectEvent(const std::string & type, const std::string & ident, uint64_t val) - { - std::unique_lock lock(m_collect_mutex); - std::string key = type + "." + ident; - if (m_collected.find(key) == m_collected.end()) - { - m_collected[key] = {type, key, 0}; - } - m_collected[key].Val += val; - } - - void EventCore::PumpCollected(EventListener * listener) - { - std::unique_lock lock(m_collect_mutex); - if(listener) - { - for(const auto & ev : m_collected) { - listener->HandlePumpEvent({{"type", ev.second.Key}, {"ident", ev.second.Ident}}, ev.second.Val); - } - } - m_collected.clear(); - } - } -} - -void QueueIntEvent(const std::string & type, const std::string & ident, uint64_t val) -{ -#ifdef WITH_EVENTS - i2p::event::core.CollectEvent(type, ident, val); -#endif -} - -void EmitEvent(const EventType & e) -{ -#if WITH_EVENTS - i2p::event::core.QueueEvent(e); -#endif -} - diff -Nru i2pd-2.29.0/libi2pd/Event.h i2pd-2.31.0/libi2pd/Event.h --- i2pd-2.29.0/libi2pd/Event.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Event.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -#ifndef EVENT_H__ -#define EVENT_H__ -#include -#include -#include -#include -#include - -#include - -typedef std::map EventType; - -namespace i2p -{ - namespace event - { - class EventListener { - public: - virtual ~EventListener() {}; - virtual void HandleEvent(const EventType & ev) = 0; - /** @brief handle collected event when pumped */ - virtual void HandlePumpEvent(const EventType & ev, const uint64_t & val) = 0; - }; - - class EventCore - { - public: - void QueueEvent(const EventType & ev); - void CollectEvent(const std::string & type, const std::string & ident, uint64_t val); - void SetListener(EventListener * l); - void PumpCollected(EventListener * l); - - private: - std::mutex m_collect_mutex; - struct CollectedEvent - { - std::string Key; - std::string Ident; - uint64_t Val; - }; - std::map m_collected; - EventListener * m_listener = nullptr; - }; -#ifdef WITH_EVENTS - extern EventCore core; -#endif - } -} - -void QueueIntEvent(const std::string & type, const std::string & ident, uint64_t val); -void EmitEvent(const EventType & ev); - -#endif diff -Nru i2pd-2.29.0/libi2pd/Garlic.cpp i2pd-2.31.0/libi2pd/Garlic.cpp --- i2pd-2.29.0/libi2pd/Garlic.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Garlic.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -11,30 +11,22 @@ #include "Timestamp.h" #include "Log.h" #include "FS.h" +#include "ECIESX25519AEADRatchetSession.h" #include "Garlic.h" namespace i2p { namespace garlic { - GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, - std::shared_ptr destination, int numTags, bool attachLeaseSet): - m_Owner (owner), m_Destination (destination), m_NumTags (numTags), - m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend), + GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet): + m_Owner (owner), m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0) { - // create new session tags and session key - RAND_bytes (m_SessionKey, 32); - m_Encryption.SetKey (m_SessionKey); } - GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag): - m_Owner (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0) + GarlicRoutingSession::GarlicRoutingSession (): + m_Owner (nullptr), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0) { - memcpy (m_SessionKey, sessionKey, 32); - m_Encryption.SetKey (m_SessionKey); - m_SessionTags.push_back (sessionTag); - m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch (); } GarlicRoutingSession::~GarlicRoutingSession () @@ -66,88 +58,64 @@ m_SharedRoutingPath = path; } - GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags () - { - auto tags = new UnconfirmedTags (m_NumTags); - tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch (); - for (int i = 0; i < m_NumTags; i++) - { - RAND_bytes (tags->sessionTags[i], 32); - tags->sessionTags[i].creationTime = tags->tagsCreationTime; - } - return tags; - } - - void GarlicRoutingSession::MessageConfirmed (uint32_t msgID) + bool GarlicRoutingSession::MessageConfirmed (uint32_t msgID) { - TagsConfirmed (msgID); - if (msgID == m_LeaseSetUpdateMsgID) + if (msgID == GetLeaseSetUpdateMsgID ()) { - m_LeaseSetUpdateStatus = eLeaseSetUpToDate; - m_LeaseSetUpdateMsgID = 0; + SetLeaseSetUpdateStatus (eLeaseSetUpToDate); + SetLeaseSetUpdateMsgID (0); LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed"); + return true; } - else - CleanupExpiredTags (); - } - - void GarlicRoutingSession::TagsConfirmed (uint32_t msgID) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - auto it = m_UnconfirmedTagsMsgs.find (msgID); - if (it != m_UnconfirmedTagsMsgs.end ()) - { - auto& tags = it->second; - if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) - { - for (int i = 0; i < tags->numTags; i++) - m_SessionTags.push_back (tags->sessionTags[i]); - } - m_UnconfirmedTagsMsgs.erase (it); - } - } + return false; + } - bool GarlicRoutingSession::CleanupExpiredTags () + void GarlicRoutingSession::CleanupUnconfirmedLeaseSet (uint64_t ts) { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();) - { - if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) - it = m_SessionTags.erase (it); - else - ++it; - } - CleanupUnconfirmedTags (); if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT) { - if (m_Owner) - m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); + if (GetOwner ()) + GetOwner ()->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); m_LeaseSetUpdateMsgID = 0; } - return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty (); + } + + std::shared_ptr GarlicRoutingSession::CreateEncryptedDeliveryStatusMsg (uint32_t msgID) + { + auto msg = CreateDeliveryStatusMsg (msgID); + if (GetOwner ()) + { + //encrypt + uint8_t key[32], tag[32]; + RAND_bytes (key, 32); // random session key + RAND_bytes (tag, 32); // random session tag + GetOwner ()->SubmitSessionKey (key, tag); + ElGamalAESSession garlic (key, tag); + msg = garlic.WrapSingleMessage (msg); + } + return msg; + } + + ElGamalAESSession::ElGamalAESSession (GarlicDestination * owner, + std::shared_ptr destination, int numTags, bool attachLeaseSet): + GarlicRoutingSession (owner, attachLeaseSet), + m_Destination (destination), m_NumTags (numTags) + { + // create new session tags and session key + RAND_bytes (m_SessionKey, 32); + m_Encryption.SetKey (m_SessionKey); } - bool GarlicRoutingSession::CleanupUnconfirmedTags () + ElGamalAESSession::ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag): + m_NumTags(1) { - bool ret = false; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - // delete expired unconfirmed tags - for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();) - { - if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT) - { - if (m_Owner) - m_Owner->RemoveDeliveryStatusSession (it->first); - it = m_UnconfirmedTagsMsgs.erase (it); - ret = true; - } - else - ++it; - } - return ret; + memcpy (m_SessionKey, sessionKey, 32); + m_Encryption.SetKey (m_SessionKey); + m_SessionTags.push_back (sessionTag); + m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch (); } - std::shared_ptr GarlicRoutingSession::WrapSingleMessage (std::shared_ptr msg) + std::shared_ptr ElGamalAESSession::WrapSingleMessage (std::shared_ptr msg) { auto m = NewI2NPMessage (); m->Align (12); // in order to get buf aligned to 16 (12 + 4) @@ -213,10 +181,10 @@ return m; } - size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr msg) + size_t ElGamalAESSession::CreateAESBlock (uint8_t * buf, std::shared_ptr msg) { size_t blockSize = 0; - bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3); + bool createNewTags = GetOwner () && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3); UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr; htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count blockSize += 2; @@ -245,7 +213,7 @@ return blockSize; } - size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags) + size_t ElGamalAESSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags) { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); uint32_t msgID; @@ -255,17 +223,17 @@ *numCloves = 0; size++; - if (m_Owner) + if (GetOwner ()) { // resubmit non-confirmed LeaseSet - if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && ts > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT) + if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT) { - m_LeaseSetUpdateStatus = eLeaseSetUpdated; + SetLeaseSetUpdateStatus (eLeaseSetUpdated); SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed } // attach DeviveryStatus if necessary - if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated + if (newTags || GetLeaseSetUpdateStatus () == eLeaseSetUpdated) // new tags created or leaseset updated { // clove is DeliveryStatus auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID); @@ -279,20 +247,20 @@ m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr(newTags))); newTags = nullptr; // got acquired } - m_Owner->DeliveryStatusSent (shared_from_this (), msgID); + GetOwner ()->DeliveryStatusSent (shared_from_this (), msgID); } else LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created"); } // attach LeaseSet - if (m_LeaseSetUpdateStatus == eLeaseSetUpdated) + if (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) { - if (m_LeaseSetUpdateMsgID) m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); // remove previous - m_LeaseSetUpdateStatus = eLeaseSetSubmitted; - m_LeaseSetUpdateMsgID = msgID; - m_LeaseSetSubmissionTime = ts; + if (GetLeaseSetUpdateMsgID ()) GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); // remove previous + SetLeaseSetUpdateStatus (eLeaseSetSubmitted); + SetLeaseSetUpdateMsgID (msgID); + SetLeaseSetSubmissionTime (ts); // clove if our leaseSet must be attached - auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ()); + auto leaseSet = CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ()); size += CreateGarlicClove (payload + size, leaseSet, false); (*numCloves)++; } @@ -313,7 +281,7 @@ return size; } - size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination) + size_t ElGamalAESSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination) { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec size_t size = 0; @@ -343,12 +311,12 @@ return size; } - size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID) + size_t ElGamalAESSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID) { size_t size = 0; - if (m_Owner) + if (GetOwner ()) { - auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel (); + auto inboundTunnel = GetOwner ()->GetTunnelPool ()->GetNextInboundTunnel (); if (inboundTunnel) { buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel @@ -359,19 +327,12 @@ htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID size += 4; // create msg - auto msg = CreateDeliveryStatusMsg (msgID); - if (m_Owner) - { - //encrypt - uint8_t key[32], tag[32]; - RAND_bytes (key, 32); // random session key - RAND_bytes (tag, 32); // random session tag - m_Owner->SubmitSessionKey (key, tag); - GarlicRoutingSession garlic (key, tag); - msg = garlic.WrapSingleMessage (msg); - } - memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); - size += msg->GetLength (); + auto msg = CreateEncryptedDeliveryStatusMsg (msgID); + if (msg) + { + memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); + size += msg->GetLength (); + } // fill clove uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec uint32_t cloveID; @@ -392,6 +353,77 @@ return size; } + ElGamalAESSession::UnconfirmedTags * ElGamalAESSession::GenerateSessionTags () + { + auto tags = new UnconfirmedTags (m_NumTags); + tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch (); + for (int i = 0; i < m_NumTags; i++) + { + RAND_bytes (tags->sessionTags[i], 32); + tags->sessionTags[i].creationTime = tags->tagsCreationTime; + } + return tags; + } + + bool ElGamalAESSession::MessageConfirmed (uint32_t msgID) + { + TagsConfirmed (msgID); + if (!GarlicRoutingSession::MessageConfirmed (msgID)) + CleanupExpiredTags (); + return true; + } + + void ElGamalAESSession::TagsConfirmed (uint32_t msgID) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + auto it = m_UnconfirmedTagsMsgs.find (msgID); + if (it != m_UnconfirmedTagsMsgs.end ()) + { + auto& tags = it->second; + if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + { + for (int i = 0; i < tags->numTags; i++) + m_SessionTags.push_back (tags->sessionTags[i]); + } + m_UnconfirmedTagsMsgs.erase (it); + } + } + + bool ElGamalAESSession::CleanupExpiredTags () + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();) + { + if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + it = m_SessionTags.erase (it); + else + ++it; + } + CleanupUnconfirmedTags (); + CleanupUnconfirmedLeaseSet (ts); + return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty (); + } + + bool ElGamalAESSession::CleanupUnconfirmedTags () + { + bool ret = false; + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + // delete expired unconfirmed tags + for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();) + { + if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT) + { + if (GetOwner ()) + GetOwner ()->RemoveDeliveryStatusSession (it->first); + it = m_UnconfirmedTagsMsgs.erase (it); + ret = true; + } + else + ++it; + } + return ret; + } + GarlicDestination::GarlicDestination (): m_NumTags (32) // 32 tags by default { m_Ctx = BN_CTX_new (); @@ -407,6 +439,8 @@ m_Sessions.clear (); m_DeliveryStatusSessions.clear (); m_Tags.clear (); + m_ECIESx25519Sessions.clear (); + m_ECIESx25519Tags.clear (); } void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) { @@ -434,6 +468,7 @@ } buf += 4; // length auto it = m_Tags.find (SessionTag(buf)); + // AES tag might be used even if encryption type is not ElGamal/AES if (it != m_Tags.end ()) { // tag found. Use AES @@ -452,9 +487,15 @@ } else { - // tag not found. Use ElGamal + // tag not found. Handle depending on encryption type + if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET)) + { + HandleECIESx25519 (buf, length); + return; + } + // otherwise assume ElGamal/AES ElGamalBlock elGamal; - if (length >= 514 && Decrypt (buf, (uint8_t *)&elGamal, m_Ctx)) + if (length >= 514 && Decrypt (buf, (uint8_t *)&elGamal, m_Ctx, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)) { auto decryption = std::make_shared(elGamal.sessionKey); uint8_t iv[32]; // IV is first 16 bytes @@ -543,7 +584,7 @@ LogPrint (eLogError, "Garlic: message is too short"); break; } - HandleI2NPMessage (buf, len - offset, from); + HandleI2NPMessage (buf, len - offset); break; case eGarlicDeliveryTypeDestination: LogPrint (eLogDebug, "Garlic: type destination"); @@ -554,7 +595,7 @@ LogPrint (eLogError, "Garlic: message is too short"); break; } - HandleI2NPMessage (buf, len - offset, from); + HandleI2NPMessage (buf, len - offset); break; case eGarlicDeliveryTypeTunnel: { @@ -638,21 +679,41 @@ std::shared_ptr GarlicDestination::GetRoutingSession ( std::shared_ptr destination, bool attachLeaseSet) { - GarlicRoutingSessionPtr session; - { - std::unique_lock l(m_SessionsMutex); - auto it = m_Sessions.find (destination->GetIdentHash ()); - if (it != m_Sessions.end ()) - session = it->second; - } - if (!session) - { - session = std::make_shared (this, destination, - attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests - std::unique_lock l(m_SessionsMutex); - m_Sessions[destination->GetIdentHash ()] = session; - } - return session; + if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET && + SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET)) + { + ECIESX25519AEADRatchetSessionPtr session; + uint8_t staticKey[32]; + destination->Encrypt (nullptr, staticKey, nullptr); // we are supposed to get static key + auto it = m_ECIESx25519Sessions.find (staticKey); + if (it != m_ECIESx25519Sessions.end ()) + session = it->second; + if (!session) + { + session = std::make_shared (this); + session->SetRemoteStaticKey (staticKey); + } + session->SetDestination (destination->GetIdentHash ()); // TODO: remove + return session; + } + else + { + ElGamalAESSessionPtr session; + { + std::unique_lock l(m_SessionsMutex); + auto it = m_Sessions.find (destination->GetIdentHash ()); + if (it != m_Sessions.end ()) + session = it->second; + } + if (!session) + { + session = std::make_shared (this, destination, + attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests + std::unique_lock l(m_SessionsMutex); + m_Sessions[destination->GetIdentHash ()] = session; + } + return session; + } } void GarlicDestination::CleanupExpiredTags () @@ -700,6 +761,25 @@ ++it; } } + // ECIESx25519 + for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();) + { + if (ts > it->second.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) + it = m_ECIESx25519Tags.erase (it); + else + ++it; + } + + for (auto it = m_ECIESx25519Sessions.begin (); it != m_ECIESx25519Sessions.end ();) + { + if (it->second->CheckExpired (ts)) + { + it->second->SetOwner (nullptr); + it = m_ECIESx25519Sessions.erase (it); + } + else + ++it; + } } void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID) @@ -714,9 +794,8 @@ m_DeliveryStatusSessions[msgID] = session; } - void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr msg) + void GarlicDestination::HandleDeliveryStatusMessage (uint32_t msgID) { - uint32_t msgID = bufbe32toh (msg->GetPayload ()); GarlicRoutingSessionPtr session; { std::unique_lock l(m_DeliveryStatusSessionsMutex); @@ -736,8 +815,12 @@ void GarlicDestination::SetLeaseSetUpdated () { - std::unique_lock l(m_SessionsMutex); - for (auto& it: m_Sessions) + { + std::unique_lock l(m_SessionsMutex); + for (auto& it: m_Sessions) + it.second->SetLeaseSetUpdated (); + } + for (auto& it: m_ECIESx25519Sessions) it.second->SetLeaseSetUpdated (); } @@ -748,7 +831,8 @@ void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr msg) { - HandleDeliveryStatusMessage (msg); + uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); + HandleDeliveryStatusMessage (msgID); } void GarlicDestination::SaveTags () @@ -806,7 +890,7 @@ m_Tags.insert (std::make_pair (SessionTag (tag, ts), decryption)); } if (!m_Tags.empty ()) - LogPrint (eLogInfo, m_Tags.size (), " loaded for ", ident); + LogPrint (eLogInfo, "Garlic: ", m_Tags.size (), " tags loaded for ", ident); } } i2p::fs::Remove (path); @@ -821,5 +905,107 @@ if (ts >= i2p::fs::GetLastUpdateTime (it) + INCOMING_TAGS_EXPIRATION_TIMEOUT) i2p::fs::Remove (it); } + + void GarlicDestination::HandleECIESx25519 (const uint8_t * buf, size_t len) + { + uint64_t tag; + memcpy (&tag, buf, 8); + ECIESX25519AEADRatchetSessionPtr session; + int index = 0; + auto it = m_ECIESx25519Tags.find (tag); + if (it != m_ECIESx25519Tags.end ()) + { + session = it->second.session; + index = it->second.index; + m_ECIESx25519Tags.erase (tag); + } + else + session = std::make_shared (this); // incoming + + if (!session->HandleNextMessage (buf, len, index)) + LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message"); + } + + void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len) + { + const uint8_t * buf1 = buf; + uint8_t flag = buf[0]; buf++; // flag + GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03); + switch (deliveryType) + { + case eGarlicDeliveryTypeDestination: + LogPrint (eLogDebug, "Garlic: type destination"); + buf += 32; // TODO: check destination +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif + // no break here + case eGarlicDeliveryTypeLocal: + { + LogPrint (eLogDebug, "Garlic: type local"); + I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid + buf += (4 + 4); // msgID + expiration + ptrdiff_t offset = buf - buf1; + if (offset <= (int)len) + HandleCloveI2NPMessage (typeID, buf, len - offset); + else + LogPrint (eLogError, "Garlic: clove is too long"); + break; + } + case eGarlicDeliveryTypeTunnel: + { + LogPrint (eLogDebug, "Garlic: type tunnel"); + // gwHash and gwTunnel sequence is reverted + const uint8_t * gwHash = buf; + buf += 32; + ptrdiff_t offset = buf - buf1; + if (offset + 13 > (int)len) + { + LogPrint (eLogError, "Garlic: message is too short"); + break; + } + uint32_t gwTunnel = bufbe32toh (buf); buf += 4; + I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid + buf += (4 + 4); // msgID + expiration + offset += 13; + if (GetTunnelPool ()) + { + auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel (); + if (tunnel) + tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset)); + else + LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); + } + else + LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel"); + break; + } + default: + LogPrint (eLogWarning, "Garlic: unexpected delivery type ", (int)deliveryType); + } + } + + void GarlicDestination::AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session) + { + m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexSession{index, session, i2p::util::GetSecondsSinceEpoch ()}); + } + + void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session) + { + i2p::data::Tag<32> staticKeyTag (staticKey); + auto it = m_ECIESx25519Sessions.find (staticKeyTag); + if (it != m_ECIESx25519Sessions.end ()) + { + if (it->second->CanBeRestarted (i2p::util::GetSecondsSinceEpoch ())) + m_ECIESx25519Sessions.erase (it); + else + { + LogPrint (eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64 (), " already exists"); + return; + } + } + m_ECIESx25519Sessions.emplace (staticKeyTag, session); + } + } } diff -Nru i2pd-2.29.0/libi2pd/Garlic.h i2pd-2.31.0/libi2pd/Garlic.h --- i2pd-2.29.0/libi2pd/Garlic.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Garlic.h 2020-04-10 17:33:54.000000000 +0000 @@ -2,7 +2,7 @@ #define GARLIC_H__ #include -#include +#include #include #include #include @@ -85,8 +85,10 @@ }; class GarlicDestination; - class GarlicRoutingSession: public std::enable_shared_from_this + class GarlicRoutingSession { + protected: + enum LeaseSetUpdateStatus { eLeaseSetUpToDate = 0, @@ -95,27 +97,15 @@ eLeaseSetDoNotSend }; - struct UnconfirmedTags - { - UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; - ~UnconfirmedTags () { delete[] sessionTags; }; - uint32_t msgID; - int numTags; - SessionTag * sessionTags; - uint32_t tagsCreationTime; - }; - public: - GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr destination, - int numTags, bool attachLeaseSet); - GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption - ~GarlicRoutingSession (); - std::shared_ptr WrapSingleMessage (std::shared_ptr msg); - void MessageConfirmed (uint32_t msgID); - bool CleanupExpiredTags (); // returns true if something left - bool CleanupUnconfirmedTags (); // returns true if something has been deleted - + GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet); + GarlicRoutingSession (); + virtual ~GarlicRoutingSession (); + virtual std::shared_ptr WrapSingleMessage (std::shared_ptr msg) = 0; + virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession + virtual bool MessageConfirmed (uint32_t msgID); + void SetLeaseSetUpdated () { if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated; @@ -123,15 +113,68 @@ bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; }; bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; }; uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; } - + void CleanupUnconfirmedLeaseSet (uint64_t ts); + std::shared_ptr GetSharedRoutingPath (); void SetSharedRoutingPath (std::shared_ptr path); - const GarlicDestination * GetOwner () const { return m_Owner; } + GarlicDestination * GetOwner () const { return m_Owner; } void SetOwner (GarlicDestination * owner) { m_Owner = owner; } + + protected: + + LeaseSetUpdateStatus GetLeaseSetUpdateStatus () const { return m_LeaseSetUpdateStatus; } + void SetLeaseSetUpdateStatus (LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; } + uint32_t GetLeaseSetUpdateMsgID () const { return m_LeaseSetUpdateMsgID; } + void SetLeaseSetUpdateMsgID (uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; } + void SetLeaseSetSubmissionTime (uint64_t ts) { m_LeaseSetSubmissionTime = ts; } + std::shared_ptr CreateEncryptedDeliveryStatusMsg (uint32_t msgID); + private: + GarlicDestination * m_Owner; + + LeaseSetUpdateStatus m_LeaseSetUpdateStatus; + uint32_t m_LeaseSetUpdateMsgID; + uint64_t m_LeaseSetSubmissionTime; // in milliseconds + + std::shared_ptr m_SharedRoutingPath; + + public: + // for HTTP only + virtual size_t GetNumOutgoingTags () const { return 0; }; + }; + //using GarlicRoutingSessionPtr = std::shared_ptr; + typedef std::shared_ptr GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8 + + class ElGamalAESSession: public GarlicRoutingSession, public std::enable_shared_from_this + { + struct UnconfirmedTags + { + UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; + ~UnconfirmedTags () { delete[] sessionTags; }; + uint32_t msgID; + int numTags; + SessionTag * sessionTags; + uint32_t tagsCreationTime; + }; + + public: + + ElGamalAESSession (GarlicDestination * owner, std::shared_ptr destination, + int numTags, bool attachLeaseSet); + ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption + ~ElGamalAESSession () {}; + + std::shared_ptr WrapSingleMessage (std::shared_ptr msg); + + bool MessageConfirmed (uint32_t msgID); + bool CleanupExpiredTags (); // returns true if something left + bool CleanupUnconfirmedTags (); // returns true if something has been deleted + + private: + size_t CreateAESBlock (uint8_t * buf, std::shared_ptr msg); size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr msg, UnconfirmedTags * newTags); size_t CreateGarlicClove (uint8_t * buf, std::shared_ptr msg, bool isDestination); @@ -139,31 +182,32 @@ void TagsConfirmed (uint32_t msgID); UnconfirmedTags * GenerateSessionTags (); + + private: + + std::shared_ptr m_Destination; - private: - - GarlicDestination * m_Owner; - std::shared_ptr m_Destination; - - i2p::crypto::AESKey m_SessionKey; + i2p::crypto::AESKey m_SessionKey; std::list m_SessionTags; int m_NumTags; std::map > m_UnconfirmedTagsMsgs; // msgID->tags - LeaseSetUpdateStatus m_LeaseSetUpdateStatus; - uint32_t m_LeaseSetUpdateMsgID; - uint64_t m_LeaseSetSubmissionTime; // in milliseconds - - i2p::crypto::CBCEncryption m_Encryption; - - std::shared_ptr m_SharedRoutingPath; + i2p::crypto::CBCEncryption m_Encryption; - public: + public: // for HTTP only - size_t GetNumOutgoingTags () const { return m_SessionTags.size (); }; + size_t GetNumOutgoingTags () const { return m_SessionTags.size (); }; + }; + typedef std::shared_ptr ElGamalAESSessionPtr; + + class ECIESX25519AEADRatchetSession; + typedef std::shared_ptr ECIESX25519AEADRatchetSessionPtr; + struct ECIESX25519AEADRatchetIndexSession + { + int index; + ECIESX25519AEADRatchetSessionPtr session; + uint64_t creationTime; // seconds since epoch }; - //using GarlicRoutingSessionPtr = std::shared_ptr; - typedef std::shared_ptr GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8 class GarlicDestination: public i2p::data::LocalDestination { @@ -174,6 +218,7 @@ void CleanUp (); void SetNumTags (int numTags) { m_NumTags = numTags; }; + int GetNumTags () const { return m_NumTags; }; std::shared_ptr GetRoutingSession (std::shared_ptr destination, bool attachLeaseSet); void CleanupExpiredTags (); void RemoveDeliveryStatusSession (uint32_t msgID); @@ -183,6 +228,9 @@ void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID); + void AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session); + void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session); + void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len); virtual void ProcessGarlicMessage (std::shared_ptr msg); virtual void ProcessDeliveryStatusMessage (std::shared_ptr msg); @@ -190,12 +238,13 @@ virtual std::shared_ptr GetLeaseSet () = 0; // TODO virtual std::shared_ptr GetTunnelPool () const = 0; - virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from) = 0; protected: + virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only + virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) = 0; void HandleGarlicMessage (std::shared_ptr msg); - void HandleDeliveryStatusMessage (std::shared_ptr msg); + void HandleDeliveryStatusMessage (uint32_t msgID); void SaveTags (); void LoadTags (); @@ -206,18 +255,23 @@ std::shared_ptr from); void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr from); + // ECIES-X25519-AEAD-Ratchet + void HandleECIESx25519 (const uint8_t * buf, size_t len); + private: BN_CTX * m_Ctx; // incoming // outgoing sessions int m_NumTags; std::mutex m_SessionsMutex; - std::map m_Sessions; + std::unordered_map m_Sessions; + std::unordered_map, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session // incoming - std::map > m_Tags; + std::unordered_map, std::hash > > m_Tags; + std::unordered_map m_ECIESx25519Tags; // session tag -> session // DeliveryStatus std::mutex m_DeliveryStatusSessionsMutex; - std::map m_DeliveryStatusSessions; // msgID -> session + std::unordered_map m_DeliveryStatusSessions; // msgID -> session public: diff -Nru i2pd-2.29.0/libi2pd/I2NPProtocol.h i2pd-2.31.0/libi2pd/I2NPProtocol.h --- i2pd-2.29.0/libi2pd/I2NPProtocol.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/I2NPProtocol.h 2020-04-10 17:33:54.000000000 +0000 @@ -95,6 +95,7 @@ // DatabaseLookup flags const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01; const uint8_t DATABASE_LOOKUP_ENCRYPTION_FLAG = 0x02; + const uint8_t DATABASE_LOOKUP_ECIES_FLAG = 0x10; const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C; const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0; const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100 diff -Nru i2pd-2.29.0/libi2pd/Identity.cpp i2pd-2.31.0/libi2pd/Identity.cpp --- i2pd-2.29.0/libi2pd/Identity.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Identity.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -34,12 +34,11 @@ } IdentityEx::IdentityEx (): - m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + m_ExtendedLen (0), m_ExtendedBuffer (nullptr) { } - IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType): - m_IsVerifierCreated (false) + IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType) { memcpy (m_StandardIdentity.publicKey, publicKey, 256); // publicKey in awlays assumed 256 regardless actual size, padding must be taken care of if (type != SIGNING_KEY_TYPE_DSA_SHA1) @@ -141,19 +140,19 @@ } IdentityEx::IdentityEx (const uint8_t * buf, size_t len): - m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + m_ExtendedLen (0), m_ExtendedBuffer (nullptr) { FromBuffer (buf, len); } IdentityEx::IdentityEx (const IdentityEx& other): - m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + m_ExtendedLen (0), m_ExtendedBuffer (nullptr) { *this = other; } IdentityEx::IdentityEx (const Identity& standard): - m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + m_ExtendedLen (0), m_ExtendedBuffer (nullptr) { *this = standard; } @@ -161,6 +160,7 @@ IdentityEx::~IdentityEx () { delete[] m_ExtendedBuffer; + delete m_Verifier; } IdentityEx& IdentityEx::operator=(const IdentityEx& other) @@ -178,8 +178,8 @@ else m_ExtendedBuffer = nullptr; + delete m_Verifier; m_Verifier = nullptr; - m_IsVerifierCreated = false; return *this; } @@ -193,8 +193,8 @@ m_ExtendedBuffer = nullptr; m_ExtendedLen = 0; + delete m_Verifier; m_Verifier = nullptr; - m_IsVerifierCreated = false; return *this; } @@ -233,6 +233,7 @@ } SHA256(buf, GetFullLen (), m_IdentHash); + delete m_Verifier; m_Verifier = nullptr; return GetFullLen (); @@ -381,33 +382,27 @@ void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const { - if (!m_Verifier) + bool del = false; { - auto created = m_IsVerifierCreated.exchange (true); - if (!created) - m_Verifier.reset (verifier); + std::lock_guard l(m_VerifierMutex); + if (!m_Verifier) + m_Verifier = verifier; else - { - delete verifier; - int count = 0; - while (!m_Verifier && count < 500) // 5 seconds - { - std::this_thread::sleep_for (std::chrono::milliseconds(10)); - count++; - } - if (!m_Verifier) - LogPrint (eLogError, "Identity: couldn't get verifier in 5 seconds"); - } + del = true; } - else + if (del) delete verifier; } void IdentityEx::DropVerifier () const { - // TODO: potential race condition with Verify - m_IsVerifierCreated = false; - m_Verifier = nullptr; + i2p::crypto::Verifier * verifier; + { + std::lock_guard l(m_VerifierMutex); + verifier = m_Verifier; + m_Verifier = nullptr; + } + delete verifier; } std::shared_ptr IdentityEx::CreateEncryptor (CryptoKeyType keyType, const uint8_t * key) @@ -417,6 +412,9 @@ case CRYPTO_KEY_TYPE_ELGAMAL: return std::make_shared(key); break; + case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET: + return std::make_shared(key); + break; case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: return std::make_shared(key); @@ -674,6 +672,9 @@ case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: return std::make_shared(key); break; + case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET: + return std::make_shared(key); + break; default: LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType); }; @@ -717,7 +718,10 @@ case SIGNING_KEY_TYPE_RSA_SHA384_3072: case SIGNING_KEY_TYPE_RSA_SHA512_4096: LogPrint (eLogWarning, "Identity: RSA signature type is not supported. Creating EdDSA"); - // no break here +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif + // no break here case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: i2p::crypto::CreateEDDSA25519RandomKeys (priv, pub); break; @@ -750,6 +754,9 @@ case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub); break; + case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET: + i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub); + break; default: LogPrint (eLogError, "Identity: Crypto key type ", (int)type, " is not supported"); } diff -Nru i2pd-2.29.0/libi2pd/Identity.h i2pd-2.31.0/libi2pd/Identity.h --- i2pd-2.29.0/libi2pd/Identity.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Identity.h 2020-04-10 17:33:54.000000000 +0000 @@ -7,6 +7,7 @@ #include #include #include +#include #include "Base.h" #include "Signature.h" #include "CryptoKey.h" @@ -55,6 +56,7 @@ const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0; const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1; + const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET = 4; const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES @@ -124,8 +126,8 @@ Identity m_StandardIdentity; IdentHash m_IdentHash; - mutable std::unique_ptr m_Verifier; - mutable std::atomic_bool m_IsVerifierCreated; // make sure we don't create twice + mutable i2p::crypto::Verifier * m_Verifier = nullptr; + mutable std::mutex m_VerifierMutex; size_t m_ExtendedLen; uint8_t * m_ExtendedBuffer; }; @@ -215,6 +217,7 @@ virtual bool IsDestination () const = 0; // for garlic const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; + virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override in LeaseSet2 }; class LocalDestination @@ -222,10 +225,12 @@ public: virtual ~LocalDestination() {}; - virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const = 0; + virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0; virtual std::shared_ptr GetIdentity () const = 0; const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; + virtual bool SupportsEncryptionType (CryptoKeyType keyType) const { return GetIdentity ()->GetCryptoKeyType () == keyType; }; // override for LeaseSet + virtual const uint8_t * GetEncryptionPublicKey (CryptoKeyType keyType) const { return GetIdentity ()->GetEncryptionPublicKey (); }; // override for LeaseSet }; } } diff -Nru i2pd-2.29.0/libi2pd/LeaseSet.cpp i2pd-2.31.0/libi2pd/LeaseSet.cpp --- i2pd-2.29.0/libi2pd/LeaseSet.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/LeaseSet.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -159,7 +159,7 @@ } } else - LogPrint (eLogWarning, "LeaseSet: Lease is expired already "); + LogPrint (eLogWarning, "LeaseSet: Lease is expired already"); } uint64_t LeaseSet::ExtractTimestamp (const uint8_t * buf, size_t len) const @@ -251,18 +251,26 @@ memcpy (m_Buffer, buf, len); } - LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases): - LeaseSet (storeLeases), m_StoreType (storeType) + void LeaseSet::SetBufferLen (size_t len) + { + if (len <= m_BufferLen) m_BufferLen = len; + else + LogPrint (eLogError, "LeaseSet2: actual buffer size ", len , " exceeds full buffer size ", m_BufferLen); + } + + LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto): + LeaseSet (storeLeases), m_StoreType (storeType), m_EncryptionType (preferredCrypto) { - SetBuffer (buf, len); + SetBuffer (buf, len); if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ReadFromBufferEncrypted (buf, len, nullptr, nullptr); else ReadFromBuffer (buf, len); } - LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key, const uint8_t * secret): - LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) + LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key, + const uint8_t * secret, CryptoKeyType preferredCrypto): + LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_EncryptionType (preferredCrypto) { ReadFromBufferEncrypted (buf, len, key, secret); } @@ -330,6 +338,8 @@ VerifySignature (identity, buf, len, offset); SetIsValid (verified); } + offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen (); + SetBufferLen (offset); } template @@ -355,7 +365,8 @@ offset += propertiesLen; // skip for now. TODO: implement properties if (offset + 1 >= len) return 0; // key sections - uint16_t currentKeyType = 0; + CryptoKeyType preferredKeyType = m_EncryptionType; + bool preferredKeyFound = false; int numKeySections = buf[offset]; offset++; for (int i = 0; i < numKeySections; i++) { @@ -363,15 +374,15 @@ if (offset + 2 >= len) return 0; uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2; if (offset + encryptionKeyLen >= len) return 0; - if (IsStoreLeases ()) // create encryptor with leases only + if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only { - // we pick first valid key, higher key type has higher priority 4-1-0 - // if two keys with of the same type, pick first + // we pick first valid key if preferred not found auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset); - if (encryptor && (!m_Encryptor || keyType > currentKeyType)) + if (encryptor && (!m_Encryptor || keyType == preferredKeyType)) { m_Encryptor = encryptor; // TODO: atomic - currentKeyType = keyType; + m_EncryptionType = keyType; + if (keyType == preferredKeyType) preferredKeyFound = true; } } offset += encryptionKeyLen; @@ -535,6 +546,12 @@ else LogPrint (eLogError, "LeaseSet2: unexpected LeaseSet type ", (int)innerPlainText[0], " inside encrypted LeaseSet"); } + else + { + // we set actual length of encrypted buffer + offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : blindedVerifier->GetSignatureLen (); + SetBufferLen (offset); + } } // helper for ExtractClientAuthData @@ -746,7 +763,7 @@ } LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, - uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey, + const KeySections& encryptionKeys, std::vector > tunnels, bool isPublic, bool isPublishedEncrypted): LocalLeaseSet (keys.GetPublic (), nullptr, 0) @@ -755,8 +772,11 @@ // assume standard LS2 int num = tunnels.size (); if (num > MAX_NUM_LEASES) num = MAX_NUM_LEASES; + size_t keySectionsLen = 0; + for (const auto& it: encryptionKeys) + keySectionsLen += 2/*key type*/ + 2/*key len*/ + it.keyLen/*key*/; m_BufferLen = identity->GetFullLen () + 4/*published*/ + 2/*expires*/ + 2/*flag*/ + 2/*properties len*/ + - 1/*num keys*/ + 2/*key type*/ + 2/*key len*/ + keyLen/*key*/ + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen (); + 1/*num keys*/ + keySectionsLen + 1/*num leases*/ + num*LEASE2_SIZE + keys.GetSignatureLen (); uint16_t flags = 0; if (keys.IsOfflineSignature ()) { @@ -787,10 +807,13 @@ } htobe16buf (m_Buffer + offset, 0); offset += 2; // properties len // keys - m_Buffer[offset] = 1; offset++; // 1 key - htobe16buf (m_Buffer + offset, keyType); offset += 2; // key type - htobe16buf (m_Buffer + offset, keyLen); offset += 2; // key len - memcpy (m_Buffer + offset, encryptionPublicKey, keyLen); offset += keyLen; // key + m_Buffer[offset] = encryptionKeys.size (); offset++; // 1 key + for (const auto& it: encryptionKeys) + { + htobe16buf (m_Buffer + offset, it.keyType); offset += 2; // key type + htobe16buf (m_Buffer + offset, it.keyLen); offset += 2; // key len + memcpy (m_Buffer + offset, it.encryptionPublicKey, it.keyLen); offset += it.keyLen; // key + } // leases uint32_t expirationTime = 0; // in seconds m_Buffer[offset] = num; offset++; // num leases diff -Nru i2pd-2.29.0/libi2pd/LeaseSet.h i2pd-2.31.0/libi2pd/LeaseSet.h --- i2pd-2.29.0/libi2pd/LeaseSet.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/LeaseSet.h 2020-04-10 17:33:54.000000000 +0000 @@ -97,6 +97,7 @@ // called from LeaseSet2 LeaseSet (bool storeLeases); void SetBuffer (const uint8_t * buf, size_t len); + void SetBufferLen (size_t len); void SetIdentity (std::shared_ptr identity) { m_Identity = identity; }; void SetExpirationTime (uint64_t t) { m_ExpirationTime = t; }; void SetIsValid (bool isValid) { m_IsValid = isValid; }; @@ -136,8 +137,8 @@ { public: - 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 key, const uint8_t * secret = nullptr); // store type 5, called from local netdb only + LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); + LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only uint8_t GetStoreType () const { return m_StoreType; }; uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; }; bool IsPublic () const { return m_IsPublic; }; @@ -147,6 +148,7 @@ // implements RoutingDestination void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; + CryptoKeyType GetEncryptionType () const { return m_EncryptionType; }; private: @@ -167,6 +169,7 @@ uint32_t m_PublishedTimestamp = 0; bool m_IsPublic = true, m_IsPublishedEncrypted = false; std::shared_ptr m_TransientVerifier; + CryptoKeyType m_EncryptionType; std::shared_ptr m_Encryptor; // for standardLS2 }; @@ -229,8 +232,15 @@ { public: + struct KeySection + { + uint16_t keyType, keyLen; + const uint8_t * encryptionPublicKey; + }; + typedef std::vector KeySections; + LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys, - uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey, + const KeySections& encryptionKeys, std::vector > tunnels, bool isPublic, bool isPublishedEncrypted = false); LocalLeaseSet2 (uint8_t storeType, std::shared_ptr identity, const uint8_t * buf, size_t len); // from I2CP diff -Nru i2pd-2.29.0/libi2pd/Log.h i2pd-2.31.0/libi2pd/Log.h --- i2pd-2.29.0/libi2pd/Log.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Log.h 2020-04-10 17:33:54.000000000 +0000 @@ -161,6 +161,7 @@ s << std::forward(arg); } +#if (__cplusplus < 201703L) // below C++ 17 /** internal usage only -- folding args array to single string */ template void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept @@ -168,6 +169,7 @@ LogPrint (s, std::forward(arg)); LogPrint (s, std::forward(args)...); } +#endif /** * @brief Create log message and send it to queue @@ -184,7 +186,11 @@ // fold message to single string std::stringstream ss(""); +#if (__cplusplus >= 201703L) // C++ 17 or higher + (LogPrint (ss, std::forward(args)), ...); +#else LogPrint (ss, std::forward(args)...); +#endif auto msg = std::make_shared(level, std::time(nullptr), ss.str()); msg->tid = std::this_thread::get_id(); diff -Nru i2pd-2.29.0/libi2pd/NetDb.cpp i2pd-2.31.0/libi2pd/NetDb.cpp --- i2pd-2.29.0/libi2pd/NetDb.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/NetDb.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -15,8 +15,9 @@ #include "NTCP2.h" #include "RouterContext.h" #include "Garlic.h" -#include "NetDb.hpp" +#include "ECIESX25519AEADRatchetSession.h" #include "Config.h" +#include "NetDb.hpp" using namespace i2p::transport; @@ -949,10 +950,20 @@ const uint8_t numTags = excluded[32]; if (numTags) { - const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag - i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag); - replyMsg = garlic.WrapSingleMessage (replyMsg); - if(replyMsg == nullptr) LogPrint(eLogError, "NetDb: failed to wrap message"); + if (flag & DATABASE_LOOKUP_ECIES_FLAG) + { + uint64_t tag; + memcpy (&tag, excluded + 33, 8); + replyMsg = i2p::garlic::WrapECIESX25519AEADRatchetMessage (replyMsg, sessionKey, tag); + } + else + { + const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag + i2p::garlic::ElGamalAESSession garlic (sessionKey, sessionTag); + replyMsg = garlic.WrapSingleMessage (replyMsg); + } + if (!replyMsg) + LogPrint (eLogError, "NetDb: failed to wrap message"); } else LogPrint(eLogWarning, "NetDb: encrypted reply requested but no tags provided"); diff -Nru i2pd-2.29.0/libi2pd/NTCP2.cpp i2pd-2.31.0/libi2pd/NTCP2.cpp --- i2pd-2.29.0/libi2pd/NTCP2.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/NTCP2.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -1,12 +1,10 @@ /* -* Copyright (c) 2013-2018, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree * -* Kovri go write your own code -* */ #include @@ -22,34 +20,29 @@ #include "Transports.h" #include "NetDb.hpp" #include "NTCP2.h" +#include "HTTP.h" +#include "util.h" namespace i2p { namespace transport { NTCP2Establisher::NTCP2Establisher (): - m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr) - { + m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr) + { } - NTCP2Establisher::~NTCP2Establisher () - { - delete[] m_SessionRequestBuffer; + NTCP2Establisher::~NTCP2Establisher () + { + delete[] m_SessionRequestBuffer; delete[] m_SessionCreatedBuffer; delete[] m_SessionConfirmedBuffer; } void NTCP2Establisher::MixKey (const uint8_t * inputKeyMaterial) { - // temp_key = HMAC-SHA256(ck, input_key_material) - uint8_t tempKey[32]; unsigned int len; - HMAC(EVP_sha256(), m_CK, 32, inputKeyMaterial, 32, tempKey, &len); - // ck = HMAC-SHA256(temp_key, byte(0x01)) - static uint8_t one[1] = { 1 }; - HMAC(EVP_sha256(), tempKey, 32, one, 1, m_CK, &len); - // derived = HMAC-SHA256(temp_key, ck || byte(0x02)) - m_CK[32] = 2; - HMAC(EVP_sha256(), tempKey, 32, m_CK, 33, m_K, &len); + i2p::crypto::HKDF (m_CK, inputKeyMaterial, 32, "", m_CK); + // ck is m_CK[0:31], k is m_CK[32:63] } void NTCP2Establisher::MixHash (const uint8_t * buf, size_t len) @@ -63,22 +56,22 @@ void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub) { - static const uint8_t protocolNameHash[] = - { - 0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed, - 0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71 + static const uint8_t protocolNameHash[] = + { + 0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed, + 0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71 }; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256") - static const uint8_t hh[32] = - { - 0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5, - 0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e + static const uint8_t hh[32] = + { + 0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5, + 0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e }; // SHA256 (protocolNameHash) - memcpy (m_CK, protocolNameHash, 32); + memcpy (m_CK, protocolNameHash, 32); // h = SHA256(hh || rs) SHA256_CTX ctx; SHA256_Init (&ctx); - SHA256_Update (&ctx, hh, 32); - SHA256_Update (&ctx, rs, 32); + SHA256_Update (&ctx, hh, 32); + SHA256_Update (&ctx, rs, 32); SHA256_Final (m_H, &ctx); // h = SHA256(h || epub) MixHash (epub, 32); @@ -92,25 +85,25 @@ { KeyDerivationFunction1 (m_RemoteStaticKey, m_EphemeralKeys, m_RemoteStaticKey, GetPub ()); } - + void NTCP2Establisher::KDF1Bob () { - KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetStaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); + KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetStaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); } void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub) - { - MixHash (sessionRequest + 32, 32); // encrypted payload + { + MixHash (sessionRequest + 32, 32); // encrypted payload int paddingLength = sessionRequestLen - 64; if (paddingLength > 0) MixHash (sessionRequest + 64, paddingLength); - MixHash (epub, 32); + MixHash (epub, 32); // x25519 between remote pub and ephemaral priv uint8_t inputKeyMaterial[32]; m_EphemeralKeys.Agree (GetRemotePub (), inputKeyMaterial); - + MixKey (inputKeyMaterial); } @@ -118,7 +111,7 @@ { KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetRemotePub ()); } - + void NTCP2Establisher::KDF2Bob () { KeyDerivationFunction2 (m_SessionRequestBuffer, m_SessionRequestBufferLen, GetPub ()); @@ -127,14 +120,14 @@ void NTCP2Establisher::KDF3Alice () { uint8_t inputKeyMaterial[32]; - i2p::context.GetStaticKeys ().Agree (GetRemotePub (), inputKeyMaterial); + i2p::context.GetStaticKeys ().Agree (GetRemotePub (), inputKeyMaterial); MixKey (inputKeyMaterial); } void NTCP2Establisher::KDF3Bob () { uint8_t inputKeyMaterial[32]; - m_EphemeralKeys.Agree (m_RemoteStaticKey, inputKeyMaterial); + m_EphemeralKeys.Agree (m_RemoteStaticKey, inputKeyMaterial); MixKey (inputKeyMaterial); } @@ -155,40 +148,40 @@ encryption.SetKey (m_RemoteIdentHash); encryption.SetIV (m_IV); encryption.Encrypt (GetPub (), 32, m_SessionRequestBuffer); // X - encryption.GetIV (m_IV); // save IV for SessionCreated + encryption.GetIV (m_IV); // save IV for SessionCreated // encryption key for next block KDF1Alice (); // fill options uint8_t options[32]; // actual options size is 16 bytes memset (options, 0, 16); options[0] = i2p::context.GetNetID (); // network ID - options[1] = 2; // ver + options[1] = 2; // ver htobe16buf (options + 2, paddingLength); // padLen - // m3p2Len + // m3p2Len auto bufLen = i2p::context.GetRouterInfo ().GetBufferLen (); - m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options + m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options htobe16buf (options + 4, m3p2Len); - // fill m3p2 payload (RouterInfo block) + // fill m3p2 payload (RouterInfo block) m_SessionConfirmedBuffer = new uint8_t[m3p2Len + 48]; // m3p1 is 48 bytes uint8_t * m3p2 = m_SessionConfirmedBuffer + 48; m3p2[0] = eNTCP2BlkRouterInfo; // block htobe16buf (m3p2 + 1, bufLen + 1); // flag + RI - m3p2[3] = 0; // flag + m3p2[3] = 0; // flag memcpy (m3p2 + 4, i2p::context.GetRouterInfo ().GetBuffer (), bufLen); // TODO: own RI should be protected by mutex // 2 bytes reserved htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsA // 4 bytes reserved - // sign and encrypt options, use m_H as AD + // sign and encrypt options, use m_H as AD uint8_t nonce[12]; memset (nonce, 0, 12); // set nonce to zero - i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_H, 32, m_K, nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt + i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionRequestBuffer + 32, 32, true); // encrypt } void NTCP2Establisher::CreateSessionCreatedMessage () { auto paddingLen = rand () % (287 - 64); m_SessionCreatedBufferLen = paddingLen + 64; - m_SessionCreatedBuffer = new uint8_t[m_SessionCreatedBufferLen]; + m_SessionCreatedBuffer = new uint8_t[m_SessionCreatedBufferLen]; RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen); // encrypt Y i2p::crypto::CBCEncryption encryption; @@ -196,15 +189,15 @@ encryption.SetIV (m_IV); encryption.Encrypt (GetPub (), 32, m_SessionCreatedBuffer); // Y // encryption key for next block (m_K) - KDF2Bob (); + KDF2Bob (); uint8_t options[16]; memset (options, 0, 16); htobe16buf (options + 2, paddingLen); // padLen htobe32buf (options + 8, i2p::util::GetSecondsSinceEpoch ()); // tsB - // sign and encrypt options, use m_H as AD + // sign and encrypt options, use m_H as AD uint8_t nonce[12]; memset (nonce, 0, 12); // set nonce to zero - i2p::crypto::AEADChaCha20Poly1305 (options, 16, m_H, 32, m_K, nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt + i2p::crypto::AEADChaCha20Poly1305 (options, 16, GetH (), 32, GetK (), nonce, m_SessionCreatedBuffer + 32, 32, true); // encrypt } @@ -214,24 +207,24 @@ MixHash (m_SessionCreatedBuffer + 32, 32); // encrypted payload int paddingLength = m_SessionCreatedBufferLen - 64; if (paddingLength > 0) - MixHash (m_SessionCreatedBuffer + 64, paddingLength); + MixHash (m_SessionCreatedBuffer + 64, paddingLength); - // part1 48 bytes - i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, m_H, 32, m_K, nonce, m_SessionConfirmedBuffer, 48, true); // encrypt + // part1 48 bytes + i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetNTCP2StaticPublicKey (), 32, GetH (), 32, GetK (), nonce, m_SessionConfirmedBuffer, 48, true); // encrypt } void NTCP2Establisher::CreateSessionConfirmedMessagePart2 (const uint8_t * nonce) { // part 2 // update AD again - MixHash (m_SessionConfirmedBuffer, 48); + MixHash (m_SessionConfirmedBuffer, 48); // encrypt m3p2, it must be filled in SessionRequest - KDF3Alice (); + KDF3Alice (); uint8_t * m3p2 = m_SessionConfirmedBuffer + 48; - i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, m_H, 32, m_K, nonce, m3p2, m3p2Len, true); // encrypt + i2p::crypto::AEADChaCha20Poly1305 (m3p2, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2, m3p2Len, true); // encrypt // update h again MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext) - } + } bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen) { @@ -240,13 +233,13 @@ decryption.SetKey (i2p::context.GetIdentHash ()); decryption.SetIV (i2p::context.GetNTCP2IV ()); decryption.Decrypt (m_SessionRequestBuffer, 32, GetRemotePub ()); - decryption.GetIV (m_IV); // save IV for SessionCreated + decryption.GetIV (m_IV); // save IV for SessionCreated // decryption key for next block KDF1Bob (); // verify MAC and decrypt options block (32 bytes), use m_H as AD uint8_t nonce[12], options[16]; memset (nonce, 0, 12); // set nonce to zero - 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, GetH (), 32, GetK (), nonce, options, 16, false)) // decrypt { // options if (options[0] && options[0] != i2p::context.GetNetID ()) @@ -254,7 +247,7 @@ 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); m_SessionRequestBufferLen = paddingLen + 64; @@ -262,11 +255,11 @@ if (m3p2Len < 16) { LogPrint (eLogWarning, "NTCP2: SessionRequest m3p2len=", m3p2Len, " is too short"); - return false; - } + return false; + } // check timestamp auto ts = i2p::util::GetSecondsSinceEpoch (); - uint32_t tsA = bufbe32toh (options + 8); + uint32_t tsA = bufbe32toh (options + 8); if (tsA < ts - NTCP2_CLOCK_SKEW || tsA > ts + NTCP2_CLOCK_SKEW) { LogPrint (eLogWarning, "NTCP2: SessionRequest time difference ", (int)(ts - tsA), " exceeds clock skew"); @@ -283,8 +276,8 @@ { LogPrint (eLogWarning, "NTCP2: SessionRequest AEAD verification failed "); return false; - } - return true; + } + return true; } bool NTCP2Establisher::ProcessSessionCreatedMessage (uint16_t& paddingLen) @@ -301,13 +294,13 @@ uint8_t payload[16]; uint8_t nonce[12]; memset (nonce, 0, 12); // set nonce to zero - if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, m_H, 32, m_K, nonce, payload, 16, false)) // decrypt + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionCreatedBuffer + 32, 16, GetH (), 32, GetK (), nonce, payload, 16, false)) // decrypt { - // options + // options paddingLen = bufbe16toh(payload + 2); // check timestamp auto ts = i2p::util::GetSecondsSinceEpoch (); - uint32_t tsB = bufbe32toh (payload + 8); + uint32_t tsB = bufbe32toh (payload + 8); if (tsB < ts - NTCP2_CLOCK_SKEW || tsB > ts + NTCP2_CLOCK_SKEW) { LogPrint (eLogWarning, "NTCP2: SessionCreated time difference ", (int)(ts - tsB), " exceeds clock skew"); @@ -315,10 +308,10 @@ } } else - { + { LogPrint (eLogWarning, "NTCP2: SessionCreated AEAD verification failed "); return false; - } + } return true; } @@ -329,8 +322,8 @@ int paddingLength = m_SessionCreatedBufferLen - 64; if (paddingLength > 0) MixHash (m_SessionCreatedBuffer + 64, paddingLength); - - if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, m_H, 32, m_K, nonce, m_RemoteStaticKey, 32, false)) // decrypt S + + if (!i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer, 32, GetH (), 32, GetK (), nonce, m_RemoteStaticKey, 32, false)) // decrypt S { LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part1 AEAD verification failed "); return false; @@ -341,10 +334,10 @@ bool NTCP2Establisher::ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf) { // update AD again - MixHash (m_SessionConfirmedBuffer, 48); + MixHash (m_SessionConfirmedBuffer, 48); - KDF3Bob (); - if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, m_H, 32, m_K, nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt + KDF3Bob (); + if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt { // caclulate new h again for KDF data memcpy (m_SessionConfirmedBuffer + 16, m_H, 32); // h || ciphertext @@ -359,8 +352,8 @@ } NTCP2Session::NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter): - TransportSession (in_RemoteRouter, NTCP2_ESTABLISH_TIMEOUT), - m_Server (server), m_Socket (m_Server.GetService ()), + TransportSession (in_RemoteRouter, NTCP2_ESTABLISH_TIMEOUT), + m_Server (server), m_Socket (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false), m_Establisher (new NTCP2Establisher), m_SendSipKey (nullptr), m_ReceiveSipKey (nullptr), @@ -380,7 +373,7 @@ memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16); } else - LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters"); + LogPrint (eLogWarning, "NTCP2: Missing NTCP2 parameters"); } } @@ -434,30 +427,24 @@ void NTCP2Session::CreateNonce (uint64_t seqn, uint8_t * nonce) { - memset (nonce, 0, 4); - htole64buf (nonce + 4, seqn); + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); } void NTCP2Session::KeyDerivationFunctionDataPhase () { - uint8_t tempKey[32]; unsigned int len; - HMAC(EVP_sha256(), m_Establisher->GetCK (), 32, nullptr, 0, tempKey, &len); // temp_key = HMAC-SHA256(ck, zerolen) - static uint8_t one[1] = { 1 }; - HMAC(EVP_sha256(), tempKey, 32, one, 1, m_Kab, &len); // k_ab = HMAC-SHA256(temp_key, byte(0x01)). - m_Kab[32] = 2; - HMAC(EVP_sha256(), tempKey, 32, m_Kab, 33, m_Kba, &len); // k_ba = HMAC-SHA256(temp_key, k_ab || byte(0x02)) - static uint8_t ask[4] = { 'a', 's', 'k', 1 }, master[32]; - HMAC(EVP_sha256(), tempKey, 32, ask, 4, master, &len); // ask_master = HMAC-SHA256(temp_key, "ask" || byte(0x01)) + uint8_t k[64]; + i2p::crypto::HKDF (m_Establisher->GetCK (), nullptr, 0, "", k); // k_ab, k_ba = HKDF(ck, zerolen) + memcpy (m_Kab, k, 32); memcpy (m_Kba, k + 32, 32); + uint8_t master[32]; + i2p::crypto::HKDF (m_Establisher->GetCK (), nullptr, 0, "ask", master, 32); // ask_master = HKDF(ck, zerolen, info="ask") uint8_t h[39]; memcpy (h, m_Establisher->GetH (), 32); memcpy (h + 32, "siphash", 7); - HMAC(EVP_sha256(), master, 32, h, 39, tempKey, &len); // temp_key = HMAC-SHA256(ask_master, h || "siphash") - HMAC(EVP_sha256(), tempKey, 32, one, 1, master, &len); // sip_master = HMAC-SHA256(temp_key, byte(0x01)) - HMAC(EVP_sha256(), master, 32, nullptr, 0, tempKey, &len); // temp_key = HMAC-SHA256(sip_master, zerolen) - HMAC(EVP_sha256(), tempKey, 32, one, 1, m_Sipkeysab, &len); // sipkeys_ab = HMAC-SHA256(temp_key, byte(0x01)). - m_Sipkeysab[32] = 2; - HMAC(EVP_sha256(), tempKey, 32, m_Sipkeysab, 33, m_Sipkeysba, &len); // sipkeys_ba = HMAC-SHA256(temp_key, sipkeys_ab || byte(0x02)) + i2p::crypto::HKDF (master, h, 39, "", master, 32); // sip_master = HKDF(ask_master, h || "siphash") + i2p::crypto::HKDF (master, nullptr, 0, "", k); // sipkeys_ab, sipkeys_ba = HKDF(sip_master, zerolen) + memcpy (m_Sipkeysab, k, 32); memcpy (m_Sipkeysba, k + 32, 32); } @@ -466,8 +453,8 @@ m_Establisher->CreateSessionRequestMessage (); // send message boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionRequestBuffer, m_Establisher->m_SessionRequestBufferLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionRequestSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + std::bind(&NTCP2Session::HandleSessionRequestSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } void NTCP2Session::HandleSessionRequestSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) { @@ -515,7 +502,7 @@ } else SendSessionCreated (); - } + } else Terminate (); } @@ -535,9 +522,9 @@ void NTCP2Session::SendSessionCreated () { m_Establisher->CreateSessionCreatedMessage (); - // send message + // send message boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionCreatedBuffer, m_Establisher->m_SessionCreatedBufferLen), boost::asio::transfer_all (), - std::bind(&NTCP2Session::HandleSessionCreatedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + std::bind(&NTCP2Session::HandleSessionCreatedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } void NTCP2Session::HandleSessionCreatedReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -592,9 +579,9 @@ { uint8_t nonce[12]; CreateNonce (1, nonce); // set nonce to 1 - m_Establisher->CreateSessionConfirmedMessagePart1 (nonce); + m_Establisher->CreateSessionConfirmedMessagePart1 (nonce); memset (nonce, 0, 12); // set nonce back to 0 - m_Establisher->CreateSessionConfirmedMessagePart2 (nonce); + m_Establisher->CreateSessionConfirmedMessagePart2 (nonce); // send message boost::asio::async_write (m_Socket, boost::asio::buffer (m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionConfirmedSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -609,12 +596,12 @@ Terminate (); } else - { + { LogPrint (eLogDebug, "NTCP2: SessionConfirmed sent"); KeyDerivationFunctionDataPhase (); // Alice data phase keys m_SendKey = m_Kab; - m_ReceiveKey = m_Kba; + m_ReceiveKey = m_Kba; SetSipKeys (m_Sipkeysab, m_Sipkeysba); memcpy (m_ReceiveIV.buf, m_Sipkeysba + 16, 8); memcpy (m_SendIV.buf, m_Sipkeysab + 16, 8); @@ -624,7 +611,7 @@ // TODO: remove // m_SendQueue.push_back (CreateDeliveryStatusMsg (1)); // SendQueue (); - } + } } void NTCP2Session::HandleSessionCreatedSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -638,7 +625,7 @@ else { LogPrint (eLogDebug, "NTCP2: SessionCreated sent"); - m_Establisher->m_SessionConfirmedBuffer = new uint8_t[m_Establisher->m3p2Len + 48]; + m_Establisher->m_SessionConfirmedBuffer = new uint8_t[m_Establisher->m3p2Len + 48]; boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionConfirmedBuffer, m_Establisher->m3p2Len + 48), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionConfirmedReceived , shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -667,7 +654,7 @@ KeyDerivationFunctionDataPhase (); // Bob data phase keys m_SendKey = m_Kba; - m_ReceiveKey = m_Kab; + m_ReceiveKey = m_Kab; SetSipKeys (m_Sipkeysba, m_Sipkeysab); memcpy (m_ReceiveIV.buf, m_Sipkeysab + 16, 8); memcpy (m_SendIV.buf, m_Sipkeysba + 16, 8); @@ -675,8 +662,8 @@ // process RI if (buf[0] != eNTCP2BlkRouterInfo) { - LogPrint (eLogWarning, "NTCP2: unexpected block ", (int)buf[0], " in SessionConfirmed"); - Terminate (); + LogPrint (eLogWarning, "NTCP2: unexpected block ", (int)buf[0], " in SessionConfirmed"); + Terminate (); return; } auto size = bufbe16toh (buf.data () + 1); @@ -690,38 +677,42 @@ i2p::data::RouterInfo ri (buf.data () + 4, size - 1); // 1 byte block type + 2 bytes size + 1 byte flag if (ri.IsUnreachable ()) { - LogPrint (eLogError, "NTCP2: Signature verification failed in SessionConfirmed"); - SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail); + LogPrint (eLogError, "NTCP2: Signature verification failed in SessionConfirmed"); + SendTerminationAndTerminate (eNTCP2RouterInfoSignatureVerificationFail); return; } if (i2p::util::GetMillisecondsSinceEpoch () > ri.GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes { - LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed"); - SendTerminationAndTerminate (eNTCP2Message3Error); + LogPrint (eLogError, "NTCP2: RouterInfo is too old in SessionConfirmed"); + SendTerminationAndTerminate (eNTCP2Message3Error); return; } auto addr = ri.GetNTCP2Address (false); // any NTCP2 address if (!addr) { - LogPrint (eLogError, "NTCP2: No NTCP2 address found in SessionConfirmed"); + LogPrint (eLogError, "NTCP2: No NTCP2 address found in SessionConfirmed"); Terminate (); return; } if (memcmp (addr->ntcp2->staticKey, m_Establisher->m_RemoteStaticKey, 32)) { - LogPrint (eLogError, "NTCP2: Static key mismatch in SessionConfirmed"); - SendTerminationAndTerminate (eNTCP2IncorrectSParameter); + LogPrint (eLogError, "NTCP2: Static key mismatch in SessionConfirmed"); + SendTerminationAndTerminate (eNTCP2IncorrectSParameter); return; } i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, buf.data () + 3, size)); // TODO: should insert ri and not parse it twice // TODO: process options - - // ready to communicate + + // ready to communicate auto existing = i2p::data::netdb.FindRouter (ri.GetRouterIdentity ()->GetIdentHash ()); // check if exists already SetRemoteIdentity (existing ? existing->GetRouterIdentity () : ri.GetRouterIdentity ()); - m_Server.AddNTCP2Session (shared_from_this (), true); - Established (); - ReceiveLength (); + if (m_Server.AddNTCP2Session (shared_from_this (), true)) + { + Established (); + ReceiveLength (); + } + else + Terminate (); } else Terminate (); @@ -735,13 +726,13 @@ { #if OPENSSL_SIPHASH m_SendSipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, sendSipKey, 16); - m_SendMDCtx = EVP_MD_CTX_create (); + m_SendMDCtx = EVP_MD_CTX_create (); EVP_PKEY_CTX *ctx = nullptr; EVP_DigestSignInit (m_SendMDCtx, &ctx, nullptr, nullptr, m_SendSipKey); - EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); + EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); m_ReceiveSipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, receiveSipKey, 16); - m_ReceiveMDCtx = EVP_MD_CTX_create (); + m_ReceiveMDCtx = EVP_MD_CTX_create (); ctx = nullptr; EVP_DigestSignInit (m_ReceiveMDCtx, &ctx, NULL, NULL, m_ReceiveSipKey); EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); @@ -785,9 +776,9 @@ { #if OPENSSL_SIPHASH EVP_DigestSignInit (m_ReceiveMDCtx, nullptr, nullptr, nullptr, nullptr); - EVP_DigestSignUpdate (m_ReceiveMDCtx, m_ReceiveIV.buf, 8); - size_t l = 8; - EVP_DigestSignFinal (m_ReceiveMDCtx, m_ReceiveIV.buf, &l); + EVP_DigestSignUpdate (m_ReceiveMDCtx, m_ReceiveIV.buf, 8); + size_t l = 8; + EVP_DigestSignFinal (m_ReceiveMDCtx, m_ReceiveIV.buf, &l); #else i2p::crypto::Siphash<8> (m_ReceiveIV.buf, m_ReceiveIV.buf, 8, m_ReceiveSipKey); #endif @@ -795,7 +786,7 @@ m_NextReceivedLen = be16toh (m_NextReceivedLen) ^ le16toh (m_ReceiveIV.key); LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen); if (m_NextReceivedLen >= 16) - { + { if (m_NextReceivedBuffer) delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen]; boost::system::error_code ec; @@ -813,7 +804,7 @@ { LogPrint (eLogError, "NTCP2: received length ", m_NextReceivedLen, " is too short"); Terminate (); - } + } } } @@ -840,7 +831,7 @@ uint8_t nonce[12]; CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++; if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, false)) - { + { LogPrint (eLogDebug, "NTCP2: received message decrypted"); ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16); delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = nullptr; // we don't need received buffer anymore @@ -850,7 +841,7 @@ { LogPrint (eLogWarning, "NTCP2: Received AEAD verification failed "); SendTerminationAndTerminate (eNTCP2DataPhaseAEADFailure); - } + } } } @@ -873,7 +864,7 @@ { case eNTCP2BlkDateTime: LogPrint (eLogDebug, "NTCP2: datetime"); - break; + break; case eNTCP2BlkOptions: LogPrint (eLogDebug, "NTCP2: options"); break; @@ -896,11 +887,11 @@ nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header memcpy (nextMsg->GetNTCP2Header (), frame + offset, size); nextMsg->FromNTCP2 (); - m_Handler.PutNextMessage (nextMsg); + m_Handler.PutNextMessage (nextMsg); break; } case eNTCP2BlkTermination: - if (size >= 9) + if (size >= 9) { LogPrint (eLogDebug, "NTCP2: termination. reason=", (int)(frame[offset + 8])); Terminate (); @@ -923,46 +914,46 @@ { #if OPENSSL_SIPHASH EVP_DigestSignInit (m_SendMDCtx, nullptr, nullptr, nullptr, nullptr); - EVP_DigestSignUpdate (m_SendMDCtx, m_SendIV.buf, 8); - size_t l = 8; - EVP_DigestSignFinal (m_SendMDCtx, m_SendIV.buf, &l); + EVP_DigestSignUpdate (m_SendMDCtx, m_SendIV.buf, 8); + size_t l = 8; + EVP_DigestSignFinal (m_SendMDCtx, m_SendIV.buf, &l); #else i2p::crypto::Siphash<8> (m_SendIV.buf, m_SendIV.buf, 8, m_SendSipKey); #endif // length must be in BigEndian htobe16buf (lengthBuf, frameLen ^ le16toh (m_SendIV.key)); LogPrint (eLogDebug, "NTCP2: sent length ", frameLen); - } + } void NTCP2Session::SendI2NPMsgs (std::vector >& msgs) { if (msgs.empty () || IsTerminated ()) return; - + size_t totalLen = 0; std::vector > encryptBufs; - std::vector bufs; + std::vector bufs; std::shared_ptr first; uint8_t * macBuf = nullptr; - for (auto& it: msgs) - { + for (auto& it: msgs) + { it->ToNTCP2 (); auto buf = it->GetNTCP2Header (); auto len = it->GetNTCP2Length (); // block header - buf -= 3; + buf -= 3; buf[0] = eNTCP2BlkI2NPMessage; // blk htobe16buf (buf + 1, len); // size - len += 3; - totalLen += len; + len += 3; + totalLen += len; encryptBufs.push_back ( {buf, len} ); if (&it == &msgs.front ()) // first message { // allocate two bytes for length buf -= 2; len += 2; first = it; - } + } if (&it == &msgs.back () && it->len + 16 < it->maxLen) // last message - { + { // if it's long enough we add padding and MAC to it // create padding block auto paddingLen = CreatePaddingBlock (totalLen, buf + len, it->maxLen - it->len - 16); @@ -975,8 +966,8 @@ macBuf = buf + len; // allocate 16 bytes for MAC len += 16; - } - + } + bufs.push_back (boost::asio::buffer (buf, len)); } @@ -988,42 +979,42 @@ auto paddingLen = CreatePaddingBlock (totalLen, m_NextSendBuffer, 287 - 16); // and padding block to encrypt and send if (paddingLen) - encryptBufs.push_back ( {m_NextSendBuffer, paddingLen} ); + encryptBufs.push_back ( {m_NextSendBuffer, paddingLen} ); bufs.push_back (boost::asio::buffer (m_NextSendBuffer, paddingLen + 16)); macBuf = m_NextSendBuffer + paddingLen; - totalLen += paddingLen; + totalLen += paddingLen; } uint8_t nonce[12]; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; i2p::crypto::AEADChaCha20Poly1305Encrypt (encryptBufs, m_SendKey, nonce, macBuf); // encrypt buffers SetNextSentFrameLength (totalLen + 16, first->GetNTCP2Header () - 5); // frame length right before first block - + // send buffers - m_IsSending = true; + m_IsSending = true; boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleI2NPMsgsSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs)); - } + } void NTCP2Session::HandleI2NPMsgsSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs) { HandleNextFrameSent (ecode, bytes_transferred); // msgs get destroyed here } - + void NTCP2Session::EncryptAndSendNextBuffer (size_t payloadLen) { - if (IsTerminated ()) + if (IsTerminated ()) { delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; - return; + return; } // encrypt uint8_t nonce[12]; CreateNonce (m_SendSequenceNumber, nonce); m_SendSequenceNumber++; - i2p::crypto::AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2); + i2p::crypto::AEADChaCha20Poly1305Encrypt ({ {m_NextSendBuffer + 2, payloadLen} }, m_SendKey, nonce, m_NextSendBuffer + payloadLen + 2); SetNextSentFrameLength (payloadLen + 16, m_NextSendBuffer); // send - m_IsSending = true; + m_IsSending = true; boost::asio::async_write (m_Socket, boost::asio::buffer (m_NextSendBuffer, payloadLen + 16 + 2), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleNextFrameSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -1032,21 +1023,23 @@ { m_IsSending = false; delete[] m_NextSendBuffer; m_NextSendBuffer = nullptr; - + if (ecode) { - LogPrint (eLogWarning, "NTCP2: Couldn't send frame ", ecode.message ()); - } + if (ecode != boost::asio::error::operation_aborted) + LogPrint (eLogWarning, "NTCP2: Couldn't send frame ", ecode.message ()); + Terminate (); + } else - { + { m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); m_NumSentBytes += bytes_transferred; i2p::transport::transports.UpdateSentBytes (bytes_transferred); LogPrint (eLogDebug, "NTCP2: Next frame sent ", bytes_transferred); SendQueue (); - } + } } - + void NTCP2Session::SendQueue () { if (!m_SendQueue.empty ()) @@ -1056,7 +1049,7 @@ while (!m_SendQueue.empty ()) { auto msg = m_SendQueue.front (); - size_t len = msg->GetNTCP2Length (); + size_t len = msg->GetNTCP2Length (); if (s + len + 3 <= NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) // 3 bytes block header { msgs.push_back (msg); @@ -1072,12 +1065,12 @@ break; } SendI2NPMsgs (msgs); - } + } } size_t NTCP2Session::CreatePaddingBlock (size_t msgLen, uint8_t * buf, size_t len) { - if (len < 3) return 0; + if (len < 3) return 0; len -= 3; if (msgLen < 256) msgLen = 256; // for short message padding should not be always zero size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100; @@ -1086,10 +1079,10 @@ if (paddingSize) paddingSize = rand () % paddingSize; buf[0] = eNTCP2BlkPadding; // blk htobe16buf (buf + 1, paddingSize); // size - memset (buf + 3, 0, paddingSize); + memset (buf + 3, 0, paddingSize); return paddingSize + 3; - } - + } + void NTCP2Session::SendRouterInfo () { if (!IsEstablished ()) return; @@ -1110,11 +1103,11 @@ void NTCP2Session::SendTermination (NTCP2TerminationReason reason) { if (!m_SendKey || !m_SendSipKey) return; - m_NextSendBuffer = new uint8_t[49]; // 49 = 12 bytes message + 16 bytes MAC + 2 bytes size + up to 19 padding block + m_NextSendBuffer = new uint8_t[49]; // 49 = 12 bytes message + 16 bytes MAC + 2 bytes size + up to 19 padding block // termination block m_NextSendBuffer[2] = eNTCP2BlkTermination; m_NextSendBuffer[3] = 0; m_NextSendBuffer[4] = 9; // 9 bytes block size - htobe64buf (m_NextSendBuffer + 5, m_ReceiveSequenceNumber); + htobe64buf (m_NextSendBuffer + 5, m_ReceiveSequenceNumber); m_NextSendBuffer[13] = (uint8_t)reason; // padding block auto paddingSize = CreatePaddingBlock (12, m_NextSendBuffer + 14, 19); @@ -1138,13 +1131,13 @@ if (m_IsTerminated) return; for (auto it: msgs) m_SendQueue.push_back (it); - if (!m_IsSending) - SendQueue (); + if (!m_IsSending) + SendQueue (); else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE) { LogPrint (eLogWarning, "NTCP2: outgoing messages queue size exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); Terminate (); - } + } } void NTCP2Session::SendLocalRouterInfo () @@ -1154,8 +1147,8 @@ } NTCP2Server::NTCP2Server (): - m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), - m_TerminationTimer (m_Service) + RunnableServiceWithWork ("NTCP2"), m_TerminationTimer (GetService ()), + m_Resolver(GetService ()) { } @@ -1166,49 +1159,70 @@ void NTCP2Server::Start () { - if (!m_IsRunning) + if (!IsRunning ()) { - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&NTCP2Server::Run, this)); - auto& addresses = context.GetRouterInfo ().GetAddresses (); - for (const auto& address: addresses) + StartIOService (); + if(UsingProxy()) { - if (!address) continue; - if (address->IsPublishedNTCP2 ()) + LogPrint(eLogError, "NTCP2: USING PROXY "); + // TODO: resolve proxy until it is resolved + boost::asio::ip::tcp::resolver::query q(m_ProxyAddress, std::to_string(m_ProxyPort)); + boost::system::error_code e; + auto itr = m_Resolver.resolve(q, e); + if(e) { - if (address->host.is_v4()) + LogPrint(eLogError, "NTCP2: Failed to resolve proxy ", e.message()); + } + else + { + m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint(*itr)); + if (m_ProxyEndpoint) + LogPrint(eLogError, "NTCP2: m_ProxyEndpoint ", *m_ProxyEndpoint); + } + } + else + { + LogPrint(eLogError, "NTCP2: NOTUSING PROXY "); + auto& addresses = context.GetRouterInfo ().GetAddresses (); + for (const auto& address: addresses) + { + if (!address) continue; + if (address->IsPublishedNTCP2 ()) { - try - { - m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port))); - } - catch ( std::exception & ex ) + if (address->host.is_v4()) { - LogPrint(eLogError, "NTCP2: Failed to bind to ip4 port ",address->port, ex.what()); - continue; + try + { + m_NTCP2Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port))); + } + catch ( std::exception & ex ) + { + LogPrint(eLogError, "NTCP2: Failed to bind to ip4 port ",address->port, ex.what()); + continue; + } + + LogPrint (eLogInfo, "NTCP2: Start listening TCP port ", address->port); + auto conn = std::make_shared(*this); + m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1)); } - - LogPrint (eLogInfo, "NTCP2: Start listening TCP port ", address->port); - auto conn = std::make_shared(*this); - m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1)); - } - else if (address->host.is_v6() && context.SupportsV6 ()) - { - m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (m_Service)); - try + else if (address->host.is_v6() && context.SupportsV6 ()) { - m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); - m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); - m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); - m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port)); - m_NTCP2V6Acceptor->listen (); - - LogPrint (eLogInfo, "NTCP2: Start listening V6 TCP port ", address->port); - auto conn = std::make_shared (*this); - m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1)); - } catch ( std::exception & ex ) { - LogPrint(eLogError, "NTCP2: failed to bind to ip6 port ", address->port); - continue; + m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ())); + try + { + m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); + m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); + m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); + m_NTCP2V6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port)); + m_NTCP2V6Acceptor->listen (); + + LogPrint (eLogInfo, "NTCP2: Start listening V6 TCP port ", address->port); + auto conn = std::make_shared (*this); + m_NTCP2V6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAcceptV6, this, conn, std::placeholders::_1)); + } catch ( std::exception & ex ) { + LogPrint(eLogError, "NTCP2: failed to bind to ip6 port ", address->port); + continue; + } } } } @@ -1229,49 +1243,32 @@ } m_NTCP2Sessions.clear (); - if (m_IsRunning) + if (IsRunning ()) { - m_IsRunning = false; m_TerminationTimer.cancel (); - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } - } - - void NTCP2Server::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "NTCP2: runtime exception: ", ex.what ()); - } + m_ProxyEndpoint = nullptr; } + StopIOService (); } bool NTCP2Server::AddNTCP2Session (std::shared_ptr session, bool incoming) { - if (!session || !session->GetRemoteIdentity ()) return false; + if (!session) return false; + if (incoming) + m_PendingIncomingSessions.remove (session); + if (!session->GetRemoteIdentity ()) return false; auto& ident = session->GetRemoteIdentity ()->GetIdentHash (); auto it = m_NTCP2Sessions.find (ident); if (it != m_NTCP2Sessions.end ()) { LogPrint (eLogWarning, "NTCP2: session to ", ident.ToBase64 (), " already exists"); - session->Terminate(); - return false; + if (incoming) + // replace by new session + it->second->Terminate (); + else + return false; } m_NTCP2Sessions.insert (std::make_pair (ident, session)); - if (incoming) - m_PendingIncomingSessions.remove (session); return true; } @@ -1292,15 +1289,15 @@ void NTCP2Server::Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn) { LogPrint (eLogDebug, "NTCP2: Connecting to ", address ,":", port); - m_Service.post([this, address, port, conn]() + GetService ().post([this, address, port, conn]() { if (this->AddNTCP2Session (conn)) { - auto timer = std::make_shared(m_Service); + auto timer = std::make_shared(GetService ()); auto timeout = NTCP2_CONNECT_TIMEOUT * 5; conn->SetTerminationTimeout(timeout * 2); timer->expires_from_now (boost::posix_time::seconds(timeout)); - timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) + timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) { if (ecode != boost::asio::error::operation_aborted) { @@ -1311,6 +1308,8 @@ }); conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer)); } + else + conn->Terminate (); }); } @@ -1342,15 +1341,21 @@ { conn->ServerLogin (); m_PendingIncomingSessions.push_back (conn); + conn = nullptr; } } else LogPrint (eLogError, "NTCP2: Connected from error ", ec.message ()); } + else + LogPrint (eLogError, "NTCP2: Accept error ", error.message ()); if (error != boost::asio::error::operation_aborted) { - conn = std::make_shared (*this); + if (!conn) // connection is used, create new one + conn = std::make_shared (*this); + else // reuse failed + conn->Close (); m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1)); } @@ -1406,13 +1411,13 @@ // pending for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();) { - if ((*it)->IsEstablished () || (*it)->IsTerminated ()) - it = m_PendingIncomingSessions.erase (it); // established or terminated - else if ((*it)->IsTerminationTimeoutExpired (ts)) + if ((*it)->IsEstablished () || (*it)->IsTerminationTimeoutExpired (ts)) { (*it)->Terminate (); - it = m_PendingIncomingSessions.erase (it); // expired + it = m_PendingIncomingSessions.erase (it); // etsablished of expired } + else if ((*it)->IsTerminated ()) + it = m_PendingIncomingSessions.erase (it); // already terminated else it++; } @@ -1420,6 +1425,231 @@ ScheduleTermination (); } } + + void NTCP2Server::UseProxy(ProxyType proxytype, const std::string & addr, uint16_t port) + { + m_ProxyType = proxytype; + m_ProxyAddress = addr; + m_ProxyPort = port; + } + + void NTCP2Server::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype) + { + if (ecode) + { + LogPrint(eLogWarning, "NTCP2: failed to connect to proxy ", ecode.message()); + timer->cancel(); + conn->Terminate(); + return; + } + switch (m_ProxyType) + { + case eSocksProxy: + { + // TODO: support username/password auth etc + static const uint8_t buff[3] = {0x05, 0x01, 0x00}; + boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(), + [] (const boost::system::error_code & ec, std::size_t transferred) + { + (void) transferred; + if(ec) + { + LogPrint(eLogWarning, "NTCP2: socks5 write error ", ec.message()); + } + }); + auto readbuff = std::make_shared >(2); + boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 2), + [this, readbuff, timer, conn, host, port, addrtype](const boost::system::error_code & ec, std::size_t transferred) + { + if(ec) + { + LogPrint(eLogError, "NTCP2: socks5 read error ", ec.message()); + timer->cancel(); + conn->Terminate(); + return; + } + else if(transferred == 2) + { + if((*readbuff)[1] == 0x00) + { + AfterSocksHandshake(conn, timer, host, port, addrtype); + return; + } + else if ((*readbuff)[1] == 0xff) + { + LogPrint(eLogError, "NTCP2: socks5 proxy rejected authentication"); + timer->cancel(); + conn->Terminate(); + return; + } + LogPrint(eLogError, "NTCP2:", (int)(*readbuff)[1]); + } + LogPrint(eLogError, "NTCP2: socks5 server gave invalid response"); + timer->cancel(); + conn->Terminate(); + }); + break; + } + case eHTTPProxy: + { + i2p::http::HTTPReq req; + req.method = "CONNECT"; + req.version ="HTTP/1.1"; + if(addrtype == eIP6Address) + req.uri = "[" + host + "]:" + std::to_string(port); + else + req.uri = host + ":" + std::to_string(port); + + boost::asio::streambuf writebuff; + std::ostream out(&writebuff); + out << req.to_string(); + + boost::asio::async_write(conn->GetSocket(), writebuff.data(), boost::asio::transfer_all(), + [](const boost::system::error_code & ec, std::size_t transferred) + { + (void) transferred; + if(ec) + LogPrint(eLogError, "NTCP2: http proxy write error ", ec.message()); + }); + + boost::asio::streambuf * readbuff = new boost::asio::streambuf; + boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n", + [this, readbuff, timer, conn] (const boost::system::error_code & ec, std::size_t transferred) + { + if(ec) + { + LogPrint(eLogError, "NTCP2: http proxy read error ", ec.message()); + timer->cancel(); + conn->Terminate(); + } + else + { + readbuff->commit(transferred); + i2p::http::HTTPRes res; + if(res.parse(boost::asio::buffer_cast(readbuff->data()), readbuff->size()) > 0) + { + if(res.code == 200) + { + timer->cancel(); + conn->ClientLogin(); + delete readbuff; + return; + } + else + LogPrint(eLogError, "NTCP2: http proxy rejected request ", res.code); + } + else + LogPrint(eLogError, "NTCP2: http proxy gave malformed response"); + timer->cancel(); + conn->Terminate(); + delete readbuff; + } + }); + break; + } + default: + LogPrint(eLogError, "NTCP2: unknown proxy type, invalid state"); + } + } + + void NTCP2Server::ConnectWithProxy (const std::string& host, uint16_t port, RemoteAddressType addrtype, std::shared_ptr conn) + { + if(!m_ProxyEndpoint) return; + GetService().post([this, host, port, addrtype, conn]() { + if (this->AddNTCP2Session (conn)) + { + + auto timer = std::make_shared(GetService()); + auto timeout = NTCP_CONNECT_TIMEOUT * 5; + conn->SetTerminationTimeout(timeout * 2); + timer->expires_from_now (boost::posix_time::seconds(timeout)); + timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + LogPrint (eLogInfo, "NTCP2: Not connected in ", timeout, " seconds"); + i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); + conn->Terminate (); + } + }); + conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCP2Server::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype)); + } + }); + } + + void NTCP2Server::AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype) + { + // build request + size_t sz = 0; + auto buff = std::make_shared >(256); + auto readbuff = std::make_shared >(256); + (*buff)[0] = 0x05; + (*buff)[1] = 0x01; + (*buff)[2] = 0x00; + + if(addrtype == eIP4Address) + { + (*buff)[3] = 0x01; + auto addr = boost::asio::ip::address::from_string(host).to_v4(); + auto addrbytes = addr.to_bytes(); + auto addrsize = addrbytes.size(); + memcpy(buff->data () + 4, addrbytes.data(), addrsize); + } + else if (addrtype == eIP6Address) + { + (*buff)[3] = 0x04; + auto addr = boost::asio::ip::address::from_string(host).to_v6(); + auto addrbytes = addr.to_bytes(); + auto addrsize = addrbytes.size(); + memcpy(buff->data () + 4, addrbytes.data(), addrsize); + } + else if (addrtype == eHostname) + { + (*buff)[3] = 0x03; + size_t addrsize = host.size(); + sz = addrsize + 1 + 4; + if (2 + sz > buff->size ()) + { + // too big + return; + } + (*buff)[4] = (uint8_t) addrsize; + memcpy(buff->data() + 5, host.c_str(), addrsize); + } + htobe16buf(buff->data () + sz, port); + sz += 2; + boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff->data (), sz), boost::asio::transfer_all(), + [](const boost::system::error_code & ec, std::size_t written) + { + if(ec) + { + LogPrint(eLogError, "NTCP2: failed to write handshake to socks proxy ", ec.message()); + return; + } + }); + + boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 10), + [timer, conn, sz, readbuff](const boost::system::error_code & e, std::size_t transferred) + { + if(e) + { + LogPrint(eLogError, "NTCP2: socks proxy read error ", e.message()); + } + else if(transferred == sz) + { + if((*readbuff)[1] == 0x00) + { + timer->cancel(); + conn->ClientLogin(); + return; + } + } + if(!e) + i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); + timer->cancel(); + conn->Terminate(); + }); + } + } } - diff -Nru i2pd-2.29.0/libi2pd/NTCP2.h i2pd-2.31.0/libi2pd/NTCP2.h --- i2pd-2.29.0/libi2pd/NTCP2.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/NTCP2.h 2020-04-10 17:33:54.000000000 +0000 @@ -1,12 +1,10 @@ /* -* Copyright (c) 2013-2018, The PurpleI2P Project +* Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree * -* Kovri go write your own code -* */ #ifndef NTCP2_H__ #define NTCP2_H__ @@ -30,7 +28,7 @@ namespace transport { - const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; + const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; const int NTCP2_MAX_PADDING_RATIO = 6; // in % const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds @@ -38,7 +36,7 @@ const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds - const int NTCP2_CLOCK_SKEW = 60; // in seconds + const int NTCP2_CLOCK_SKEW = 60; // in seconds const int NTCP2_MAX_OUTGOING_QUEUE_SIZE = 500; // how many messages we can queue up enum NTCP2BlockType @@ -48,8 +46,8 @@ eNTCP2BlkRouterInfo, // 2 eNTCP2BlkI2NPMessage, // 3 eNTCP2BlkTermination, // 4 - eNTCP2BlkPadding = 254 - }; + eNTCP2BlkPadding = 254 + }; enum NTCP2TerminationReason { @@ -71,21 +69,21 @@ eNTCP2RouterInfoSignatureVerificationFail, // 15 eNTCP2IncorrectSParameter, // 16 eNTCP2Banned, // 17 - }; - + }; + // RouterInfo flags - const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; + const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; struct NTCP2Establisher { NTCP2Establisher (); ~NTCP2Establisher (); - + const uint8_t * GetPub () const { return m_EphemeralKeys.GetPublicKey (); }; const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set - const uint8_t * GetK () const { return m_K; }; + const uint8_t * GetK () const { return m_CK + 32; }; const uint8_t * GetCK () const { return m_CK; }; const uint8_t * GetH () const { return m_H; }; @@ -114,34 +112,36 @@ i2p::crypto::X25519Keys m_EphemeralKeys; uint8_t m_RemoteEphemeralPublicKey[32]; // x25519 - uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /*k*/; + uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[64] /* [ck, k]*/; i2p::data::IdentHash m_RemoteIdentHash; - uint16_t m3p2Len; + uint16_t m3p2Len; uint8_t * m_SessionRequestBuffer, * m_SessionCreatedBuffer, * m_SessionConfirmedBuffer; size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; - }; + }; class NTCP2Server; class NTCP2Session: public TransportSession, public std::enable_shared_from_this { public: - NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter = nullptr); + + NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter = nullptr); ~NTCP2Session (); void Terminate (); void TerminateByTimeout (); void Done (); + void Close () { m_Socket.close (); }; // for accept boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; bool IsEstablished () const { return m_IsEstablished; }; bool IsTerminated () const { return m_IsTerminated; }; - void ClientLogin (); // Alice + void ClientLogin (); // Alice void ServerLogin (); // Bob - + void SendLocalRouterInfo (); // after handshake void SendI2NPMessages (const std::vector >& msgs); @@ -194,15 +194,15 @@ std::unique_ptr m_Establisher; // data phase - uint8_t m_Kab[33], m_Kba[32], m_Sipkeysab[33], m_Sipkeysba[32]; + uint8_t m_Kab[32], m_Kba[32], m_Sipkeysab[32], m_Sipkeysba[32]; const uint8_t * m_SendKey, * m_ReceiveKey; -#if OPENSSL_SIPHASH +#if OPENSSL_SIPHASH EVP_PKEY * m_SendSipKey, * m_ReceiveSipKey; EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx; #else const uint8_t * m_SendSipKey, * m_ReceiveSipKey; #endif - uint16_t m_NextReceivedLen; + uint16_t m_NextReceivedLen; uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer; union { @@ -217,31 +217,51 @@ std::list > m_SendQueue; }; - class NTCP2Server + class NTCP2Server: private i2p::util::RunnableServiceWithWork { public: + enum RemoteAddressType + { + eIP4Address, + eIP6Address, + eHostname + }; + + enum ProxyType + { + eNoProxy, + eSocksProxy, + eHTTPProxy + }; + NTCP2Server (); ~NTCP2Server (); void Start (); void Stop (); + boost::asio::io_service& GetService () { return GetIOService (); }; bool AddNTCP2Session (std::shared_ptr session, bool incoming = false); void RemoveNTCP2Session (std::shared_ptr session); std::shared_ptr FindNTCP2Session (const i2p::data::IdentHash& ident); - boost::asio::io_service& GetService () { return m_Service; }; - + void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr conn); void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn); + void AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype); + + + bool UsingProxy() const { return m_ProxyType != eNoProxy; }; + void UseProxy(ProxyType proxy, const std::string & address, uint16_t port); + private: - void Run (); void HandleAccept (std::shared_ptr conn, const boost::system::error_code& error); void HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error); - void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); + void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); + void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype); // timer void ScheduleTermination (); @@ -249,15 +269,17 @@ private: - bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; - boost::asio::io_service::work m_Work; boost::asio::deadline_timer m_TerminationTimer; std::unique_ptr m_NTCP2Acceptor, m_NTCP2V6Acceptor; - std::map > m_NTCP2Sessions; + std::map > m_NTCP2Sessions; std::list > m_PendingIncomingSessions; + ProxyType m_ProxyType =eNoProxy; + std::string m_ProxyAddress; + uint16_t m_ProxyPort; + boost::asio::ip::tcp::resolver m_Resolver; + std::unique_ptr m_ProxyEndpoint; + public: // for HTTP/I2PControl diff -Nru i2pd-2.29.0/libi2pd/NTCPSession.cpp i2pd-2.31.0/libi2pd/NTCPSession.cpp --- i2pd-2.29.0/libi2pd/NTCPSession.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/NTCPSession.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -14,9 +14,6 @@ #include "NTCPSession.h" #include "HTTP.h" #include "util.h" -#ifdef WITH_EVENTS -#include "Event.h" -#endif using namespace i2p::crypto; @@ -649,9 +646,6 @@ { if (!m_NextMessage->IsExpired ()) { -#ifdef WITH_EVENTS - QueueIntEvent("transport.recvmsg", GetIdentHashBase64(), 1); -#endif m_Handler.PutNextMessage (m_NextMessage); } else diff -Nru i2pd-2.29.0/libi2pd/RouterContext.cpp i2pd-2.31.0/libi2pd/RouterContext.cpp --- i2pd-2.29.0/libi2pd/RouterContext.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/RouterContext.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -27,12 +27,8 @@ void RouterContext::Init () { srand (i2p::util::GetMillisecondsSinceEpoch () % 1000); -#ifdef WIN32 - // for compatibility with WinXP - m_StartupTime = i2p::util::GetSecondsSinceEpoch (); -#else m_StartupTime = std::chrono::steady_clock::now(); -#endif + if (!Load ()) CreateNewRouter (); m_Decryptor = m_Keys.CreateDecryptor (nullptr); @@ -194,11 +190,11 @@ if (address->IsNTCP2 () && (address->port != port || address->ntcp2->isPublished != publish) && (!v4only || address->host.is_v4 ())) { if (!port && !address->port) - { + { // select random port only if address's port is not set port = rand () % (30777 - 9111) + 9111; // I2P network ports range if (port == 9150) port = 9151; // Tor browser - } + } if (port) address->port = port; address->cost = publish ? 3 : 14; address->ntcp2->isPublished = publish; @@ -342,7 +338,11 @@ { case low : /* not set */; break; case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P' - case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; // no break here, extra + high means 'X' + case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; + #if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; + #endif + // no break here, extra + high means 'X' case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break; } m_RouterInfo.SetCaps (caps); @@ -438,14 +438,14 @@ } // remove NTCP or NTCP2 v4 address bool ntcp; i2p::config::GetOption("ntcp", ntcp); - if (ntcp) + if (ntcp) PublishNTCPAddress (false); else { bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); if (ntcp2) PublishNTCP2Address (port, false, true); - } + } // update UpdateRouterInfo (); } @@ -495,7 +495,7 @@ void RouterContext::SetSupportsV6 (bool supportsV6) { if (supportsV6) - { + { m_RouterInfo.EnableV6 (); // insert v6 addresses if necessary bool foundSSU = false, foundNTCP = false, foundNTCP2 = false; @@ -513,7 +513,7 @@ } else foundNTCP = true; - } + } port = addr->port; } if (!port) i2p::config::GetOption("port", port); @@ -525,7 +525,7 @@ { std::string host = "::1"; // TODO: read host m_RouterInfo.AddSSUAddress (host.c_str (), port, GetIdentHash ()); - } + } } // NTCP2 if (!foundNTCP2) @@ -534,11 +534,11 @@ bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published); if (ntcp2 && ntcp2Published) { - std::string ntcp2Host; + std::string ntcp2Host; if (!i2p::config::IsDefault ("ntcp2.addressv6")) i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host); else - ntcp2Host = "::1"; + ntcp2Host = "::1"; uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port); if (!ntcp2Port) ntcp2Port = port; m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port); @@ -550,10 +550,10 @@ bool ntcp; i2p::config::GetOption("ntcp", ntcp); if (ntcp) { - std::string host = "::1"; + std::string host = "::1"; m_RouterInfo.AddNTCPAddress (host.c_str (), port); } - } + } } else m_RouterInfo.DisableV6 (); @@ -696,9 +696,9 @@ return i2p::tunnel::tunnels.GetExploratoryPool (); } - void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from) + void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len) { - i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from)); + i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len))); } void RouterContext::ProcessGarlicMessage (std::shared_ptr msg) @@ -721,15 +721,10 @@ uint32_t RouterContext::GetUptime () const { -#ifdef WIN32 - // for compatibility with WinXP - return i2p::util::GetSecondsSinceEpoch () - m_StartupTime; -#else return std::chrono::duration_cast (std::chrono::steady_clock::now() - m_StartupTime).count (); -#endif } - bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const + bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const { return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false; } diff -Nru i2pd-2.29.0/libi2pd/RouterContext.h i2pd-2.31.0/libi2pd/RouterContext.h --- i2pd-2.29.0/libi2pd/RouterContext.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/RouterContext.h 2020-04-10 17:33:54.000000000 +0000 @@ -108,19 +108,24 @@ // implements LocalDestination std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const; + bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; void SetLeaseSetUpdated () {}; // implements GarlicDestination std::shared_ptr GetLeaseSet () { return nullptr; }; std::shared_ptr GetTunnelPool () const; - void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from); // override GarlicDestination void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatusMessage (std::shared_ptr msg); + protected: + + // implements GarlicDestination + void HandleI2NPMessage (const uint8_t * buf, size_t len); + bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) { return false; }; // not implemented + private: void CreateNewRouter (); @@ -136,12 +141,8 @@ i2p::data::PrivateKeys m_Keys; std::shared_ptr m_Decryptor; uint64_t m_LastUpdateTime; // in seconds - bool m_AcceptsTunnels, m_IsFloodfill; -#ifdef WIN32 - uint64_t m_StartupTime = 0; // in seconds since epoch -#else + bool m_AcceptsTunnels, m_IsFloodfill; std::chrono::time_point m_StartupTime; -#endif uint64_t m_BandwidthLimit; // allowed bandwidth int m_ShareRatio; RouterStatus m_Status; diff -Nru i2pd-2.29.0/libi2pd/SSU.cpp i2pd-2.31.0/libi2pd/SSU.cpp --- i2pd-2.29.0/libi2pd/SSU.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/SSU.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -160,6 +160,13 @@ catch (std::exception& ex) { LogPrint (eLogError, "SSU: receivers runtime exception: ", ex.what ()); + if (m_IsRunning) + { + // restart socket + m_Socket.close (); + OpenSocket (); + Receive (); + } } } } @@ -175,6 +182,12 @@ catch (std::exception& ex) { LogPrint (eLogError, "SSU: v6 receivers runtime exception: ", ex.what ()); + if (m_IsRunning) + { + m_SocketV6.close (); + OpenSocketV6 (); + ReceiveV6 (); + } } } } diff -Nru i2pd-2.29.0/libi2pd/SSUData.cpp i2pd-2.31.0/libi2pd/SSUData.cpp --- i2pd-2.29.0/libi2pd/SSUData.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/SSUData.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -5,9 +5,6 @@ #include "NetDb.hpp" #include "SSU.h" #include "SSUData.h" -#ifdef WITH_EVENTS -#include "Event.h" -#endif namespace i2p { @@ -241,9 +238,6 @@ m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch (); if (!msg->IsExpired ()) { -#ifdef WITH_EVENTS - QueueIntEvent("transport.recvmsg", m_Session.GetIdentHashBase64(), 1); -#endif m_Handler.PutNextMessage (msg); } else @@ -448,7 +442,7 @@ } catch (boost::system::system_error& ec) { - LogPrint (eLogWarning, "SSU: Can't resend data fragment ", ec.what ()); + LogPrint (eLogWarning, "SSU: Can't resend message ", it->first, " data fragment: ", ec.what ()); } } @@ -458,7 +452,7 @@ } else { - LogPrint (eLogInfo, "SSU: message has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted"); + LogPrint (eLogInfo, "SSU: message ", it->first, " has not been ACKed after ", MAX_NUM_RESENDS, " attempts, deleted"); it = m_SentMessages.erase (it); } } @@ -494,7 +488,7 @@ { if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) { - LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); + LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); it = m_IncompleteMessages.erase (it); } else diff -Nru i2pd-2.29.0/libi2pd/Streaming.cpp i2pd-2.31.0/libi2pd/Streaming.cpp --- i2pd-2.29.0/libi2pd/Streaming.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Streaming.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -86,13 +86,14 @@ LogPrint (eLogDebug, "Streaming: Stream deleted"); } - void Stream::Terminate () + void Stream::Terminate (bool deleteFromDestination) // shoudl be called from StreamingDestination::Stop only { m_AckSendTimer.cancel (); m_ReceiveTimer.cancel (); m_ResendTimer.cancel (); //CleanUp (); /* Need to recheck - broke working on windows */ - m_LocalDestination.DeleteStream (shared_from_this ()); + if (deleteFromDestination) + m_LocalDestination.DeleteStream (shared_from_this ()); } void Stream::CleanUp () @@ -847,6 +848,9 @@ break; case 2: m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif // no break here case 4: if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); @@ -989,6 +993,8 @@ m_PendingIncomingStreams.clear (); { std::unique_lock l(m_StreamsMutex); + for (auto it: m_Streams) + it.second->Terminate (false); // we delete here m_Streams.clear (); m_IncomingStreams.clear (); } @@ -1123,6 +1129,15 @@ } } + bool StreamingDestination::DeleteStream (uint32_t recvStreamID) + { + auto it = m_Streams.find (recvStreamID); + if (it == m_Streams.end ()) + return false; + DeleteStream (it->second); + return true; + } + void StreamingDestination::SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; // we must set it immediately for IsAcceptorSet diff -Nru i2pd-2.29.0/libi2pd/Streaming.h i2pd-2.31.0/libi2pd/Streaming.h --- i2pd-2.29.0/libi2pd/Streaming.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Streaming.h 2020-04-10 17:33:54.000000000 +0000 @@ -180,8 +180,7 @@ int GetWindowSize () const { return m_WindowSize; }; int GetRTT () const { return m_RTT; }; - /** don't call me */ - void Terminate (); + void Terminate (bool deleteFromDestination = true); private: @@ -251,6 +250,7 @@ std::shared_ptr CreateNewOutgoingStream (std::shared_ptr remote, int port = 0); void DeleteStream (std::shared_ptr stream); + bool DeleteStream (uint32_t recvStreamID); void SetAcceptor (const Acceptor& acceptor); void ResetAcceptor (); bool IsAcceptorSet () const { return m_Acceptor != nullptr; }; diff -Nru i2pd-2.29.0/libi2pd/Tag.h i2pd-2.31.0/libi2pd/Tag.h --- i2pd-2.29.0/libi2pd/Tag.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Tag.h 2020-04-10 17:33:54.000000000 +0000 @@ -93,4 +93,16 @@ } // data } // i2p +namespace std +{ + // hash for std::unordered_map + template struct hash > + { + size_t operator()(const i2p::data::Tag& s) const + { + return s.GetLL ()[0]; + } + }; +} + #endif /* TAG_H__ */ diff -Nru i2pd-2.29.0/libi2pd/Transports.cpp i2pd-2.31.0/libi2pd/Transports.cpp --- i2pd-2.29.0/libi2pd/Transports.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Transports.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -6,10 +6,6 @@ #include "Transports.h" #include "Config.h" #include "HTTP.h" -#ifdef WITH_EVENTS -#include "Event.h" -#include "util.h" -#endif using namespace i2p::data; @@ -157,6 +153,7 @@ m_IsRunning = true; m_Thread = new std::thread (std::bind (&Transports::Run, this)); std::string ntcpproxy; i2p::config::GetOption("ntcpproxy", ntcpproxy); + std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); i2p::http::URL proxyurl; uint16_t softLimit, hardLimit, threads; i2p::config::GetOption("limits.ntcpsoft", softLimit); @@ -196,13 +193,38 @@ LogPrint(eLogError, "Transports: invalid NTCP proxy url ", ntcpproxy); return; } - // create NTCP2. TODO: move to acceptor + // create NTCP2. TODO: move to acceptor bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); if (ntcp2) { - m_NTCP2Server = new NTCP2Server (); - m_NTCP2Server->Start (); - } + if(!ntcp2proxy.empty()) + { + if(proxyurl.parse(ntcp2proxy)) + { + if(proxyurl.schema == "socks" || proxyurl.schema == "http") + { + m_NTCP2Server = new NTCP2Server (); + NTCP2Server::ProxyType proxytype = NTCP2Server::eSocksProxy; + + if (proxyurl.schema == "http") + proxytype = NTCP2Server::eHTTPProxy; + + m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port) ; + m_NTCP2Server->Start(); + } + else + LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy); + } + else + LogPrint(eLogError, "Transports: invalid NTCP2 proxy url ", ntcp2proxy); + return; + } + else + { + m_NTCP2Server = new NTCP2Server (); + m_NTCP2Server->Start (); + } + } // create acceptors auto& addresses = context.GetRouterInfo ().GetAddresses (); @@ -249,11 +271,11 @@ m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); - if (m_IsNAT) - { - m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL)); - m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); - } + if (m_IsNAT) + { + m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL)); + m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); + } } void Transports::Stop () @@ -346,9 +368,6 @@ void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector >& msgs) { -#ifdef WITH_EVENTS - QueueIntEvent("transport.send", ident.ToBase64(), msgs.size()); -#endif m_Service->post (std::bind (&Transports::PostMessages, this, ident, msgs)); } @@ -405,24 +424,36 @@ { if (peer.router) // we have RI already { - if (!peer.numAttempts) // NTCP2 - { - peer.numAttempts++; - if (m_NTCP2Server) // we support NTCP2 - { - // NTCP2 have priority over NTCP - auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only - if (address) - { - auto s = std::make_shared (*m_NTCP2Server, peer.router); - m_NTCP2Server->Connect (address->host, address->port, s); - return true; - } - } - } + if (!peer.numAttempts) // NTCP2 + { + peer.numAttempts++; + if (m_NTCP2Server) // we support NTCP2 + { + // NTCP2 have priority over NTCP + auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only + if (address) + { + auto s = std::make_shared (*m_NTCP2Server, peer.router); + + if(m_NTCP2Server->UsingProxy()) + { + NTCP2Server::RemoteAddressType remote = NTCP2Server::eIP4Address; + std::string addr = address->host.to_string(); + + if(address->host.is_v6()) + remote = NTCP2Server::eIP6Address; + + m_NTCP2Server->ConnectWithProxy(addr, address->port, remote, s); + } + else + m_NTCP2Server->Connect (address->host, address->port, s); + return true; + } + } + } if (peer.numAttempts == 1) // NTCP1 { - peer.numAttempts++; + peer.numAttempts++; auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ()); if (address && m_NTCPServer) { @@ -465,6 +496,7 @@ } } LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available"); + i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed peer.Done (); std::unique_lock l(m_PeersMutex); m_Peers.erase (ident); @@ -557,6 +589,7 @@ if (RoutesRestricted() || !i2p::context.SupportsV4 ()) return; if (m_SSUServer) { + LogPrint (eLogInfo, "Transports: Started peer test"); bool statusChanged = false; for (int i = 0; i < 5; i++) { @@ -572,7 +605,7 @@ } } if (!statusChanged) - LogPrint (eLogWarning, "Can't find routers for peer test"); + LogPrint (eLogWarning, "Transports: Can't find routers for peer test"); } } @@ -596,9 +629,6 @@ auto it = m_Peers.find (ident); if (it != m_Peers.end ()) { -#ifdef WITH_EVENTS - EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "false"}}); -#endif bool sendDatabaseStore = true; if (it->second.delayedMessages.size () > 0) { @@ -624,9 +654,6 @@ session->Done(); return; } -#ifdef WITH_EVENTS - EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "true"}}); -#endif session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore std::unique_lock l(m_PeersMutex); m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} })); @@ -641,17 +668,19 @@ auto remoteIdentity = session->GetRemoteIdentity (); if (!remoteIdentity) return; auto ident = remoteIdentity->GetIdentHash (); -#ifdef WITH_EVENTS - EmitEvent({{"type" , "transport.disconnected"}, {"ident", ident.ToBase64()}}); -#endif auto it = m_Peers.find (ident); if (it != m_Peers.end ()) { + auto before = it->second.sessions.size (); it->second.sessions.remove (session); - if (it->second.sessions.empty ()) // TODO: why? + if (it->second.sessions.empty ()) { if (it->second.delayedMessages.size () > 0) + { + if (before > 0) // we had an active session before + it->second.numAttempts = 0; // start over ConnectToPeer (ident, it->second); + } else { std::unique_lock l(m_PeersMutex); diff -Nru i2pd-2.29.0/libi2pd/TransportSession.h i2pd-2.31.0/libi2pd/TransportSession.h --- i2pd-2.29.0/libi2pd/TransportSession.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/TransportSession.h 2020-04-10 17:33:54.000000000 +0000 @@ -5,6 +5,7 @@ #include #include #include +#include #include "Identity.h" #include "Crypto.h" #include "RouterInfo.h" @@ -67,8 +68,16 @@ std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; } - std::shared_ptr GetRemoteIdentity () { return m_RemoteIdentity; }; - void SetRemoteIdentity (std::shared_ptr ident) { m_RemoteIdentity = ident; }; + std::shared_ptr GetRemoteIdentity () + { + std::lock_guard l(m_RemoteIdentityMutex); + return m_RemoteIdentity; + } + void SetRemoteIdentity (std::shared_ptr ident) + { + std::lock_guard l(m_RemoteIdentityMutex); + m_RemoteIdentity = ident; + } size_t GetNumSentBytes () const { return m_NumSentBytes; }; size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; @@ -85,6 +94,7 @@ protected: std::shared_ptr m_RemoteIdentity; + mutable std::mutex m_RemoteIdentityMutex; std::shared_ptr m_DHKeysPair; // X - for client and Y - for server size_t m_NumSentBytes, m_NumReceivedBytes; bool m_IsOutgoing; diff -Nru i2pd-2.29.0/libi2pd/Tunnel.cpp i2pd-2.31.0/libi2pd/Tunnel.cpp --- i2pd-2.29.0/libi2pd/Tunnel.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Tunnel.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -1,5 +1,6 @@ #include #include "I2PEndian.h" +#include #include #include #include @@ -13,9 +14,6 @@ #include "Config.h" #include "Tunnel.h" #include "TunnelPool.h" -#ifdef WITH_EVENTS -#include "Event.h" -#endif namespace i2p { @@ -34,9 +32,6 @@ void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel) { -#ifdef WITH_EVENTS - std::string peers = i2p::context.GetIdentity()->GetIdentHash().ToBase64(); -#endif auto numHops = m_Config->GetNumHops (); int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops; auto msg = NewI2NPShortMessage (); @@ -45,7 +40,7 @@ // shuffle records std::vector recordIndicies; for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); - std::random_shuffle (recordIndicies.begin(), recordIndicies.end()); + std::shuffle (recordIndicies.begin(), recordIndicies.end(), std::mt19937(std::random_device()())); // create real records uint8_t * records = msg->GetPayload () + 1; @@ -63,15 +58,9 @@ hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID, ctx); hop->recordIndex = idx; i++; -#ifdef WITH_EVENTS - peers += ":" + hop->ident->GetIdentHash().ToBase64(); -#endif hop = hop->next; } BN_CTX_free (ctx); -#ifdef WITH_EVENTS - EmitTunnelEvent("tunnel.build", this, peers); -#endif // fill up fake records with random data for (int i = numHops; i < numRecords; i++) { @@ -206,9 +195,6 @@ void Tunnel::SetState(TunnelState state) { m_State = state; -#ifdef WITH_EVENTS - EmitTunnelEvent("tunnel.state", this, state); -#endif } @@ -590,7 +576,6 @@ for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();) { auto tunnel = it->second; - auto pool = tunnel->GetTunnelPool(); switch (tunnel->GetState ()) { case eTunnelStatePending: @@ -613,11 +598,6 @@ hop = hop->next; } } -#ifdef WITH_EVENTS - EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed); -#endif - // for i2lua - if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultTimeout); // delete it = pendingTunnels.erase (it); m_NumFailedTunnelCreations++; @@ -627,12 +607,6 @@ break; case eTunnelStateBuildFailed: LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " failed, deleted"); -#ifdef WITH_EVENTS - EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed); -#endif - // for i2lua - if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultRejected); - it = pendingTunnels.erase (it); m_NumFailedTunnelCreations++; break; diff -Nru i2pd-2.29.0/libi2pd/Tunnel.h i2pd-2.31.0/libi2pd/Tunnel.h --- i2pd-2.29.0/libi2pd/Tunnel.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/Tunnel.h 2020-04-10 17:33:54.000000000 +0000 @@ -19,49 +19,11 @@ #include "TunnelGateway.h" #include "TunnelBase.h" #include "I2NPProtocol.h" -#include "Event.h" namespace i2p { namespace tunnel { - - template - static void EmitTunnelEvent(const std::string & ev, const TunnelT & t) - { -#ifdef WITH_EVENTS - EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}}); -#else - (void) ev; - (void) t; -#endif - } - - template - static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const T & val) - { -#ifdef WITH_EVENTS - EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", std::to_string(val)}, {"inbound", std::to_string(t->IsInbound())}}); -#else - (void) ev; - (void) t; - (void) val; -#endif - } - - template - static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const std::string & val) - { -#ifdef WITH_EVENTS - EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", val}, {"inbound", std::to_string(t->IsInbound())}}); -#else - (void) ev; - (void) t; - (void) val; -#endif - } - - const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes diff -Nru i2pd-2.29.0/libi2pd/TunnelPool.cpp i2pd-2.31.0/libi2pd/TunnelPool.cpp --- i2pd-2.29.0/libi2pd/TunnelPool.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/TunnelPool.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -1,4 +1,5 @@ #include +#include #include "I2PEndian.h" #include "Crypto.h" #include "Tunnel.h" @@ -10,9 +11,6 @@ #include "Tunnel.h" #include "TunnelPool.h" #include "Destination.h" -#ifdef WITH_EVENTS -#include "Event.h" -#endif namespace i2p { @@ -85,25 +83,17 @@ { if (!m_IsActive) return; { -#ifdef WITH_EVENTS - EmitTunnelEvent("tunnels.created", createdTunnel); -#endif std::unique_lock l(m_InboundTunnelsMutex); m_InboundTunnels.insert (createdTunnel); } if (m_LocalDestination) m_LocalDestination->SetLeaseSetUpdated (); - - OnTunnelBuildResult(createdTunnel, eBuildResultOkay); } void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) { if (expiredTunnel) { -#ifdef WITH_EVENTS - EmitTunnelEvent("tunnels.expired", expiredTunnel); -#endif expiredTunnel->SetTunnelPool (nullptr); for (auto& it: m_Tests) if (it.second.second == expiredTunnel) it.second.second = nullptr; @@ -117,14 +107,9 @@ { if (!m_IsActive) return; { -#ifdef WITH_EVENTS - EmitTunnelEvent("tunnels.created", createdTunnel); -#endif std::unique_lock l(m_OutboundTunnelsMutex); m_OutboundTunnels.insert (createdTunnel); } - OnTunnelBuildResult(createdTunnel, eBuildResultOkay); - //CreatePairedInboundTunnel (createdTunnel); } @@ -132,9 +117,6 @@ { if (expiredTunnel) { -#ifdef WITH_EVENTS - EmitTunnelEvent("tunnels.expired", expiredTunnel); -#endif expiredTunnel->SetTunnelPool (nullptr); for (auto& it: m_Tests) if (it.second.first == expiredTunnel) it.second.first = nullptr; @@ -441,7 +423,7 @@ int size = m_ExplicitPeers->size (); std::vector peerIndicies; for (int i = 0; i < size; i++) peerIndicies.push_back(i); - std::random_shuffle (peerIndicies.begin(), peerIndicies.end()); + std::shuffle (peerIndicies.begin(), peerIndicies.end(), std::mt19937(std::random_device()())); int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; for (int i = 0; i < numHops; i++) @@ -610,11 +592,5 @@ } return tun; } - - void TunnelPool::OnTunnelBuildResult(std::shared_ptr tunnel, TunnelBuildResult result) - { - auto peers = tunnel->GetPeers(); - if(m_CustomPeerSelector) m_CustomPeerSelector->OnBuildResult(peers, tunnel->IsInbound(), result); - } } } diff -Nru i2pd-2.29.0/libi2pd/TunnelPool.h i2pd-2.31.0/libi2pd/TunnelPool.h --- i2pd-2.29.0/libi2pd/TunnelPool.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/TunnelPool.h 2020-04-10 17:33:54.000000000 +0000 @@ -23,13 +23,6 @@ class InboundTunnel; class OutboundTunnel; - - enum TunnelBuildResult { - eBuildResultOkay, // tunnel was built okay - eBuildResultRejected, // tunnel build was explicitly rejected - eBuildResultTimeout // tunnel build timed out - }; - typedef std::shared_ptr Peer; typedef std::vector Path; @@ -38,7 +31,6 @@ { virtual ~ITunnelPeerSelector() {}; virtual bool SelectPeers(Path & peers, int hops, bool isInbound) = 0; - virtual bool OnBuildResult(const Path & peers, bool isInbound, TunnelBuildResult result) = 0; }; @@ -98,8 +90,6 @@ std::shared_ptr GetLowestLatencyInboundTunnel(std::shared_ptr exclude=nullptr) const; std::shared_ptr GetLowestLatencyOutboundTunnel(std::shared_ptr exclude=nullptr) const; - void OnTunnelBuildResult(std::shared_ptr tunnel, TunnelBuildResult result); - // for overriding tunnel peer selection std::shared_ptr SelectNextHop (std::shared_ptr prevHop) const; diff -Nru i2pd-2.29.0/libi2pd/util.cpp i2pd-2.31.0/libi2pd/util.cpp --- i2pd-2.29.0/libi2pd/util.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/util.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -22,7 +22,7 @@ #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) #define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) -// inet_pton exists Windows since Vista, but XP haven't that function! +// inet_pton exists Windows since Vista, but XP doesn't have that function! // This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found int inet_pton_xp(int af, const char *src, void *dst) { @@ -57,21 +57,65 @@ { namespace util { + + void RunnableService::StartIOService () + { + if (!m_IsRunning) + { + m_IsRunning = true; + m_Thread.reset (new std::thread (std::bind (& RunnableService::Run, this))); + } + } + + void RunnableService::StopIOService () + { + if (m_IsRunning) + { + m_IsRunning = false; + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + m_Thread = nullptr; + } + } + } + + void RunnableService::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, m_Name, ": runtime exception: ", ex.what ()); + } + } + } + namespace net { #ifdef WIN32 bool IsWindowsXPorLater() { - OSVERSIONINFO osvi; - - ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&osvi); - - if (osvi.dwMajorVersion <= 5) - return true; - else - return false; + static bool isRequested = false; + static bool isXP = false; + if (!isRequested) + { + // request + OSVERSIONINFO osvi; + + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + + isXP = osvi.dwMajorVersion <= 5; + isRequested = true; + } + return isXP; } int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback) @@ -202,21 +246,20 @@ std::string localAddressUniversal = localAddress.to_string(); #endif - if (IsWindowsXPorLater()) - { - #define inet_pton inet_pton_xp - } + typedef int (* IPN)(int af, const char *src, void *dst); + IPN inetpton = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton"); + if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found if(localAddress.is_v4()) { sockaddr_in inputAddress; - inet_pton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr)); + inetpton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr)); return GetMTUWindowsIpv4(inputAddress, fallback); } else if(localAddress.is_v6()) { sockaddr_in6 inputAddress; - inet_pton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr)); + inetpton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr)); return GetMTUWindowsIpv6(inputAddress, fallback); } else { LogPrint(eLogError, "NetIface: GetMTU(): address family is not supported"); @@ -281,7 +324,7 @@ int GetMTU(const boost::asio::ip::address& localAddress) { - const int fallback = 576; // fallback MTU + int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU #ifdef WIN32 return GetMTUWindows(localAddress, fallback); @@ -312,15 +355,14 @@ if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af) { // match - char * addr = new char[INET6_ADDRSTRLEN]; - bzero(addr, INET6_ADDRSTRLEN); + char addr[INET6_ADDRSTRLEN]; + memset (addr, 0, INET6_ADDRSTRLEN); if(af == AF_INET) inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN); else inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN); freeifaddrs(addrs); std::string cur_ifaddr(addr); - delete[] addr; return boost::asio::ip::address::from_string(cur_ifaddr); } cur = cur->ifa_next; diff -Nru i2pd-2.29.0/libi2pd/util.h i2pd-2.31.0/libi2pd/util.h --- i2pd-2.29.0/libi2pd/util.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/util.h 2020-04-10 17:33:54.000000000 +0000 @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -122,6 +123,43 @@ std::mutex m_Mutex; }; + class RunnableService + { + protected: + + RunnableService (const std::string& name): m_Name (name), m_IsRunning (false) {} + virtual ~RunnableService () {} + + boost::asio::io_service& GetIOService () { return m_Service; } + bool IsRunning () const { return m_IsRunning; }; + + void StartIOService (); + void StopIOService (); + + private: + + void Run (); + + private: + + std::string m_Name; + volatile bool m_IsRunning; + std::unique_ptr m_Thread; + boost::asio::io_service m_Service; + }; + + class RunnableServiceWithWork: public RunnableService + { + protected: + + RunnableServiceWithWork (const std::string& name): + RunnableService (name), m_Work (GetIOService ()) {} + + private: + + boost::asio::io_service::work m_Work; + }; + namespace net { int GetMTU (const boost::asio::ip::address& localAddress); diff -Nru i2pd-2.29.0/libi2pd/version.h i2pd-2.31.0/libi2pd/version.h --- i2pd-2.29.0/libi2pd/version.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd/version.h 2020-04-10 17:33:54.000000000 +0000 @@ -7,7 +7,7 @@ #define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) #define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 29 +#define I2PD_VERSION_MINOR 31 #define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 #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_MINOR 9 -#define I2P_VERSION_MICRO 43 +#define I2P_VERSION_MICRO 45 #define I2P_VERSION_PATCH 0 #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) diff -Nru i2pd-2.29.0/libi2pd_client/BOB.cpp i2pd-2.31.0/libi2pd_client/BOB.cpp --- i2pd-2.29.0/libi2pd_client/BOB.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/BOB.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -743,8 +743,8 @@ } BOBCommandChannel::BOBCommandChannel (const std::string& address, int port): - m_IsRunning (false), m_Thread (nullptr), - m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)) + RunnableService ("BOB"), + m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)) { // command -> handler m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler; @@ -794,7 +794,8 @@ BOBCommandChannel::~BOBCommandChannel () { - Stop (); + if (IsRunning ()) + Stop (); for (const auto& it: m_Destinations) delete it.second; } @@ -802,38 +803,15 @@ void BOBCommandChannel::Start () { Accept (); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&BOBCommandChannel::Run, this)); + StartIOService (); } void BOBCommandChannel::Stop () { - m_IsRunning = false; for (auto& it: m_Destinations) it.second->Stop (); m_Acceptor.cancel (); - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } - - void BOBCommandChannel::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "BOB: runtime exception: ", ex.what ()); - } - } + StopIOService (); } void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest) diff -Nru i2pd-2.29.0/libi2pd_client/BOB.h i2pd-2.31.0/libi2pd_client/BOB.h --- i2pd-2.29.0/libi2pd_client/BOB.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/BOB.h 2020-04-10 17:33:54.000000000 +0000 @@ -7,6 +7,7 @@ #include #include #include +#include "util.h" #include "I2PTunnel.h" #include "I2PService.h" #include "Identity.h" @@ -231,7 +232,7 @@ }; typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len); - class BOBCommandChannel + class BOBCommandChannel: private i2p::util::RunnableService { public: @@ -241,22 +242,18 @@ void Start (); void Stop (); - boost::asio::io_service& GetService () { return m_Service; }; + boost::asio::io_service& GetService () { return GetIOService (); }; void AddDestination (const std::string& name, BOBDestination * dest); void DeleteDestination (const std::string& name); BOBDestination * FindDestination (const std::string& name); private: - void Run (); void Accept (); void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session); private: - bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; boost::asio::ip::tcp::acceptor m_Acceptor; std::map m_Destinations; std::map m_CommandHandlers; diff -Nru i2pd-2.29.0/libi2pd_client/ClientContext.cpp i2pd-2.31.0/libi2pd_client/ClientContext.cpp --- i2pd-2.29.0/libi2pd_client/ClientContext.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/ClientContext.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -9,7 +9,6 @@ #include "util.h" #include "ClientContext.h" #include "SOCKS.h" -#include "WebSocks.h" #include "MatchedDestination.h" namespace i2p @@ -53,14 +52,19 @@ // SAM bool sam; i2p::config::GetOption("sam.enabled", sam); - if (sam) { + if (sam) + { std::string samAddr; i2p::config::GetOption("sam.address", samAddr); uint16_t samPort; i2p::config::GetOption("sam.port", samPort); + bool singleThread; i2p::config::GetOption("sam.singlethread", singleThread); LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort); - try { - m_SamBridge = new SAMBridge (samAddr, samPort); - m_SamBridge->Start (); - } catch (std::exception& e) { + try + { + m_SamBridge = new SAMBridge (samAddr, samPort, singleThread); + m_SamBridge->Start (); + } + catch (std::exception& e) + { LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what()); } } @@ -305,21 +309,34 @@ const std::map * params) { i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); - auto localDestination = std::make_shared (keys, isPublic, params); - std::unique_lock l(m_DestinationsMutex); - m_Destinations[localDestination->GetIdentHash ()] = localDestination; - localDestination->Start (); + auto localDestination = std::make_shared (keys, isPublic, params); + AddLocalDestination (localDestination); + return localDestination; + } + + std::shared_ptr ClientContext::CreateNewLocalDestination ( + boost::asio::io_service& service, bool isPublic, + i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType, + const std::map * params) + { + i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); + auto localDestination = std::make_shared (service, keys, isPublic, params); + AddLocalDestination (localDestination); return localDestination; } std::shared_ptr ClientContext::CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map * params) { - MatchedTunnelDestination * cl = new MatchedTunnelDestination(keys, name, params); - auto localDestination = std::shared_ptr(cl); + auto localDestination = std::make_shared(keys, name, params); + AddLocalDestination (localDestination); + return localDestination; + } + + void ClientContext::AddLocalDestination (std::shared_ptr localDestination) + { std::unique_lock l(m_DestinationsMutex); m_Destinations[localDestination->GetIdentHash ()] = localDestination; localDestination->Start (); - return localDestination; } void ClientContext::DeleteLocalDestination (std::shared_ptr destination) @@ -344,14 +361,26 @@ if (it != m_Destinations.end ()) { LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists"); - if (!it->second->IsRunning ()) - it->second->Start (); + it->second->Start (); // make sure to start return it->second; } - auto localDestination = std::make_shared (keys, isPublic, params); - std::unique_lock l(m_DestinationsMutex); - m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination; - localDestination->Start (); + auto localDestination = std::make_shared (keys, isPublic, params); + AddLocalDestination (localDestination); + return localDestination; + } + + std::shared_ptr ClientContext::CreateNewLocalDestination (boost::asio::io_service& service, + const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params) + { + auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ()); + if (it != m_Destinations.end ()) + { + LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists"); + it->second->Start (); // make sure to start + return it->second; + } + auto localDestination = std::make_shared (service, keys, isPublic, params); + AddLocalDestination (localDestination); return localDestination; } @@ -568,10 +597,8 @@ } else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS) { - // websocks proxy - auto tun = std::make_shared(address, port, localDestination); - clientTunnel = tun; - clientEndpoint = tun->GetLocalEndpoint(); + LogPrint(eLogError, "Clients: I2P Client tunnel websocks is deprecated"); + continue; } else { diff -Nru i2pd-2.29.0/libi2pd_client/ClientContext.h i2pd-2.31.0/libi2pd_client/ClientContext.h --- i2pd-2.29.0/libi2pd_client/ClientContext.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/ClientContext.h 2020-04-10 17:33:54.000000000 +0000 @@ -37,7 +37,7 @@ const char I2P_CLIENT_TUNNEL_CRYPTO_TYPE[] = "cryptotype"; const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport"; const char I2P_CLIENT_TUNNEL_MATCH_TUNNELS[] = "matchtunnels"; - const char I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT[] = "connecttimeout"; + const char I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT[] = "connecttimeout"; const char I2P_SERVER_TUNNEL_HOST[] = "host"; const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride"; const char I2P_SERVER_TUNNEL_PORT[] = "port"; @@ -68,8 +68,15 @@ i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, const std::map * params = nullptr); // used by SAM only + std::shared_ptr CreateNewLocalDestination (boost::asio::io_service& service, + bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, + i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, + const std::map * params = nullptr); // same as previous but on external io_service std::shared_ptr CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, const std::map * params = nullptr); + std::shared_ptr CreateNewLocalDestination (boost::asio::io_service& service, + const i2p::data::PrivateKeys& keys, bool isPublic = true, + const std::map * params = nullptr); // same as previous but on external io_service std::shared_ptr CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map * params = nullptr); void DeleteLocalDestination (std::shared_ptr destination); std::shared_ptr FindLocalDestination (const i2p::data::IdentHash& destination) const; @@ -107,6 +114,7 @@ void VisitTunnels (Visitor v); // Visitor: (I2PService *) -> bool, true means retain void CreateNewSharedLocalDestination (); + void AddLocalDestination (std::shared_ptr localDestination); private: diff -Nru i2pd-2.29.0/libi2pd_client/I2CP.cpp i2pd-2.31.0/libi2pd_client/I2CP.cpp --- i2pd-2.29.0/libi2pd_client/I2CP.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/I2CP.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -24,17 +24,42 @@ { I2CPDestination::I2CPDestination (std::shared_ptr owner, std::shared_ptr identity, bool isPublic, const std::map& params): - LeaseSetDestination (isPublic, ¶ms), m_Owner (owner), m_Identity (identity) + RunnableService ("I2CP"), LeaseSetDestination (GetIOService (), isPublic, ¶ms), + m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()) { } + I2CPDestination::~I2CPDestination () + { + if (IsRunning ()) + Stop (); + } + + void I2CPDestination::Start () + { + if (!IsRunning ()) + { + LeaseSetDestination::Start (); + StartIOService (); + } + } + + void I2CPDestination::Stop () + { + if (IsRunning ()) + { + LeaseSetDestination::Stop (); + StopIOService (); + } + } + void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key) { memcpy (m_EncryptionPrivateKey, key, 256); m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_Identity->GetCryptoKeyType (), m_EncryptionPrivateKey); } - bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const + bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const { if (m_Decryptor) return m_Decryptor->Decrypt (encrypted, data, ctx, true); @@ -221,8 +246,16 @@ m_PayloadLen = bufbe32toh (m_Header + I2CP_HEADER_LENGTH_OFFSET); if (m_PayloadLen > 0) { - m_Payload = new uint8_t[m_PayloadLen]; - ReceivePayload (); + if (m_PayloadLen <= I2CP_MAX_MESSAGE_LENGTH) + { + m_Payload = new uint8_t[m_PayloadLen]; + ReceivePayload (); + } + else + { + LogPrint (eLogError, "I2CP: Unexpected payload length ", m_PayloadLen); + Terminate (); + } } else // no following payload { @@ -547,7 +580,7 @@ uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length if (offset + keyLen > len) return; - if (keyType > currentKeyType) + if (!currentKey || keyType > currentKeyType) { currentKeyType = keyType; currentKey = buf + offset; @@ -556,7 +589,10 @@ } // TODO: support multiple keys if (currentKey) + { m_Destination->SetEncryptionPrivateKey (currentKey); + m_Destination->SetEncryptionType (currentKeyType); + } m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ()); } @@ -786,8 +822,11 @@ { m_IsRunning = false; m_Acceptor.cancel (); - for (auto& it: m_Sessions) - it.second->Stop (); + { + auto sessions = m_Sessions; + for (auto& it: sessions) + it.second->Stop (); + } m_Sessions.clear (); m_Service.stop (); if (m_Thread) diff -Nru i2pd-2.29.0/libi2pd_client/I2CP.h i2pd-2.31.0/libi2pd_client/I2CP.h --- i2pd-2.29.0/libi2pd_client/I2CP.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/I2CP.h 2020-04-10 17:33:54.000000000 +0000 @@ -15,6 +15,7 @@ #include #include #include +#include "util.h" #include "Destination.h" namespace i2p @@ -23,7 +24,8 @@ { const uint8_t I2CP_PROTOCOL_BYTE = 0x2A; const size_t I2CP_SESSION_BUFFER_SIZE = 4096; - + const size_t I2CP_MAX_MESSAGE_LENGTH = 65535; + const size_t I2CP_HEADER_LENGTH_OFFSET = 0; const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4; const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1; @@ -61,19 +63,26 @@ const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability"; class I2CPSession; - class I2CPDestination: public LeaseSetDestination + class I2CPDestination: private i2p::util::RunnableService, public LeaseSetDestination { public: I2CPDestination (std::shared_ptr owner, std::shared_ptr identity, bool isPublic, const std::map& params); - + ~I2CPDestination (); + + void Start (); + void Stop (); + void SetEncryptionPrivateKey (const uint8_t * key); + void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; }; void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession void LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len); // called from I2CPSession void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession // implements LocalDestination - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const; + bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; + bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const { return m_EncryptionKeyType == keyType; }; + // TODO: implement GetEncryptionPublicKey std::shared_ptr GetIdentity () const { return m_Identity; }; protected: @@ -93,6 +102,7 @@ std::shared_ptr m_Owner; std::shared_ptr m_Identity; uint8_t m_EncryptionPrivateKey[256]; + i2p::data::CryptoKeyType m_EncryptionKeyType; std::shared_ptr m_Decryptor; uint64_t m_LeaseSetExpirationTime; }; diff -Nru i2pd-2.29.0/libi2pd_client/MatchedDestination.cpp i2pd-2.31.0/libi2pd_client/MatchedDestination.cpp --- i2pd-2.29.0/libi2pd_client/MatchedDestination.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/MatchedDestination.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -8,7 +8,7 @@ namespace client { MatchedTunnelDestination::MatchedTunnelDestination(const i2p::data::PrivateKeys & keys, const std::string & remoteName, const std::map * params) - : ClientDestination(keys, false, params), + : RunnableClientDestination(keys, false, params), m_RemoteName(remoteName) {} @@ -45,29 +45,19 @@ } - bool MatchedTunnelDestination::Start() + void MatchedTunnelDestination::Start() { - if(ClientDestination::Start()) - { - m_ResolveTimer = std::make_shared(GetService()); - GetTunnelPool()->SetCustomPeerSelector(this); - ResolveCurrentLeaseSet(); - return true; - } - else - return false; + ClientDestination::Start(); + m_ResolveTimer = std::make_shared(GetService()); + GetTunnelPool()->SetCustomPeerSelector(this); + ResolveCurrentLeaseSet(); } - bool MatchedTunnelDestination::Stop() + void MatchedTunnelDestination::Stop() { - if(ClientDestination::Stop()) - { - if(m_ResolveTimer) - m_ResolveTimer->cancel(); - return true; - } - else - return false; + ClientDestination::Stop(); + if(m_ResolveTimer) + m_ResolveTimer->cancel(); } @@ -104,10 +94,5 @@ } return true; } - - bool MatchedTunnelDestination::OnBuildResult(const i2p::tunnel::Path & path, bool inbound, i2p::tunnel::TunnelBuildResult result) - { - return true; - } } } diff -Nru i2pd-2.29.0/libi2pd_client/MatchedDestination.h i2pd-2.31.0/libi2pd_client/MatchedDestination.h --- i2pd-2.29.0/libi2pd_client/MatchedDestination.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/MatchedDestination.h 2020-04-10 17:33:54.000000000 +0000 @@ -10,15 +10,14 @@ /** client tunnel that uses same OBEP as IBGW of each remote lease for a remote destination */ - class MatchedTunnelDestination : public ClientDestination, public i2p::tunnel::ITunnelPeerSelector + class MatchedTunnelDestination : public RunnableClientDestination, public i2p::tunnel::ITunnelPeerSelector { public: MatchedTunnelDestination(const i2p::data::PrivateKeys& keys, const std::string & remoteName, const std::map * params = nullptr); - bool Start(); - bool Stop(); + void Start(); + void Stop(); bool SelectPeers(i2p::tunnel::Path & peers, int hops, bool inbound); - bool OnBuildResult(const i2p::tunnel::Path & peers, bool inbound, i2p::tunnel::TunnelBuildResult result); private: void ResolveCurrentLeaseSet(); diff -Nru i2pd-2.29.0/libi2pd_client/SAM.cpp i2pd-2.31.0/libi2pd_client/SAM.cpp --- i2pd-2.29.0/libi2pd_client/SAM.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/SAM.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -1000,10 +1000,10 @@ } } - SAMBridge::SAMBridge (const std::string& address, int port): - m_IsRunning (false), m_Thread (nullptr), - m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)), - m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint), + SAMBridge::SAMBridge (const std::string& address, int port, bool singleThread): + RunnableService ("SAM"), m_IsSingleThread (singleThread), + m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)), + m_DatagramEndpoint (boost::asio::ip::address::from_string(address), port-1), m_DatagramSocket (GetIOService (), m_DatagramEndpoint), m_SignatureTypes { {"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1}, @@ -1020,7 +1020,7 @@ SAMBridge::~SAMBridge () { - if (m_IsRunning) + if (IsRunning ()) Stop (); } @@ -1028,14 +1028,11 @@ { Accept (); ReceiveDatagram (); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&SAMBridge::Run, this)); + StartIOService (); } void SAMBridge::Stop () { - m_IsRunning = false; - try { m_Acceptor.cancel (); @@ -1045,31 +1042,13 @@ LogPrint (eLogError, "SAM: runtime exception: ", ex.what ()); } - for (auto& it: m_Sessions) - it.second->CloseStreams (); - m_Sessions.clear (); - m_Service.stop (); - if (m_Thread) { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - } - - void SAMBridge::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "SAM: runtime exception: ", ex.what ()); - } + std::unique_lock l(m_SessionsMutex); + for (auto& it: m_Sessions) + it.second->CloseStreams (); + m_Sessions.clear (); } + StopIOService (); } void SAMBridge::Accept () @@ -1118,7 +1097,9 @@ { i2p::data::PrivateKeys keys; if (!keys.FromBase64 (destination)) return nullptr; - localDestination = i2p::client::context.CreateNewLocalDestination (keys, true, params); + localDestination = m_IsSingleThread ? + i2p::client::context.CreateNewLocalDestination (GetIOService (), keys, true, params) : + i2p::client::context.CreateNewLocalDestination (keys, true, params); } else // transient { @@ -1146,7 +1127,9 @@ } } } - localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, params); + localDestination = m_IsSingleThread ? + i2p::client::context.CreateNewLocalDestination (GetIOService (), true, signatureType, cryptoType, params) : + i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, params); } if (localDestination) { @@ -1178,6 +1161,15 @@ session->localDestination->Release (); session->localDestination->StopAcceptingStreams (); session->CloseStreams (); + if (m_IsSingleThread) + { + auto timer = std::make_shared(GetService ()); + timer->expires_from_now (boost::posix_time::seconds(5)); // postpone destination clean for 5 seconds + timer->async_wait ([timer, session](const boost::system::error_code& ecode) + { + // session's destructor is called here + }); + } } } diff -Nru i2pd-2.29.0/libi2pd_client/SAM.h i2pd-2.31.0/libi2pd_client/SAM.h --- i2pd-2.29.0/libi2pd_client/SAM.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/SAM.h 2020-04-10 17:33:54.000000000 +0000 @@ -9,6 +9,7 @@ #include #include #include +#include "util.h" #include "Identity.h" #include "LeaseSet.h" #include "Streaming.h" @@ -174,17 +175,17 @@ void CloseStreams (); }; - class SAMBridge + class SAMBridge: private i2p::util::RunnableService { public: - SAMBridge (const std::string& address, int port); + SAMBridge (const std::string& address, int port, bool singleThread); ~SAMBridge (); void Start (); void Stop (); - boost::asio::io_service& GetService () { return m_Service; }; + boost::asio::io_service& GetService () { return GetIOService (); }; std::shared_ptr CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient const std::map * params); void CloseSession (const std::string& id); @@ -201,8 +202,6 @@ private: - void Run (); - void Accept (); void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); @@ -211,9 +210,7 @@ private: - bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; + bool m_IsSingleThread; boost::asio::ip::tcp::acceptor m_Acceptor; boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint; boost::asio::ip::udp::socket m_DatagramSocket; diff -Nru i2pd-2.29.0/libi2pd_client/SOCKS.cpp i2pd-2.31.0/libi2pd_client/SOCKS.cpp --- i2pd-2.29.0/libi2pd_client/SOCKS.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/SOCKS.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -433,6 +433,9 @@ break; case CMD_UDP: if (m_socksv == SOCKS5) break; +#if (__cplusplus >= 201703L) // C++ 17 or higher + [[fallthrough]]; +#endif default: LogPrint(eLogError, "SOCKS: invalid command: ", ((int)*sock_buff)); SocksRequestFailed(SOCKS5_GEN_FAIL); diff -Nru i2pd-2.29.0/libi2pd_client/Websocket.cpp i2pd-2.31.0/libi2pd_client/Websocket.cpp --- i2pd-2.29.0/libi2pd_client/Websocket.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/Websocket.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,195 +0,0 @@ -#ifdef WITH_EVENTS -#include "Websocket.h" -#include "Log.h" - -#include -#include - -#include -#include -#include -#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) -#if !GCC47_BOOST149 -#include -#endif - -#include - -namespace i2p -{ - namespace event - { - - typedef websocketpp::server ServerImpl; - typedef websocketpp::connection_hdl ServerConn; - - class WebsocketServerImpl : public EventListener - { - private: - typedef ServerImpl::message_ptr MessagePtr; - public: - - WebsocketServerImpl(const std::string & addr, int port) : - m_run(false), - m_ws_thread(nullptr), - m_ev_thread(nullptr), - m_WebsocketTicker(m_Service) - { - m_server.init_asio(); - m_server.set_open_handler(std::bind(&WebsocketServerImpl::ConnOpened, this, std::placeholders::_1)); - m_server.set_close_handler(std::bind(&WebsocketServerImpl::ConnClosed, this, std::placeholders::_1)); - m_server.set_message_handler(std::bind(&WebsocketServerImpl::OnConnMessage, this, std::placeholders::_1, std::placeholders::_2)); - - m_server.listen(boost::asio::ip::address::from_string(addr), port); - } - - ~WebsocketServerImpl() - { - } - - void Start() { - m_run = true; - m_server.start_accept(); - m_ws_thread = new std::thread([&] () { - while(m_run) { - try { - m_server.run(); - } catch (std::exception & e ) { - LogPrint(eLogError, "Websocket server: ", e.what()); - } - } - }); - m_ev_thread = new std::thread([&] () { - while(m_run) { - try { - m_Service.run(); - break; - } catch (std::exception & e ) { - LogPrint(eLogError, "Websocket service: ", e.what()); - } - } - }); - ScheduleTick(); - } - - void Stop() { - m_run = false; - m_Service.stop(); - m_server.stop(); - - if(m_ev_thread) { - m_ev_thread->join(); - delete m_ev_thread; - } - m_ev_thread = nullptr; - - if(m_ws_thread) { - m_ws_thread->join(); - delete m_ws_thread; - } - m_ws_thread = nullptr; - } - - void ConnOpened(ServerConn c) - { - std::lock_guard lock(m_connsMutex); - m_conns.insert(c); - } - - void ConnClosed(ServerConn c) - { - std::lock_guard lock(m_connsMutex); - m_conns.erase(c); - } - - void OnConnMessage(ServerConn conn, ServerImpl::message_ptr msg) - { - (void) conn; - (void) msg; - } - - void HandleTick(const boost::system::error_code & ec) - { - - if(ec != boost::asio::error::operation_aborted) - LogPrint(eLogError, "Websocket ticker: ", ec.message()); - // pump collected events to us - i2p::event::core.PumpCollected(this); - ScheduleTick(); - } - - void ScheduleTick() - { - LogPrint(eLogDebug, "Websocket schedule tick"); - boost::posix_time::seconds dlt(1); - m_WebsocketTicker.expires_from_now(dlt); - m_WebsocketTicker.async_wait(std::bind(&WebsocketServerImpl::HandleTick, this, std::placeholders::_1)); - } - - /** @brief called from m_ev_thread */ - void HandlePumpEvent(const EventType & ev, const uint64_t & val) - { - EventType e; - for (const auto & i : ev) - e[i.first] = i.second; - - e["number"] = std::to_string(val); - HandleEvent(e); - } - - /** @brief called from m_ws_thread */ - void HandleEvent(const EventType & ev) - { - std::lock_guard lock(m_connsMutex); - boost::property_tree::ptree event; - for (const auto & item : ev) { - event.put(item.first, item.second); - } - std::ostringstream ss; - write_json(ss, event); - std::string s = ss.str(); - - ConnList::iterator it; - for (it = m_conns.begin(); it != m_conns.end(); ++it) { - ServerImpl::connection_ptr con = m_server.get_con_from_hdl(*it); - con->send(s); - } - } - - private: - typedef std::set > ConnList; - bool m_run; - std::thread * m_ws_thread; - std::thread * m_ev_thread; - std::mutex m_connsMutex; - ConnList m_conns; - ServerImpl m_server; - boost::asio::io_service m_Service; - boost::asio::deadline_timer m_WebsocketTicker; - }; - - - WebsocketServer::WebsocketServer(const std::string & addr, int port) : m_impl(new WebsocketServerImpl(addr, port)) {} - WebsocketServer::~WebsocketServer() - { - delete m_impl; - } - - - void WebsocketServer::Start() - { - m_impl->Start(); - } - - void WebsocketServer::Stop() - { - m_impl->Stop(); - } - - EventListener * WebsocketServer::ToListener() - { - return m_impl; - } - } -} -#endif diff -Nru i2pd-2.29.0/libi2pd_client/Websocket.h i2pd-2.31.0/libi2pd_client/Websocket.h --- i2pd-2.29.0/libi2pd_client/Websocket.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/Websocket.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -#ifndef WEBSOCKET_H__ -#define WEBSOCKET_H__ -#include "Event.h" -namespace i2p -{ - namespace event - { - - class WebsocketServerImpl; - - class WebsocketServer - { - public: - WebsocketServer(const std::string & addr, int port); - ~WebsocketServer(); - - void Start(); - void Stop(); - - EventListener * ToListener(); - - private: - WebsocketServerImpl * m_impl; - }; - - } -} -#endif diff -Nru i2pd-2.29.0/libi2pd_client/WebSocks.cpp i2pd-2.31.0/libi2pd_client/WebSocks.cpp --- i2pd-2.29.0/libi2pd_client/WebSocks.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/WebSocks.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,559 +0,0 @@ -#include "WebSocks.h" -#include "Log.h" -#include - -#ifdef WITH_EVENTS -#include "ClientContext.h" -#include "Identity.h" -#include "Destination.h" -#include "Streaming.h" -#include - -#include -#include - -#include -#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) -#if !GCC47_BOOST149 -#include -#endif - -namespace i2p -{ -namespace client -{ - typedef websocketpp::server WebSocksServerImpl; - - typedef std::function)> StreamConnectFunc; - - - struct IWebSocksConn : public I2PServiceHandler - { - IWebSocksConn(I2PService * parent) : I2PServiceHandler(parent) {} - virtual void Close() = 0; - virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg) = 0; - }; - - typedef std::shared_ptr WebSocksConn_ptr; - - WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent); - - class WebSocksImpl - { - - typedef std::mutex mutex_t; - typedef std::unique_lock lock_t; - - typedef std::shared_ptr Destination_t; - public: - - typedef WebSocksServerImpl ServerImpl; - typedef ServerImpl::message_ptr MessagePtr; - - WebSocksImpl(const std::string & addr, int port) : - Parent(nullptr), - m_Run(false), - m_Addr(addr), - m_Port(port), - m_Thread(nullptr) - { - m_Server.init_asio(); - m_Server.set_open_handler(std::bind(&WebSocksImpl::ConnOpened, this, std::placeholders::_1)); - } - - void InitializeDestination(WebSocks * parent) - { - Parent = parent; - m_Dest = Parent->GetLocalDestination(); - } - - ServerImpl::connection_ptr GetConn(const websocketpp::connection_hdl & conn) - { - return m_Server.get_con_from_hdl(conn); - } - - void CloseConn(const websocketpp::connection_hdl & conn) - { - auto c = GetConn(conn); - if(c) c->close(websocketpp::close::status::normal, "closed"); - } - - void CreateStreamTo(const std::string & addr, int port, StreamConnectFunc complete) - { - auto & addressbook = i2p::client::context.GetAddressBook(); - auto a = addressbook.GetAddress (addr); - if (a && a->IsIdentHash ()) - { - // address found - m_Dest->CreateStream(complete, a->identHash, port); - } - else - { - // not found - complete(nullptr); - } - } - - void ConnOpened(websocketpp::connection_hdl conn) - { - auto ptr = CreateWebSocksConn(conn, this); - Parent->AddHandler(ptr); - m_Conns.push_back(ptr); - } - - void Start() - { - if(m_Run) return; // already started - m_Server.listen(boost::asio::ip::address::from_string(m_Addr), m_Port); - m_Server.start_accept(); - m_Run = true; - m_Thread = new std::thread([&] (){ - while(m_Run) { - try { - m_Server.run(); - } catch( std::exception & ex) { - LogPrint(eLogError, "Websocks runtime exception: ", ex.what()); - } - } - }); - m_Dest->Start(); - } - - void Stop() - { - for(const auto & conn : m_Conns) - conn->Close(); - - m_Dest->Stop(); - m_Run = false; - m_Server.stop(); - if(m_Thread) { - m_Thread->join(); - delete m_Thread; - } - m_Thread = nullptr; - } - - boost::asio::ip::tcp::endpoint GetLocalEndpoint() - { - return boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(m_Addr), m_Port); - } - - i2p::datagram::DatagramDestination * GetDatagramDest() const - { - auto dgram = m_Dest->GetDatagramDestination(); - if(!dgram) dgram = m_Dest->CreateDatagramDestination(); - return dgram; - } - - WebSocks * Parent; - - private: - std::vector m_Conns; - bool m_Run; - ServerImpl m_Server; - std::string m_Addr; - int m_Port; - std::thread * m_Thread; - Destination_t m_Dest; - }; - - struct WebSocksConn : public IWebSocksConn , public std::enable_shared_from_this - { - enum ConnState - { - eWSCInitial, - eWSCTryConnect, - eWSCFailConnect, - eWSCOkayConnect, - eWSCDatagram, - eWSCClose, - eWSCEnd - }; - - typedef WebSocksServerImpl ServerImpl; - typedef ServerImpl::message_ptr Message_t; - typedef websocketpp::connection_hdl ServerConn; - typedef std::shared_ptr Destination_t; - typedef std::shared_ptr StreamDest_t; - typedef std::shared_ptr Stream_t; - - ServerConn m_Conn; - Stream_t m_Stream; - ConnState m_State; - WebSocksImpl * m_Parent; - std::string m_RemoteAddr; - int m_RemotePort; - uint8_t m_RecvBuf[2048]; - bool m_IsDatagram; - i2p::datagram::DatagramDestination * m_Datagram; - - WebSocksConn(const ServerConn & conn, WebSocksImpl * parent) : - IWebSocksConn(parent->Parent), - m_Conn(conn), - m_Stream(nullptr), - m_State(eWSCInitial), - m_Parent(parent), - m_IsDatagram(false), - m_Datagram(nullptr) - { - - } - - ~WebSocksConn() - { - Close(); - } - - void HandleDatagram(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - auto conn = m_Parent->GetConn(m_Conn); - if(conn) - { - std::stringstream ss; - ss << from.GetIdentHash().ToBase32(); - ss << ".b32.i2p:"; - ss << std::to_string(fromPort); - ss << "\n"; - ss.write((char *)buf, len); - conn->send(ss.str()); - } - } - - void BeginDatagram() - { - m_Datagram = m_Parent->GetDatagramDest(); - m_Datagram->SetReceiver( - std::bind( - &WebSocksConn::HandleDatagram, - this, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3, - std::placeholders::_4, - std::placeholders::_5), m_RemotePort); - } - - void EnterState(ConnState state) - { - LogPrint(eLogDebug, "websocks: state ", m_State, " -> ", state); - switch(m_State) - { - case eWSCInitial: - if (state == eWSCClose) { - m_State = eWSCClose; - // connection was opened but never used - LogPrint(eLogInfo, "websocks: connection closed but never used"); - Close(); - return; - } else if (state == eWSCTryConnect) { - // we will try to connect - m_State = eWSCTryConnect; - m_Parent->CreateStreamTo(m_RemoteAddr, m_RemotePort, std::bind(&WebSocksConn::ConnectResult, this, std::placeholders::_1)); - } else if (state == eWSCDatagram) { - if (m_RemotePort >= 0 && m_RemotePort <= 65535) - { - LogPrint(eLogDebug, "websocks: datagram mode initiated"); - m_State = eWSCDatagram; - BeginDatagram(); - SendResponse(""); - } - else - SendResponse("invalid port"); - } else { - LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); - } - return; - case eWSCTryConnect: - if(state == eWSCOkayConnect) { - // we connected okay - LogPrint(eLogDebug, "websocks: connected to ", m_RemoteAddr, ":", m_RemotePort); - SendResponse(""); - m_State = eWSCOkayConnect; - } else if(state == eWSCFailConnect) { - // we did not connect okay - LogPrint(eLogDebug, "websocks: failed to connect to ", m_RemoteAddr, ":", m_RemotePort); - SendResponse("failed to connect"); - m_State = eWSCFailConnect; - EnterState(eWSCInitial); - } else if(state == eWSCClose) { - // premature close - LogPrint(eLogWarning, "websocks: websocket connection closed prematurely"); - m_State = eWSCClose; - } else { - LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); - } - return; - case eWSCFailConnect: - if (state == eWSCInitial) { - // reset to initial state so we can try connecting again - m_RemoteAddr = ""; - m_RemotePort = 0; - LogPrint(eLogDebug, "websocks: reset websocket conn to initial state"); - m_State = eWSCInitial; - } else if (state == eWSCClose) { - // we are going to close the connection - m_State = eWSCClose; - Close(); - } else { - LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); - } - return; - case eWSCDatagram: - if(state != eWSCClose) { - LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); - } - m_State = eWSCClose; - Close(); - return; - case eWSCOkayConnect: - if(state == eWSCClose) { - // graceful close - m_State = eWSCClose; - Close(); - } else { - LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); - } - return; - case eWSCClose: - if(state == eWSCEnd) { - LogPrint(eLogDebug, "websocks: socket ended"); - Kill(); - auto me = shared_from_this(); - Done(me); - } else { - LogPrint(eLogWarning, "websocks: invalid state change ", m_State, " -> ", state); - } - return; - default: - LogPrint(eLogError, "websocks: bad state ", m_State); - } - } - - void StartForwarding() - { - LogPrint(eLogDebug, "websocks: begin forwarding data"); - uint8_t b[1]; - m_Stream->Send(b, 0); - AsyncRecv(); - } - - void HandleAsyncRecv(const boost::system::error_code &ec, std::size_t n) - { - if(ec) { - // error - LogPrint(eLogWarning, "websocks: connection error ", ec.message()); - EnterState(eWSCClose); - } else { - // forward data - LogPrint(eLogDebug, "websocks recv ", n); - - std::string str((char*)m_RecvBuf, n); - auto conn = m_Parent->GetConn(m_Conn); - if(!conn) { - LogPrint(eLogWarning, "websocks: connection is gone"); - EnterState(eWSCClose); - return; - } - conn->send(str); - AsyncRecv(); - - } - } - - void AsyncRecv() - { - m_Stream->AsyncReceive( - boost::asio::buffer(m_RecvBuf, sizeof(m_RecvBuf)), - std::bind(&WebSocksConn::HandleAsyncRecv, this, std::placeholders::_1, std::placeholders::_2), 60); - } - - /** @brief send error message or empty string for success */ - void SendResponse(const std::string & errormsg) - { - boost::property_tree::ptree resp; - if(errormsg.size()) { - resp.put("error", errormsg); - resp.put("success", 0); - } else { - resp.put("success", 1); - } - std::ostringstream ss; - write_json(ss, resp); - auto conn = m_Parent->GetConn(m_Conn); - if(conn) conn->send(ss.str()); - } - - void ConnectResult(Stream_t stream) - { - m_Stream = stream; - if(m_State == eWSCClose) { - // premature close of websocket - Close(); - return; - } - if(m_Stream) { - // connect good - EnterState(eWSCOkayConnect); - StartForwarding(); - } else { - // connect failed - EnterState(eWSCFailConnect); - } - } - - virtual void GotMessage(const websocketpp::connection_hdl & conn, WebSocksServerImpl::message_ptr msg) - { - (void) conn; - std::string payload = msg->get_payload(); - if(m_State == eWSCOkayConnect) - { - // forward to server - LogPrint(eLogDebug, "websocks: forward ", payload.size()); - m_Stream->Send((uint8_t*)payload.c_str(), payload.size()); - } else if (m_State == eWSCInitial) { - // recv connect request - auto itr = payload.find(":"); - if(itr == std::string::npos) { - // no port - m_RemotePort = 0; - m_RemoteAddr = payload; - } else { - // includes port - m_RemotePort = std::stoi(payload.substr(itr+1)); - m_RemoteAddr = payload.substr(0, itr); - } - m_IsDatagram = m_RemoteAddr == "DATAGRAM"; - if(m_IsDatagram) - EnterState(eWSCDatagram); - else - EnterState(eWSCTryConnect); - } else if (m_State == eWSCDatagram) { - // send datagram - // format is "host:port\npayload" - auto idx = payload.find("\n"); - std::string line = payload.substr(0, idx); - auto itr = line.find(":"); - auto & addressbook = i2p::client::context.GetAddressBook(); - std::string addr; - int port = 0; - if (itr == std::string::npos) - { - addr = line; - } - else - { - addr = line.substr(0, itr); - port = std::atoi(line.substr(itr+1).c_str()); - } - auto a = addressbook.GetAddress (addr); - if (a && a->IsIdentHash ()) - { - const char * data = payload.c_str() + idx + 1; - size_t len = payload.size() - (1 + line.size()); - m_Datagram->SendDatagramTo((const uint8_t*)data, len, a->identHash, m_RemotePort, port); - } - } else { - // wtf? - LogPrint(eLogWarning, "websocks: got message in invalid state ", m_State); - } - } - - virtual void Close() - { - if(m_State == eWSCClose) { - LogPrint(eLogDebug, "websocks: closing connection"); - if(m_Stream) m_Stream->Close(); - if(m_Datagram) m_Datagram->ResetReceiver(m_RemotePort); - m_Parent->CloseConn(m_Conn); - EnterState(eWSCEnd); - } else { - EnterState(eWSCClose); - } - } - }; - - WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent) - { - auto ptr = std::make_shared(conn, parent); - auto c = parent->GetConn(conn); - c->set_message_handler(std::bind(&WebSocksConn::GotMessage, ptr.get(), std::placeholders::_1, std::placeholders::_2)); - return ptr; - } - -} -} -#else - -// no websocket support - -namespace i2p -{ -namespace client -{ - class WebSocksImpl - { - public: - WebSocksImpl(const std::string & addr, int port) : m_Addr(addr), m_Port(port) - { - } - - ~WebSocksImpl() - { - } - - void Start() - { - LogPrint(eLogInfo, "WebSockets not enabled on compile time"); - } - - void Stop() - { - } - - void InitializeDestination(WebSocks * parent) - { - } - - boost::asio::ip::tcp::endpoint GetLocalEndpoint() - { - return boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(m_Addr), m_Port); - } - - std::string m_Addr; - int m_Port; - - }; -} -} - -#endif -namespace i2p -{ -namespace client -{ - WebSocks::WebSocks(const std::string & addr, int port, std::shared_ptr localDestination) : m_Impl(new WebSocksImpl(addr, port)) - { - m_Impl->InitializeDestination(this); - } - WebSocks::~WebSocks() { delete m_Impl; } - - void WebSocks::Start() - { - m_Impl->Start(); - GetLocalDestination()->Start(); - } - - boost::asio::ip::tcp::endpoint WebSocks::GetLocalEndpoint() const - { - return m_Impl->GetLocalEndpoint(); - } - - void WebSocks::Stop() - { - m_Impl->Stop(); - GetLocalDestination()->Stop(); - } -} -} - diff -Nru i2pd-2.29.0/libi2pd_client/WebSocks.h i2pd-2.31.0/libi2pd_client/WebSocks.h --- i2pd-2.29.0/libi2pd_client/WebSocks.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/libi2pd_client/WebSocks.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -#ifndef WEBSOCKS_H_ -#define WEBSOCKS_H_ -#include -#include -#include "I2PService.h" -#include "Destination.h" - -namespace i2p -{ -namespace client -{ - - class WebSocksImpl; - - /** @brief websocket socks proxy server */ - class WebSocks : public i2p::client::I2PService - { - public: - WebSocks(const std::string & addr, int port, std::shared_ptr localDestination); - ~WebSocks(); - - void Start(); - void Stop(); - - boost::asio::ip::tcp::endpoint GetLocalEndpoint() const; - - const char * GetName() { return "WebSOCKS Proxy"; } - - private: - WebSocksImpl * m_Impl; - }; -} -} -#endif diff -Nru i2pd-2.29.0/Makefile i2pd-2.31.0/Makefile --- i2pd-2.29.0/Makefile 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/Makefile 2020-04-10 17:33:54.000000000 +0000 @@ -27,10 +27,6 @@ LD_DEBUG = -s endif -ifeq ($(WEBSOCKETS),1) - NEEDED_CXXFLAGS += -DWITH_EVENTS -endif - ifneq (, $(findstring darwin, $(SYS))) DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp ifeq ($(HOMEBREW),1) diff -Nru i2pd-2.29.0/Makefile.linux i2pd-2.31.0/Makefile.linux --- i2pd-2.29.0/Makefile.linux 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/Makefile.linux 2020-04-10 17:33:54.000000000 +0000 @@ -1,5 +1,5 @@ # set defaults instead redefine -CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation +CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation -Wno-psabi LDFLAGS ?= ${LD_DEBUG} ## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time @@ -15,13 +15,14 @@ NEEDED_CXXFLAGS += -std=c++11 else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10 NEEDED_CXXFLAGS += -std=c++11 -else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7 +else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # gcc 4.7 - 4.9 NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6 - NEEDED_CXXFLAGS += -std=c++0x -else ifeq ($(shell expr match ${CXXVER} "[5-9]"),1) # gcc >= 5 +else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6 NEEDED_CXXFLAGS += -std=c++11 LDLIBS = -latomic +else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc >= 7 + NEEDED_CXXFLAGS += -std=c++17 + LDLIBS = -latomic else # not supported $(error Compiler too old) endif diff -Nru i2pd-2.29.0/Makefile.mingw i2pd-2.31.0/Makefile.mingw --- i2pd-2.29.0/Makefile.mingw 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/Makefile.mingw 2020-04-10 17:33:54.000000000 +0000 @@ -37,6 +37,10 @@ DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) endif +ifeq ($(USE_WINXP_FLAGS), yes) + CXXFLAGS += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501 +endif + # don't change following line to ifeq ($(USE_AESNI),yes) !!! ifeq ($(USE_AESNI),1) CPU_FLAGS += -maes diff -Nru i2pd-2.29.0/Makefile.osx i2pd-2.31.0/Makefile.osx --- i2pd-2.29.0/Makefile.osx 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/Makefile.osx 2020-04-10 17:33:54.000000000 +0000 @@ -2,6 +2,9 @@ CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX INCFLAGS = -I/usr/local/include LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib +LDFLAGS += -Wl,-dead_strip +LDFLAGS += -Wl,-dead_strip_dylibs +LDFLAGS += -Wl,-bind_at_load ifeq ($(USE_STATIC),yes) LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread diff -Nru i2pd-2.29.0/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml i2pd-2.31.0/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml --- i2pd-2.29.0/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml 2020-04-10 17:33:54.000000000 +0000 @@ -35,6 +35,8 @@ + + diff -Nru i2pd-2.29.0/qt/i2pd_qt/DelayedSaveManager.cpp i2pd-2.31.0/qt/i2pd_qt/DelayedSaveManager.cpp --- i2pd-2.29.0/qt/i2pd_qt/DelayedSaveManager.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/DelayedSaveManager.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,3 @@ +#include "DelayedSaveManager.h" + +DelayedSaveManager::DelayedSaveManager(){} diff -Nru i2pd-2.29.0/qt/i2pd_qt/DelayedSaveManager.h i2pd-2.31.0/qt/i2pd_qt/DelayedSaveManager.h --- i2pd-2.29.0/qt/i2pd_qt/DelayedSaveManager.h 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/DelayedSaveManager.h 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,24 @@ +#ifndef DELAYEDSAVEMANAGER_H +#define DELAYEDSAVEMANAGER_H + +#include "Saver.h" + +class DelayedSaveManager +{ +public: + DelayedSaveManager(); + + virtual void setSaver(Saver* saver)=0; + + typedef unsigned int DATA_SERIAL_TYPE; + + virtual void delayedSave(DATA_SERIAL_TYPE dataSerial, bool needsTunnelFocus, std::string tunnelNameToFocus)=0; + + //returns false iff save failed + virtual bool appExiting()=0; + + virtual bool needsFocusOnTunnel()=0; + virtual std::string& getTunnelNameToFocus()=0; +}; + +#endif // DELAYEDSAVEMANAGER_H diff -Nru i2pd-2.29.0/qt/i2pd_qt/DelayedSaveManagerImpl.cpp i2pd-2.31.0/qt/i2pd_qt/DelayedSaveManagerImpl.cpp --- i2pd-2.29.0/qt/i2pd_qt/DelayedSaveManagerImpl.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/DelayedSaveManagerImpl.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,140 @@ +#include "DelayedSaveManagerImpl.h" + +DelayedSaveManagerImpl::DelayedSaveManagerImpl() : + saver(nullptr), + lastDataSerialSeen(DelayedSaveManagerImpl::INITIAL_DATA_SERIAL), + lastSaveStartedTimestamp(A_VERY_OBSOLETE_TIMESTAMP), + exiting(false), + thread(new DelayedSaveThread(this)) +{ +} + +void DelayedSaveManagerImpl::setSaver(Saver* saver) { + this->saver = saver; +} + +void DelayedSaveManagerImpl::start() { + thread->start(); +} + +bool DelayedSaveManagerImpl::isSaverValid() { + return saver != nullptr; +} + +void DelayedSaveManagerImpl::delayedSave(DATA_SERIAL_TYPE dataSerial, bool focusOnTunnel, std::string tunnelNameToFocus_) { + if(lastDataSerialSeen==dataSerial)return; + this->focusOnTunnel = focusOnTunnel; + tunnelNameToFocus = tunnelNameToFocus_; + lastDataSerialSeen=dataSerial; + assert(isSaverValid()); + TIMESTAMP_TYPE now = getTime(); + TIMESTAMP_TYPE wakeTime = lastSaveStartedTimestamp + DelayedSaveThread::WAIT_TIME_MILLIS; + if(now < wakeTime) { + //defer save until lastSaveStartedTimestamp + DelayedSaveThread::WAIT_TIME_MILLIS + thread->deferSaveUntil(wakeTime); + return; + } + lastSaveStartedTimestamp = now; + thread->startSavingNow(); +} + +bool DelayedSaveManagerImpl::appExiting() { + exiting=true; + thread->wakeThreadAndJoinThread(); + assert(isSaverValid()); + saver->save(false, ""); + return true; +} + +DelayedSaveThread::DelayedSaveThread(DelayedSaveManagerImpl* delayedSaveManagerImpl_): + delayedSaveManagerImpl(delayedSaveManagerImpl_), + mutex(new QMutex()), + waitCondition(new QWaitCondition()), + saveNow(false), + defer(false) +{ + mutex->lock(); +} + +DelayedSaveThread::~DelayedSaveThread(){ + mutex->unlock(); + delete mutex; + delete waitCondition; +} + +void DelayedSaveThread::run() { + forever { + if(delayedSaveManagerImpl->isExiting())return; + waitCondition->wait(mutex, WAIT_TIME_MILLIS); + if(delayedSaveManagerImpl->isExiting())return; + Saver* saver = delayedSaveManagerImpl->getSaver(); + assert(saver!=nullptr); + if(saveNow) { + saveNow = false; + const bool focusOnTunnel = delayedSaveManagerImpl->needsFocusOnTunnel(); + const std::string tunnelNameToFocus = delayedSaveManagerImpl->getTunnelNameToFocus(); + saver->save(focusOnTunnel, tunnelNameToFocus); + continue; + } + if(defer) { + defer=false; +#define max(a,b) (((a)>(b))?(a):(b)) + forever { + TIMESTAMP_TYPE now = DelayedSaveManagerImpl::getTime(); + TIMESTAMP_TYPE millisToWait = max(wakeTime-now, 0); + if(millisToWait>0) { + waitCondition->wait(mutex, millisToWait); + if(delayedSaveManagerImpl->isExiting())return; + continue; + } + const bool focusOnTunnel = delayedSaveManagerImpl->needsFocusOnTunnel(); + const std::string tunnelNameToFocus = delayedSaveManagerImpl->getTunnelNameToFocus(); + saver->save(focusOnTunnel, tunnelNameToFocus); + break; //break inner loop + } + } + } +} + +void DelayedSaveThread::wakeThreadAndJoinThread() { + waitCondition->wakeAll(); + quit(); + wait();//join //"similar to the POSIX pthread_join()" +} + +DelayedSaveManagerImpl::TIMESTAMP_TYPE DelayedSaveManagerImpl::getTime() { + return QDateTime::currentMSecsSinceEpoch(); +} + +void DelayedSaveThread::deferSaveUntil(TIMESTAMP_TYPE wakeTime_) { + wakeTime = wakeTime_; + defer = true; + waitCondition->wakeAll(); +} + +void DelayedSaveThread::startSavingNow() { + //mutex->lock(); + saveNow=true; + waitCondition->wakeAll(); + //mutex->unlock(); +} + +DelayedSaveManagerImpl::~DelayedSaveManagerImpl() { + thread->wakeThreadAndJoinThread(); + delete thread; +} + +bool DelayedSaveManagerImpl::isExiting() { + return exiting; +} +Saver* DelayedSaveManagerImpl::getSaver() { + return saver; +} + +bool DelayedSaveManagerImpl::needsFocusOnTunnel() { + return focusOnTunnel; +} + +std::string& DelayedSaveManagerImpl::getTunnelNameToFocus() { + return tunnelNameToFocus; +} diff -Nru i2pd-2.29.0/qt/i2pd_qt/DelayedSaveManagerImpl.h i2pd-2.31.0/qt/i2pd_qt/DelayedSaveManagerImpl.h --- i2pd-2.29.0/qt/i2pd_qt/DelayedSaveManagerImpl.h 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/DelayedSaveManagerImpl.h 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,82 @@ +#ifndef DELAYEDSAVEMANAGERIMPL_H +#define DELAYEDSAVEMANAGERIMPL_H + +#include +#include +#include +#include +#include + +#include "mainwindow.h" +#include "DelayedSaveManager.h" +#include "Saver.h" + +class DelayedSaveManagerImpl; + +class DelayedSaveThread : public QThread +{ + Q_OBJECT + +public: + static constexpr unsigned long WAIT_TIME_MILLIS = 1000L; + + typedef qint64 TIMESTAMP_TYPE; + static constexpr TIMESTAMP_TYPE A_VERY_OBSOLETE_TIMESTAMP=0; + + DelayedSaveThread(DelayedSaveManagerImpl* delayedSaveManagerImpl); + virtual ~DelayedSaveThread(); + + void run() override; + + void deferSaveUntil(TIMESTAMP_TYPE wakeTime); + void startSavingNow(); + + void wakeThreadAndJoinThread(); + +private: + DelayedSaveManagerImpl* delayedSaveManagerImpl; + QMutex* mutex; + QWaitCondition* waitCondition; + volatile bool saveNow; + volatile bool defer; + volatile TIMESTAMP_TYPE wakeTime; +}; + +class DelayedSaveManagerImpl : public DelayedSaveManager +{ +public: + DelayedSaveManagerImpl(); + virtual ~DelayedSaveManagerImpl(); + virtual void setSaver(Saver* saver); + virtual void start(); + virtual void delayedSave(DATA_SERIAL_TYPE dataSerial, bool focusOnTunnel, std::string tunnelNameToFocus); + virtual bool appExiting(); + + typedef DelayedSaveThread::TIMESTAMP_TYPE TIMESTAMP_TYPE; + + static constexpr DATA_SERIAL_TYPE INITIAL_DATA_SERIAL=0; + bool isExiting(); + Saver* getSaver(); + static TIMESTAMP_TYPE getTime(); + + bool needsFocusOnTunnel(); + std::string& getTunnelNameToFocus(); + +private: + Saver* saver; + bool isSaverValid(); + + DATA_SERIAL_TYPE lastDataSerialSeen; + + static constexpr TIMESTAMP_TYPE A_VERY_OBSOLETE_TIMESTAMP=DelayedSaveThread::A_VERY_OBSOLETE_TIMESTAMP; + TIMESTAMP_TYPE lastSaveStartedTimestamp; + + bool exiting; + DelayedSaveThread* thread; + void wakeThreadAndJoinThread(); + + bool focusOnTunnel; + std::string tunnelNameToFocus; +}; + +#endif // DELAYEDSAVEMANAGERIMPL_H diff -Nru i2pd-2.29.0/qt/i2pd_qt/generalsettingswidget.ui i2pd-2.31.0/qt/i2pd_qt/generalsettingswidget.ui --- i2pd-2.29.0/qt/i2pd_qt/generalsettingswidget.ui 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/generalsettingswidget.ui 2020-04-10 17:33:54.000000000 +0000 @@ -2135,121 +2135,6 @@ - - - - - 0 - 0 - - - - - 0 - 105 - - - - - 16777215 - 105 - - - - Websockets server - - - - - 0 - 20 - 85 - 21 - - - - Enable - - - - - - 0 - 40 - 661 - 31 - - - - - - - Address to bind websocket server on: - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - 70 - 661 - 31 - - - - - - - Port to bind websocket server on: - - - - - - - - 80 - 16777215 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - diff -Nru i2pd-2.29.0/qt/i2pd_qt/i2pd_qt.pro i2pd-2.31.0/qt/i2pd_qt/i2pd_qt.pro --- i2pd-2.29.0/qt/i2pd_qt/i2pd_qt.pro 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/i2pd_qt.pro 2020-04-10 17:33:54.000000000 +0000 @@ -28,7 +28,6 @@ ../../libi2pd/Datagram.cpp \ ../../libi2pd/Destination.cpp \ ../../libi2pd/Ed25519.cpp \ - ../../libi2pd/Event.cpp \ ../../libi2pd/Family.cpp \ ../../libi2pd/FS.cpp \ ../../libi2pd/Garlic.cpp \ @@ -62,6 +61,8 @@ ../../libi2pd/TunnelGateway.cpp \ ../../libi2pd/TunnelPool.cpp \ ../../libi2pd/util.cpp \ + ../../libi2pd/Elligator.cpp \ + ../../libi2pd/ECIESX25519AEADRatchetSession.cpp \ ../../libi2pd_client/AddressBook.cpp \ ../../libi2pd_client/BOB.cpp \ ../../libi2pd_client/ClientContext.cpp \ @@ -72,8 +73,6 @@ ../../libi2pd_client/MatchedDestination.cpp \ ../../libi2pd_client/SAM.cpp \ ../../libi2pd_client/SOCKS.cpp \ - ../../libi2pd_client/Websocket.cpp \ - ../../libi2pd_client/WebSocks.cpp \ ../../daemon/Daemon.cpp \ ../../daemon/HTTPServer.cpp \ ../../daemon/I2PControl.cpp \ @@ -89,7 +88,11 @@ pagewithbackbutton.cpp \ widgetlock.cpp \ widgetlockregistry.cpp \ - logviewermanager.cpp + logviewermanager.cpp \ + DelayedSaveManager.cpp \ + Saver.cpp \ + DelayedSaveManagerImpl.cpp \ + SaverImpl.cpp HEADERS += DaemonQT.h mainwindow.h \ ../../libi2pd/api.h \ @@ -105,7 +108,6 @@ ../../libi2pd/Datagram.h \ ../../libi2pd/Destination.h \ ../../libi2pd/Ed25519.h \ - ../../libi2pd/Event.h \ ../../libi2pd/Family.h \ ../../libi2pd/FS.h \ ../../libi2pd/Garlic.h \ @@ -147,6 +149,8 @@ ../../libi2pd/TunnelPool.h \ ../../libi2pd/util.h \ ../../libi2pd/version.h \ + ../../libi2pd/Elligator.h \ + ../../libi2pd/ECIESX25519AEADRatchetSession.h \ ../../libi2pd_client/AddressBook.h \ ../../libi2pd_client/BOB.h \ ../../libi2pd_client/ClientContext.h \ @@ -157,8 +161,6 @@ ../../libi2pd_client/MatchedDestination.h \ ../../libi2pd_client/SAM.h \ ../../libi2pd_client/SOCKS.h \ - ../../libi2pd_client/Websocket.h \ - ../../libi2pd_client/WebSocks.h \ ../../daemon/Daemon.h \ ../../daemon/HTTPServer.h \ ../../daemon/I2PControl.h \ @@ -175,7 +177,11 @@ widgetlock.h \ widgetlockregistry.h \ i2pd.rc \ - logviewermanager.h + logviewermanager.h \ + DelayedSaveManager.h \ + Saver.h \ + DelayedSaveManagerImpl.h \ + SaverImpl.h INCLUDEPATH += ../../libi2pd INCLUDEPATH += ../../libi2pd_client @@ -206,6 +212,9 @@ LIBS += $$BOOSTROOT/lib/libboost_filesystem.a LIBS += $$BOOSTROOT/lib/libboost_program_options.a LIBS += $$UPNPROOT/lib/libminiupnpc.a + LIBS += -Wl,-dead_strip + LIBS += -Wl,-dead_strip_dylibs + LIBS += -Wl,-bind_at_load } linux:!android { diff -Nru i2pd-2.29.0/qt/i2pd_qt/mainwindow.cpp i2pd-2.31.0/qt/i2pd_qt/mainwindow.cpp --- i2pd-2.29.0/qt/i2pd_qt/mainwindow.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/mainwindow.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -1,11 +1,8 @@ -#include -#include #include "mainwindow.h" #include "ui_mainwindow.h" #include "ui_statusbuttons.h" #include "ui_routercommandswidget.h" #include "ui_generalsettingswidget.h" -#include #include #include #include @@ -29,17 +26,22 @@ #include "logviewermanager.h" +#include "DelayedSaveManagerImpl.h" +#include "SaverImpl.h" + std::string programOptionsWriterCurrentSection; MainWindow::MainWindow(std::shared_ptr logStream_, QWidget *parent) : QMainWindow(parent) ,logStream(logStream_) -#ifndef ANDROID - ,quitting(false) -#endif + ,delayedSaveManagerPtr(new DelayedSaveManagerImpl()) + ,dataSerial(DelayedSaveManagerImpl::INITIAL_DATA_SERIAL) ,wasSelectingAtStatusMainPage(false) ,showHiddenInfoStatusMainPage(false) ,logViewerManagerPtr(nullptr) +#ifndef ANDROID + ,quitting(false) +#endif ,ui(new Ui::MainWindow) ,statusButtonsUI(new Ui::StatusButtonsForm) ,routerCommandsUI(new Ui::routerCommandsWidget) @@ -51,9 +53,14 @@ ,datadir() ,confpath() ,tunconfpath() + ,tunnelConfigs() ,tunnelsPageUpdateListener(this) + ,saverPtr(new SaverImpl(this, &configItems, &tunnelConfigs)) { + assert(delayedSaveManagerPtr!=nullptr); + assert(saverPtr!=nullptr); + ui->setupUi(this); statusButtonsUI->setupUi(ui->statusButtonsPane); routerCommandsUI->setupUi(routerCommandsParent); @@ -75,7 +82,7 @@ ui->stackedWidget->setCurrentIndex(0); ui->settingsScrollArea->resize(uiSettings->settingsContentsGridLayout->sizeHint().width()+10,380); - QScrollBar* const barSett = ui->settingsScrollArea->verticalScrollBar(); + //QScrollBar* const barSett = ui->settingsScrollArea->verticalScrollBar(); int w = 683; int h = 3060; ui->settingsContents->setFixedSize(w, h); @@ -258,10 +265,6 @@ initStringBox( OPTION("trust","routers",[]{return "";}), uiSettings->lineEditTrustRouters); initCheckBox( OPTION("trust","hidden",[]{return "false";}), uiSettings->checkBoxTrustHidden); - initCheckBox( OPTION("websockets","enabled",[]{return "false";}), uiSettings->checkBoxWebsocketsEnable); - initIPAddressBox( OPTION("websockets","address",[]{return "127.0.0.1";}), uiSettings->lineEdit_webSock_addr, tr("Websocket server -> IP address")); - initTCPPortBox( OPTION("websockets","port",[]{return "7666";}), uiSettings->lineEdit_webSock_port, tr("Websocket server -> Port")); - # undef OPTION //widgetlocks.add(new widgetlock(widget,lockbtn)); @@ -270,7 +273,13 @@ widgetlocks.add(new widgetlock(uiSettings->comboBox_httpPorxySignatureType,uiSettings->httpProxySignTypeComboEditPushButton)); widgetlocks.add(new widgetlock(uiSettings->comboBox_socksProxySignatureType,uiSettings->socksProxySignTypeComboEditPushButton)); - loadAllConfigs(); + loadAllConfigs(saverPtr); + + QObject::connect(saverPtr, SIGNAL(reloadTunnelsConfigAndUISignal(const QString)), + this, SLOT(reloadTunnelsConfigAndUI_QString(const QString))); + + delayedSaveManagerPtr->setSaver(saverPtr); + delayedSaveManagerPtr->start(); QObject::connect(uiSettings->logDestinationComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(logDestinationComboBoxValueChanged(const QString &))); @@ -292,7 +301,6 @@ QObject::connect(uiSettings->tunnelsConfigFileLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(reloadTunnelsConfigAndUI())); - QObject::connect(ui->addServerTunnelPushButton, SIGNAL(released()), this, SLOT(addServerTunnelPushButtonReleased())); QObject::connect(ui->addClientTunnelPushButton, SIGNAL(released()), this, SLOT(addClientTunnelPushButtonReleased())); @@ -307,7 +315,7 @@ logViewerManagerPtr=new LogViewerManager(logStream_,ui->logViewerTextEdit,this); assert(logViewerManagerPtr!=nullptr); - onLoggingOptionsChange(); + //onLoggingOptionsChange(); //QMetaObject::connectSlotsByName(this); } @@ -500,6 +508,8 @@ quitting=true; #endif close(); + delayedSaveManagerPtr->appExiting(); + qDebug("Performing quit"); QApplication::instance()->quit(); } @@ -526,6 +536,7 @@ quitting=true; #endif close(); + delayedSaveManagerPtr->appExiting(); qDebug("Performing quit"); QApplication::instance()->quit(); } @@ -534,6 +545,8 @@ { qDebug("Destroying main window"); delete statusPageUpdateTimer; + delete delayedSaveManagerPtr; + delete saverPtr; for(QList::iterator it = configItems.begin(); it!= configItems.end(); ++it) { MainWindowItem* item = *it; item->deleteLater(); @@ -594,7 +607,7 @@ return retValue; } -void MainWindow::loadAllConfigs(){ +void MainWindow::loadAllConfigs(SaverImpl* saverPtr){ //BORROWED FROM ??? //TODO move this code into single location std::string config; i2p::config::GetOption("conf", config); @@ -635,6 +648,9 @@ this->datadir = datadir.c_str(); this->tunconfpath = tunConf.c_str(); + saverPtr->setConfPath(this->confpath); + saverPtr->setTunnelsConfPath(this->tunconfpath); + for(QList::iterator it = configItems.begin(); it!= configItems.end(); ++it) { MainWindowItem* item = *it; item->loadFromConfigOption(); @@ -642,10 +658,40 @@ ReadTunnelsConfig(); - onLoggingOptionsChange(); + //onLoggingOptionsChange(); } + +void MainWindow::layoutTunnels() { + + int height=0; + ui->tunnelsScrollAreaWidgetContents->setGeometry(0,0,0,0); + for(std::map::iterator it = tunnelConfigs.begin(); it != tunnelConfigs.end(); ++it) { + const std::string& name=it->first; + TunnelConfig* tunconf = it->second; + TunnelPane * tunnelPane=tunconf->getTunnelPane(); + if(!tunnelPane)continue; + int h=tunnelPane->height(); + height+=h; + //qDebug() << "tun.height:" << height << "sz:" << tunnelPanes.size(); + //int h=tunnelPane->appendClientTunnelForm(ctc, ui->tunnelsScrollAreaWidgetContents, tunnelPanes.size(), height); + } + //qDebug() << "tun.setting height:" << height; + ui->tunnelsScrollAreaWidgetContents->setGeometry(QRect(0, 0, 621, height)); + /*QList childWidgets = ui->tunnelsScrollAreaWidgetContents->findChildren(); + foreach(QWidget* widget, childWidgets) + widget->show();*/ +} + +void MainWindow::deleteTunnelFromUI(std::string tunnelName, TunnelConfig* cnf) { + TunnelPane* tp = cnf->getTunnelPane(); + if(!tp)return; + tunnelPanes.remove(tp); + tp->deleteWidget(); + layoutTunnels(); +} + /** returns false iff not valid items present and save was aborted */ -bool MainWindow::saveAllConfigs(){ +bool MainWindow::saveAllConfigs(bool focusOnTunnel, std::string tunnelNameToFocus){ QString cannotSaveSettings = QApplication::tr("Cannot save settings."); programOptionsWriterCurrentSection=""; /*if(!logFileNameOption->lineEdit->text().trimmed().isEmpty())logOption->optionValue=boost::any(std::string("file")); @@ -653,7 +699,6 @@ daemonOption->optionValue=boost::any(false); serviceOption->optionValue=boost::any(false); - std::stringstream out; for(QList::iterator it = configItems.begin(); it!= configItems.end(); ++it) { MainWindowItem* item = *it; if(!item->isValid()){ @@ -661,27 +706,9 @@ return false; } } + delayedSaveManagerPtr->delayedSave(++dataSerial, focusOnTunnel, tunnelNameToFocus); - for(QList::iterator it = configItems.begin(); it!= configItems.end(); ++it) { - MainWindowItem* item = *it; - item->saveToStringStream(out); - } - - using namespace std; - - - QString backup=confpath+"~"; - if(QFile::exists(backup)) QFile::remove(backup);//TODO handle errors - if(QFile::exists(confpath)) QFile::rename(confpath, backup);//TODO handle errors - ofstream outfile; - outfile.open(confpath.toStdString());//TODO handle errors - outfile << out.str().c_str(); - outfile.close(); - - SaveTunnelsConfig(); - - onLoggingOptionsChange(); - + //onLoggingOptionsChange(); return true; } @@ -711,7 +738,7 @@ adjustSizesAccordingToWrongLabel(); applyTunnelsUiToConfigs(); - saveAllConfigs(); + saveAllConfigs(false); } void MainWindowItem::installListeners(MainWindow *mainWindow) {} @@ -725,6 +752,7 @@ ServerTunnelConfig* stc = tunconf->asServerTunnelConfig(); if(stc){ ServerTunnelPane * tunnelPane=new ServerTunnelPane(&tunnelsPageUpdateListener, stc, ui->wrongInputLabel, ui->wrongInputLabel, this); + tunconf->setTunnelPane(tunnelPane); int h=tunnelPane->appendServerTunnelForm(stc, ui->tunnelsScrollAreaWidgetContents, tunnelPanes.size(), height); height+=h; //qDebug() << "tun.height:" << height << "sz:" << tunnelPanes.size(); @@ -738,6 +766,7 @@ ClientTunnelConfig* ctc = tunconf->asClientTunnelConfig(); if(ctc){ ClientTunnelPane * tunnelPane=new ClientTunnelPane(&tunnelsPageUpdateListener, ctc, ui->wrongInputLabel, ui->wrongInputLabel, this); + tunconf->setTunnelPane(tunnelPane); int h=tunnelPane->appendClientTunnelForm(ctc, ui->tunnelsScrollAreaWidgetContents, tunnelPanes.size(), height); height+=h; //qDebug() << "tun.height:" << height << "sz:" << tunnelPanes.size(); @@ -784,6 +813,10 @@ return true; } +void MainWindow::reloadTunnelsConfigAndUI_QString(const QString tunnelNameToFocus) { + reloadTunnelsConfigAndUI(tunnelNameToFocus.toStdString()); +} + void MainWindow::reloadTunnelsConfigAndUI(std::string tunnelNameToFocus) { deleteTunnelForms(); for (std::map::iterator it=tunnelConfigs.begin(); it!=tunnelConfigs.end(); ++it) { @@ -795,31 +828,6 @@ appendTunnelForms(tunnelNameToFocus); } -void MainWindow::SaveTunnelsConfig() { - std::stringstream out; - - for (std::map::iterator it=tunnelConfigs.begin(); it!=tunnelConfigs.end(); ++it) { - const std::string& name = it->first; - TunnelConfig* tunconf = it->second; - tunconf->saveHeaderToStringStream(out); - tunconf->saveToStringStream(out); - tunconf->saveI2CPParametersToStringStream(out); - } - - using namespace std; - - QString backup=tunconfpath+"~"; - if(QFile::exists(backup)) QFile::remove(backup);//TODO handle errors - if(QFile::exists(tunconfpath)) QFile::rename(tunconfpath, backup);//TODO handle errors - ofstream outfile; - outfile.open(tunconfpath.toStdString());//TODO handle errors - outfile << out.str().c_str(); - outfile.close(); - - i2p::client::context.ReloadConfig(); - -} - void MainWindow::TunnelsPageUpdateListenerMainWindowImpl::updated(std::string oldName, TunnelConfig* tunConf) { if(oldName!=tunConf->getName()) { //name has changed @@ -827,7 +835,7 @@ if(it!=mainWindow->tunnelConfigs.end())mainWindow->tunnelConfigs.erase(it); mainWindow->tunnelConfigs[tunConf->getName()]=tunConf; } - mainWindow->saveAllConfigs(); + mainWindow->saveAllConfigs(true, tunConf->getName()); } void MainWindow::TunnelsPageUpdateListenerMainWindowImpl::needsDeleting(std::string oldName){ @@ -875,7 +883,8 @@ pageWithBackButton->show(); textBrowser->hide(); std::stringstream s; - i2p::http::ShowLocalDestination(s,str.toStdString()); + std::string strstd = str.toStdString(); + i2p::http::ShowLocalDestination(s,strstd,0); childTextBrowser->setHtml(QString::fromStdString(s.str())); } } diff -Nru i2pd-2.29.0/qt/i2pd_qt/mainwindow.h i2pd-2.31.0/qt/i2pd_qt/mainwindow.h --- i2pd-2.29.0/qt/i2pd_qt/mainwindow.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/mainwindow.h 2020-04-10 17:33:54.000000000 +0000 @@ -62,6 +62,12 @@ #include "widgetlockregistry.h" #include "widgetlock.h" +#include "DelayedSaveManager.h" +#include "DelayedSaveManagerImpl.h" +#include "SaverImpl.h" + +class SaverImpl; + class LogViewerManager; template @@ -373,10 +379,14 @@ class Controller; +class DelayedSaveManagerImpl; + class MainWindow : public QMainWindow { Q_OBJECT private: std::shared_ptr logStream; + DelayedSaveManagerImpl* delayedSaveManagerPtr; + DelayedSaveManager::DATA_SERIAL_TYPE dataSerial; public: explicit MainWindow(std::shared_ptr logStream_, QWidget *parent=nullptr); ~MainWindow(); @@ -502,16 +512,17 @@ void initStringBox(ConfigOption option, QLineEdit* lineEdit); NonGUIOptionItem* initNonGUIOption(ConfigOption option); - void loadAllConfigs(); + void loadAllConfigs(SaverImpl* saverPtr); + void layoutTunnels(); public slots: /** returns false iff not valid items present and save was aborted */ - bool saveAllConfigs(); - void SaveTunnelsConfig(); + bool saveAllConfigs(bool focusOnTunnel, std::string tunnelNameToFocus=""); void reloadTunnelsConfigAndUI(std::string tunnelNameToFocus); //focus none void reloadTunnelsConfigAndUI() { reloadTunnelsConfigAndUI(""); } + void reloadTunnelsConfigAndUI_QString(const QString tunnelNameToFocus); void addServerTunnelPushButtonReleased(); void addClientTunnelPushButtonReleased(); @@ -530,6 +541,7 @@ void appendTunnelForms(std::string tunnelNameToFocus); void deleteTunnelForms(); + void deleteTunnelFromUI(std::string tunnelName, TunnelConfig* cnf); template std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const @@ -575,11 +587,11 @@ std::map::const_iterator it=tunnelConfigs.find(name); if(it!=tunnelConfigs.end()){ TunnelConfig* tc=it->second; + deleteTunnelFromUI(name, tc); tunnelConfigs.erase(it); delete tc; } - saveAllConfigs(); - reloadTunnelsConfigAndUI(""); + saveAllConfigs(false); } std::string GenerateNewTunnelName() { @@ -614,8 +626,7 @@ destinationPort, sigType); - saveAllConfigs(); - reloadTunnelsConfigAndUI(name); + saveAllConfigs(true, name); } void CreateDefaultServerTunnel() {//TODO dedup default values with ReadTunnelsConfig() and with ClientContext.cpp::ReadTunnels () @@ -651,8 +662,7 @@ isUniqueLocal); - saveAllConfigs(); - reloadTunnelsConfigAndUI(name); + saveAllConfigs(true, name); } void ReadTunnelsConfig() //TODO deduplicate the code with ClientContext.cpp::ReadTunnels () @@ -793,7 +803,9 @@ TunnelsPageUpdateListenerMainWindowImpl tunnelsPageUpdateListener; - void onLoggingOptionsChange() {} + //void onLoggingOptionsChange() {} + + SaverImpl* saverPtr; }; #endif // MAINWINDOW_H diff -Nru i2pd-2.29.0/qt/i2pd_qt/Saver.cpp i2pd-2.31.0/qt/i2pd_qt/Saver.cpp --- i2pd-2.29.0/qt/i2pd_qt/Saver.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/Saver.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,6 @@ +#include "Saver.h" + +Saver::Saver() +{ + +} diff -Nru i2pd-2.29.0/qt/i2pd_qt/Saver.h i2pd-2.31.0/qt/i2pd_qt/Saver.h --- i2pd-2.29.0/qt/i2pd_qt/Saver.h 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/Saver.h 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,22 @@ +#ifndef SAVER_H +#define SAVER_H + +#include +#include +#include + +class Saver : public QObject +{ + Q_OBJECT + +public: + Saver(); + //false iff failures + virtual bool save(const bool focusOnTunnel, const std::string& tunnelNameToFocus)=0; + +signals: + void reloadTunnelsConfigAndUISignal(const QString); + +}; + +#endif // SAVER_H diff -Nru i2pd-2.29.0/qt/i2pd_qt/SaverImpl.cpp i2pd-2.31.0/qt/i2pd_qt/SaverImpl.cpp --- i2pd-2.29.0/qt/i2pd_qt/SaverImpl.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/SaverImpl.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,79 @@ +#include "SaverImpl.h" + +#include +#include +#include + +#include "QList" +#include "QString" + +#include "mainwindow.h" + +SaverImpl::SaverImpl(MainWindow *mainWindowPtr_, QList * configItems_, std::map* tunnelConfigs_) : + configItems(configItems_), tunnelConfigs(tunnelConfigs_), confpath(), tunconfpath(), mainWindowPtr(mainWindowPtr_) +{} + +SaverImpl::~SaverImpl() {} + +bool SaverImpl::save(const bool focusOnTunnel, const std::string& tunnelNameToFocus) { + //save main config + { + std::stringstream out; + for(QList::iterator it = configItems->begin(); it!= configItems->end(); ++it) { + MainWindowItem* item = *it; + item->saveToStringStream(out); + } + + using namespace std; + + + QString backup=confpath+"~"; + if(QFile::exists(backup)) QFile::remove(backup);//TODO handle errors + if(QFile::exists(confpath)) QFile::rename(confpath, backup);//TODO handle errors + ofstream outfile; + outfile.open(confpath.toStdString());//TODO handle errors + outfile << out.str().c_str(); + outfile.close(); + } + + //save tunnels config + { + std::stringstream out; + + for (std::map::iterator it=tunnelConfigs->begin(); it!=tunnelConfigs->end(); ++it) { + //const std::string& name = it->first; + TunnelConfig* tunconf = it->second; + tunconf->saveHeaderToStringStream(out); + tunconf->saveToStringStream(out); + tunconf->saveI2CPParametersToStringStream(out); + } + + using namespace std; + + QString backup=tunconfpath+"~"; + if(QFile::exists(backup)) QFile::remove(backup);//TODO handle errors + if(QFile::exists(tunconfpath)) QFile::rename(tunconfpath, backup);//TODO handle errors + ofstream outfile; + outfile.open(tunconfpath.toStdString());//TODO handle errors + outfile << out.str().c_str(); + outfile.close(); + } + + //reload saved configs +#if 0 + i2p::client::context.ReloadConfig(); +#endif + + if(focusOnTunnel) emit reloadTunnelsConfigAndUISignal(QString::fromStdString(tunnelNameToFocus)); + + return true; +} + +void SaverImpl::setConfPath(QString& confpath_) { confpath = confpath_; } + +void SaverImpl::setTunnelsConfPath(QString& tunconfpath_) { tunconfpath = tunconfpath_; } + +/*void SaverImpl::setTunnelFocus(bool focusOnTunnel, std::string tunnelNameToFocus) { + this->focusOnTunnel=focusOnTunnel; + this->tunnelNameToFocus=tunnelNameToFocus; +}*/ diff -Nru i2pd-2.29.0/qt/i2pd_qt/SaverImpl.h i2pd-2.31.0/qt/i2pd_qt/SaverImpl.h --- i2pd-2.29.0/qt/i2pd_qt/SaverImpl.h 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/SaverImpl.h 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,33 @@ +#ifndef SAVERIMPL_H +#define SAVERIMPL_H + +#include +#include + +#include +#include "QList" + +#include "mainwindow.h" +#include "TunnelConfig.h" +#include "Saver.h" + +class MainWindowItem; +class TunnelConfig; + +class SaverImpl : public Saver +{ +public: + SaverImpl(MainWindow *mainWindowPtr_, QList * configItems_, std::map* tunnelConfigs_); + virtual ~SaverImpl(); + virtual bool save(const bool focusOnTunnel, const std::string& tunnelNameToFocus); + void setConfPath(QString& confpath_); + void setTunnelsConfPath(QString& tunconfpath_); +private: + QList * configItems; + std::map* tunnelConfigs; + QString confpath; + QString tunconfpath; + MainWindow* mainWindowPtr; +}; + +#endif // SAVERIMPL_H diff -Nru i2pd-2.29.0/qt/i2pd_qt/TunnelConfig.h i2pd-2.31.0/qt/i2pd_qt/TunnelConfig.h --- i2pd-2.29.0/qt/i2pd_qt/TunnelConfig.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/TunnelConfig.h 2020-04-10 17:33:54.000000000 +0000 @@ -40,6 +40,9 @@ class ClientTunnelConfig; class ServerTunnelConfig; + +class TunnelPane; + class TunnelConfig { /* const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client"; @@ -54,6 +57,7 @@ */ QString type; std::string name; + TunnelPane* tunnelPane; public: TunnelConfig(std::string name_, QString& type_, I2CPParameters& i2cpParameters_): type(type_), name(name_), i2cpParameters(i2cpParameters_) {} @@ -68,7 +72,8 @@ virtual void saveToStringStream(std::stringstream& out)=0; virtual ClientTunnelConfig* asClientTunnelConfig()=0; virtual ServerTunnelConfig* asServerTunnelConfig()=0; - + void setTunnelPane(TunnelPane* tp){this->tunnelPane = tp;} + TunnelPane* getTunnelPane() {return tunnelPane;} private: I2CPParameters i2cpParameters; }; diff -Nru i2pd-2.29.0/qt/i2pd_qt/TunnelPane.cpp i2pd-2.31.0/qt/i2pd_qt/TunnelPane.cpp --- i2pd-2.29.0/qt/i2pd_qt/TunnelPane.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/TunnelPane.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -64,7 +64,7 @@ //type { - const QString& type = tunnelConfig->getType(); + //const QString& type = tunnelConfig->getType(); QHBoxLayout * horizontalLayout_ = new QHBoxLayout(); horizontalLayout_->setObjectName(QStringLiteral("horizontalLayout_")); typeLabel = new QLabel(gridLayoutWidget_2); @@ -83,6 +83,11 @@ retranslateTunnelForm(*this); } +void TunnelPane::deleteWidget() { + //gridLayoutWidget_2->deleteLater(); + tunnelGroupBox->deleteLater(); +} + void TunnelPane::appendControlsForI2CPParameters(I2CPParameters& i2cpParameters, int& gridIndex) { { //number of hops of an inbound tunnel diff -Nru i2pd-2.29.0/qt/i2pd_qt/TunnelPane.h i2pd-2.31.0/qt/i2pd_qt/TunnelPane.h --- i2pd-2.29.0/qt/i2pd_qt/TunnelPane.h 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/qt/i2pd_qt/TunnelPane.h 2020-04-10 17:33:54.000000000 +0000 @@ -41,6 +41,8 @@ virtual ServerTunnelPane* asServerTunnelPane()=0; virtual ClientTunnelPane* asClientTunnelPane()=0; + void deleteWidget(); + protected: MainWindow* mainWindow; QWidget * wrongInputPane; diff -Nru i2pd-2.29.0/README.md i2pd-2.31.0/README.md --- i2pd-2.29.0/README.md 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/README.md 2020-04-10 17:33:54.000000000 +0000 @@ -1,6 +1,7 @@ [![GitHub release](https://img.shields.io/github/release/PurpleI2P/i2pd.svg?label=latest%20release)](https://github.com/PurpleI2P/i2pd/releases/latest) [![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) +[![Packaging status](https://repology.org/badge/tiny-repos/i2pd.svg)](https://repology.org/project/i2pd/versions) i2pd ==== @@ -63,9 +64,10 @@ **Supported systems:** * GNU/Linux - [![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/) + * Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc. * Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/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/) * 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 diff -Nru i2pd-2.29.0/tests/Makefile i2pd-2.31.0/tests/Makefile --- i2pd-2.29.0/tests/Makefile 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/tests/Makefile 2020-04-10 17:33:54.000000000 +0000 @@ -1,6 +1,6 @@ CXXFLAGS += -Wall -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -I../libi2pd/ -pthread -Wl,--unresolved-symbols=ignore-in-object-files -TESTS = test-gost test-gost-sig test-base-64 test-x25519 test-aeadchacha20poly1305 test-blinding +TESTS = test-gost test-gost-sig test-base-64 test-x25519 test-aeadchacha20poly1305 test-blinding test-elligator all: $(TESTS) run @@ -25,6 +25,9 @@ test-blinding: ../libi2pd/Crypto.cpp ../libi2pd/Blinding.cpp ../libi2pd/Ed25519.cpp ../libi2pd/I2PEndian.cpp ../libi2pd/Log.cpp ../libi2pd/util.cpp ../libi2pd/Identity.cpp ../libi2pd/Signature.cpp ../libi2pd/Timestamp.cpp test-blinding.cpp $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system +test-elligator: ../libi2pd/Elligator.cpp ../libi2pd/Crypto.cpp test-elligator.cpp + $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -o $@ $^ -lcrypto -lssl -lboost_system + run: $(TESTS) @for TEST in $(TESTS); do ./$$TEST ; done diff -Nru i2pd-2.29.0/tests/test-elligator.cpp i2pd-2.31.0/tests/test-elligator.cpp --- i2pd-2.29.0/tests/test-elligator.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.31.0/tests/test-elligator.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -0,0 +1,85 @@ +#include +#include +#include + +#include "Elligator.h" + +const uint8_t key[32] = +{ + 0x33, 0x95, 0x19, 0x64, 0x00, 0x3c, 0x94, 0x08, 0x78, 0x06, 0x3c, 0xcf, 0xd0, 0x34, 0x8a, 0xf4, + 0x21, 0x50, 0xca, 0x16, 0xd2, 0x64, 0x6f, 0x2c, 0x58, 0x56, 0xe8, 0x33, 0x83, 0x77, 0xd8, 0x80 +}; + +const uint8_t encoded_key[32] = +{ + 0x28, 0x20, 0xb6, 0xb2, 0x41, 0xe0, 0xf6, 0x8a, 0x6c, 0x4a, 0x7f, 0xee, 0x3d, 0x97, 0x82, 0x28, + 0xef, 0x3a, 0xe4, 0x55, 0x33, 0xcd, 0x41, 0x0a, 0xa9, 0x1a, 0x41, 0x53, 0x31, 0xd8, 0x61, 0x2d +}; + +const uint8_t encoded_key_high_y[32] = +{ + 0x3c, 0xfb, 0x87, 0xc4, 0x6c, 0x0b, 0x45, 0x75, 0xca, 0x81, 0x75, 0xe0, 0xed, 0x1c, 0x0a, 0xe9, + 0xda, 0xe7, 0x9d, 0xb7, 0x8d, 0xf8, 0x69, 0x97, 0xc4, 0x84, 0x7b, 0x9f, 0x20, 0xb2, 0x77, 0x18 +}; + +const uint8_t encoded1[32] = +{ + 0xe7, 0x35, 0x07, 0xd3, 0x8b, 0xae, 0x63, 0x99, 0x2b, 0x3f, 0x57, 0xaa, 0xc4, 0x8c, 0x0a, 0xbc, + 0x14, 0x50, 0x95, 0x89, 0x28, 0x84, 0x57, 0x99, 0x5a, 0x2b, 0x4c, 0xa3, 0x49, 0x0a, 0xa2, 0x07 +}; + +const uint8_t key1[32] = +{ + 0x1e, 0x8a, 0xff, 0xfe, 0xd6, 0xbf, 0x53, 0xfe, 0x27, 0x1a, 0xd5, 0x72, 0x47, 0x32, 0x62, 0xde, + 0xd8, 0xfa, 0xec, 0x68, 0xe5, 0xe6, 0x7e, 0xf4, 0x5e, 0xbb, 0x82, 0xee, 0xba, 0x52, 0x60, 0x4f +}; + +const uint8_t encoded2[32] = +{ + 0x95, 0xa1, 0x60, 0x19, 0x04, 0x1d, 0xbe, 0xfe, 0xd9, 0x83, 0x20, 0x48, 0xed, 0xe1, 0x19, 0x28, + 0xd9, 0x03, 0x65, 0xf2, 0x4a, 0x38, 0xaa, 0x7a, 0xef, 0x1b, 0x97, 0xe2, 0x39, 0x54, 0x10, 0x1b +}; + +const uint8_t key2[32] = +{ + 0x79, 0x4f, 0x05, 0xba, 0x3e, 0x3a, 0x72, 0x95, 0x80, 0x22, 0x46, 0x8c, 0x88, 0x98, 0x1e, 0x0b, + 0xe5, 0x78, 0x2b, 0xe1, 0xe1, 0x14, 0x5c, 0xe2, 0xc3, 0xc6, 0xfd, 0xe1, 0x6d, 0xed, 0x53, 0x63 +}; + +const uint8_t encoded3[32] = +{ + 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f +}; + +const uint8_t key3[32] = +{ + 0x9c, 0xdb, 0x52, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 +}; + +const uint8_t failed_key[32] = +{ + 0xe6, 0xf6, 0x6f, 0xdf, 0x6e, 0x23, 0x0c, 0x60, 0x3c, 0x5e, 0x6e, 0x59, 0xa2, 0x54, 0xea, 0x14, + 0x76, 0xa1, 0x3e, 0xb9, 0x51, 0x1b, 0x95, 0x49, 0x84, 0x67, 0x81, 0xe1, 0x2e, 0x52, 0x23, 0x0a +}; + +int main () +{ + uint8_t buf[32]; + i2p::crypto::Elligator2 el; + // encoding tests + el.Encode (key, buf, false, false); + assert(memcmp (buf, encoded_key, 32) == 0); + el.Encode (key, buf, true, false); // with highY + assert(memcmp (buf, encoded_key_high_y, 32) == 0); + // decoding tests + el.Decode (encoded1, buf); + assert(memcmp (buf, key1, 32) == 0); + el.Decode (encoded2, buf); + assert(memcmp (buf, key2, 32) == 0); + el.Decode (encoded3, buf); + assert(memcmp (buf, key3, 32) == 0); + // encoding fails + assert (!el.Encode (failed_key, buf)); +} diff -Nru i2pd-2.29.0/tests/test-x25519.cpp i2pd-2.31.0/tests/test-x25519.cpp --- i2pd-2.29.0/tests/test-x25519.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/tests/test-x25519.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -27,10 +27,13 @@ int main () { +#if !OPENSSL_X25519 +// we test it for openssl < 1.1.0 uint8_t buf[32]; BN_CTX * ctx = BN_CTX_new (); i2p::crypto::GetEd25519 ()->ScalarMul (u, k, buf, ctx); BN_CTX_free (ctx); assert(memcmp (buf, p, 32) == 0); +#endif } diff -Nru i2pd-2.29.0/.travis.yml i2pd-2.31.0/.travis.yml --- i2pd-2.29.0/.travis.yml 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/.travis.yml 2020-04-10 17:33:54.000000000 +0000 @@ -4,7 +4,7 @@ os: - linux #- osx -dist: trusty +dist: xenial sudo: required compiler: - g++ diff -Nru i2pd-2.29.0/Win32/installer.iss i2pd-2.31.0/Win32/installer.iss --- i2pd-2.29.0/Win32/installer.iss 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/Win32/installer.iss 2020-04-10 17:33:54.000000000 +0000 @@ -1,5 +1,5 @@ #define I2Pd_AppName "i2pd" -#define I2Pd_ver "2.29.0" +#define I2Pd_ver "2.31.0" #define I2Pd_Publisher "PurpleI2P" [Setup] diff -Nru i2pd-2.29.0/Win32/Win32App.cpp i2pd-2.31.0/Win32/Win32App.cpp --- i2pd-2.29.0/Win32/Win32App.cpp 2019-10-21 16:02:43.000000000 +0000 +++ i2pd-2.31.0/Win32/Win32App.cpp 2020-04-10 17:33:54.000000000 +0000 @@ -13,10 +13,6 @@ #include "Win32App.h" #include -#if defined(_MSC_VER) && _MSC_VER < 1900 -#define snprintf _snprintf -#endif - #define ID_ABOUT 2000 #define ID_EXIT 2001 #define ID_CONSOLE 2002 @@ -39,6 +35,9 @@ namespace win32 { static DWORD GracefulShutdownEndtime = 0; + + typedef DWORD (* IPN)(); + IPN GetTickCountLocal = (IPN)GetProcAddress (GetModuleHandle ("KERNEL32.dll"), "GetTickCount"); static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem) { @@ -53,7 +52,7 @@ ID_ACCEPT_TRANSIT, "Accept &transit"); else InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload configs"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config"); if (!i2p::util::DaemonWin32::Instance ().isGraceful) InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown"); else @@ -161,7 +160,7 @@ s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); if (GracefulShutdownEndtime != 0) { - DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCount()) / 1000; + DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCountLocal()) / 1000; s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft); } else @@ -239,7 +238,7 @@ i2p::context.SetAcceptsTunnels (false); SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second - GracefulShutdownEndtime = GetTickCount() + 10*60*1000; + GracefulShutdownEndtime = GetTickCountLocal() + 10*60*1000; i2p::util::DaemonWin32::Instance ().isGraceful = true; return 0; }