Merge pull request #1499 from PurpleI2P/openssl

recent updates
This commit is contained in:
orignal 2020-03-30 18:20:07 -04:00 committed by GitHub
commit 814f854c5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 1808 additions and 1851 deletions

8
.gitignore vendored
View File

@ -258,9 +258,15 @@ build/Makefile
# 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*

0
.gitmodules vendored Normal file
View File

View File

@ -4,7 +4,7 @@ cache:
os:
- linux
#- osx
dist: trusty
dist: xenial
sudo: required
compiler:
- g++

View File

@ -27,10 +27,6 @@ else
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)

View File

@ -15,13 +15,14 @@ ifeq ($(shell expr match $(CXX) 'clang'),5)
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

View File

@ -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 @@ Build instructions:
**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

View File

@ -17,6 +17,7 @@
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light.DarkActionBar"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"
>
<receiver android:name=".NetworkStateChangeReceiver">
<intent-filter>

19
android/README.md Normal file
View File

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

View File

@ -32,6 +32,7 @@ android {
minSdkVersion 14
versionCode 2300
versionName "2.30.0"
setProperty("archivesBaseName", archivesBaseName + "-" + versionName)
ndk {
abiFilters 'armeabi-v7a'
abiFilters 'x86'
@ -56,9 +57,10 @@ android {
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 @@ android {
}
buildTypes {
release {
minifyEnabled true
minifyEnabled false
signingConfig signingConfigs.orignal
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
}
@ -87,3 +89,16 @@ android {
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
}
}
}

View File

@ -25,29 +25,29 @@ include $(BUILD_SHARED_LIBRARY)
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)

View File

@ -9,15 +9,15 @@ NDK_TOOLCHAIN_VERSION := clang
#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

View File

@ -0,0 +1,13 @@
<LinearLayout android:id="@+id/layout_prompt"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".I2PDActivity">
<WebView
android:id="@+id/webview1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>

View File

@ -12,6 +12,10 @@
android:id="@+id/action_graceful_stop"
android:orderInCategory="98"
android:title="@string/action_graceful_stop" />
<item
android:id="@+id/action_start_webview"
android:orderInCategory="97"
android:title="@string/action_start_webview" />
</group>
<group android:id="@+id/group_various" >
<item

View File

@ -25,4 +25,5 @@
<string name="menu_item_battery_optimizations_str">Battery Optimizations</string>
<string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Your Android OS version does not support showing the dialog for battery optimizations for applications.</string>
<string name="shutdown_canceled">Planned shutdown canceled</string>
<string name="action_start_webview">Start webview</string>
</resources>

View File

@ -39,15 +39,23 @@ import android.view.MenuItem;
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 @@ public class I2PDActivity extends Activity {
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 @@ public class I2PDActivity extends Activity {
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);

View File

@ -26,29 +26,29 @@ include $(BUILD_EXECUTABLE)
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)

View File

@ -9,8 +9,8 @@ APP_PLATFORM := android-14
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 @@ endif
# 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

View File

@ -20,7 +20,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,7 +74,6 @@ set (LIBI2PD_SRC
"${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"
@ -86,11 +84,6 @@ set (LIBI2PD_SRC
"${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 ()
@ -124,13 +117,8 @@ set (CLIENT_SRC
"${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 "")
@ -184,17 +172,17 @@ else()
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")
@ -427,7 +415,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

View File

@ -55,14 +55,22 @@ cd build
%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

View File

@ -53,14 +53,22 @@ cd build
%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

View File

@ -26,9 +26,6 @@
#include "Timestamp.h"
#include "util.h"
#include "Event.h"
#include "Websocket.h"
namespace i2p
{
namespace util
@ -43,9 +40,6 @@ namespace i2p
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
std::unique_ptr<i2p::transport::UPnP> UPnP;
std::unique_ptr<i2p::util::NTPTimeSync> m_NTPSync;
#ifdef WITH_EVENTS
std::unique_ptr<i2p::event::WebsocketServer> m_WebsocketServer;
#endif
};
Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {}
@ -62,12 +56,12 @@ namespace i2p
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<std::ostream> logstream)
{
bool Daemon_Singleton::init(int argc, char* argv[], std::shared_ptr<std::ostream> logstream)
{
i2p::config::Init();
i2p::config::ParseCmdline(argc, argv);
@ -110,10 +104,10 @@ namespace i2p
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 @@ namespace i2p
// 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 @@ namespace i2p
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 @@ namespace i2p
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<std::string> fams;
size_t pos = 0, comma;
do
@ -253,7 +247,7 @@ namespace i2p
while (comma != std::string::npos);
i2p::transport::transports.RestrictRoutesToFamilies(fams);
restricted = fams.size() > 0;
}
}
if (routers.length() > 0) {
std::set<i2p::data::IdentHash> idents;
size_t pos = 0, comma;
@ -272,14 +266,15 @@ namespace i2p
}
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 @@ namespace i2p
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<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
d.httpServer->Start();
@ -344,26 +339,11 @@ namespace i2p
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(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<i2p::event::WebsocketServer>(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 @@ namespace i2p
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();

View File

@ -54,6 +54,7 @@ namespace http {
" 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 @@ namespace http {
" .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"
"</style>\r\n";
@ -89,10 +92,12 @@ namespace http {
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<uint32_t, uint32_t> HTTPConnection::m_Tokens;
static void ShowUptime (std::stringstream& s, int seconds)
{
@ -177,8 +182,10 @@ namespace http {
"<div class=left>\r\n"
" <a href=\"" << webroot << "\">Main page</a><br>\r\n<br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">Router commands</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">Local destinations</a><br>\r\n";
if (i2p::context.IsFloodfill ())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">LeaseSets</a><br>\r\n";
s <<
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">Tunnels</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">Transit tunnels</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">Transports</a><br>\r\n"
@ -203,10 +210,7 @@ namespace http {
s << "<b>ERROR:</b>&nbsp;" << string << "<br>\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 << "<b>Uptime:</b> ";
ShowUptime(s, i2p::context.GetUptime ());
@ -233,11 +237,8 @@ namespace http {
}
s << "<br>\r\n";
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
if (auto remains = Daemon.gracefulShutdownInterval) {
s << "<b>Stopping in:</b> ";
s << remains << " seconds";
s << "<br>\r\n";
}
if (auto remains = Daemon.gracefulShutdownInterval)
s << "<b>Stopping in:</b> " << remains << " seconds<br>\r\n";
#endif
auto family = i2p::context.GetFamily ();
if (family.length () > 0)
@ -253,51 +254,56 @@ namespace http {
ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ());
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)<br>\r\n";
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n";
s << "<div class='slide'>";
if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) {
s << "<label for='slide-info'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide-info'/>\r\n<p class='content'>\r\n";
}
if(includeHiddenContent) {
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
s << "<div class='slide'>";
if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) {
s << "<label for='slide-info'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide-info'/>\r\n<div class='content'>\r\n";
}
if(includeHiddenContent) {
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
if (!i2p::context.GetRouterInfo().GetProperty("family").empty())
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
s << "<b>Router Caps:</b> " << i2p::context.GetRouterInfo().GetProperty("caps") << "<br>\r\n";
s << "<b>Our external address:</b>" << "<br>\r\n" ;
s << "<b>Our external address:</b>" << "<br>\r\n<table class=\"extaddr\"><tbody>\r\n";
for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
{
s << "<tr>\r\n";
if (address->IsNTCP2 () && !address->IsPublishedNTCP2 ())
{
s << "NTCP2";
s << "<td>NTCP2";
if (address->host.is_v6 ()) s << "v6";
s << "&nbsp;&nbsp; supported <br>\r\n";
s << "</td><td>supported</td>\r\n</tr>\r\n";
continue;
}
switch (address->transportStyle)
{
case i2p::data::RouterInfo::eTransportNTCP:
{
s << "NTCP";
s << "<td>NTCP";
if (address->IsPublishedNTCP2 ()) s << "2";
if (address->host.is_v6 ()) s << "v6";
s << "&nbsp;&nbsp;";
s << "</td>\r\n";
break;
}
case i2p::data::RouterInfo::eTransportSSU:
{
s << "<td>SSU";
if (address->host.is_v6 ())
s << "SSUv6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
else
s << "SSU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
break;
s << "v6";
s << "</td>\r\n";
break;
}
default:
s << "Unknown&nbsp;&nbsp;";
s << "<td>Unknown</td>\r\n";
}
s << address->host.to_string() << ":" << address->port << "<br>\r\n";
s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n</tr>\r\n";
}
}
s << "</p>\r\n</div>\r\n";
if(outputFormat==OutputFormatEnum::forQtUi) {
s << "<br>";
}
s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "</tbody></table>\r\n";
}
s << "</div>\r\n</div>\r\n";
if(outputFormat==OutputFormatEnum::forQtUi) {
s << "<br>";
}
s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
@ -308,17 +314,17 @@ namespace http {
s << "<b>Client Tunnels:</b> " << std::to_string(clientTunnelCount) << " ";
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n";
if(outputFormat==OutputFormatEnum::forWebConsole) {
s << "<table><caption>Services</caption><tr><th>Service</th><th>State</th></tr>\r\n";
s << "<tr><td>" << "HTTP Proxy" << "</td><td><div class='" << ((i2p::client::context.GetHttpProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SOCKS Proxy" << "</td><td><div class='" << ((i2p::client::context.GetSocksProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "BOB" << "</td><td><div class='" << ((i2p::client::context.GetBOBCommandChannel ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SAM" << "</td><td><div class='" << ((i2p::client::context.GetSAMBridge ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "I2CP" << "</td><td><div class='" << ((i2p::client::context.GetI2CPServer ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
s << "<tr><td>" << "I2PControl" << "</td><td><div class='" << ((i2pcontrol) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "</table>\r\n";
}
if(outputFormat==OutputFormatEnum::forWebConsole) {
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
s << "<table class=\"services\"><caption>Services</caption><tbody>\r\n";
s << "<tr><td>" << "HTTP Proxy" << "</td><td><div class='" << ((i2p::client::context.GetHttpProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SOCKS Proxy" << "</td><td><div class='" << ((i2p::client::context.GetSocksProxy ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "BOB" << "</td><td><div class='" << ((i2p::client::context.GetBOBCommandChannel ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "SAM" << "</td><td><div class='" << ((i2p::client::context.GetSAMBridge ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "I2CP" << "</td><td><div class='" << ((i2p::client::context.GetI2CPServer ()) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "<tr><td>" << "I2PControl" << "</td><td><div class='" << ((i2pcontrol) ? "enabled" : "disabled") << "'></div></td></tr>\r\n";
s << "</tbody></table>\r\n";
}
}
void ShowLocalDestinations (std::stringstream& s)
@ -352,24 +358,25 @@ namespace http {
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest)
{
s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
s << "<b>Base64:</b><br>\r\n<textarea readonly cols=\"80\" rows=\"11\" wrap=\"on\">";
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
if (dest->IsEncryptedLeaseSet ())
{
i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ());
s << "<div class='slide'><label for='slide-b33'><b>Encrypted B33 address:</b></label>\r\n<input type='checkbox' id='slide-b33'/>\r\n<p class='content'>\r\n";
s << "<div class='slide'><label for='slide-b33'><b>Encrypted B33 address:</b></label>\r\n<input type='checkbox' id='slide-b33'/>\r\n<div class='content'>\r\n";
s << blinded.ToB33 () << ".b32.i2p<br>\r\n";
s << "</p>\r\n</div>\r\n";
s << "</div>\r\n</div>\r\n";
}
if(dest->GetNumRemoteLeaseSets())
{
s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i></label>\r\n<input type='checkbox' id='slide-lease'/>\r\n<p class='content'>\r\n";
s << "<div class='slide'><label for='slide-lease'><b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets ()
<< "</i></label>\r\n<input type='checkbox' id='slide-lease'/>\r\n<div class='content'>\r\n<table><thead><th>Address</th><th>Type</th><th>EncType</th></thead><tbody>";
for(auto& it: dest->GetLeaseSets ())
s << it.first.ToBase32 () << " " << (int)it.second->GetStoreType () << "<br>\r\n";
s << "</p>\r\n</div>\r\n";
s << "<tr><td>" << it.first.ToBase32 () << "</td><td>" << (int)it.second->GetStoreType () << "</td><td>" << (int)it.second->GetEncryptionType () <<"</td></tr>\r\n";
s << "</tbody></table>\r\n</div>\r\n</div>\r\n<br>\r\n";
} else
s << "<b>LeaseSets:</b> <i>0</i><br>\r\n";
s << "<b>LeaseSets:</b> <i>0</i><br>\r\n<br>\r\n";
auto pool = dest->GetTunnelPool ();
if (pool)
{
@ -394,28 +401,31 @@ namespace http {
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 () << "<br>\r\n";
tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "</td><td>" << it.second->GetNumOutgoingTags () << "</td></tr>\r\n";
out_tags = out_tags + it.second->GetNumOutgoingTags ();
}
s << "<div class='slide'><label for='slide-tags'>Outgoing: <i>" << out_tags << "</i></label>\r\n<input type='checkbox' id='slide-tags'/>\r\n<p class='content'>\r\n" << tmp_s.str () << "</p>\r\n</div>\r\n";
s << "<div class='slide'><label for='slide-tags'>Outgoing: <i>" << out_tags << "</i></label>\r\n<input type='checkbox' id='slide-tags'/>\r\n"
<< "<div class='content'>\r\n<table><tbody><thead><th>Destination</th><th>Amount</th></thead>\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
} else
s << "Outgoing: <i>0</i><br>\r\n";
s << "<br>\r\n";
}
void ShowLocalDestination (std::stringstream& s, const std::string& b32)
void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token)
{
s << "<b>Local Destination:</b><br>\r\n<br>\r\n";
i2p::data::IdentHash ident;
ident.FromBase32 (b32);
auto dest = i2p::client::context.FindLocalDestination (ident);
if (dest)
{
ShowLeaseSetDestination (s, dest);
// show streams
s << "<table><caption>Streams</caption>\r\n<tr>";
s << "<th>StreamID</th>";
s << "<th>Destination</th>";
s << "<table>\r\n<caption>Streams</caption>\r\n<thead>\r\n<tr>";
s << "<th style=\"width:25px;\">StreamID</th>";
s << "<th style=\"width:5px;\" \\>"; // Stream closing button column
s << "<th class=\"streamdest\">Destination</th>";
s << "<th>Sent</th>";
s << "<th>Received</th>";
s << "<th>Out</th>";
@ -424,13 +434,20 @@ namespace http {
s << "<th>RTT</th>";
s << "<th>Window</th>";
s << "<th>Status</th>";
s << "</tr>\r\n";
s << "</tr>\r\n</thead>\r\n<tbody>\r\n";
for (const auto& it: dest->GetAllStreams ())
{
auto streamDest = i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ());
s << "<tr>";
s << "<td>" << it->GetSendStreamID () << "</td>";
s << "<td>" << i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()) << "</td>";
s << "<td>" << it->GetRecvStreamID () << "</td>";
if (it->GetRecvStreamID ()) {
s << "<td><a class=\"button\" href=\"/?cmd=" << HTTP_COMMAND_KILLSTREAM << "&b32=" << b32 << "&streamID="
<< it->GetRecvStreamID () << "&token=" << token << "\" title=\"Close stream\"> &#10008; </a></td>";
} else {
s << "<td \\>";
}
s << "<td class=\"streamdest\" title=\"" << streamDest << "\">" << streamDest << "</td>";
s << "<td>" << it->GetNumSentBytes () << "</td>";
s << "<td>" << it->GetNumReceivedBytes () << "</td>";
s << "<td>" << it->GetSendQueueSize () << "</td>";
@ -441,7 +458,7 @@ namespace http {
s << "<td>" << (int)it->GetStatus () << "</td>";
s << "</tr>\r\n";
}
s << "</table>";
s << "</tbody>\r\n</table>";
}
}
@ -463,46 +480,57 @@ namespace http {
void ShowLeasesSets(std::stringstream& s)
{
s << "<b>LeaseSets:</b><br>\r\n<br>\r\n";
int counter = 1;
// for each lease set
i2p::data::netdb.VisitLeaseSets(
[&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
// create copy of lease set so we extract leases
auto storeType = leaseSet->GetStoreType ();
std::unique_ptr<i2p::data::LeaseSet> 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 << "<div class='leaseset";
if (ls->IsExpired())
s << " expired"; // additional css class for expired
s << "'>\r\n";
if (!ls->IsValid())
s << "<div class='invalid'>!! Invalid !! </div>\r\n";
s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<p class='content'>\r\n";
s << "<b>Store type:</b> " << (int)storeType << "<br>\r\n";
s << "<b>Expires:</b> " << ConvertTime(ls->GetExpirationTime()) << "<br>\r\n";
if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2)
if (i2p::data::netdb.GetNumLeaseSets ())
{
s << "<b>LeaseSets:</b><br>\r\n<br>\r\n";
int counter = 1;
// for each lease set
i2p::data::netdb.VisitLeaseSets(
[&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
{
// leases information is available
auto leases = ls->GetNonExpiredLeases();
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
for ( auto & l : leases )
// create copy of lease set so we extract leases
auto storeType = leaseSet->GetStoreType ();
std::unique_ptr<i2p::data::LeaseSet> 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 << "<div class='leaseset";
if (ls->IsExpired())
s << " expired"; // additional css class for expired
s << "'>\r\n";
if (!ls->IsValid())
s << "<div class='invalid'>!! Invalid !! </div>\r\n";
s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<div class='content'>\r\n";
s << "<b>Store type:</b> " << (int)storeType << "<br>\r\n";
s << "<b>Expires:</b> " << ConvertTime(ls->GetExpirationTime()) << "<br>\r\n";
if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2)
{
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
s << "<b>EndDate:</b> " << ConvertTime(l->endDate) << "<br>\r\n";
// leases information is available
auto leases = ls->GetNonExpiredLeases();
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
for ( auto & l : leases )
{
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
s << "<b>EndDate:</b> " << ConvertTime(l->endDate) << "<br>\r\n";
}
}
s << "</div>\r\n</div>\r\n</div>\r\n";
}
s << "</p>\r\n</div>\r\n</div>\r\n";
}
);
// end for each lease set
);
// end for each lease set
}
else if (!i2p::context.IsFloodfill ())
{
s << "<b>LeaseSets:</b> not floodfill.<br>\r\n";
}
else
{
s << "<b>LeaseSets:</b> 0<br>\r\n";
}
}
void ShowTunnels (std::stringstream& s)
@ -564,16 +592,23 @@ namespace http {
void ShowTransitTunnels (std::stringstream& s)
{
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
if(i2p::tunnel::tunnels.CountTransitTunnels())
{
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
s << it->GetTunnelID () << " &#8658; ";
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
s << " &#8658; " << it->GetTunnelID ();
else
s << " &#8658; " << it->GetTunnelID () << " &#8658; ";
s << " " << it->GetNumTransmittedBytes () << "<br>\r\n";
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
{
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
s << it->GetTunnelID () << " &#8658; ";
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
s << " &#8658; " << it->GetTunnelID ();
else
s << " &#8658; " << it->GetTunnelID () << " &#8658; ";
s << " " << it->GetNumTransmittedBytes () << "<br>\r\n";
}
}
else
{
s << "<b>Transit tunnels:</b> no transit tunnels currently built.<br>\r\n";
}
}
@ -607,13 +642,15 @@ namespace http {
}
if (!tmp_s.str ().empty ())
{
s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "'><b>" << name << "</b> ( " << cnt << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "'/>\r\n<p class='content'>";
s << tmp_s.str () << "</p>\r\n</div>\r\n";
s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "'><b>" << name
<< "</b> ( " << cnt << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "'/>\r\n<div class='content'>"
<< tmp_s.str () << "</div>\r\n</div>\r\n";
}
if (!tmp_s6.str ().empty ())
{
s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "v6'><b>" << name << "v6</b> ( " << cnt6 << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "v6'/>\r\n<p class='content'>";
s << tmp_s6.str () << "</p>\r\n</div>\r\n";
s << "<div class='slide'><label for='slide_" << boost::algorithm::to_lower_copy(name) << "v6'><b>" << name
<< "v6</b> ( " << cnt6 << " )</label>\r\n<input type='checkbox' id='slide_" << boost::algorithm::to_lower_copy(name) << "v6'/>\r\n<div class='content'>"
<< tmp_s6.str () << "</div>\r\n</div>\r\n";
}
}
@ -640,7 +677,7 @@ namespace http {
auto sessions = ssuServer->GetSessions ();
if (!sessions.empty ())
{
s << "<div class='slide'><label for='slide_ssu'><b>SSU</b> ( " << (int) sessions.size() << " )</label>\r\n<input type='checkbox' id='slide_ssu'/>\r\n<p class='content'>";
s << "<div class='slide'><label for='slide_ssu'><b>SSU</b> ( " << (int) sessions.size() << " )</label>\r\n<input type='checkbox' id='slide_ssu'/>\r\n<div class='content'>";
for (const auto& it: sessions)
{
auto endpoint = it.second->GetRemoteEndpoint ();
@ -652,12 +689,12 @@ namespace http {
s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>\r\n" << std::endl;
}
s << "</p>\r\n</div>\r\n";
s << "</div>\r\n</div>\r\n";
}
auto sessions6 = ssuServer->GetSessionsV6 ();
if (!sessions6.empty ())
{
s << "<div class='slide'><label for='slide_ssuv6'><b>SSUv6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type='checkbox' id='slide_ssuv6'/>\r\n<p class='content'>";
s << "<div class='slide'><label for='slide_ssuv6'><b>SSUv6</b> ( " << (int) sessions6.size() << " )</label>\r\n<input type='checkbox' id='slide_ssuv6'/>\r\n<div class='content'>";
for (const auto& it: sessions6)
{
auto endpoint = it.second->GetRemoteEndpoint ();
@ -669,7 +706,7 @@ namespace http {
s << " [itag:" << it.second->GetRelayTag () << "]";
s << "<br>\r\n" << std::endl;
}
s << "</p>\r\n</div>\r\n";
s << "</div>\r\n</div>\r\n";
}
}
}
@ -678,17 +715,24 @@ namespace http {
{
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 << "<b>SAM Sessions:</b><br>\r\n<br>\r\n";
for (auto& it: sam->GetSessions ())
if(sam->GetSessions ().size ())
{
auto& name = it.second->localDestination->GetNickname ();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
s << name << " (" << it.first << ")</a><br>\r\n" << std::endl;
s << "<b>SAM Sessions:</b><br>\r\n<br>\r\n";
for (auto& it: sam->GetSessions ())
{
auto& name = it.second->localDestination->GetNickname ();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSION << "&sam_id=" << it.first << "\">";
s << name << " (" << it.first << ")</a><br>\r\n" << std::endl;
}
}
else
s << "<b>SAM Sessions:</b> no sessions currently running.<br>\r\n";
}
static void ShowSAMSession (std::stringstream& s, const std::string& id)
@ -858,7 +902,8 @@ namespace http {
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 @@ namespace http {
} 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 @@ namespace http {
SendReply (res, content);
}
std::map<uint32_t, uint32_t> 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<std::string, std::string> params;
@ -947,18 +1008,7 @@ namespace http {
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 @@ namespace http {
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 @@ namespace http {
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 @@ namespace http {
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 << "<b>SUCCESS</b>:&nbsp;Stream closed<br><br>\r\n";
else
s << "<b>ERROR</b>:&nbsp;Stream not found or already was closed<br><br>\r\n";
}
else
s << "<b>ERROR</b>:&nbsp;Destination not found<br><br>\r\n";
}
else
s << "<b>ERROR</b>:&nbsp;StreamID can be null<br><br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">Return to destination page</a><br>\r\n";
s << "<p>You will be redirected back in 5 seconds</b>";
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 << "<b>SUCCESS</b>:&nbsp;Command accepted<br><br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">Back to commands list</a><br>\r\n";
s << "<p>You will be redirected in 5 seconds</b>";

View File

@ -35,6 +35,7 @@ namespace http
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 @@ namespace http
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

View File

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

View File

@ -218,13 +218,6 @@ namespace config {
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?")
;
options_description websocket("Websocket Options");
websocket.add_options()
("websockets.enabled", value<bool>()->default_value(false), "Enable websocket server")
("websockets.address", value<std::string>()->default_value("127.0.0.1"), "Address to bind websocket server on")
("websockets.port", value<uint16_t>()->default_value(7666), "Port to bind websocket server on")
;
options_description exploratory("Exploratory Options");
exploratory.add_options()
("exploratory.inbound.length", value<int>()->default_value(2), "Exploratory inbound tunnel length")
@ -239,6 +232,7 @@ namespace config {
("ntcp2.published", value<bool>()->default_value(true), "Publish NTCP2 (default: enabled)")
("ntcp2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming NTCP2 connections (default: auto)")
("ntcp2.addressv6", value<std::string>()->default_value("::"), "Address to bind NTCP2 on")
("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport")
;
options_description nettime("Time sync options");
@ -274,7 +268,6 @@ namespace config {
.add(reseed)
.add(addressbook)
.add(trust)
.add(websocket)
.add(exploratory)
.add(ntcp2)
.add(nettime)

View File

@ -388,7 +388,7 @@ namespace client
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
else
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetEncryptionType ()); // LeaseSet2
leaseSet = std::make_shared<i2p::data::LeaseSet2> (buf[DATABASE_STORE_TYPE_OFFSET], buf + offset, len - offset, true, GetPreferredCryptoType () ); // LeaseSet2
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
{
if (leaseSet->GetIdentHash () != GetIdentHash ())
@ -412,7 +412,7 @@ namespace client
auto it2 = m_LeaseSetRequests.find (key);
if (it2 != m_LeaseSetRequests.end () && it2->second->requestedBlindedKey)
{
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (buf + offset, len - offset, it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? *m_LeaseSetPrivKey : nullptr, GetEncryptionType ());
auto ls2 = std::make_shared<i2p::data::LeaseSet2> (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
@ -822,6 +822,13 @@ namespace client
}
}
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<std::string, std::string> * params):
LeaseSetDestination (service, isPublic, params),
@ -1149,10 +1156,11 @@ namespace client
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::LocalLeaseSet2> (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<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys, GetAuthType (), m_AuthKeys);
leaseSet = ls2;
@ -1191,6 +1199,16 @@ namespace client
}
}
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<std::string, std::string> * params):
RunnableService ("Destination"),
ClientDestination (GetIOService (), keys, isPublic, params)

View File

@ -161,6 +161,7 @@ namespace client
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:
@ -232,14 +233,14 @@ namespace client
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;
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
i2p::data::CryptoKeyType GetEncryptionType () const { return m_EncryptionKeyType; };
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const { return m_EncryptionKeyType == keyType; };
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const { return m_EncryptionPublicKey; };
protected:
@ -281,6 +282,7 @@ namespace client
// for HTTP only
std::vector<std::shared_ptr<const i2p::stream::Stream> > GetAllStreams () const;
bool DeleteStream (uint32_t recvStreamID);
};
class RunnableClientDestination: private i2p::util::RunnableService, public ClientDestination

View File

@ -6,6 +6,8 @@
#include "Tag.h"
#include "I2PEndian.h"
#include "Timestamp.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#include "ECIESX25519AEADRatchetSession.h"
namespace i2p
@ -36,28 +38,32 @@ namespace garlic
{
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 ();
}
const uint8_t * RatchetTagSet::GetSymmKey (int index)
void RatchetTagSet::GetSymmKey (int index, uint8_t * key)
{
// TODO: store intermediate keys
if (m_NextSymmKeyIndex > 0 && index == m_NextSymmKeyIndex)
if (m_NextSymmKeyIndex > 0 && index >= m_NextSymmKeyIndex)
{
i2p::crypto::HKDF (m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK);
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);
return m_CurrentSymmKeyCK + 32;
CalculateSymmKeyCK (index, key);
}
void RatchetTagSet::CalculateSymmKeyCK (int index)
void RatchetTagSet::CalculateSymmKeyCK (int index, uint8_t * key)
{
i2p::crypto::HKDF (m_SymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
// 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 (m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK); // keydata_n = HKDF(symmKey_chainKey_(n-1), SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
m_NextSymmKeyIndex = index + 1;
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):
@ -121,7 +127,7 @@ namespace garlic
if (!GetOwner ()) return false;
// we are Bob
// KDF1
MixHash (GetOwner ()->GetEncryptionPublicKey (), 32); // h = SHA256(h || bpk)
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))
{
@ -173,7 +179,7 @@ namespace garlic
return true;
}
void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len)
void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len, int index)
{
size_t offset = 0;
while (offset < len)
@ -202,6 +208,12 @@ namespace garlic
case eECIESx25519BlkPadding:
LogPrint (eLogDebug, "Garlic: padding");
break;
case eECIESx25519BlkAckRequest:
{
LogPrint (eLogDebug, "Garlic: ack request");
m_AckRequests.push_back ( {bufbe16toh (buf + offset), index});
break;
}
default:
LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk);
}
@ -230,7 +242,7 @@ namespace garlic
// encrypt static key section
uint8_t nonce[12];
CreateNonce (0, nonce);
if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt
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;
@ -373,7 +385,9 @@ namespace garlic
memcpy (out, &tag, 8);
// ad = The session tag, 8 bytes
// ciphertext = ENCRYPT(k, n, payload, ad)
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, m_SendTagset.GetSymmKey (index), nonce, out + 8, outLen - 8, true)) // encrypt
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;
@ -387,14 +401,15 @@ namespace garlic
CreateNonce (index, nonce); // tag's index
len -= 8; // tag
std::vector<uint8_t> payload (len - 16);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 8, len - 16, buf, 8, m_ReceiveTagset.GetSymmKey (index), nonce, payload.data (), len - 16, false)) // decrypt
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);
if (m_NumReceiveTags > 0)m_NumReceiveTags--;
if (m_NumReceiveTags <= GetOwner ()->GetNumTags ()*2/3)
HandlePayload (payload.data (), len - 16, index);
if (m_ReceiveTagset.GetNextIndex () - index <= GetOwner ()->GetNumTags ()*2/3)
GenerateMoreReceiveTags (GetOwner ()->GetNumTags ());
return true;
}
@ -453,12 +468,25 @@ namespace garlic
std::vector<uint8_t> ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg)
{
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
size_t payloadLen = 7; // datatime
if (msg && m_Destination)
payloadLen += msg->GetPayloadLength () + 13 + 32;
auto leaseSet = CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ());
if (leaseSet)
auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated) ? CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ()) : nullptr;
std::shared_ptr<I2NPMessage> 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
@ -468,13 +496,28 @@ namespace garlic
// DateTime
v[offset] = eECIESx25519BlkDateTime; offset++;
htobe16buf (v.data () + offset, 4); offset += 2;
htobe32buf (v.data () + offset, i2p::util::GetSecondsSinceEpoch ()); offset += 4;
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;
@ -506,6 +549,39 @@ namespace garlic
return cloveSize + 3;
}
size_t ECIESX25519AEADRatchetSession::CreateDeliveryStatusClove (std::shared_ptr<const I2NPMessage> 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++)
@ -514,7 +590,12 @@ namespace garlic
uint64_t tag = m_ReceiveTagset.GetNextSessionTag ();
GetOwner ()->AddECIESx25519SessionTag (index, tag, shared_from_this ());
}
m_NumReceiveTags += numTags;
}
bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts)
{
CleanupUnconfirmedLeaseSet (ts);
return ts > m_LastActivityTimestamp + ECIESX25519_EXPIRATION_TIMEOUT;
}
}
}

View File

@ -6,6 +6,7 @@
#include <functional>
#include <memory>
#include <vector>
#include <list>
#include "Identity.h"
#include "Crypto.h"
#include "Garlic.h"
@ -22,11 +23,11 @@ namespace garlic
void NextSessionTagRatchet ();
uint64_t GetNextSessionTag ();
int GetNextIndex () const { return m_NextIndex; };
const uint8_t * GetSymmKey (int index);
void GetSymmKey (int index, uint8_t * key);
private:
void CalculateSymmKeyCK (int index);
void CalculateSymmKeyCK (int index, uint8_t * key);
private:
@ -51,6 +52,8 @@ namespace garlic
eECIESx25519BlkTermination = 4,
eECIESx25519BlkOptions = 5,
eECIESx25519BlkNextSessionKey = 7,
eECIESx25519BlkAck = 8,
eECIESx25519BlkAckRequest = 9,
eECIESx25519BlkGalicClove = 11,
eECIESx25519BlkPadding = 254
};
@ -85,7 +88,7 @@ namespace garlic
if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest));
}
bool IsExpired (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_EXPIRATION_TIMEOUT; }
bool CheckExpired (uint64_t ts); // true is expired
bool CanBeRestarted (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_RESTART_TIMEOUT; }
private:
@ -99,7 +102,7 @@ namespace garlic
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);
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);
@ -107,6 +110,7 @@ namespace garlic
std::vector<uint8_t> CreatePayload (std::shared_ptr<const I2NPMessage> msg);
size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len, bool isDestination = false);
size_t CreateDeliveryStatusClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len);
void GenerateMoreReceiveTags (int numTags);
@ -118,8 +122,8 @@ namespace garlic
SessionState m_State = eSessionStateNew;
uint64_t m_LastActivityTimestamp = 0; // incoming
RatchetTagSet m_SendTagset, m_ReceiveTagset;
int m_NumReceiveTags = 0;
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
std::list<std::pair<uint16_t, int> > m_AckRequests; // (key_id, indeX)
};
}
}

View File

@ -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<std::mutex> 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<std::mutex> 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
}

View File

@ -1,53 +0,0 @@
#ifndef EVENT_H__
#define EVENT_H__
#include <map>
#include <string>
#include <memory>
#include <mutex>
#include <tuple>
#include <boost/asio.hpp>
typedef std::map<std::string, std::string> 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<std::string, CollectedEvent> 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

View File

@ -58,6 +58,44 @@ namespace garlic
m_SharedRoutingPath = path;
}
bool GarlicRoutingSession::MessageConfirmed (uint32_t msgID)
{
if (msgID == GetLeaseSetUpdateMsgID ())
{
SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
SetLeaseSetUpdateMsgID (0);
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
return true;
}
return false;
}
void GarlicRoutingSession::CleanupUnconfirmedLeaseSet (uint64_t ts)
{
if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
{
if (GetOwner ())
GetOwner ()->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
m_LeaseSetUpdateMsgID = 0;
}
}
std::shared_ptr<I2NPMessage> 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<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
GarlicRoutingSession (owner, attachLeaseSet),
@ -289,19 +327,12 @@ namespace garlic
htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID
size += 4;
// create msg
auto msg = CreateDeliveryStatusMsg (msgID);
if (GetOwner ())
auto msg = CreateEncryptedDeliveryStatusMsg (msgID);
if (msg)
{
//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);
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength ();
}
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
size += msg->GetLength ();
// fill clove
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
uint32_t cloveID;
@ -334,17 +365,12 @@ namespace garlic
return tags;
}
void ElGamalAESSession::MessageConfirmed (uint32_t msgID)
bool ElGamalAESSession::MessageConfirmed (uint32_t msgID)
{
TagsConfirmed (msgID);
if (msgID == GetLeaseSetUpdateMsgID ())
{
SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
SetLeaseSetUpdateMsgID (0);
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
}
else
if (!GarlicRoutingSession::MessageConfirmed (msgID))
CleanupExpiredTags ();
return true;
}
void ElGamalAESSession::TagsConfirmed (uint32_t msgID)
@ -374,12 +400,7 @@ namespace garlic
++it;
}
CleanupUnconfirmedTags ();
if (GetLeaseSetUpdateMsgID () && ts*1000LL > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)
{
if (GetOwner ())
GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ());
SetLeaseSetUpdateMsgID (0);
}
CleanupUnconfirmedLeaseSet (ts);
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
}
@ -467,7 +488,7 @@ namespace garlic
else
{
// tag not found. Handle depending on encryption type
if (GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET)
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET))
{
HandleECIESx25519 (buf, length);
return;
@ -659,7 +680,7 @@ namespace garlic
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
{
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET &&
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];
@ -751,7 +772,7 @@ namespace garlic
for (auto it = m_ECIESx25519Sessions.begin (); it != m_ECIESx25519Sessions.end ();)
{
if (it->second->IsExpired (ts))
if (it->second->CheckExpired (ts))
{
it->second->SetOwner (nullptr);
it = m_ECIESx25519Sessions.erase (it);
@ -767,7 +788,7 @@ namespace garlic
m_DeliveryStatusSessions.erase (msgID);
}
void GarlicDestination::DeliveryStatusSent (ElGamalAESSessionPtr session, uint32_t msgID)
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
{
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
m_DeliveryStatusSessions[msgID] = session;
@ -775,7 +796,7 @@ namespace garlic
void GarlicDestination::HandleDeliveryStatusMessage (uint32_t msgID)
{
ElGamalAESSessionPtr session;
GarlicRoutingSessionPtr session;
{
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
auto it = m_DeliveryStatusSessions.find (msgID);
@ -794,8 +815,12 @@ namespace garlic
void GarlicDestination::SetLeaseSetUpdated ()
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto& it: m_Sessions)
{
std::unique_lock<std::mutex> l(m_SessionsMutex);
for (auto& it: m_Sessions)
it.second->SetLeaseSetUpdated ();
}
for (auto& it: m_ECIESx25519Sessions)
it.second->SetLeaseSetUpdated ();
}
@ -865,7 +890,7 @@ namespace garlic
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);
@ -911,7 +936,10 @@ namespace garlic
case eGarlicDeliveryTypeDestination:
LogPrint (eLogDebug, "Garlic: type destination");
buf += 32; // TODO: check destination
// no break here
#if (__cplusplus >= 201703L) // C++ 17 or higher
[[fallthrough]];
#endif
// no break here
case eGarlicDeliveryTypeLocal:
{
LogPrint (eLogDebug, "Garlic: type local");

View File

@ -104,6 +104,7 @@ namespace garlic
virtual ~GarlicRoutingSession ();
virtual std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) = 0;
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession
virtual bool MessageConfirmed (uint32_t msgID);
void SetLeaseSetUpdated ()
{
@ -112,6 +113,7 @@ namespace garlic
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<GarlicRoutingPath> GetSharedRoutingPath ();
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
@ -127,6 +129,8 @@ namespace garlic
void SetLeaseSetUpdateMsgID (uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; }
void SetLeaseSetSubmissionTime (uint64_t ts) { m_LeaseSetSubmissionTime = ts; }
std::shared_ptr<I2NPMessage> CreateEncryptedDeliveryStatusMsg (uint32_t msgID);
private:
GarlicDestination * m_Owner;
@ -165,7 +169,7 @@ namespace garlic
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
void MessageConfirmed (uint32_t msgID);
bool MessageConfirmed (uint32_t msgID);
bool CleanupExpiredTags (); // returns true if something left
bool CleanupUnconfirmedTags (); // returns true if something has been deleted
@ -223,7 +227,7 @@ namespace garlic
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 (ElGamalAESSessionPtr session, uint32_t msgID);
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);
@ -267,7 +271,7 @@ namespace garlic
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexSession> m_ECIESx25519Tags; // session tag -> session
// DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex;
std::unordered_map<uint32_t, ElGamalAESSessionPtr> m_DeliveryStatusSessions; // msgID -> session
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
public:

View File

@ -723,7 +723,10 @@ namespace data
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;

View File

@ -228,8 +228,8 @@ namespace data
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override for LeaseSet
virtual const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ()->GetEncryptionPublicKey (); }; // override for LeaseSet
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
};
}
}

View File

@ -159,7 +159,7 @@ namespace data
}
}
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,6 +251,13 @@ namespace data
memcpy (m_Buffer, buf, len);
}
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)
{
@ -331,6 +338,8 @@ namespace data
VerifySignature (identity, buf, len, offset);
SetIsValid (verified);
}
offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen ();
SetBufferLen (offset);
}
template<typename Verifier>
@ -537,6 +546,12 @@ namespace data
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
@ -748,7 +763,7 @@ namespace data
}
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<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
bool isPublic, bool isPublishedEncrypted):
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
@ -757,8 +772,11 @@ namespace data
// 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 ())
{
@ -789,10 +807,13 @@ namespace data
}
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

View File

@ -97,6 +97,7 @@ namespace data
// 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<const IdentityEx> identity) { m_Identity = identity; };
void SetExpirationTime (uint64_t t) { m_ExpirationTime = t; };
void SetIsValid (bool isValid) { m_IsValid = isValid; };
@ -231,8 +232,15 @@ namespace data
{
public:
struct KeySection
{
uint16_t keyType, keyLen;
const uint8_t * encryptionPublicKey;
};
typedef std::vector<KeySection> 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<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
bool isPublic, bool isPublishedEncrypted = false);
LocalLeaseSet2 (uint8_t storeType, std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP

View File

@ -161,6 +161,7 @@ void LogPrint (std::stringstream& s, TValue&& arg) noexcept
s << std::forward<TValue>(arg);
}
#if (__cplusplus < 201703L) // below C++ 17
/** internal usage only -- folding args array to single string */
template<typename TValue, typename... TArgs>
void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
@ -168,6 +169,7 @@ void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
LogPrint (s, std::forward<TValue>(arg));
LogPrint (s, std::forward<TArgs>(args)...);
}
#endif
/**
* @brief Create log message and send it to queue
@ -184,7 +186,11 @@ void LogPrint (LogLevel level, TArgs&&... args) noexcept
// fold message to single string
std::stringstream ss("");
#if (__cplusplus >= 201703L) // C++ 17 or higher
(LogPrint (ss, std::forward<TArgs>(args)), ...);
#else
LogPrint (ss, std::forward<TArgs>(args)...);
#endif
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), ss.str());
msg->tid = std::this_thread::get_id();

View File

@ -20,6 +20,8 @@
#include "Transports.h"
#include "NetDb.hpp"
#include "NTCP2.h"
#include "HTTP.h"
#include "util.h"
namespace i2p
{
@ -1141,8 +1143,8 @@ namespace transport
}
NTCP2Server::NTCP2Server ():
RunnableServiceWithWork ("NTCP2"),
m_TerminationTimer (GetService ())
RunnableServiceWithWork ("NTCP2"), m_TerminationTimer (GetService ()),
m_Resolver(GetService ())
{
}
@ -1156,45 +1158,67 @@ namespace transport
if (!IsRunning ())
{
StartIOService ();
auto& addresses = context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses)
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
if (address->host.is_v4())
{
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;
}
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<NTCP2Session>(*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 (GetService ()));
try
LogPrint (eLogInfo, "NTCP2: Start listening TCP port ", address->port);
auto conn = std::make_shared<NTCP2Session>(*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->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 ();
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<NTCP2Session> (*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;
LogPrint (eLogInfo, "NTCP2: Start listening V6 TCP port ", address->port);
auto conn = std::make_shared<NTCP2Session> (*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;
}
}
}
}
@ -1216,7 +1240,10 @@ namespace transport
m_NTCP2Sessions.clear ();
if (IsRunning ())
{
m_TerminationTimer.cancel ();
m_ProxyEndpoint = nullptr;
}
StopIOService ();
}
@ -1389,6 +1416,231 @@ namespace transport
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<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> 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<std::vector<uint8_t> >(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<const char*>(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<NTCP2Session> conn)
{
if(!m_ProxyEndpoint) return;
GetService().post([this, host, port, addrtype, conn]() {
if (this->AddNTCP2Session (conn))
{
auto timer = std::make_shared<boost::asio::deadline_timer>(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<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType addrtype)
{
// build request
size_t sz = 0;
auto buff = std::make_shared<std::vector<int8_t> >(256);
auto readbuff = std::make_shared<std::vector<int8_t> >(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();
});
}
}
}

View File

@ -126,6 +126,7 @@ namespace transport
{
public:
NTCP2Session (NTCP2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
~NTCP2Session ();
void Terminate ();
@ -220,6 +221,20 @@ namespace transport
{
public:
enum RemoteAddressType
{
eIP4Address,
eIP6Address,
eHostname
};
enum ProxyType
{
eNoProxy,
eSocksProxy,
eHTTPProxy
};
NTCP2Server ();
~NTCP2Server ();
@ -231,14 +246,22 @@ namespace transport
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
std::shared_ptr<NTCP2Session> FindNTCP2Session (const i2p::data::IdentHash& ident);
void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCP2Session> conn);
void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCP2Session> conn);
void AfterSocksHandshake(std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> 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 HandleAccept (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
void HandleAcceptV6 (std::shared_ptr<NTCP2Session> conn, const boost::system::error_code& error);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCP2Session> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype);
// timer
void ScheduleTermination ();
@ -251,6 +274,12 @@ namespace transport
std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions;
std::list<std::shared_ptr<NTCP2Session> > m_PendingIncomingSessions;
ProxyType m_ProxyType =eNoProxy;
std::string m_ProxyAddress;
uint16_t m_ProxyPort;
boost::asio::ip::tcp::resolver m_Resolver;
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
public:
// for HTTP/I2PControl

View File

@ -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 @@ namespace transport
{
if (!m_NextMessage->IsExpired ())
{
#ifdef WITH_EVENTS
QueueIntEvent("transport.recvmsg", GetIdentHashBase64(), 1);
#endif
m_Handler.PutNextMessage (m_NextMessage);
}
else

View File

@ -338,7 +338,11 @@ namespace i2p
{
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);

View File

@ -160,6 +160,13 @@ namespace transport
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 @@ namespace transport
catch (std::exception& ex)
{
LogPrint (eLogError, "SSU: v6 receivers runtime exception: ", ex.what ());
if (m_IsRunning)
{
m_SocketV6.close ();
OpenSocketV6 ();
ReceiveV6 ();
}
}
}
}

View File

@ -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 @@ namespace transport
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 @@ namespace transport
}
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 @@ namespace transport
}
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 @@ namespace transport
{
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

View File

@ -86,13 +86,14 @@ namespace stream
LogPrint (eLogDebug, "Streaming: Stream deleted");
}
void Stream::Terminate () // shoudl be called from StreamingDestination::Stop only
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 @@ namespace stream
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);
@ -990,7 +994,7 @@ namespace stream
{
std::unique_lock<std::mutex> l(m_StreamsMutex);
for (auto it: m_Streams)
it.second->Terminate ();
it.second->Terminate (false); // we delete here
m_Streams.clear ();
m_IncomingStreams.clear ();
}
@ -1125,6 +1129,15 @@ namespace stream
}
}
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

View File

@ -180,7 +180,7 @@ namespace stream
int GetWindowSize () const { return m_WindowSize; };
int GetRTT () const { return m_RTT; };
void Terminate ();
void Terminate (bool deleteFromDestination = true);
private:
@ -250,6 +250,7 @@ namespace stream
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void DeleteStream (std::shared_ptr<Stream> stream);
bool DeleteStream (uint32_t recvStreamID);
void SetAcceptor (const Acceptor& acceptor);
void ResetAcceptor ();
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };

View File

@ -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 @@ namespace transport
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);
@ -200,8 +197,33 @@ namespace transport
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
@ -249,11 +271,11 @@ namespace transport
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 @@ namespace transport
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
{
#ifdef WITH_EVENTS
QueueIntEvent("transport.send", ident.ToBase64(), msgs.size());
#endif
m_Service->post (std::bind (&Transports::PostMessages, this, ident, msgs));
}
@ -405,21 +424,33 @@ namespace transport
{
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<NTCP2Session> (*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<NTCP2Session> (*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++;
@ -558,6 +589,7 @@ namespace transport
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++)
{
@ -573,7 +605,7 @@ namespace transport
}
}
if (!statusChanged)
LogPrint (eLogWarning, "Can't find routers for peer test");
LogPrint (eLogWarning, "Transports: Can't find routers for peer test");
}
}
@ -597,9 +629,6 @@ namespace transport
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)
{
@ -625,9 +654,6 @@ namespace transport
session->Done();
return;
}
#ifdef WITH_EVENTS
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "true"}});
#endif
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} }));
@ -642,9 +668,6 @@ namespace transport
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 ())
{

View File

@ -14,9 +14,6 @@
#include "Config.h"
#include "Tunnel.h"
#include "TunnelPool.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
namespace i2p
{
@ -35,9 +32,6 @@ namespace tunnel
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> 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 ();
@ -64,15 +58,9 @@ namespace tunnel
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++)
{
@ -207,9 +195,6 @@ namespace tunnel
void Tunnel::SetState(TunnelState state)
{
m_State = state;
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.state", this, state);
#endif
}
@ -614,9 +599,6 @@ namespace tunnel
hop = hop->next;
}
}
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
#endif
// for i2lua
if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultTimeout);
// delete
@ -628,9 +610,6 @@ namespace tunnel
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);

View File

@ -19,49 +19,11 @@
#include "TunnelGateway.h"
#include "TunnelBase.h"
#include "I2NPProtocol.h"
#include "Event.h"
namespace i2p
{
namespace tunnel
{
template<typename TunnelT>
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<typename TunnelT, typename T>
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<typename TunnelT>
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

View File

@ -11,9 +11,6 @@
#include "Tunnel.h"
#include "TunnelPool.h"
#include "Destination.h"
#ifdef WITH_EVENTS
#include "Event.h"
#endif
namespace i2p
{
@ -86,9 +83,6 @@ namespace tunnel
{
if (!m_IsActive) return;
{
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnels.created", createdTunnel);
#endif
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
m_InboundTunnels.insert (createdTunnel);
}
@ -102,9 +96,6 @@ namespace tunnel
{
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;
@ -118,9 +109,6 @@ namespace tunnel
{
if (!m_IsActive) return;
{
#ifdef WITH_EVENTS
EmitTunnelEvent("tunnels.created", createdTunnel);
#endif
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.insert (createdTunnel);
}
@ -133,9 +121,6 @@ namespace tunnel
{
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;

View File

@ -9,7 +9,6 @@
#include "util.h"
#include "ClientContext.h"
#include "SOCKS.h"
#include "WebSocks.h"
#include "MatchedDestination.h"
namespace i2p
@ -598,10 +597,8 @@ namespace client
}
else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS)
{
// websocks proxy
auto tun = std::make_shared<WebSocks>(address, port, localDestination);
clientTunnel = tun;
clientEndpoint = tun->GetLocalEndpoint();
LogPrint(eLogError, "Clients: I2P Client tunnel websocks is deprecated");
continue;
}
else
{

View File

@ -37,7 +37,7 @@ namespace client
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";

View File

@ -246,8 +246,16 @@ namespace client
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
{
@ -572,7 +580,7 @@ namespace client
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;
@ -814,8 +822,11 @@ namespace client
{
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)

View File

@ -24,6 +24,7 @@ namespace client
{
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;
@ -80,7 +81,8 @@ namespace client
// implements LocalDestination
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const;
i2p::data::CryptoKeyType GetEncryptionType () const { return m_EncryptionKeyType; };
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const { return m_EncryptionKeyType == keyType; };
// TODO: implement GetEncryptionPublicKey
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Identity; };
protected:

View File

@ -433,6 +433,9 @@ namespace proxy
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);

View File

@ -1,559 +0,0 @@
#include "WebSocks.h"
#include "Log.h"
#include <string>
#ifdef WITH_EVENTS
#include "ClientContext.h"
#include "Identity.h"
#include "Destination.h"
#include "Streaming.h"
#include <functional>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <boost/property_tree/ini_parser.hpp>
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
#if !GCC47_BOOST149
#include <boost/property_tree/json_parser.hpp>
#endif
namespace i2p
{
namespace client
{
typedef websocketpp::server<websocketpp::config::asio> WebSocksServerImpl;
typedef std::function<void(std::shared_ptr<i2p::stream::Stream>)> 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<IWebSocksConn> WebSocksConn_ptr;
WebSocksConn_ptr CreateWebSocksConn(const websocketpp::connection_hdl & conn, WebSocksImpl * parent);
class WebSocksImpl
{
typedef std::mutex mutex_t;
typedef std::unique_lock<mutex_t> lock_t;
typedef std::shared_ptr<ClientDestination> 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<WebSocksConn_ptr> 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<WebSocksConn>
{
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<ClientDestination> Destination_t;
typedef std::shared_ptr<i2p::stream::StreamingDestination> StreamDest_t;
typedef std::shared_ptr<i2p::stream::Stream> 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<WebSocksConn>(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<ClientDestination> 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();
}
}
}

View File

@ -1,34 +0,0 @@
#ifndef WEBSOCKS_H_
#define WEBSOCKS_H_
#include <string>
#include <memory>
#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<ClientDestination> localDestination);
~WebSocks();
void Start();
void Stop();
boost::asio::ip::tcp::endpoint GetLocalEndpoint() const;
const char * GetName() { return "WebSOCKS Proxy"; }
private:
WebSocksImpl * m_Impl;
};
}
}
#endif

View File

@ -1,195 +0,0 @@
#ifdef WITH_EVENTS
#include "Websocket.h"
#include "Log.h"
#include <set>
#include <functional>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <boost/property_tree/ini_parser.hpp>
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
#if !GCC47_BOOST149
#include <boost/property_tree/json_parser.hpp>
#endif
#include <stdexcept>
namespace i2p
{
namespace event
{
typedef websocketpp::server<websocketpp::config::asio> 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<std::mutex> lock(m_connsMutex);
m_conns.insert(c);
}
void ConnClosed(ServerConn c)
{
std::lock_guard<std::mutex> 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<std::mutex> 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<ServerConn, std::owner_less<ServerConn> > 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

View File

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

View File

@ -0,0 +1,3 @@
#include "DelayedSaveManager.h"
DelayedSaveManager::DelayedSaveManager(){}

View File

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

View File

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

View File

@ -0,0 +1,82 @@
#ifndef DELAYEDSAVEMANAGERIMPL_H
#define DELAYEDSAVEMANAGERIMPL_H
#include <QObject>
#include <QThread>
#include <QWaitCondition>
#include <QMutex>
#include <QDateTime>
#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

6
qt/i2pd_qt/Saver.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "Saver.h"
Saver::Saver()
{
}

22
qt/i2pd_qt/Saver.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef SAVER_H
#define SAVER_H
#include <string>
#include <QObject>
#include <QString>
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

79
qt/i2pd_qt/SaverImpl.cpp Normal file
View File

@ -0,0 +1,79 @@
#include "SaverImpl.h"
#include <fstream>
#include <assert.h>
#include <sstream>
#include "QList"
#include "QString"
#include "mainwindow.h"
SaverImpl::SaverImpl(MainWindow *mainWindowPtr_, QList<MainWindowItem*> * configItems_, std::map<std::string,TunnelConfig*>* 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<MainWindowItem*>::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<std::string,TunnelConfig*>::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;
}*/

33
qt/i2pd_qt/SaverImpl.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef SAVERIMPL_H
#define SAVERIMPL_H
#include <map>
#include <string>
#include <QObject>
#include "QList"
#include "mainwindow.h"
#include "TunnelConfig.h"
#include "Saver.h"
class MainWindowItem;
class TunnelConfig;
class SaverImpl : public Saver
{
public:
SaverImpl(MainWindow *mainWindowPtr_, QList<MainWindowItem*> * configItems_, std::map<std::string,TunnelConfig*>* tunnelConfigs_);
virtual ~SaverImpl();
virtual bool save(const bool focusOnTunnel, const std::string& tunnelNameToFocus);
void setConfPath(QString& confpath_);
void setTunnelsConfPath(QString& tunconfpath_);
private:
QList<MainWindowItem*> * configItems;
std::map<std::string,TunnelConfig*>* tunnelConfigs;
QString confpath;
QString tunconfpath;
MainWindow* mainWindowPtr;
};
#endif // SAVERIMPL_H

View File

@ -40,6 +40,9 @@ public:
class ClientTunnelConfig;
class ServerTunnelConfig;
class TunnelPane;
class TunnelConfig {
/*
const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client";
@ -54,6 +57,7 @@ class TunnelConfig {
*/
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 @@ public:
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;
};

View File

@ -64,7 +64,7 @@ void TunnelPane::setupTunnelPane(
//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 @@ void TunnelPane::setupTunnelPane(
retranslateTunnelForm(*this);
}
void TunnelPane::deleteWidget() {
//gridLayoutWidget_2->deleteLater();
tunnelGroupBox->deleteLater();
}
void TunnelPane::appendControlsForI2CPParameters(I2CPParameters& i2cpParameters, int& gridIndex) {
{
//number of hops of an inbound tunnel

View File

@ -41,6 +41,8 @@ public:
virtual ServerTunnelPane* asServerTunnelPane()=0;
virtual ClientTunnelPane* asClientTunnelPane()=0;
void deleteWidget();
protected:
MainWindow* mainWindow;
QWidget * wrongInputPane;

View File

@ -2135,121 +2135,6 @@ Comma separated list of base64 identities:</string>
</widget>
</widget>
</item>
<item row="25" column="0">
<widget class="QGroupBox" name="groupBox_websock">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>105</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>105</height>
</size>
</property>
<property name="title">
<string>Websockets server</string>
</property>
<widget class="QCheckBox" name="checkBoxWebsocketsEnable">
<property name="geometry">
<rect>
<x>0</x>
<y>20</y>
<width>85</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Enable</string>
</property>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_32">
<property name="geometry">
<rect>
<x>0</x>
<y>40</y>
<width>661</width>
<height>31</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_38">
<item>
<widget class="QLabel" name="label_40">
<property name="text">
<string>Address to bind websocket server on:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_webSock_addr"/>
</item>
<item>
<spacer name="horizontalSpacer_26">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_33">
<property name="geometry">
<rect>
<x>0</x>
<y>70</y>
<width>661</width>
<height>31</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_39">
<item>
<widget class="QLabel" name="label_41">
<property name="text">
<string>Port to bind websocket server on:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_webSock_port">
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_27">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item row="11" column="0">
<widget class="QGroupBox" name="webconsoleGroupBox">
<property name="minimumSize">

View File

@ -28,7 +28,6 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \
../../libi2pd/Datagram.cpp \
../../libi2pd/Destination.cpp \
../../libi2pd/Ed25519.cpp \
../../libi2pd/Event.cpp \
../../libi2pd/Family.cpp \
../../libi2pd/FS.cpp \
../../libi2pd/Garlic.cpp \
@ -74,8 +73,6 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \
../../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 \
@ -91,7 +88,11 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \
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 \
@ -107,7 +108,6 @@ HEADERS += DaemonQT.h mainwindow.h \
../../libi2pd/Datagram.h \
../../libi2pd/Destination.h \
../../libi2pd/Ed25519.h \
../../libi2pd/Event.h \
../../libi2pd/Family.h \
../../libi2pd/FS.h \
../../libi2pd/Garlic.h \
@ -161,8 +161,6 @@ HEADERS += DaemonQT.h mainwindow.h \
../../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 \
@ -179,7 +177,11 @@ HEADERS += DaemonQT.h mainwindow.h \
widgetlock.h \
widgetlockregistry.h \
i2pd.rc \
logviewermanager.h
logviewermanager.h \
DelayedSaveManager.h \
Saver.h \
DelayedSaveManagerImpl.h \
SaverImpl.h
INCLUDEPATH += ../../libi2pd
INCLUDEPATH += ../../libi2pd_client

View File

@ -1,11 +1,8 @@
#include <fstream>
#include <assert.h>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "ui_statusbuttons.h"
#include "ui_routercommandswidget.h"
#include "ui_generalsettingswidget.h"
#include <sstream>
#include <QScrollBar>
#include <QMessageBox>
#include <QTimer>
@ -29,17 +26,22 @@
#include "logviewermanager.h"
#include "DelayedSaveManagerImpl.h"
#include "SaverImpl.h"
std::string programOptionsWriterCurrentSection;
MainWindow::MainWindow(std::shared_ptr<std::iostream> 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 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
,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 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
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 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
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 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
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 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
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 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
logViewerManagerPtr=new LogViewerManager(logStream_,ui->logViewerTextEdit,this);
assert(logViewerManagerPtr!=nullptr);
onLoggingOptionsChange();
//onLoggingOptionsChange();
//QMetaObject::connectSlotsByName(this);
}
@ -500,6 +508,8 @@ void MainWindow::handleQuitButton() {
quitting=true;
#endif
close();
delayedSaveManagerPtr->appExiting();
qDebug("Performing quit");
QApplication::instance()->quit();
}
@ -526,6 +536,7 @@ void MainWindow::handleGracefulQuitTimerEvent() {
quitting=true;
#endif
close();
delayedSaveManagerPtr->appExiting();
qDebug("Performing quit");
QApplication::instance()->quit();
}
@ -534,6 +545,8 @@ MainWindow::~MainWindow()
{
qDebug("Destroying main window");
delete statusPageUpdateTimer;
delete delayedSaveManagerPtr;
delete saverPtr;
for(QList<MainWindowItem*>::iterator it = configItems.begin(); it!= configItems.end(); ++it) {
MainWindowItem* item = *it;
item->deleteLater();
@ -594,7 +607,7 @@ NonGUIOptionItem* MainWindow::initNonGUIOption(ConfigOption option) {
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 @@ void MainWindow::loadAllConfigs(){
this->datadir = datadir.c_str();
this->tunconfpath = tunConf.c_str();
saverPtr->setConfPath(this->confpath);
saverPtr->setTunnelsConfPath(this->tunconfpath);
for(QList<MainWindowItem*>::iterator it = configItems.begin(); it!= configItems.end(); ++it) {
MainWindowItem* item = *it;
item->loadFromConfigOption();
@ -642,10 +658,40 @@ void MainWindow::loadAllConfigs(){
ReadTunnelsConfig();
onLoggingOptionsChange();
//onLoggingOptionsChange();
}
void MainWindow::layoutTunnels() {
int height=0;
ui->tunnelsScrollAreaWidgetContents->setGeometry(0,0,0,0);
for(std::map<std::string, TunnelConfig*>::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<QWidget*> childWidgets = ui->tunnelsScrollAreaWidgetContents->findChildren<QWidget*>();
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 @@ bool MainWindow::saveAllConfigs(){
daemonOption->optionValue=boost::any(false);
serviceOption->optionValue=boost::any(false);
std::stringstream out;
for(QList<MainWindowItem*>::iterator it = configItems.begin(); it!= configItems.end(); ++it) {
MainWindowItem* item = *it;
if(!item->isValid()){
@ -661,27 +706,9 @@ bool MainWindow::saveAllConfigs(){
return false;
}
}
delayedSaveManagerPtr->delayedSave(++dataSerial, focusOnTunnel, tunnelNameToFocus);
for(QList<MainWindowItem*>::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 @@ void MainWindow::updated() {
adjustSizesAccordingToWrongLabel();
applyTunnelsUiToConfigs();
saveAllConfigs();
saveAllConfigs(false);
}
void MainWindowItem::installListeners(MainWindow *mainWindow) {}
@ -725,6 +752,7 @@ void MainWindow::appendTunnelForms(std::string tunnelNameToFocus) {
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 @@ void MainWindow::appendTunnelForms(std::string tunnelNameToFocus) {
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 @@ bool MainWindow::applyTunnelsUiToConfigs() {
return true;
}
void MainWindow::reloadTunnelsConfigAndUI_QString(const QString tunnelNameToFocus) {
reloadTunnelsConfigAndUI(tunnelNameToFocus.toStdString());
}
void MainWindow::reloadTunnelsConfigAndUI(std::string tunnelNameToFocus) {
deleteTunnelForms();
for (std::map<std::string,TunnelConfig*>::iterator it=tunnelConfigs.begin(); it!=tunnelConfigs.end(); ++it) {
@ -795,31 +828,6 @@ void MainWindow::reloadTunnelsConfigAndUI(std::string tunnelNameToFocus) {
appendTunnelForms(tunnelNameToFocus);
}
void MainWindow::SaveTunnelsConfig() {
std::stringstream out;
for (std::map<std::string,TunnelConfig*>::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 @@ void MainWindow::TunnelsPageUpdateListenerMainWindowImpl::updated(std::string ol
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 @@ void MainWindow::anchorClickedHandler(const QUrl & link) {
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()));
}
}

View File

@ -62,6 +62,12 @@
#include "widgetlockregistry.h"
#include "widgetlock.h"
#include "DelayedSaveManager.h"
#include "DelayedSaveManagerImpl.h"
#include "SaverImpl.h"
class SaverImpl;
class LogViewerManager;
template<typename ValueType>
@ -373,10 +379,14 @@ using namespace i2p::qt;
class Controller;
class DelayedSaveManagerImpl;
class MainWindow : public QMainWindow {
Q_OBJECT
private:
std::shared_ptr<std::iostream> logStream;
DelayedSaveManagerImpl* delayedSaveManagerPtr;
DelayedSaveManager::DATA_SERIAL_TYPE dataSerial;
public:
explicit MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *parent=nullptr);
~MainWindow();
@ -502,16 +512,17 @@ protected:
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 @@ private:
void appendTunnelForms(std::string tunnelNameToFocus);
void deleteTunnelForms();
void deleteTunnelFromUI(std::string tunnelName, TunnelConfig* cnf);
template<typename Section, typename Type>
std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const
@ -575,11 +587,11 @@ private:
std::map<std::string,TunnelConfig*>::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 @@ private:
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 @@ private:
isUniqueLocal);
saveAllConfigs();
reloadTunnelsConfigAndUI(name);
saveAllConfigs(true, name);
}
void ReadTunnelsConfig() //TODO deduplicate the code with ClientContext.cpp::ReadTunnels ()
@ -793,7 +803,9 @@ private:
TunnelsPageUpdateListenerMainWindowImpl tunnelsPageUpdateListener;
void onLoggingOptionsChange() {}
//void onLoggingOptionsChange() {}
SaverImpl* saverPtr;
};
#endif // MAINWINDOW_H