mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
253 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e37df26a3 | ||
|
|
5e606573b1 | ||
|
|
b3974cb52a | ||
|
|
b7c206c44b | ||
|
|
49c1e47736 | ||
|
|
4e1319d874 | ||
|
|
a9436aa9af | ||
|
|
d503f07564 | ||
|
|
aa7750bfd3 | ||
|
|
8872d1f389 | ||
|
|
f21af4068f | ||
|
|
f4ca6bbb52 | ||
|
|
869d0156ce | ||
|
|
744e893dce | ||
|
|
fe9ac10f02 | ||
|
|
6fb80f226a | ||
|
|
ff19bab800 | ||
|
|
962c2160c7 | ||
|
|
168da33d8b | ||
|
|
b6b25dc9f3 | ||
|
|
3ca17fdc03 | ||
|
|
2249708097 | ||
|
|
2fcaa7d260 | ||
|
|
f3b0e57a54 | ||
|
|
5da92437a1 | ||
|
|
b5bc05ac2b | ||
|
|
45145fa50a | ||
|
|
dd8200e8b0 | ||
|
|
2f56547d5f | ||
|
|
82bdcfbbcb | ||
|
|
0e38e43315 | ||
|
|
63746be4d5 | ||
|
|
ee73ee365f | ||
|
|
3c53479864 | ||
|
|
4adc741de3 | ||
|
|
64da62dbe6 | ||
|
|
dd9b5faa5c | ||
|
|
51d018acc6 | ||
|
|
5eec580727 | ||
|
|
1e9a53da3f | ||
|
|
8dae044600 | ||
|
|
2d3fad2cdb | ||
|
|
a59a8f62ca | ||
|
|
35cfa7d9fb | ||
|
|
c6ccb373a2 | ||
|
|
b6368170ed | ||
|
|
35e8424293 | ||
|
|
e969d58689 | ||
|
|
ae20e3aa95 | ||
|
|
de4cb74173 | ||
|
|
4f0da87a7a | ||
|
|
97f0347715 | ||
|
|
2ffe62ba41 | ||
|
|
fe1724e7e6 | ||
|
|
2ac2da41cf | ||
|
|
ed574f9d79 | ||
|
|
e0cb26bd9e | ||
|
|
1893127e84 | ||
|
|
b02c9fb118 | ||
|
|
bca0809918 | ||
|
|
00db527377 | ||
|
|
2c6e041ae2 | ||
|
|
a0d6c654cc | ||
|
|
5115c27e72 | ||
|
|
d09c3ccb2d | ||
|
|
5c308026ac | ||
|
|
91919c6d64 | ||
|
|
7168738835 | ||
|
|
9c9b723cf5 | ||
|
|
50450923df | ||
|
|
f392edd66c | ||
|
|
24b48e5d50 | ||
|
|
47f384a0e0 | ||
|
|
88594887f9 | ||
|
|
32e2f0b1fa | ||
|
|
09ed57ad42 | ||
|
|
53a6162b0c | ||
|
|
694d851cdb | ||
|
|
8e53c30a00 | ||
|
|
63e807b0b4 | ||
|
|
012f22cc47 | ||
|
|
9d891ab5dd | ||
|
|
d0e78be867 | ||
|
|
cbedebc9dd | ||
|
|
969f9aa436 | ||
|
|
b982be5ff5 | ||
|
|
2d154ee640 | ||
|
|
49810eb153 | ||
|
|
85b88b8749 | ||
|
|
239c8b5172 | ||
|
|
8c800dc178 | ||
|
|
cdd068d99a | ||
|
|
48fa10b080 | ||
|
|
a1dbec0fcb | ||
|
|
abe668f1c3 | ||
|
|
77440c235d | ||
|
|
fd1ee48dbe | ||
|
|
205e807b66 | ||
|
|
34295adb05 | ||
|
|
7c212bef63 | ||
|
|
76f95644b7 | ||
|
|
928b90d5bc | ||
|
|
09c6c2a4f3 | ||
|
|
2b2bd733e9 | ||
|
|
0d2d7e5e71 | ||
|
|
6142e93252 | ||
|
|
ccec3376ba | ||
|
|
f497a74ec4 | ||
|
|
0e666e7d6a | ||
|
|
f498fabd27 | ||
|
|
8b49a55442 | ||
|
|
a26eb942a9 | ||
|
|
eabcafa516 | ||
|
|
6cc388c1bc | ||
|
|
62e39ddfbd | ||
|
|
80373623cd | ||
|
|
451c3945f0 | ||
|
|
00cb15d9b4 | ||
|
|
67dd59125e | ||
|
|
b6800dd125 | ||
|
|
dc9da69509 | ||
|
|
d7d964bf57 | ||
|
|
bcfe44db54 | ||
|
|
376bf6ba72 | ||
|
|
f651baab25 | ||
|
|
61752e2aab | ||
|
|
b7d3fd959e | ||
|
|
7ac05f8487 | ||
|
|
4f70822b13 | ||
|
|
0007f304d0 | ||
|
|
4afef91359 | ||
|
|
815b6db0bf | ||
|
|
433d3bf582 | ||
|
|
a335841509 | ||
|
|
26ad793d82 | ||
|
|
5337aa10f7 | ||
|
|
9f79bdae9b | ||
|
|
db84be2488 | ||
|
|
599ec62bb0 | ||
|
|
19a88300c6 | ||
|
|
b5d55e1ffb | ||
|
|
521fb83e38 | ||
|
|
553d59c32b | ||
|
|
9ed58e5186 | ||
|
|
36eaaa748c | ||
|
|
4d7b86ca26 | ||
|
|
5faf84c732 | ||
|
|
d7b819267f | ||
|
|
7417867d0f | ||
|
|
8d74905257 | ||
|
|
c38298c06e | ||
|
|
3100d587d1 | ||
|
|
ba849d0300 | ||
|
|
95df3e4b39 | ||
|
|
72492e33a0 | ||
|
|
934f1269f5 | ||
|
|
e6956d9bb0 | ||
|
|
2877900233 | ||
|
|
df1aa52e08 | ||
|
|
5fa2485a7d | ||
|
|
29f0e10411 | ||
|
|
39300a5bbf | ||
|
|
35d6268675 | ||
|
|
0abb871f3f | ||
|
|
704fca969f | ||
|
|
95debf8c80 | ||
|
|
dd94b77b2a | ||
|
|
6cfe4fa580 | ||
|
|
515c086099 | ||
|
|
34ce06ac17 | ||
|
|
a104c9881e | ||
|
|
c3e3c091cc | ||
|
|
651240113c | ||
|
|
77189bf8e9 | ||
|
|
60fd3a4542 | ||
|
|
c66f9c8d6d | ||
|
|
569088eaca | ||
|
|
a7e8dd04fe | ||
|
|
dfdd76a1bb | ||
|
|
28aac6f93b | ||
|
|
c2f47119ce | ||
|
|
d6b1d0d4fb | ||
|
|
03a861745b | ||
|
|
9a7aed20e9 | ||
|
|
b7f17d4cb1 | ||
|
|
2497c3d187 | ||
|
|
f7a084969a | ||
|
|
2900bc26a5 | ||
|
|
2334c56a96 | ||
|
|
90a5d02bf6 | ||
|
|
81d9626da9 | ||
|
|
44a2549b81 | ||
|
|
a2b8d468bc | ||
|
|
d523f0cadd | ||
|
|
99116ff097 | ||
|
|
3939ca9eb4 | ||
|
|
b5aa67b491 | ||
|
|
e42efec220 | ||
|
|
9d06aa2f6a | ||
|
|
80765a797b | ||
|
|
0b5509a1ed | ||
|
|
478d7b4a83 | ||
|
|
9d3b38141a | ||
|
|
ab3a4d902e | ||
|
|
5eab5f2437 | ||
|
|
80f632c19a | ||
|
|
6e4f18543d | ||
|
|
54586c9076 | ||
|
|
351c899807 | ||
|
|
fe45d431d7 | ||
|
|
488c2f6d05 | ||
|
|
75ab0909b3 | ||
|
|
8f82d563c1 | ||
|
|
9bbce5dba6 | ||
|
|
099adab9ed | ||
|
|
c8cbf425ac | ||
|
|
ad9c11cd92 | ||
|
|
3872c2a3f5 | ||
|
|
e6a09b49c9 | ||
|
|
db107602bd | ||
|
|
a6558a61a7 | ||
|
|
254d2b82b3 | ||
|
|
97d9795fc9 | ||
|
|
54071b0e5d | ||
|
|
925e8316c7 | ||
|
|
99e1b74023 | ||
|
|
7d68ccca53 | ||
|
|
a090114066 | ||
|
|
a204841abb | ||
|
|
cc451809cc | ||
|
|
a605e4bab6 | ||
|
|
3f0534134d | ||
|
|
3acfb129cd | ||
|
|
6ccef66920 | ||
|
|
e9fa4e94a6 | ||
|
|
fecc0c4640 | ||
|
|
b759294975 | ||
|
|
a23e845c03 | ||
|
|
cb8373e487 | ||
|
|
8e919ddc8e | ||
|
|
832a9ab6b5 | ||
|
|
13732ac333 | ||
|
|
3e932a55f4 | ||
|
|
74e8610ec9 | ||
|
|
089a60ded6 | ||
|
|
c8eeefe194 | ||
|
|
85eeba14c1 | ||
|
|
f6f45eab39 | ||
|
|
a74065f775 | ||
|
|
48d02f7e09 | ||
|
|
e60549f8df | ||
|
|
41f4f4713e | ||
|
|
213a292fd5 |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -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
0
.gitmodules
vendored
Normal file
@@ -4,7 +4,7 @@ cache:
|
||||
os:
|
||||
- linux
|
||||
#- osx
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
sudo: required
|
||||
compiler:
|
||||
- g++
|
||||
|
||||
71
ChangeLog
71
ChangeLog
@@ -1,6 +1,77 @@
|
||||
# for this file format description,
|
||||
# see https://github.com/olivierlacan/keep-a-changelog
|
||||
|
||||
## [2.31.0] - 2020-04-10
|
||||
### Added
|
||||
- NTCP2 through HTTP proxy
|
||||
- Publish LeaseSet2 for I2CP destinations
|
||||
- Show status page on main activity for android
|
||||
- Handle ECIESFlag in DatabaseLookup at floodfill
|
||||
- C++17 features for eligible compilers
|
||||
### Changed
|
||||
- Droped Websockets and Lua support
|
||||
- Send DeliveryStatusMsg for LeaseSet for ECIES-X25519-AEAD-Ratchet
|
||||
- Keep sending new session reply until established for ECIES-X25519-AEAD-Ratchet
|
||||
- Updated SSU log messages
|
||||
- Reopen SSU socket on exception
|
||||
- Security hardening headers in web console
|
||||
- Various web console changes
|
||||
- Various QT changes
|
||||
### Fixed
|
||||
- NTCP2 socket descriptors leak
|
||||
- Race condition with router's identity in transport sessions
|
||||
- Not terminated streams remain forever
|
||||
|
||||
## [2.30.0] - 2020-02-25
|
||||
### Added
|
||||
- Single threaded SAM
|
||||
- Experimental support of ECIES-X25519-AEAD-Ratchet crypto type
|
||||
### Changed
|
||||
- Minimal MTU size is 1280 for ipv6
|
||||
- Use unordered_map instead map for destination's sessions and tags list
|
||||
- Use std::shuffle instead std::random_shuffle
|
||||
- SAM is single threaded by default
|
||||
- Reseeds list
|
||||
### Fixed
|
||||
- Correct termination of streaming destination
|
||||
- Extra ',' in RouterInfo response in I2PControl
|
||||
- SAM crash on session termination
|
||||
- Storage for Android 10
|
||||
|
||||
## [2.29.0] - 2019-10-21
|
||||
### Added
|
||||
- Client auth flag for b33 address
|
||||
### Changed
|
||||
- Remove incoming NTCP2 session from pending list when established
|
||||
- Handle errors for NTCP2 SessionConfrimed send
|
||||
### Fixed
|
||||
- Failure to start on Windows XP
|
||||
- SAM crash if invalid lookup address
|
||||
- Possible crash when UPnP enabled on shutdown
|
||||
|
||||
## [2.28.0] - 2019-08-27
|
||||
### Added
|
||||
- RAW datagrams in SAM
|
||||
- Publishing encrypted LeaseSet2 with DH or PSH authentication
|
||||
- Ability to disable battery optimization for Android
|
||||
- Transport Network ID Check
|
||||
### Changed
|
||||
- Set and handle published encrypted flag for LeaseSet2
|
||||
### Fixed
|
||||
- ReceiveID changes in the same stream
|
||||
- "\r\n" command terminator in SAM
|
||||
- Addressbook lines with signatures
|
||||
|
||||
## [2.27.0] - 2019-07-03
|
||||
### Added
|
||||
- Support of PSK and DH authentication for encrypted LeaseSet2
|
||||
### Changed
|
||||
- Uptime is based on monotonic timer
|
||||
### Fixed
|
||||
- BOB status command response
|
||||
- Correct NTCP2 port if NTCP is disabled
|
||||
- Flood encrypted LeaseSet2 with store hash
|
||||
|
||||
## [2.26.0] - 2019-06-07
|
||||
### Added
|
||||
- HTTP method "PROPFIND"
|
||||
|
||||
4
Makefile
4
Makefile
@@ -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)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# set defaults instead redefine
|
||||
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
|
||||
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation -Wno-psabi
|
||||
LDFLAGS ?= ${LD_DEBUG}
|
||||
|
||||
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
|
||||
@@ -15,13 +15,14 @@ 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
|
||||
|
||||
@@ -37,6 +37,10 @@ ifeq ($(USE_WIN32_APP), yes)
|
||||
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
||||
endif
|
||||
|
||||
ifeq ($(USE_WINXP_FLAGS), yes)
|
||||
CXXFLAGS += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
|
||||
endif
|
||||
|
||||
# don't change following line to ifeq ($(USE_AESNI),yes) !!!
|
||||
ifeq ($(USE_AESNI),1)
|
||||
CPU_FLAGS += -maes
|
||||
|
||||
@@ -2,6 +2,9 @@ CXX = clang++
|
||||
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++11 -DMAC_OSX
|
||||
INCFLAGS = -I/usr/local/include
|
||||
LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
LDFLAGS += -Wl,-dead_strip
|
||||
LDFLAGS += -Wl,-dead_strip_dylibs
|
||||
LDFLAGS += -Wl,-bind_at_load
|
||||
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
|
||||
|
||||
10
README.md
10
README.md
@@ -1,5 +1,7 @@
|
||||

|
||||

|
||||
[](https://github.com/PurpleI2P/i2pd/releases/latest)
|
||||
[](https://snapcraft.io/i2pd)
|
||||
[](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE)
|
||||
[](https://repology.org/project/i2pd/versions)
|
||||
|
||||
i2pd
|
||||
====
|
||||
@@ -62,10 +64,12 @@ Build instructions:
|
||||
**Supported systems:**
|
||||
|
||||
* GNU/Linux - [](https://travis-ci.org/PurpleI2P/i2pd)
|
||||
* CentOS / Fedora / Mageia - [](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
|
||||
* Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc.
|
||||
* Windows - [](https://ci.appveyor.com/project/PurpleI2P/i2pd)
|
||||
* Mac OS X - [](https://travis-ci.org/PurpleI2P/i2pd)
|
||||
* CentOS / Fedora / Mageia - [](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
|
||||
* Docker image - [](https://hub.docker.com/r/meeh/i2pd/builds/)
|
||||
* Snap - [](https://build.snapcraft.io/user/PurpleI2P/i2pd-snap)
|
||||
* FreeBSD
|
||||
* Android
|
||||
* iOS
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
#include "Win32App.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#define ID_ABOUT 2000
|
||||
#define ID_EXIT 2001
|
||||
#define ID_CONSOLE 2002
|
||||
@@ -39,6 +35,9 @@ namespace i2p
|
||||
namespace win32
|
||||
{
|
||||
static DWORD GracefulShutdownEndtime = 0;
|
||||
|
||||
typedef DWORD (* IPN)();
|
||||
IPN GetTickCountLocal = (IPN)GetProcAddress (GetModuleHandle ("KERNEL32.dll"), "GetTickCount");
|
||||
|
||||
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
|
||||
{
|
||||
@@ -53,7 +52,7 @@ namespace win32
|
||||
ID_ACCEPT_TRANSIT, "Accept &transit");
|
||||
else
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload configs");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config");
|
||||
if (!i2p::util::DaemonWin32::Instance ().isGraceful)
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
|
||||
else
|
||||
@@ -161,7 +160,7 @@ namespace win32
|
||||
s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
|
||||
if (GracefulShutdownEndtime != 0)
|
||||
{
|
||||
DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCount()) / 1000;
|
||||
DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCountLocal()) / 1000;
|
||||
s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft);
|
||||
}
|
||||
else
|
||||
@@ -239,7 +238,7 @@ namespace win32
|
||||
i2p::context.SetAcceptsTunnels (false);
|
||||
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
|
||||
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
|
||||
GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
|
||||
GracefulShutdownEndtime = GetTickCountLocal() + 10*60*1000;
|
||||
i2p::util::DaemonWin32::Instance ().isGraceful = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#define I2Pd_AppName "i2pd"
|
||||
#define I2Pd_ver "2.26.0"
|
||||
#define I2Pd_ver "2.31.0"
|
||||
#define I2Pd_Publisher "PurpleI2P"
|
||||
|
||||
[Setup]
|
||||
|
||||
4
android/.gitignore
vendored
4
android/.gitignore
vendored
@@ -12,5 +12,5 @@ local.properties
|
||||
build.sh
|
||||
android.iml
|
||||
build
|
||||
|
||||
|
||||
*.iml
|
||||
*.local
|
||||
|
||||
@@ -9,12 +9,15 @@
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/icon"
|
||||
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
19
android/README.md
Normal 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
|
||||
@@ -5,7 +5,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.3.2'
|
||||
classpath 'com.android.tools.build:gradle:3.4.2'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,21 +16,23 @@ repositories {
|
||||
maven {
|
||||
url 'https://maven.google.com'
|
||||
}
|
||||
google()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.android.support:support-compat:28.0.0'
|
||||
implementation 'androidx.core:core:1.0.2'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "28.0.3"
|
||||
defaultConfig {
|
||||
applicationId "org.purplei2p.i2pd"
|
||||
targetSdkVersion 28
|
||||
targetSdkVersion 29
|
||||
minSdkVersion 14
|
||||
versionCode 2260
|
||||
versionName "2.26.0"
|
||||
versionCode 2310
|
||||
versionName "2.31.0"
|
||||
setProperty("archivesBaseName", archivesBaseName + "-" + versionName)
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a'
|
||||
abiFilters 'x86'
|
||||
@@ -55,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
|
||||
}
|
||||
}
|
||||
@@ -71,7 +74,7 @@ android {
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
minifyEnabled false
|
||||
signingConfig signingConfigs.orignal
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
|
||||
}
|
||||
@@ -81,4 +84,21 @@ android {
|
||||
path './jni/Android.mk'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = '1.8'
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
org.gradle.parallel=true
|
||||
@@ -1,6 +1,6 @@
|
||||
#Thu Mar 14 18:21:08 MSK 2019
|
||||
#Tue Aug 20 14:39:08 MSK 2019
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-28
|
||||
target=android-29
|
||||
|
||||
13
android/res/layout/webview.xml
Normal file
13
android/res/layout/webview.xml
Normal 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>
|
||||
@@ -3,14 +3,23 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".I2PDActivity">
|
||||
<item
|
||||
android:id="@+id/action_graceful_stop"
|
||||
android:title="@string/action_graceful_stop"
|
||||
android:orderInCategory="98"
|
||||
/>
|
||||
<item
|
||||
android:id="@+id/action_stop"
|
||||
android:title="@string/action_stop"
|
||||
android:orderInCategory="99"
|
||||
/>
|
||||
<group android:id="@+id/group_i2pd_control" >
|
||||
<item
|
||||
android:id="@+id/action_stop"
|
||||
android:orderInCategory="99"
|
||||
android:title="@string/action_stop" />
|
||||
<item
|
||||
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
|
||||
android:id="@+id/action_battery_otimizations"
|
||||
android:title="@string/menu_item_battery_optimizations_str" />
|
||||
</group>
|
||||
</menu>
|
||||
|
||||
@@ -17,4 +17,12 @@
|
||||
<string name="remaining">осталось</string>
|
||||
<string name="title_activity_i2_pdperms_asker_prompt">Запрос</string>
|
||||
<string name="permDenied">Права для записи на SD карту отклонены, вам необходимо предоставить их для продолжения</string>
|
||||
<string name="menu_item_battery_optimizations_str">Оптимизации аккумулятора</string>
|
||||
<string name="battery_optimizations_enabled">Оптимизации аккумулятора включены</string>
|
||||
<string name="device_does_not_support_disabling_battery_optimizations">Ваша версия Андроид не поддерживает отключение оптимизаций аккумулятора</string>
|
||||
<string name="battery_optimizations_enabled_explained">Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\nРекомендуется отключить эти оптимизации.</string>
|
||||
<string name="battery_optimizations_enabled_dialog" >Ваша операционная система осуществляет оптимизации расхода аккумулятора, которые могут приводить к выгрузке I2PD из памяти и прекращению его работы с целью сэкономить заряд аккумулятора.\n\nВам сейчас будет предложено разрешить отключение этих оптимизаций.</string>
|
||||
<string name="continue_str">Продолжить</string>
|
||||
<string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Ваша версия Андроид не поддерживает показ диалога об оптимизациях аккумулятора для приложений.</string>
|
||||
<string name="shutdown_canceled">Плановая остановка отменена</string>
|
||||
</resources>
|
||||
|
||||
@@ -17,4 +17,13 @@
|
||||
<string name="remaining">remaining</string>
|
||||
<string name="title_activity_i2_pdperms_asker_prompt">Prompt</string>
|
||||
<string name="permDenied">SD card write permission denied, you need to allow this to continue</string>
|
||||
<string name="battery_optimizations_enabled">Battery optimizations enabled</string>
|
||||
<string name="battery_optimizations_enabled_explained">Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\nIt is recommended to allow disabling those battery optimizations.</string>
|
||||
<string name="battery_optimizations_enabled_dialog" >Your Android is doing some heavy battery optimizations on I2PD that might lead to daemon closing with no other reason.\n\nYou will now be asked to allow to disable those.</string>
|
||||
<string name="continue_str">Continue</string>
|
||||
<string name="device_does_not_support_disabling_battery_optimizations">Your Android version does not support opting out of battery optimizations</string>
|
||||
<string name="menu_item_battery_optimizations_str">Battery Optimizations</string>
|
||||
<string name="os_version_does_not_support_battery_optimizations_show_os_dialog_api">Your Android OS version does not support showing the dialog for battery optimizations for applications.</string>
|
||||
<string name="shutdown_canceled">Planned shutdown canceled</string>
|
||||
<string name="action_start_webview">Start webview</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.purplei2p.i2pd;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
@@ -11,10 +10,9 @@ import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class ForegroundService extends Service {
|
||||
private static final String TAG="FgService";
|
||||
@@ -112,14 +110,15 @@ public class ForegroundService extends Service {
|
||||
|
||||
// If earlier version channel ID is not used
|
||||
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
|
||||
String channelId = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) ? createNotificationChannel() : "";
|
||||
String channelId = Build.VERSION.SDK_INT >= 26 ? createNotificationChannel() : "";
|
||||
|
||||
// Set the info for the views that show in the notification panel.
|
||||
Notification notification = new NotificationCompat.Builder(this, channelId)
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.drawable.itoopie_notification_icon) // the status icon
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setCategory(Notification.CATEGORY_SERVICE)
|
||||
.setSmallIcon(R.drawable.itoopie_notification_icon); // the status icon
|
||||
if(Build.VERSION.SDK_INT >= 16) builder = builder.setPriority(Notification.PRIORITY_DEFAULT);
|
||||
if(Build.VERSION.SDK_INT >= 21) builder = builder.setCategory(Notification.CATEGORY_SERVICE);
|
||||
Notification notification = builder
|
||||
.setTicker(text) // the status text
|
||||
.setWhen(System.currentTimeMillis()) // the time stamp
|
||||
.setContentTitle(getText(R.string.app_name)) // the label of the entry
|
||||
@@ -141,9 +140,10 @@ public class ForegroundService extends Service {
|
||||
//chan.setLightColor(Color.PURPLE);
|
||||
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
|
||||
NotificationManager service = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
service.createNotificationChannel(chan);
|
||||
if(service!=null)service.createNotificationChannel(chan);
|
||||
else Log.e(TAG, "error: NOTIFICATION_SERVICE is null");
|
||||
return channelId;
|
||||
}
|
||||
|
||||
private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
|
||||
private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
|
||||
}
|
||||
|
||||
@@ -14,36 +14,57 @@ import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
// For future package update checking
|
||||
import org.purplei2p.i2pd.BuildConfig;
|
||||
|
||||
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;
|
||||
public static final String PACKAGE_URI_SCHEME = "package:";
|
||||
|
||||
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();
|
||||
|
||||
@@ -53,32 +74,27 @@ public class I2PDActivity extends Activity {
|
||||
public void daemonStateUpdate()
|
||||
{
|
||||
processAssets();
|
||||
runOnUiThread(new Runnable(){
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if(textView==null) return;
|
||||
Throwable tr = daemon.getLastThrowable();
|
||||
if(tr!=null) {
|
||||
textView.setText(throwableToString(tr));
|
||||
return;
|
||||
}
|
||||
DaemonSingleton.State state = daemon.getState();
|
||||
textView.setText(
|
||||
String.valueOf(getText(state.getStatusStringResourceId()))+
|
||||
(DaemonSingleton.State.startFailed.equals(state) ? ": "+daemon.getDaemonStartResult() : "")+
|
||||
(DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? ": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining) : "")
|
||||
);
|
||||
} catch (Throwable tr) {
|
||||
Log.e(TAG,"error ignored",tr);
|
||||
}
|
||||
}
|
||||
});
|
||||
runOnUiThread(() -> {
|
||||
try {
|
||||
if(textView==null) return;
|
||||
Throwable tr = daemon.getLastThrowable();
|
||||
if(tr!=null) {
|
||||
textView.setText(throwableToString(tr));
|
||||
return;
|
||||
}
|
||||
DaemonSingleton.State state = daemon.getState();
|
||||
String startResultStr = DaemonSingleton.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : "";
|
||||
String graceStr = DaemonSingleton.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : "";
|
||||
textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr));
|
||||
} catch (Throwable tr) {
|
||||
Log.e(TAG,"error ignored",tr);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
private static volatile long graceStartedMillis;
|
||||
private static final Object graceStartedMillis_LOCK=new Object();
|
||||
private Menu optionsMenu;
|
||||
|
||||
private static String formatGraceTimeRemaining() {
|
||||
long remainingSeconds;
|
||||
@@ -92,6 +108,7 @@ public class I2PDActivity extends Activity {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
Log.i(TAG, "onCreate");
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
textView = new TextView(this);
|
||||
@@ -121,6 +138,8 @@ public class I2PDActivity extends Activity {
|
||||
}
|
||||
rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis);
|
||||
}
|
||||
|
||||
openBatteryOptimizationDialogIfNeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,7 +147,7 @@ public class I2PDActivity extends Activity {
|
||||
super.onDestroy();
|
||||
textView = null;
|
||||
daemon.removeStateChangeListener(daemonStateUpdatedListener);
|
||||
//cancelGracefulStop();
|
||||
//cancelGracefulStop0();
|
||||
try{
|
||||
doUnbindService();
|
||||
}catch(Throwable tr){
|
||||
@@ -137,24 +156,20 @@ public class I2PDActivity extends Activity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
|
||||
{
|
||||
switch (requestCode)
|
||||
{
|
||||
case MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE:
|
||||
{
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
|
||||
Log.e(TAG, "Memory permission granted");
|
||||
else
|
||||
Log.e(TAG, "Memory permission declined");
|
||||
// TODO: terminate
|
||||
return;
|
||||
}
|
||||
default: ;
|
||||
}
|
||||
if (requestCode == MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
|
||||
Log.e(TAG, "WR_EXT_STORAGE perm granted");
|
||||
else {
|
||||
Log.e(TAG, "WR_EXT_STORAGE perm declined, stopping i2pd");
|
||||
i2pdStop();
|
||||
//TODO must work w/o this perm, ask orignal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void cancelGracefulStop() {
|
||||
private void cancelGracefulStop0() {
|
||||
Timer gracefulQuitTimer = getGracefulQuitTimer();
|
||||
if(gracefulQuitTimer!=null) {
|
||||
gracefulQuitTimer.cancel();
|
||||
@@ -225,11 +240,17 @@ public class I2PDActivity extends Activity {
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.options_main, menu);
|
||||
menu.findItem(R.id.action_battery_otimizations).setVisible(isBatteryOptimizationsOpenOsDialogApiAvailable());
|
||||
this.optionsMenu = menu;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isBatteryOptimizationsOpenOsDialogApiAvailable() {
|
||||
return android.os.Build.VERSION.SDK_INT >= 23;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
// Handle action bar item clicks here. The action bar will
|
||||
// automatically handle clicks on the Home/Up button, so long
|
||||
// as you specify a parent activity in AndroidManifest.xml.
|
||||
@@ -240,37 +261,53 @@ public class I2PDActivity extends Activity {
|
||||
i2pdStop();
|
||||
return true;
|
||||
case R.id.action_graceful_stop:
|
||||
if (getGracefulQuitTimer()!= null)
|
||||
{
|
||||
item.setTitle(R.string.action_graceful_stop);
|
||||
i2pdCancelGracefulStop ();
|
||||
synchronized (graceStartedMillis_LOCK) {
|
||||
if (getGracefulQuitTimer() != null)
|
||||
cancelGracefulStop();
|
||||
else
|
||||
i2pdGracefulStop();
|
||||
}
|
||||
else
|
||||
{
|
||||
item.setTitle(R.string.action_cancel_graceful_stop);
|
||||
i2pdGracefulStop();
|
||||
}
|
||||
return true;
|
||||
return true;
|
||||
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);
|
||||
}
|
||||
|
||||
private void i2pdStop() {
|
||||
cancelGracefulStop();
|
||||
new Thread(new Runnable(){
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(TAG, "stopping");
|
||||
try{
|
||||
daemon.stopDaemon();
|
||||
}catch (Throwable tr) {
|
||||
Log.e(TAG, "", tr);
|
||||
}
|
||||
private void onActionBatteryOptimizations() {
|
||||
if (isBatteryOptimizationsOpenOsDialogApiAvailable()) {
|
||||
try {
|
||||
startActivity(new Intent(ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS));
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(TAG,"BATT_OPTIM_DIALOG_ActvtNotFound", e);
|
||||
Toast.makeText(this, R.string.os_version_does_not_support_battery_optimizations_show_os_dialog_api, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},"stop").start();
|
||||
private void i2pdStop() {
|
||||
cancelGracefulStop0();
|
||||
new Thread(() -> {
|
||||
Log.d(TAG, "stopping");
|
||||
try {
|
||||
daemon.stopDaemon();
|
||||
} catch (Throwable tr) {
|
||||
Log.e(TAG, "", tr);
|
||||
}
|
||||
quit(); //TODO make menu items for starting i2pd. On my Android, I need to reboot the OS to restart i2pd.
|
||||
},"stop").start();
|
||||
}
|
||||
|
||||
private static volatile Timer gracefulQuitTimer;
|
||||
@@ -288,55 +325,45 @@ public class I2PDActivity extends Activity {
|
||||
}
|
||||
Toast.makeText(this, R.string.graceful_stop_is_in_progress,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
new Thread(new Runnable(){
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Log.d(TAG, "grac stopping");
|
||||
if(daemon.isStartedOkay()) {
|
||||
daemon.stopAcceptingTunnels();
|
||||
long gracefulStopAtMillis;
|
||||
synchronized (graceStartedMillis_LOCK) {
|
||||
graceStartedMillis = System.currentTimeMillis();
|
||||
gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
|
||||
}
|
||||
rescheduleGraceStop(null,gracefulStopAtMillis);
|
||||
} else {
|
||||
i2pdStop();
|
||||
}
|
||||
} catch(Throwable tr) {
|
||||
Log.e(TAG,"",tr);
|
||||
}
|
||||
}
|
||||
|
||||
},"gracInit").start();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Log.d(TAG, "grac stopping");
|
||||
if(daemon.isStartedOkay()) {
|
||||
daemon.stopAcceptingTunnels();
|
||||
long gracefulStopAtMillis;
|
||||
synchronized (graceStartedMillis_LOCK) {
|
||||
graceStartedMillis = System.currentTimeMillis();
|
||||
gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS;
|
||||
}
|
||||
rescheduleGraceStop(null,gracefulStopAtMillis);
|
||||
} else {
|
||||
i2pdStop();
|
||||
}
|
||||
} catch(Throwable tr) {
|
||||
Log.e(TAG,"",tr);
|
||||
}
|
||||
},"gracInit").start();
|
||||
}
|
||||
|
||||
private void i2pdCancelGracefulStop()
|
||||
private void cancelGracefulStop()
|
||||
{
|
||||
cancelGracefulStop();
|
||||
Toast.makeText(this, R.string.startedOkay, Toast.LENGTH_SHORT).show();
|
||||
new Thread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.d(TAG, "grac stopping cancel");
|
||||
if(daemon.isStartedOkay())
|
||||
daemon.startAcceptingTunnels();
|
||||
else
|
||||
i2pdStop();
|
||||
cancelGracefulStop0();
|
||||
new Thread(() -> {
|
||||
try
|
||||
{
|
||||
Log.d(TAG, "canceling grac stop");
|
||||
if(daemon.isStartedOkay()) {
|
||||
daemon.startAcceptingTunnels();
|
||||
runOnUiThread(() -> Toast.makeText(this, R.string.shutdown_canceled, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
catch(Throwable tr)
|
||||
{
|
||||
Log.e(TAG,"",tr);
|
||||
}
|
||||
}
|
||||
|
||||
},"gracCancel").start();
|
||||
else
|
||||
i2pdStop();
|
||||
}
|
||||
catch(Throwable tr)
|
||||
{
|
||||
Log.e(TAG,"",tr);
|
||||
}
|
||||
},"gracCancel").start();
|
||||
}
|
||||
|
||||
private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) {
|
||||
@@ -364,8 +391,19 @@ public class I2PDActivity extends Activity {
|
||||
return gracefulQuitTimer;
|
||||
}
|
||||
|
||||
private static void setGracefulQuitTimer(Timer gracefulQuitTimer) {
|
||||
private void setGracefulQuitTimer(Timer gracefulQuitTimer) {
|
||||
I2PDActivity.gracefulQuitTimer = gracefulQuitTimer;
|
||||
runOnUiThread(()-> {
|
||||
Menu menu = optionsMenu;
|
||||
if (menu != null) {
|
||||
MenuItem item = menu.findItem(R.id.action_graceful_stop);
|
||||
if (item != null) {
|
||||
synchronized (graceStartedMillis_LOCK) {
|
||||
item.setTitle(getGracefulQuitTimer() != null ? R.string.action_cancel_graceful_stop : R.string.action_graceful_stop);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,19 +426,22 @@ public class I2PDActivity extends Activity {
|
||||
// to a file. That doesn't appear to be the case. If the returned array is
|
||||
// null or has 0 length, we assume the path is to a file. This means empty
|
||||
// directories will get turned into files.
|
||||
if (contents == null || contents.length == 0)
|
||||
throw new IOException();
|
||||
if (contents == null || contents.length == 0) {
|
||||
copyFileAsset(path);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make the directory.
|
||||
File dir = new File(i2pdpath, path);
|
||||
dir.mkdirs();
|
||||
boolean result = dir.mkdirs();
|
||||
Log.d(TAG, "dir.mkdirs() returned " + result);
|
||||
|
||||
// Recurse on the contents.
|
||||
for (String entry : contents) {
|
||||
copyAsset(path + "/" + entry);
|
||||
copyAsset(path + '/' + entry);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
copyFileAsset(path);
|
||||
Log.e(TAG, "ex ignored for path='" + path + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,63 +454,89 @@ public class I2PDActivity extends Activity {
|
||||
*/
|
||||
private void copyFileAsset(String path) {
|
||||
File file = new File(i2pdpath, path);
|
||||
if(!file.exists()) try {
|
||||
InputStream in = getAssets().open(path);
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
byte[] buffer = new byte[1024];
|
||||
int read = in.read(buffer);
|
||||
while (read != -1) {
|
||||
out.write(buffer, 0, read);
|
||||
read = in.read(buffer);
|
||||
if(!file.exists()) {
|
||||
try {
|
||||
try (InputStream in = getAssets().open(path) ) {
|
||||
try (OutputStream out = new FileOutputStream(file)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int read = in.read(buffer);
|
||||
while (read != -1) {
|
||||
out.write(buffer, 0, read);
|
||||
read = in.read(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
out.close();
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteRecursive(File fileOrDirectory) {
|
||||
if (fileOrDirectory.isDirectory()) {
|
||||
for (File child : fileOrDirectory.listFiles()) {
|
||||
deleteRecursive(child);
|
||||
File[] files = fileOrDirectory.listFiles();
|
||||
if(files!=null) {
|
||||
for (File child : files) {
|
||||
deleteRecursive(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
fileOrDirectory.delete();
|
||||
boolean deleteResult = fileOrDirectory.delete();
|
||||
if(!deleteResult)Log.e(TAG, "fileOrDirectory.delete() returned "+deleteResult+", absolute path='"+fileOrDirectory.getAbsolutePath()+"'");
|
||||
}
|
||||
|
||||
private void processAssets() {
|
||||
if (!assetsCopied) try {
|
||||
assetsCopied = true; // prevent from running on every state update
|
||||
|
||||
File holderfile = new File(i2pdpath, "assets.ready");
|
||||
File holderFile = new File(i2pdpath, "assets.ready");
|
||||
String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
if (holderfile.exists()) try { // if holder file exists, read assets version string
|
||||
BufferedReader br = new BufferedReader(new FileReader(holderfile));
|
||||
String line;
|
||||
if (holderFile.exists()) {
|
||||
try { // if holder file exists, read assets version string
|
||||
FileReader fileReader = new FileReader(holderFile);
|
||||
|
||||
while ((line = br.readLine()) != null) {
|
||||
text.append(line);
|
||||
}
|
||||
br.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(fileReader);
|
||||
|
||||
try {
|
||||
String line;
|
||||
|
||||
while ((line = br.readLine()) != null) {
|
||||
text.append(line);
|
||||
}
|
||||
}finally {
|
||||
try{
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try{
|
||||
fileReader.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
}
|
||||
|
||||
// if version differs from current app version or null, try to delete certificates folder
|
||||
if (!text.toString().contains(versionName)) try {
|
||||
holderfile.delete();
|
||||
File certpath = new File(i2pdpath, "certificates");
|
||||
deleteRecursive(certpath);
|
||||
boolean deleteResult = holderFile.delete();
|
||||
if(!deleteResult)Log.e(TAG, "holderFile.delete() returned "+deleteResult+", absolute path='"+holderFile.getAbsolutePath()+"'");
|
||||
File certPath = new File(i2pdpath, "certificates");
|
||||
deleteRecursive(certPath);
|
||||
}
|
||||
catch (Throwable tr) {
|
||||
Log.e(TAG, "", tr);
|
||||
}
|
||||
|
||||
// copy assets. If processed file exists, it won't be overwrited
|
||||
// copy assets. If processed file exists, it won't be overwritten
|
||||
copyAsset("addressbook");
|
||||
copyAsset("certificates");
|
||||
copyAsset("tunnels.d");
|
||||
@@ -478,14 +545,95 @@ public class I2PDActivity extends Activity {
|
||||
copyAsset("tunnels.conf");
|
||||
|
||||
// update holder file about successful copying
|
||||
FileWriter writer = new FileWriter(holderfile);
|
||||
writer.append(versionName);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
FileWriter writer = new FileWriter(holderFile);
|
||||
try {
|
||||
writer.append(versionName);
|
||||
} finally {
|
||||
try{
|
||||
writer.close();
|
||||
}catch (IOException e){
|
||||
Log.e(TAG,"on writer close", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable tr)
|
||||
{
|
||||
Log.e(TAG,"copy assets",tr);
|
||||
Log.e(TAG,"on assets copying", tr);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("BatteryLife")
|
||||
private void openBatteryOptimizationDialogIfNeeded() {
|
||||
boolean questionEnabled = getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true);
|
||||
Log.i(TAG,"BATT_OPTIM_questionEnabled=="+questionEnabled);
|
||||
if (!isKnownIgnoringBatteryOptimizations()
|
||||
&& android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M
|
||||
&& questionEnabled) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.battery_optimizations_enabled);
|
||||
builder.setMessage(R.string.battery_optimizations_enabled_dialog);
|
||||
builder.setPositiveButton(R.string.continue_str, (dialog, which) -> {
|
||||
try {
|
||||
startActivity(new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse(PACKAGE_URI_SCHEME + getPackageName())));
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(TAG,"BATT_OPTIM_ActvtNotFound", e);
|
||||
Toast.makeText(this, R.string.device_does_not_support_disabling_battery_optimizations, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
builder.setOnDismissListener(dialog -> setNeverAskForBatteryOptimizationsAgain());
|
||||
final AlertDialog dialog = builder.create();
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void setNeverAskForBatteryOptimizationsAgain() {
|
||||
getPreferences().edit().putBoolean(getBatteryOptimizationPreferenceKey(), false).apply();
|
||||
}
|
||||
|
||||
protected boolean isKnownIgnoringBatteryOptimizations() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
final PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
|
||||
if (pm == null) {
|
||||
Log.i(TAG, "BATT_OPTIM: POWER_SERVICE==null");
|
||||
return false;
|
||||
}
|
||||
boolean ignoring = pm.isIgnoringBatteryOptimizations(getPackageName());
|
||||
Log.i(TAG, "BATT_OPTIM: ignoring==" + ignoring);
|
||||
return ignoring;
|
||||
} else {
|
||||
Log.i(TAG, "BATT_OPTIM: old sdk version=="+Build.VERSION.SDK_INT);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected SharedPreferences getPreferences() {
|
||||
return PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
}
|
||||
|
||||
private String getBatteryOptimizationPreferenceKey() {
|
||||
@SuppressLint("HardwareIds") String device = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||
return "show_battery_optimization" + (device == null ? "" : device);
|
||||
}
|
||||
|
||||
private void quit() {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
finishAndRemoveTask();
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
finishAffinity();
|
||||
} else {
|
||||
//moveTaskToBack(true);
|
||||
finish();
|
||||
}
|
||||
}catch (Throwable tr) {
|
||||
Log.e(TAG, "", tr);
|
||||
}
|
||||
try{
|
||||
daemon.stopDaemon();
|
||||
}catch (Throwable tr) {
|
||||
Log.e(TAG, "", tr);
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 2.26.0.{build}
|
||||
version: 2.31.0.{build}
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
branches:
|
||||
@@ -18,9 +18,9 @@ environment:
|
||||
|
||||
install:
|
||||
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc mingw-w64-{i686,x86_64}-gcc-ada mingw-w64-{i686,x86_64}-gcc-objc"
|
||||
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu --force"
|
||||
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
|
||||
|
||||
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu --force"
|
||||
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
|
||||
|
||||
- if "%MSYSTEM%" == "MINGW64" (
|
||||
c:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-boost mingw-w64-x86_64-miniupnpc"
|
||||
|
||||
@@ -19,8 +19,6 @@ option(WITH_GUI "Include GUI (currently MS Windows only)" ON)
|
||||
option(WITH_MESHNET "Build for cjdns test network" OFF)
|
||||
option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF)
|
||||
option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF)
|
||||
option(WITH_I2LUA "Build for i2lua" OFF)
|
||||
option(WITH_WEBSOCKETS "Build with websocket ui" OFF)
|
||||
|
||||
# paths
|
||||
set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" )
|
||||
@@ -75,28 +73,20 @@ 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"
|
||||
"${LIBI2PD_SRC_DIR}/Ed25519.cpp"
|
||||
"${LIBI2PD_SRC_DIR}/NTCP2.cpp"
|
||||
"${LIBI2PD_SRC_DIR}/Blinding.cpp"
|
||||
"${LIBI2PD_SRC_DIR}/Elligator.cpp"
|
||||
"${LIBI2PD_SRC_DIR}/ECIESX25519AEADRatchetSession.cpp"
|
||||
)
|
||||
|
||||
if (WITH_WEBSOCKETS)
|
||||
add_definitions(-DWITH_EVENTS)
|
||||
find_package(websocketpp REQUIRED)
|
||||
endif ()
|
||||
|
||||
if (WIN32 OR MSYS)
|
||||
list (APPEND LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/I2PEndian.cpp")
|
||||
endif ()
|
||||
|
||||
if (WITH_I2LUA)
|
||||
add_definitions(-DI2LUA)
|
||||
endif()
|
||||
|
||||
add_library(libi2pd ${LIBI2PD_SRC})
|
||||
set_target_properties(libi2pd PROPERTIES PREFIX "")
|
||||
|
||||
@@ -122,13 +112,8 @@ 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 "")
|
||||
|
||||
@@ -182,17 +167,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")
|
||||
@@ -424,8 +409,6 @@ message(STATUS " PCH : ${WITH_PCH}")
|
||||
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
|
||||
|
||||
@@ -54,6 +54,14 @@ set bitness=64
|
||||
call :BUILDING
|
||||
echo.
|
||||
|
||||
REM building for WinXP
|
||||
set "WD=C:\msys64-xp\usr\bin\"
|
||||
set MSYSTEM=MINGW32
|
||||
set bitness=32
|
||||
set "xSH=%WD%bash -lc"
|
||||
call :BUILDING_XP
|
||||
echo.
|
||||
|
||||
del README.txt >> nul
|
||||
|
||||
echo Build complete...
|
||||
@@ -71,5 +79,11 @@ echo Build AESNI...
|
||||
%xSH% "make DEBUG=no USE_UPNP=yes USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_aesni.zip %FILELIST% && make clean" > build/build_win%bitness%_aesni_%tag%.log 2>&1
|
||||
echo Build without extensions...
|
||||
%xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST% && make clean" > build/build_win%bitness%_%tag%.log 2>&1
|
||||
goto EOF
|
||||
|
||||
:BUILDING_XP
|
||||
%xSH% "make clean" >> nul
|
||||
echo Building i2pd %tag% for winxp...
|
||||
%xSH% "make DEBUG=no USE_UPNP=yes USE_WINXP_FLAGS=yes -j%threads% && zip -r9 build/i2pd_%tag%_winxp_mingw.zip %FILELIST% && make clean" > build/build_winxp_%tag%.log 2>&1
|
||||
|
||||
:EOF
|
||||
@@ -1,32 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFfTCCA2WgAwIBAgIEOprmhjANBgkqhkiG9w0BAQ0FADBvMQswCQYDVQQGEwJY
|
||||
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
|
||||
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEYMBYGA1UEAwwPYmFja3VwQG1haWwu
|
||||
aTJwMB4XDTEzMTAxMzEzNDQ1NVoXDTIzMTAxMzEzNDQ1NVowbzELMAkGA1UEBhMC
|
||||
WFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFub255
|
||||
bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGDAWBgNVBAMMD2JhY2t1cEBtYWls
|
||||
LmkycDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIoAkobXwk/Enf1d
|
||||
roHyqCyvcJfZJVTwb/LgYWAvCBMCr+RGqlSgtk3g69Y3I0xU08fD2kGt3r5Pwsbr
|
||||
omXIbJAcccyLqmQ5QX6QgL+X9VpMDp9C4h2RogCrqLBAWw4cuZ4RS9VCpP1Yis7H
|
||||
uejYqENP86p7BsRnuW/4cYnfunAdMpss4LpRGQXt1nTX+kfgCYgnKFbFqwAHt7yV
|
||||
Ds+Pe6FuBHPlp+sc1amKRcUnSvhXLsv43VicnT7xYL/kUsN83wrtHA3B4aGDx3aA
|
||||
3/EzuRmIXQB0BlTZILMEyYwG/nc4OsW82QYrvEZ9BIg9A4lF/wS/KZCICPxLF2zo
|
||||
dGjnmlgkiA4s8eO+va/ElHyELjckVXqmG1eXHhSkEsDvOQJy01IUuwLinvq7cUbJ
|
||||
HfJBZJllEg+sLDCv3FkEqN+XjBNFfQN4oNew4w6IPY6YH1INVB9LL0Cmdu4DudLv
|
||||
TY8OcI8eSfez3hmm+pYQ23PJRYYnvRDnRECyIWBegkckWRh8U/WvZUYUvETK6EDl
|
||||
/0KpTtfzX6MqHA5D6bTAB8Y3ijGMLrZ/B5vj5yCoZbLiGme9X2moR2k1LEhdhtzV
|
||||
exsqezCpg6dn48FTX7mHjvR5/r4kz2jqBGmdPUWIIxnjFUzDUK3llVQiHihleHpe
|
||||
jL4LqnhBGKWFRTaVwaIkBG4zAfIzAgMBAAGjITAfMB0GA1UdDgQWBBQNkfW7bSMl
|
||||
1/4KDbgwrkf9x1Zu/TANBgkqhkiG9w0BAQ0FAAOCAgEAGg3a3rTf0EznQocmio0T
|
||||
5gCoL0n8h6yKW/PyPAIELrd9wiYjhJFcWvMTcJJJnVqmAL5vpvhaAFVtAfx70MGa
|
||||
0DZ7FvytK5hEfF4IqOFDyEEVGJR5rIpVK4MeI1nmwEsxdbW+FhODjtRzgYO8XBME
|
||||
Xj4aY1FWg9vxc3reUj6PSFsZtsB0aLiRgL9JDovJIiRw0Uqr1v2wXBte5yVCxDge
|
||||
vTREZtpK4cKetoOa68pwSXI32JwKE18j6bfdKVBCcYQKlKP/3gHGduaDrQv3w32S
|
||||
DRym5s6MREeTUOtAw4wq46KpdOX8yyAqJPrCfMwS6ORd3t+egqOw0PUnsqb97w4O
|
||||
lUtrRYvb2cOj60SmRx4vJvItyuHbKqIK7o2e1RcUZPXYoAVx2ww4XB2Wk4D7LSAs
|
||||
cS7nLj8yAqzJ2qqtBzxu+zILJtkVa12dKF0xmS0BxBp4sCYiBtmAVE8AWQqEuSHA
|
||||
FrMWqoXcjcfdvvyX487FFWWUE7ZBIn0hee2sK9J9+SPtqczJaN7TF3K3nzo65WJG
|
||||
1epltmq2Ugjb67Gz7v4y7H23DJ/qhm8yLtCHTj69HTta5I08j6Kut924WLZaiMO/
|
||||
4YoEL5AE63X0sxYibKFQiq7FW5nUJA280GRlY3xSMFzlB2ggazrUV3YAWVDhfdnI
|
||||
flpzWXkFM2D36OUaubfe9YY=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,34 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF3DCCA8SgAwIBAgIQPxUlcrbHX/xdyJ09E36rJzANBgkqhkiG9w0BAQsFADB3
|
||||
MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK
|
||||
ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEgMB4GA1UEAwwX
|
||||
cmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwHhcNMTgxMjA3MTYzNDIxWhcNMjgxMjA3
|
||||
MTYzNDIxWjB3MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhY
|
||||
MR4wHAYDVQQKExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEg
|
||||
MB4GA1UEAwwXcmVzZWVkaTJwbmV0aW5AbWFpbC5pMnAwggIiMA0GCSqGSIb3DQEB
|
||||
AQUAA4ICDwAwggIKAoICAQC912NDk6x85uqB4gyQQcded0RVrbWehWOanDRv7kC3
|
||||
92jeziPbeMtqrLsfU1MdDtQiGijpNkQ/IIitPw+6vJAIh82gyOUZvsn2XOyb/Fz0
|
||||
Fu8OrDghwl39yK8kwtqCFw3VAgafgKxz2oRge9mxFBECi50vYEPIBwNhr4yc/opu
|
||||
wWUmzmRyX4gD7vKmRU6ZTwX4LXnwdl+5VbW3updcZKsDuTnKvC9FGhDRR9kIk2G9
|
||||
43sLN263nCYPykP7DaB1cUdi1vDEMw5dot+eu16qTIbuypEvYNvbB/9FyCQllm1h
|
||||
vBbSku3IYpcnRPmoeyhoR/MmCySRbK5R4SrSsVD1YBpwxgn0Q4+fzEgFzT9P4oez
|
||||
HkDGKVP2HdgmXx9j36fEqqvjqzRleWDwEWwIZVRLCFO+hhhT3JAjnNGJTWv1SQGB
|
||||
8tz9nyYTJuhvyHE/CO5owFeCdeOGMq2KPge9w34T+mvewTEEhGU8yRAt8Xp8s5Y9
|
||||
RCUGvuQ79+edRtj7FJg7yVB8pAQ+VB9msNQvzrTnPYC9Wo7chJhBiraMiIabzIhC
|
||||
f34Gg9lkX1N0dVND5rnZWwzBM6JhNG1iZZCRHVPnXdZRixUlqmFpCP/eekshksj/
|
||||
6UP/WeGA6X4HyEsC6QEf7eMhcHYjyyTzYagKrwCHg77fmIjF8rmpP2LqWSQW8bDD
|
||||
uQIDAQABo2QwYjAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIG
|
||||
CCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wIAYDVR0OBBkEF3Jlc2VlZGkycG5l
|
||||
dGluQG1haWwuaTJwMA0GCSqGSIb3DQEBCwUAA4ICAQCWpXs6iuTy/w2R7q7Ua6vl
|
||||
JYZwbQ+czk5ydzkBgcNkMMMNRT7sZR9xYvV+ftiL4bFQP/3ZJyo7cYz2Q6+M3oAm
|
||||
YDcZWBkLUVihSlMxhWwmeFTKV2EL+bzwY1V/cy7wgukKnFIes75dLP/v25jgjdlw
|
||||
Xe6R+fQM0EoHeVzzrWk/qYp6oEwtQXfZnUu/Bf45hRnnHBzzh1wCql41vbEs3Niq
|
||||
+SVwY1wLT0yC1L8HqjCLX1/L5PAXxbvEGzwnXSkLKK4bPxdmVDZvS9uzXrWmTbNi
|
||||
HpKIFnOif16zSgyeaOM7HETIJuVzgooUMtt+Vsr1VGdtm6K7I9J5C+rX/ckU8oaX
|
||||
UjmzhWXudN0VTslogsKUCV6xG2CskeE3wnuT8HYXz9NMw6c/kIGH4hY7LcfU8Teu
|
||||
QjSy2RRvy6InmZNV5sY9lzzO6romEycSoUlpCa3Ltb/5KKoYZFTsXr8suqJk89lC
|
||||
e+TVMHqOZdLK/usqQDcafLypHpw9SH2Tg4jrzV/zLqacbjx6bZD5IrpY0Gf7BXg/
|
||||
pikwyA9c490G6ZcWrSEP8bzh6LL2rA2AwxaeJJNVyLHCSLrn/7DezM5J/qhd90Qg
|
||||
kcZGJrUOCSWl6mDvUZn5XiQ955XwOnZQ+wsM85B3CVX22x5bp0SYWHCQBPnthPwP
|
||||
Q5DD3jExbpwG5n35HEcHYw==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -36,8 +36,8 @@ RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib
|
||||
&& cd /usr/local/bin \
|
||||
&& strip i2pd \
|
||||
&& rm -fr /tmp/build && apk --no-cache --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \
|
||||
boost-python3 python3 gdbm boost-unit_test_framework boost-python linux-headers boost-prg_exec_monitor \
|
||||
boost-serialization boost-signals boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \
|
||||
boost-python3 python3 gdbm boost-unit_test_framework linux-headers boost-prg_exec_monitor \
|
||||
boost-serialization boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \
|
||||
libtool g++ gcc pkgconfig
|
||||
|
||||
# 2. Adding required libraries to run i2pd to ensure it will run.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
||||
|
||||
Name: i2pd-git
|
||||
Version: 2.26.0
|
||||
Version: 2.31.0
|
||||
Release: git%{git_hash}%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd
|
||||
@@ -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
|
||||
@@ -110,6 +118,21 @@ getent passwd i2pd >/dev/null || \
|
||||
|
||||
|
||||
%changelog
|
||||
* Fri Apr 10 2020 orignal <i2porignal@yandex.ru> - 2.31.0
|
||||
- update to 2.31.0
|
||||
|
||||
* Tue Feb 25 2020 orignal <i2porignal@yandex.ru> - 2.30.0
|
||||
- update to 2.30.0
|
||||
|
||||
* Mon Oct 21 2019 orignal <i2porignal@yandex.ru> - 2.29.0
|
||||
- update to 2.29.0
|
||||
|
||||
* Tue Aug 27 2019 orignal <i2porignal@yandex.ru> - 2.28.0
|
||||
- update to 2.28.0
|
||||
|
||||
* Wed Jul 3 2019 orignal <i2porignal@yandex.ru> - 2.27.0
|
||||
- update to 2.27.0
|
||||
|
||||
* Fri Jun 7 2019 orignal <i2porignal@yandex.ru> - 2.26.0
|
||||
- update to 2.26.0
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: i2pd
|
||||
Version: 2.26.0
|
||||
Version: 2.31.0
|
||||
Release: 1%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd-git
|
||||
@@ -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
|
||||
@@ -108,6 +116,21 @@ getent passwd i2pd >/dev/null || \
|
||||
|
||||
|
||||
%changelog
|
||||
* Fri Apr 10 2020 orignal <i2porignal@yandex.ru> - 2.31.0
|
||||
- update to 2.31.0
|
||||
|
||||
* Tue Feb 25 2020 orignal <i2porignal@yandex.ru> - 2.30.0
|
||||
- update to 2.30.0
|
||||
|
||||
* Mon Oct 21 2019 orignal <i2porignal@yandex.ru> - 2.29.0
|
||||
- update to 2.29.0
|
||||
|
||||
* Tue Aug 27 2019 orignal <i2porignal@yandex.ru> - 2.28.0
|
||||
- update to 2.28.0
|
||||
|
||||
* Wed Jul 3 2019 orignal <i2porignal@yandex.ru> - 2.27.0
|
||||
- update to 2.27.0
|
||||
|
||||
* Fri Jun 7 2019 orignal <i2porignal@yandex.ru> - 2.26.0
|
||||
- update to 2.26.0
|
||||
|
||||
@@ -124,7 +147,7 @@ getent passwd i2pd >/dev/null || \
|
||||
- update to 2.22.0
|
||||
- add support of tunnelsdir option
|
||||
|
||||
* Thu Oct 22 2018 orignal <i2porignal@yandex.ru> - 2.21.1
|
||||
* Mon Oct 22 2018 orignal <i2porignal@yandex.ru> - 2.21.1
|
||||
- update to 2.21.1
|
||||
|
||||
* Thu Oct 4 2018 orignal <i2porignal@yandex.ru> - 2.21.0
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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> " << 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 << " 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 << " ";
|
||||
s << "</td>\r\n";
|
||||
break;
|
||||
}
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
{
|
||||
s << "<td>SSU";
|
||||
if (address->host.is_v6 ())
|
||||
s << "SSUv6 ";
|
||||
else
|
||||
s << "SSU ";
|
||||
break;
|
||||
s << "v6";
|
||||
s << "</td>\r\n";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
s << "Unknown ";
|
||||
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 ());
|
||||
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";
|
||||
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<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\"> ✘ </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)
|
||||
{
|
||||
// leases information is available
|
||||
auto leases = ls->GetNonExpiredLeases();
|
||||
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
|
||||
for ( auto & l : leases )
|
||||
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)
|
||||
{
|
||||
// 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 << "</p>\r\n</div>\r\n</div>\r\n";
|
||||
}
|
||||
);
|
||||
// end for each lease set
|
||||
s << "</div>\r\n</div>\r\n</div>\r\n";
|
||||
}
|
||||
);
|
||||
// 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 () << " ⇒ ";
|
||||
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
|
||||
s << " ⇒ " << it->GetTunnelID ();
|
||||
else
|
||||
s << " ⇒ " << it->GetTunnelID () << " ⇒ ";
|
||||
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 () << " ⇒ ";
|
||||
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
|
||||
s << " ⇒ " << it->GetTunnelID ();
|
||||
else
|
||||
s << " ⇒ " << it->GetTunnelID () << " ⇒ ";
|
||||
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>: Stream closed<br><br>\r\n";
|
||||
else
|
||||
s << "<b>ERROR</b>: Stream not found or already was closed<br><br>\r\n";
|
||||
}
|
||||
else
|
||||
s << "<b>ERROR</b>: Destination not found<br><br>\r\n";
|
||||
}
|
||||
else
|
||||
s << "<b>ERROR</b>: 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>: 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>";
|
||||
@@ -1047,6 +1141,8 @@ namespace http {
|
||||
void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
|
||||
{
|
||||
reply.add_header("X-Frame-Options", "SAMEORIGIN");
|
||||
reply.add_header("X-Content-Type-Options", "nosniff");
|
||||
reply.add_header("X-XSS-Protection", "1; mode=block");
|
||||
reply.add_header("Content-Type", "text/html");
|
||||
reply.body = content;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -394,13 +394,15 @@ namespace client
|
||||
|
||||
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
bool first = true;
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
|
||||
auto it1 = m_RouterInfoHandlers.find (it->first);
|
||||
if (it1 != m_RouterInfoHandlers.end ())
|
||||
{
|
||||
if (it != params.begin ()) results << ",";
|
||||
if (!first) results << ",";
|
||||
else first = false;
|
||||
(this->*(it1->second))(results);
|
||||
}
|
||||
else
|
||||
|
||||
147
daemon/UPnP.cpp
147
daemon/UPnP.cpp
@@ -79,43 +79,59 @@ namespace transport
|
||||
|
||||
void UPnP::Discover ()
|
||||
{
|
||||
#if MINIUPNPC_API_VERSION >= 14
|
||||
int nerror = 0;
|
||||
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, 2, &nerror);
|
||||
#elif ( MINIUPNPC_API_VERSION >= 8 || defined(UPNPDISCOVER_SUCCESS) )
|
||||
int nerror = 0;
|
||||
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror);
|
||||
bool isError;
|
||||
int err;
|
||||
|
||||
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
|
||||
err = UPNPDISCOVER_SUCCESS;
|
||||
|
||||
#if (MINIUPNPC_API_VERSION >= 14)
|
||||
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, 2, &err);
|
||||
#else
|
||||
m_Devlist = upnpDiscover (2000, m_MulticastIf, m_Minissdpdpath, 0);
|
||||
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, &err);
|
||||
#endif
|
||||
|
||||
isError = err != UPNPDISCOVER_SUCCESS;
|
||||
#else // MINIUPNPC_API_VERSION >= 8
|
||||
err = 0;
|
||||
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0);
|
||||
isError = m_Devlist == NULL;
|
||||
#endif // MINIUPNPC_API_VERSION >= 8
|
||||
{
|
||||
// notify satrting thread
|
||||
// notify starting thread
|
||||
std::unique_lock<std::mutex> l(m_StartedMutex);
|
||||
m_Started.notify_all ();
|
||||
}
|
||||
|
||||
int r;
|
||||
r = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
|
||||
if (r == 1)
|
||||
if (isError)
|
||||
{
|
||||
r = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
|
||||
if(r != UPNPCOMMAND_SUCCESS)
|
||||
LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
|
||||
m_upnpUrlsInitialized=err!=0;
|
||||
if (err == UPNP_IGD_VALID_CONNECTED)
|
||||
{
|
||||
err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
|
||||
if(err != UPNPCOMMAND_SUCCESS)
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: UPNP_GetExternalIPAddress() returned ", r);
|
||||
LogPrint (eLogError, "UPnP: unable to get external address: error ", err);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: found Internet Gateway Device ", m_upnpUrls.controlURL);
|
||||
if (!m_externalIPAddress[0])
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: GetExternalIPAddress() failed.");
|
||||
LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: GetValidIGD() failed.");
|
||||
LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -126,6 +142,20 @@ namespace transport
|
||||
PortMapping ();
|
||||
}
|
||||
|
||||
int UPnP::CheckMapping (const char* port, const char* type)
|
||||
{
|
||||
int err = UPNPCOMMAND_SUCCESS;
|
||||
|
||||
#if (MINIUPNPC_API_VERSION >= 10)
|
||||
err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
#elif ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
|
||||
err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL, NULL, NULL, NULL);
|
||||
#else
|
||||
err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
void UPnP::PortMapping ()
|
||||
{
|
||||
const auto& a = context.GetRouterInfo().GetAddresses();
|
||||
@@ -134,13 +164,47 @@ namespace transport
|
||||
if (!address->host.is_v6 () && address->port)
|
||||
TryPortMapping (address);
|
||||
}
|
||||
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
|
||||
m_Timer.expires_from_now (boost::posix_time::minutes(20)); // every 20 minutes
|
||||
m_Timer.async_wait ([this](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
PortMapping ();
|
||||
});
|
||||
}
|
||||
|
||||
void UPnP::TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
|
||||
{
|
||||
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
|
||||
std::string strDesc; i2p::config::GetOption("upnp.name", strDesc);
|
||||
int err = UPNPCOMMAND_SUCCESS;
|
||||
|
||||
// check for existing mapping
|
||||
err = CheckMapping (strPort.c_str (), strType.c_str ());
|
||||
if (err != UPNPCOMMAND_SUCCESS) // if mapping not found
|
||||
{
|
||||
LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err);
|
||||
|
||||
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
|
||||
err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL, NULL);
|
||||
#else
|
||||
err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL);
|
||||
#endif
|
||||
if (err != UPNPCOMMAND_SUCCESS)
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: port forwarding to ", m_NetworkAddr, ":", strPort, " failed: return code ", err);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "UPnP: port successfully forwarded (", m_externalIPAddress ,":", strPort, " type ", strType, " -> ", m_NetworkAddr ,":", strPort ,")");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UPnP::CloseMapping ()
|
||||
@@ -153,53 +217,42 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void UPnP::TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
|
||||
{
|
||||
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
|
||||
int r;
|
||||
std::string strDesc; i2p::config::GetOption("upnp.name", strDesc);
|
||||
#ifdef UPNPDISCOVER_SUCCESS
|
||||
r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0");
|
||||
#else
|
||||
r = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0);
|
||||
#endif
|
||||
if (r!=UPNPCOMMAND_SUCCESS)
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: AddPortMapping (", m_NetworkAddr, ":", strPort, ") failed with code ", r);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "UPnP: Port Mapping successful. (", m_NetworkAddr ,":", strPort, " type ", strType, " -> ", m_externalIPAddress ,":", strPort ,")");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UPnP::CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
|
||||
{
|
||||
if(!m_upnpUrlsInitialized) {
|
||||
return;
|
||||
}
|
||||
std::string strType (GetProto (address)), strPort (std::to_string (address->port));
|
||||
int r = 0;
|
||||
r = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
|
||||
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", r);
|
||||
int err = UPNPCOMMAND_SUCCESS;
|
||||
|
||||
err = CheckMapping (strPort.c_str (), strType.c_str ());
|
||||
if (err == UPNPCOMMAND_SUCCESS)
|
||||
{
|
||||
err = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), NULL);
|
||||
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", err);
|
||||
}
|
||||
}
|
||||
|
||||
void UPnP::Close ()
|
||||
{
|
||||
freeUPNPDevlist (m_Devlist);
|
||||
m_Devlist = 0;
|
||||
FreeUPNPUrls (&m_upnpUrls);
|
||||
}
|
||||
if(m_upnpUrlsInitialized){
|
||||
FreeUPNPUrls (&m_upnpUrls);
|
||||
m_upnpUrlsInitialized=false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string UPnP::GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address)
|
||||
{
|
||||
switch (address->transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
return "TCP";
|
||||
break;
|
||||
return "TCP";
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
default:
|
||||
return "UDP";
|
||||
return "UDP";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,31 @@ namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
const int UPNP_RESPONSE_TIMEOUT = 2000; // in milliseconds
|
||||
|
||||
enum
|
||||
{
|
||||
UPNP_IGD_NONE = 0,
|
||||
UPNP_IGD_VALID_CONNECTED = 1,
|
||||
UPNP_IGD_VALID_NOT_CONNECTED = 2,
|
||||
UPNP_IGD_INVALID = 3
|
||||
};
|
||||
|
||||
class UPnP
|
||||
{
|
||||
public:
|
||||
public:
|
||||
|
||||
UPnP ();
|
||||
~UPnP ();
|
||||
void Close ();
|
||||
void Close ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
private:
|
||||
private:
|
||||
|
||||
void Discover ();
|
||||
int CheckMapping (const char* port, const char* type);
|
||||
void PortMapping ();
|
||||
void TryPortMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address);
|
||||
void CloseMapping ();
|
||||
@@ -41,23 +52,22 @@ namespace transport
|
||||
void Run ();
|
||||
std::string GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address);
|
||||
|
||||
private:
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
std::condition_variable m_Started;
|
||||
std::mutex m_StartedMutex;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
struct UPNPUrls m_upnpUrls;
|
||||
struct IGDdatas m_upnpData;
|
||||
bool m_upnpUrlsInitialized=false;
|
||||
struct UPNPUrls m_upnpUrls;
|
||||
struct IGDdatas m_upnpData;
|
||||
|
||||
// For miniupnpc
|
||||
char * m_MulticastIf = 0;
|
||||
char * m_Minissdpdpath = 0;
|
||||
struct UPNPDev * m_Devlist = 0;
|
||||
char m_NetworkAddr[64];
|
||||
char m_externalIPAddress[40];
|
||||
// For miniupnpc
|
||||
struct UPNPDev * m_Devlist = 0;
|
||||
char m_NetworkAddr[64];
|
||||
char m_externalIPAddress[40];
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -65,14 +75,15 @@ namespace transport
|
||||
#else // USE_UPNP
|
||||
namespace i2p {
|
||||
namespace transport {
|
||||
/* class stub */
|
||||
class UPnP {
|
||||
public:
|
||||
UPnP () {};
|
||||
~UPnP () {};
|
||||
void Start () { LogPrint(eLogWarning, "UPnP: this module was disabled at compile-time"); }
|
||||
void Stop () {};
|
||||
};
|
||||
/* class stub */
|
||||
class UPnP {
|
||||
public:
|
||||
|
||||
UPnP () {};
|
||||
~UPnP () {};
|
||||
void Start () { LogPrint(eLogWarning, "UPnP: this module was disabled at compile-time"); }
|
||||
void Stop () {};
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif // USE_UPNP
|
||||
|
||||
32
debian/changelog
vendored
32
debian/changelog
vendored
@@ -1,3 +1,33 @@
|
||||
i2pd (2.31.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.31.0
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Fri, 10 Apr 2020 16:00:00 +0000
|
||||
|
||||
i2pd (2.30.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.30.0/0.9.45
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Tue, 25 Feb 2020 16:00:00 +0000
|
||||
|
||||
i2pd (2.29.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.29.0/0.9.43
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Mon, 21 Oct 2019 16:00:00 +0000
|
||||
|
||||
i2pd (2.28.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.28.0/0.9.42
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Tue, 27 Aug 2019 16:00:00 +0000
|
||||
|
||||
i2pd (2.27.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.27.0/0.9.41
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Wed, 3 Jul 2019 16:00:00 +0000
|
||||
|
||||
i2pd (2.26.0-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.26.0
|
||||
@@ -36,7 +66,7 @@ i2pd (2.21.1-1) unstable; urgency=medium
|
||||
|
||||
* updated to version 2.21.1
|
||||
|
||||
-- orignal <orignal@i2pmail.org> Thu, 22 Oct 2018 16:00:00 +0000
|
||||
-- orignal <orignal@i2pmail.org> Mon, 22 Oct 2018 16:00:00 +0000
|
||||
|
||||
i2pd (2.21.0-1) unstable; urgency=medium
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -124,8 +124,14 @@ namespace data
|
||||
return publicKeyLength;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity)
|
||||
const uint8_t B33_TWO_BYTES_SIGTYPE_FLAG = 0x01;
|
||||
const uint8_t B33_PER_SECRET_FLAG = 0x02; // not used for now
|
||||
const uint8_t B33_PER_CLIENT_AUTH_FLAG = 0x04;
|
||||
|
||||
BlindedPublicKey::BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth):
|
||||
m_IsClientAuth (clientAuth)
|
||||
{
|
||||
if (!identity) return;
|
||||
auto len = identity->GetSigningPublicKeyLen ();
|
||||
@@ -135,16 +141,22 @@ namespace data
|
||||
m_BlindedSigType = m_SigType;
|
||||
}
|
||||
|
||||
BlindedPublicKey::BlindedPublicKey (const std::string& b33)
|
||||
BlindedPublicKey::BlindedPublicKey (const std::string& b33):
|
||||
m_SigType (0) // 0 means invalid, we can't blind DSA, set it later
|
||||
{
|
||||
uint8_t addr[40]; // TODO: define length from b33
|
||||
size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40);
|
||||
if (l < 32)
|
||||
{
|
||||
LogPrint (eLogError, "Blinding: malformed b33 ", b33);
|
||||
return;
|
||||
}
|
||||
uint32_t checksum = crc32 (0, addr + 3, l - 3);
|
||||
// checksum is Little Endian
|
||||
addr[0] ^= checksum; addr[1] ^= (checksum >> 8); addr[2] ^= (checksum >> 16);
|
||||
uint8_t flag = addr[0];
|
||||
uint8_t flags = addr[0];
|
||||
size_t offset = 1;
|
||||
if (flag & 0x01) // two bytes signatures
|
||||
if (flags & B33_TWO_BYTES_SIGTYPE_FLAG) // two bytes signatures
|
||||
{
|
||||
m_SigType = bufbe16toh (addr + offset); offset += 2;
|
||||
m_BlindedSigType = bufbe16toh (addr + offset); offset += 2;
|
||||
@@ -154,6 +166,8 @@ namespace data
|
||||
m_SigType = addr[offset]; offset++;
|
||||
m_BlindedSigType = addr[offset]; offset++;
|
||||
}
|
||||
m_IsClientAuth = flags & B33_PER_CLIENT_AUTH_FLAG;
|
||||
|
||||
std::unique_ptr<i2p::crypto::Verifier> blindedVerifier (i2p::data::IdentityEx::CreateVerifier (m_SigType));
|
||||
if (blindedVerifier)
|
||||
{
|
||||
@@ -174,7 +188,9 @@ namespace data
|
||||
{
|
||||
if (m_PublicKey.size () > 32) return ""; // assume 25519
|
||||
uint8_t addr[35]; char str[60]; // TODO: define actual length
|
||||
addr[0] = 0; // flags
|
||||
uint8_t flags = 0;
|
||||
if (m_IsClientAuth) flags |= B33_PER_CLIENT_AUTH_FLAG;
|
||||
addr[0] = flags; // flags
|
||||
addr[1] = m_SigType; // sig type
|
||||
addr[2] = m_BlindedSigType; // blinded sig type
|
||||
memcpy (addr + 3, m_PublicKey.data (), m_PublicKey.size ());
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace data
|
||||
{
|
||||
public:
|
||||
|
||||
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity);
|
||||
BlindedPublicKey (std::shared_ptr<const IdentityEx> identity, bool clientAuth = false);
|
||||
BlindedPublicKey (const std::string& b33); // from b33 without .b32.i2p
|
||||
std::string ToB33 () const;
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace data
|
||||
size_t GetPublicKeyLen () const { return m_PublicKey.size (); };
|
||||
SigningKeyType GetSigType () const { return m_SigType; };
|
||||
SigningKeyType GetBlindedSigType () const { return m_BlindedSigType; };
|
||||
bool IsValid () const { return GetSigType (); }; // signature type 0 means invalid
|
||||
|
||||
void GetSubcredential (const uint8_t * blinded, size_t len, uint8_t * subcredential) const; // 32 bytes
|
||||
size_t GetBlindedKey (const char * date, uint8_t * blindedKey) const; // date is 8 chars "YYYYMMDD", return public key length
|
||||
@@ -38,6 +39,7 @@ namespace data
|
||||
|
||||
std::vector<uint8_t> m_PublicKey;
|
||||
i2p::data::SigningKeyType m_SigType, m_BlindedSigType;
|
||||
bool m_IsClientAuth = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace config {
|
||||
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
|
||||
("ntcp", value<bool>()->default_value(false), "Enable NTCP transport (default: disabled)")
|
||||
("ssu", value<bool>()->default_value(true), "Enable SSU transport (default: enabled)")
|
||||
("ntcpproxy", value<std::string>()->default_value(""), "Proxy URL for NTCP transport")
|
||||
("ntcpproxy", value<std::string>()->default_value(""), "Proxy URL for NTCP transport")
|
||||
#ifdef _WIN32
|
||||
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
|
||||
("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)")
|
||||
@@ -131,6 +131,7 @@ namespace config {
|
||||
("sam.enabled", value<bool>()->default_value(true), "Enable or disable SAM Application bridge")
|
||||
("sam.address", value<std::string>()->default_value("127.0.0.1"), "SAM listen address")
|
||||
("sam.port", value<uint16_t>()->default_value(7656), "SAM listen port")
|
||||
("sam.singlethread", value<bool>()->default_value(true), "Sessions run in the SAM bridge's thread")
|
||||
;
|
||||
|
||||
options_description bob("BOB options");
|
||||
@@ -190,10 +191,10 @@ namespace config {
|
||||
"https://reseed.i2p-projekt.de/,"
|
||||
"https://i2p.mooo.com/netDb/,"
|
||||
"https://netdb.i2p2.no/,"
|
||||
"https://reseed.i2p2.no/,"
|
||||
"https://reseed2.i2p2.no/,"
|
||||
// "https://us.reseed.i2p2.no:444/," // mamoth's shit
|
||||
// "https://uk.reseed.i2p2.no:444/," // mamoth's shit
|
||||
"https://reseed.i2p.net.in/,"
|
||||
"https://download.xxlspeed.com/,"
|
||||
"https://reseed-fr.i2pd.xyz/,"
|
||||
"https://reseed.memcpy.io/,"
|
||||
"https://reseed.onion.im/,"
|
||||
@@ -217,13 +218,6 @@ 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")
|
||||
@@ -238,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");
|
||||
@@ -273,7 +268,6 @@ namespace config {
|
||||
.add(reseed)
|
||||
.add(addressbook)
|
||||
.add(trust)
|
||||
.add(websocket)
|
||||
.add(exploratory)
|
||||
.add(ntcp2)
|
||||
.add(nettime)
|
||||
@@ -304,16 +298,16 @@ namespace config {
|
||||
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
|
||||
std::cout << m_OptionsDesc;
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
else if (m_Options.count("version"))
|
||||
{
|
||||
std::cout << "i2pd version " << I2PD_VERSION << " (" << I2P_VERSION << ")" << std::endl;
|
||||
std::cout << "Boost version "
|
||||
std::cout << "Boost version "
|
||||
<< BOOST_VERSION / 100000 << "." // maj. version
|
||||
<< BOOST_VERSION / 100 % 1000 << "." // min. version
|
||||
<< BOOST_VERSION % 100 // patch version
|
||||
<< std::endl;
|
||||
#if defined(OPENSSL_VERSION_TEXT)
|
||||
#if defined(OPENSSL_VERSION_TEXT)
|
||||
std::cout << OPENSSL_VERSION_TEXT << std::endl;
|
||||
#endif
|
||||
#if defined(LIBRESSL_VERSION_TEXT)
|
||||
|
||||
@@ -296,11 +296,20 @@ namespace crypto
|
||||
#if OPENSSL_X25519
|
||||
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
|
||||
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
|
||||
memcpy (m_PublicKey, pub, 32); // TODO: verify against m_Pkey
|
||||
if (pub)
|
||||
memcpy (m_PublicKey, pub, 32); // TODO: verify against m_Pkey
|
||||
else
|
||||
{
|
||||
size_t len = 32;
|
||||
EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
|
||||
}
|
||||
#else
|
||||
m_Ctx = BN_CTX_new ();
|
||||
memcpy (m_PrivateKey, priv, 32);
|
||||
memcpy (m_PublicKey, pub, 32);
|
||||
m_Ctx = BN_CTX_new ();
|
||||
if (pub)
|
||||
memcpy (m_PublicKey, pub, 32);
|
||||
else
|
||||
GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -358,6 +367,18 @@ namespace crypto
|
||||
#endif
|
||||
}
|
||||
|
||||
void X25519Keys::SetPrivateKey (const uint8_t * priv)
|
||||
{
|
||||
#if OPENSSL_X25519
|
||||
if (m_Ctx) EVP_PKEY_CTX_free (m_Ctx);
|
||||
if (m_Pkey) EVP_PKEY_free (m_Pkey);
|
||||
m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
|
||||
m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
|
||||
#else
|
||||
memcpy (m_PrivateKey, priv, 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ElGamal
|
||||
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding)
|
||||
{
|
||||
@@ -1250,18 +1271,29 @@ namespace crypto
|
||||
#endif
|
||||
}
|
||||
|
||||
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out)
|
||||
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info,
|
||||
uint8_t * out, size_t outLen)
|
||||
{
|
||||
#if OPENSSL_HKDF
|
||||
EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, NULL);
|
||||
EVP_PKEY_CTX * pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_HKDF, nullptr);
|
||||
EVP_PKEY_derive_init (pctx);
|
||||
EVP_PKEY_CTX_set_hkdf_md (pctx, EVP_sha256());
|
||||
EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32);
|
||||
EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen);
|
||||
if (key && keyLen)
|
||||
{
|
||||
EVP_PKEY_CTX_set1_hkdf_salt (pctx, salt, 32);
|
||||
EVP_PKEY_CTX_set1_hkdf_key (pctx, key, keyLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
// zerolen
|
||||
EVP_PKEY_CTX_hkdf_mode (pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY);
|
||||
uint8_t tempKey[32]; unsigned int len;
|
||||
HMAC(EVP_sha256(), salt, 32, nullptr, 0, tempKey, &len);
|
||||
EVP_PKEY_CTX_set1_hkdf_key (pctx, tempKey, len);
|
||||
}
|
||||
if (info.length () > 0)
|
||||
EVP_PKEY_CTX_add1_hkdf_info (pctx, info.c_str (), info.length ());
|
||||
size_t outlen = 64;
|
||||
EVP_PKEY_derive (pctx, out, &outlen);
|
||||
EVP_PKEY_derive (pctx, out, &outLen);
|
||||
EVP_PKEY_CTX_free (pctx);
|
||||
#else
|
||||
uint8_t prk[32]; unsigned int len;
|
||||
@@ -1269,8 +1301,11 @@ namespace crypto
|
||||
auto l = info.length ();
|
||||
memcpy (out, info.c_str (), l); out[l] = 0x01;
|
||||
HMAC(EVP_sha256(), prk, 32, out, l + 1, out, &len);
|
||||
memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02;
|
||||
HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len);
|
||||
if (outLen > 32) // 64
|
||||
{
|
||||
memcpy (out + 32, info.c_str (), l); out[l + 32] = 0x02;
|
||||
HMAC(EVP_sha256(), prk, 32, out, l + 33, out + 32, &len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
# define X509_getm_notAfter X509_get_notAfter
|
||||
#else
|
||||
# define LEGACY_OPENSSL 0
|
||||
# define OPENSSL_HKDF 1
|
||||
# if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1
|
||||
# define OPENSSL_HKDF 1
|
||||
# define OPENSSL_EDDSA 1
|
||||
# define OPENSSL_X25519 1
|
||||
# define OPENSSL_SIPHASH 1
|
||||
@@ -74,12 +74,13 @@ namespace crypto
|
||||
public:
|
||||
|
||||
X25519Keys ();
|
||||
X25519Keys (const uint8_t * priv, const uint8_t * pub); // for RouterContext
|
||||
X25519Keys (const uint8_t * priv, const uint8_t * pub); // if pub is null, derive from priv
|
||||
~X25519Keys ();
|
||||
|
||||
void GenerateKeys ();
|
||||
const uint8_t * GetPublicKey () const { return m_PublicKey; };
|
||||
void GetPrivateKey (uint8_t * priv) const;
|
||||
void SetPrivateKey (const uint8_t * priv); // wihout calculating public
|
||||
void Agree (const uint8_t * pub, uint8_t * shared);
|
||||
|
||||
private:
|
||||
@@ -296,7 +297,7 @@ namespace crypto
|
||||
|
||||
// HKDF
|
||||
|
||||
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out); // salt - 32, out - 64, info <= 32
|
||||
void HKDF (const uint8_t * salt, const uint8_t * key, size_t keyLen, const std::string& info, uint8_t * out, size_t outLen = 64); // salt - 32, out - 32 or 64, info <= 32
|
||||
|
||||
// init and terminate
|
||||
void InitCrypto (bool precomputation);
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace crypto
|
||||
|
||||
void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding)
|
||||
{
|
||||
if (!ctx) return;
|
||||
ElGamalEncrypt (m_PublicKey, data, encrypted, ctx, zeroPadding);
|
||||
}
|
||||
|
||||
@@ -24,6 +25,7 @@ namespace crypto
|
||||
|
||||
bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding)
|
||||
{
|
||||
if (!ctx) return false;
|
||||
return ElGamalDecrypt (m_PrivateKey, encrypted, data, ctx, zeroPadding);
|
||||
}
|
||||
|
||||
@@ -146,6 +148,34 @@ namespace crypto
|
||||
BN_free (x); BN_free (y);
|
||||
}
|
||||
|
||||
ECIESX25519AEADRatchetEncryptor::ECIESX25519AEADRatchetEncryptor (const uint8_t * pub)
|
||||
{
|
||||
memcpy (m_PublicKey, pub, 32);
|
||||
}
|
||||
|
||||
void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool)
|
||||
{
|
||||
memcpy (pub, m_PublicKey, 32);
|
||||
}
|
||||
|
||||
ECIESX25519AEADRatchetDecryptor::ECIESX25519AEADRatchetDecryptor (const uint8_t * priv)
|
||||
{
|
||||
m_StaticKeys.SetPrivateKey (priv);
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding)
|
||||
{
|
||||
m_StaticKeys.Agree (epub, sharedSecret);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub)
|
||||
{
|
||||
X25519Keys k;
|
||||
k.GenerateKeys ();
|
||||
k.GetPrivateKey (priv);
|
||||
memcpy (pub, k.GetPublicKey (), 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,39 @@ namespace crypto
|
||||
};
|
||||
|
||||
void CreateECIESGOSTR3410RandomKeys (uint8_t * priv, uint8_t * pub);
|
||||
|
||||
// ECIES-X25519-AEAD-Ratchet
|
||||
|
||||
class ECIESX25519AEADRatchetEncryptor: public CryptoKeyEncryptor
|
||||
{
|
||||
public:
|
||||
|
||||
ECIESX25519AEADRatchetEncryptor (const uint8_t * pub);
|
||||
~ECIESX25519AEADRatchetEncryptor () {};
|
||||
void Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool);
|
||||
// copies m_PublicKey to pub
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_PublicKey[32];
|
||||
};
|
||||
|
||||
class ECIESX25519AEADRatchetDecryptor: public CryptoKeyDecryptor
|
||||
{
|
||||
public:
|
||||
|
||||
ECIESX25519AEADRatchetDecryptor (const uint8_t * priv);
|
||||
~ECIESX25519AEADRatchetDecryptor () {};
|
||||
bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding);
|
||||
// agree with static and return in sharedSecret (32 bytes)
|
||||
size_t GetPublicKeyLen () const { return 32; };
|
||||
|
||||
private:
|
||||
|
||||
X25519Keys m_StaticKeys;
|
||||
};
|
||||
|
||||
void CreateECIESX25519AEADRatchetRandomKeys (uint8_t * priv, uint8_t * pub);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,8 @@ namespace i2p
|
||||
namespace datagram
|
||||
{
|
||||
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
|
||||
m_Owner (owner.get()),
|
||||
m_Receiver (nullptr)
|
||||
m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr)
|
||||
{
|
||||
m_Identity.FromBase64 (owner->GetIdentity()->ToBase64());
|
||||
}
|
||||
|
||||
DatagramDestination::~DatagramDestination ()
|
||||
@@ -28,14 +26,15 @@ namespace datagram
|
||||
auto owner = m_Owner;
|
||||
std::vector<uint8_t> v(MAX_DATAGRAM_SIZE);
|
||||
uint8_t * buf = v.data();
|
||||
auto identityLen = m_Identity.ToBuffer (buf, MAX_DATAGRAM_SIZE);
|
||||
auto localIdentity = m_Owner->GetIdentity ();
|
||||
auto identityLen = localIdentity->ToBuffer (buf, MAX_DATAGRAM_SIZE);
|
||||
uint8_t * signature = buf + identityLen;
|
||||
auto signatureLen = m_Identity.GetSignatureLen ();
|
||||
auto signatureLen = localIdentity->GetSignatureLen ();
|
||||
uint8_t * buf1 = signature + signatureLen;
|
||||
size_t headerLen = identityLen + signatureLen;
|
||||
|
||||
memcpy (buf1, payload, len);
|
||||
if (m_Identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
if (localIdentity->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
uint8_t hash[32];
|
||||
SHA256(buf1, len, hash);
|
||||
@@ -49,7 +48,13 @@ namespace datagram
|
||||
session->SendMsg(msg);
|
||||
}
|
||||
|
||||
|
||||
void DatagramDestination::SendRawDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
|
||||
{
|
||||
auto msg = CreateDataMessage (payload, len, fromPort, toPort, true); // raw
|
||||
auto session = ObtainSession(identity);
|
||||
session->SendMsg(msg);
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len)
|
||||
{
|
||||
i2p::data::IdentityEx identity;
|
||||
@@ -82,6 +87,14 @@ namespace datagram
|
||||
LogPrint (eLogWarning, "Datagram signature verification failed");
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (m_RawReceiver)
|
||||
m_RawReceiver (fromPort, toPort, buf, len);
|
||||
else
|
||||
LogPrint (eLogWarning, "DatagramDestination: no receiver for raw datagram");
|
||||
}
|
||||
|
||||
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
|
||||
@@ -92,18 +105,24 @@ namespace datagram
|
||||
return r;
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw)
|
||||
{
|
||||
// unzip it
|
||||
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
|
||||
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
|
||||
if (uncompressedLen)
|
||||
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
|
||||
{
|
||||
if (isRaw)
|
||||
HandleRawDatagram (fromPort, toPort, uncompressed, uncompressedLen);
|
||||
else
|
||||
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Datagram: decompression failed");
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
|
||||
|
||||
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort, bool isRaw)
|
||||
{
|
||||
auto msg = NewI2NPMessage ();
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
@@ -114,7 +133,7 @@ namespace datagram
|
||||
htobe32buf (msg->GetPayload (), size); // length
|
||||
htobe16buf (buf + 4, fromPort); // source port
|
||||
htobe16buf (buf + 6, toPort); // destination port
|
||||
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
|
||||
buf[9] = isRaw ? i2p::client::PROTOCOL_TYPE_RAW : i2p::client::PROTOCOL_TYPE_DATAGRAM; // raw or datagram protocol
|
||||
msg->len += size + 4;
|
||||
msg->FillI2NPMessageHeader (eI2NPData);
|
||||
}
|
||||
@@ -170,7 +189,7 @@ namespace datagram
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination,
|
||||
DatagramSession::DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||
const i2p::data::IdentHash & remoteIdent) :
|
||||
m_LocalDestination(localDestination),
|
||||
m_RemoteIdent(remoteIdent),
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace datagram
|
||||
class DatagramSession : public std::enable_shared_from_this<DatagramSession>
|
||||
{
|
||||
public:
|
||||
DatagramSession(i2p::client::ClientDestination * localDestination, const i2p::data::IdentHash & remoteIdent);
|
||||
DatagramSession(std::shared_ptr<i2p::client::ClientDestination> localDestination, const i2p::data::IdentHash & remoteIdent);
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
@@ -81,7 +81,7 @@ namespace datagram
|
||||
void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls);
|
||||
|
||||
private:
|
||||
i2p::client::ClientDestination * m_LocalDestination;
|
||||
std::shared_ptr<i2p::client::ClientDestination> m_LocalDestination;
|
||||
i2p::data::IdentHash m_RemoteIdent;
|
||||
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
|
||||
@@ -99,22 +99,28 @@ namespace datagram
|
||||
class DatagramDestination
|
||||
{
|
||||
typedef std::function<void (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
|
||||
typedef std::function<void (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> RawReceiver;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
|
||||
DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner);
|
||||
~DatagramDestination ();
|
||||
|
||||
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
||||
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||
|
||||
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
||||
void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
||||
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false);
|
||||
|
||||
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
|
||||
void ResetReceiver () { m_Receiver = nullptr; };
|
||||
|
||||
void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; };
|
||||
void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); };
|
||||
|
||||
void SetRawReceiver (const RawReceiver& receiver) { m_RawReceiver = receiver; };
|
||||
void ResetRawReceiver () { m_RawReceiver = nullptr; };
|
||||
|
||||
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote);
|
||||
|
||||
// clean up stale sessions
|
||||
@@ -124,17 +130,19 @@ namespace datagram
|
||||
|
||||
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
|
||||
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort, bool isRaw = false);
|
||||
|
||||
void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len);
|
||||
|
||||
void HandleRawDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||
|
||||
/** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */
|
||||
Receiver FindReceiver(uint16_t port);
|
||||
|
||||
private:
|
||||
i2p::client::ClientDestination * m_Owner;
|
||||
i2p::data::IdentityEx m_Identity;
|
||||
|
||||
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
|
||||
Receiver m_Receiver; // default
|
||||
RawReceiver m_RawReceiver; // default
|
||||
std::mutex m_SessionsMutex;
|
||||
std::map<i2p::data::IdentHash, DatagramSession_ptr > m_Sessions;
|
||||
std::mutex m_ReceiversMutex;
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
#include "Timestamp.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "Destination.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_IsPublic (isPublic),
|
||||
m_PublishReplyToken (0), m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
|
||||
LeaseSetDestination::LeaseSetDestination (boost::asio::io_service& service,
|
||||
bool isPublic, const std::map<std::string, std::string> * params):
|
||||
m_Service (service), m_IsPublic (isPublic), m_PublishReplyToken (0),
|
||||
m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
|
||||
m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service),
|
||||
m_LeaseSetType (DEFAULT_LEASESET_TYPE)
|
||||
m_LeaseSetType (DEFAULT_LEASESET_TYPE), m_AuthType (i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE)
|
||||
{
|
||||
int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
|
||||
int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY;
|
||||
@@ -70,6 +70,29 @@ namespace client
|
||||
it = params->find (I2CP_PARAM_LEASESET_TYPE);
|
||||
if (it != params->end ())
|
||||
m_LeaseSetType = std::stoi(it->second);
|
||||
if (m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
||||
{
|
||||
// authentication for encrypted LeaseSet
|
||||
it = params->find (I2CP_PARAM_LEASESET_AUTH_TYPE);
|
||||
if (it != params->end ())
|
||||
{
|
||||
auto authType = std::stoi (it->second);
|
||||
if (authType >= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_NONE && authType <= i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
|
||||
m_AuthType = authType;
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Unknown auth type ", authType);
|
||||
}
|
||||
}
|
||||
it = params->find (I2CP_PARAM_LEASESET_PRIV_KEY);
|
||||
if (it != params->end ())
|
||||
{
|
||||
m_LeaseSetPrivKey.reset (new i2p::data::Tag<32>());
|
||||
if (m_LeaseSetPrivKey->FromBase64 (it->second) != 32)
|
||||
{
|
||||
LogPrint(eLogError, "Destination: invalid value i2cp.leaseSetPrivKey ", it->second);
|
||||
m_LeaseSetPrivKey.reset (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception & ex)
|
||||
@@ -100,77 +123,36 @@ namespace client
|
||||
|
||||
LeaseSetDestination::~LeaseSetDestination ()
|
||||
{
|
||||
if (m_IsRunning)
|
||||
Stop ();
|
||||
if (m_Pool)
|
||||
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
|
||||
for (auto& it: m_LeaseSetRequests)
|
||||
it.second->Complete (nullptr);
|
||||
}
|
||||
|
||||
void LeaseSetDestination::Run ()
|
||||
void LeaseSetDestination::Start ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "Destination: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
if (m_Nickname.empty ())
|
||||
m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname
|
||||
LoadTags ();
|
||||
m_Pool->SetLocalDestination (shared_from_this ());
|
||||
m_Pool->SetActive (true);
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::Start ()
|
||||
void LeaseSetDestination::Stop ()
|
||||
{
|
||||
if (!m_IsRunning)
|
||||
m_CleanupTimer.cancel ();
|
||||
m_PublishConfirmationTimer.cancel ();
|
||||
m_PublishVerificationTimer.cancel ();
|
||||
if (m_Pool)
|
||||
{
|
||||
if (m_Nickname.empty ())
|
||||
m_Nickname = i2p::data::GetIdentHashAbbreviation (GetIdentHash ()); // set default nickname
|
||||
LoadTags ();
|
||||
m_IsRunning = true;
|
||||
m_Pool->SetLocalDestination (shared_from_this ());
|
||||
m_Pool->SetActive (true);
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
|
||||
|
||||
return true;
|
||||
m_Pool->SetLocalDestination (nullptr);
|
||||
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::Stop ()
|
||||
{
|
||||
if (m_IsRunning)
|
||||
{
|
||||
m_CleanupTimer.cancel ();
|
||||
m_PublishConfirmationTimer.cancel ();
|
||||
m_PublishVerificationTimer.cancel ();
|
||||
|
||||
m_IsRunning = false;
|
||||
if (m_Pool)
|
||||
{
|
||||
m_Pool->SetLocalDestination (nullptr);
|
||||
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
|
||||
}
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
SaveTags ();
|
||||
CleanUp (); // GarlicDestination
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
SaveTags ();
|
||||
CleanUp (); // GarlicDestination
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::Reconfigure(std::map<std::string, std::string> params)
|
||||
@@ -330,30 +312,38 @@ namespace client
|
||||
|
||||
void LeaseSetDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msg));
|
||||
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
|
||||
m_Service.post (std::bind (&LeaseSetDestination::HandleDeliveryStatusMessage, shared_from_this (), msgID));
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
||||
void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]);
|
||||
LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
|
||||
{
|
||||
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
|
||||
switch (typeID)
|
||||
{
|
||||
case eI2NPData:
|
||||
HandleDataMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
|
||||
HandleDataMessage (payload, len);
|
||||
break;
|
||||
case eI2NPDeliveryStatus:
|
||||
// we assume tunnel tests non-encrypted
|
||||
HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from));
|
||||
HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET));
|
||||
break;
|
||||
case eI2NPDatabaseStore:
|
||||
HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
|
||||
HandleDatabaseStoreMessage (payload, len);
|
||||
break;
|
||||
case eI2NPDatabaseSearchReply:
|
||||
HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE);
|
||||
HandleDatabaseSearchReplyMessage (payload, len);
|
||||
break;
|
||||
default:
|
||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from));
|
||||
LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
|
||||
@@ -398,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); // 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 ())
|
||||
@@ -422,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);
|
||||
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
|
||||
@@ -488,9 +478,8 @@ namespace client
|
||||
LogPrint (eLogWarning, "Destination: Request for ", key.ToBase64 (), " not found");
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
void LeaseSetDestination::HandleDeliveryStatusMessage (uint32_t msgID)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
|
||||
if (msgID == m_PublishReplyToken)
|
||||
{
|
||||
LogPrint (eLogDebug, "Destination: Publishing LeaseSet confirmed for ", GetIdentHash().ToBase32());
|
||||
@@ -502,7 +491,7 @@ namespace client
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
else
|
||||
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
|
||||
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID);
|
||||
}
|
||||
|
||||
void LeaseSetDestination::SetLeaseSetUpdated ()
|
||||
@@ -833,10 +822,19 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
|
||||
LeaseSetDestination (isPublic, params), m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
|
||||
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),
|
||||
m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
|
||||
m_DatagramDestination (nullptr), m_RefCounter (0),
|
||||
m_ReadyChecker(GetService())
|
||||
m_ReadyChecker(service)
|
||||
{
|
||||
if (keys.IsOfflineSignature () && GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
|
||||
SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // offline keys can be published with LS2 only
|
||||
@@ -849,21 +847,54 @@ namespace client
|
||||
if (it != params->end ())
|
||||
m_EncryptionKeyType = std::stoi(it->second);
|
||||
}
|
||||
|
||||
if (isPublic && m_EncryptionKeyType == GetIdentity ()->GetCryptoKeyType ()) // TODO: presist key type
|
||||
|
||||
memset (m_EncryptionPrivateKey, 0, 256);
|
||||
memset (m_EncryptionPublicKey, 0, 256);
|
||||
if (isPublic)
|
||||
PersistTemporaryKeys ();
|
||||
else
|
||||
i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
|
||||
m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_EncryptionKeyType, m_EncryptionPrivateKey);
|
||||
if (isPublic)
|
||||
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
|
||||
|
||||
// extract streaming params
|
||||
if (params)
|
||||
try
|
||||
{
|
||||
if (params)
|
||||
{
|
||||
// extract streaming params
|
||||
auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
|
||||
if (it != params->end ())
|
||||
m_StreamingAckDelay = std::stoi(it->second);
|
||||
|
||||
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
||||
{
|
||||
// authentication for encrypted LeaseSet
|
||||
auto authType = GetAuthType ();
|
||||
if (authType > 0)
|
||||
{
|
||||
m_AuthKeys = std::make_shared<std::vector<i2p::data::AuthPublicKey> >();
|
||||
if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_DH)
|
||||
ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_DH, params);
|
||||
else if (authType == i2p::data::ENCRYPTED_LEASESET_AUTH_TYPE_PSK)
|
||||
ReadAuthKey (I2CP_PARAM_LEASESET_CLIENT_PSK, params);
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Unexpected auth type ", authType);
|
||||
if (m_AuthKeys->size ())
|
||||
LogPrint (eLogInfo, "Destination: ", m_AuthKeys->size (), " auth keys read");
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Destination: No auth keys read for auth type ", authType);
|
||||
m_AuthKeys = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception & ex)
|
||||
{
|
||||
auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
|
||||
if (it != params->end ())
|
||||
m_StreamingAckDelay = std::stoi(it->second);
|
||||
LogPrint(eLogError, "Destination: unable to parse parameters for destination: ", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -871,71 +902,35 @@ namespace client
|
||||
{
|
||||
}
|
||||
|
||||
bool ClientDestination::Start ()
|
||||
void ClientDestination::Start ()
|
||||
{
|
||||
if (LeaseSetDestination::Start ())
|
||||
LeaseSetDestination::Start ();
|
||||
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
|
||||
m_StreamingDestination->Start ();
|
||||
for (auto& it: m_StreamingDestinationsByPorts)
|
||||
it.second->Start ();
|
||||
}
|
||||
|
||||
void ClientDestination::Stop ()
|
||||
{
|
||||
LeaseSetDestination::Stop ();
|
||||
m_ReadyChecker.cancel();
|
||||
m_StreamingDestination->Stop ();
|
||||
//m_StreamingDestination->SetOwner (nullptr);
|
||||
m_StreamingDestination = nullptr;
|
||||
for (auto& it: m_StreamingDestinationsByPorts)
|
||||
{
|
||||
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis ()); // TODO:
|
||||
m_StreamingDestination->Start ();
|
||||
for (auto& it: m_StreamingDestinationsByPorts)
|
||||
it.second->Start ();
|
||||
return true;
|
||||
it.second->Stop ();
|
||||
//it.second->SetOwner (nullptr);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClientDestination::Stop ()
|
||||
{
|
||||
if (LeaseSetDestination::Stop ())
|
||||
m_StreamingDestinationsByPorts.clear ();
|
||||
if (m_DatagramDestination)
|
||||
{
|
||||
m_ReadyChecker.cancel();
|
||||
m_StreamingDestination->Stop ();
|
||||
//m_StreamingDestination->SetOwner (nullptr);
|
||||
m_StreamingDestination = nullptr;
|
||||
for (auto& it: m_StreamingDestinationsByPorts)
|
||||
{
|
||||
it.second->Stop ();
|
||||
//it.second->SetOwner (nullptr);
|
||||
}
|
||||
m_StreamingDestinationsByPorts.clear ();
|
||||
if (m_DatagramDestination)
|
||||
{
|
||||
delete m_DatagramDestination;
|
||||
m_DatagramDestination = nullptr;
|
||||
}
|
||||
return true;
|
||||
delete m_DatagramDestination;
|
||||
m_DatagramDestination = nullptr;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef I2LUA
|
||||
void ClientDestination::Ready(ReadyPromise & p)
|
||||
{
|
||||
ScheduleCheckForReady(&p);
|
||||
}
|
||||
|
||||
void ClientDestination::ScheduleCheckForReady(ReadyPromise * p)
|
||||
{
|
||||
// tick every 100ms
|
||||
m_ReadyChecker.expires_from_now(boost::posix_time::milliseconds(100));
|
||||
m_ReadyChecker.async_wait([&, p] (const boost::system::error_code & ecode) {
|
||||
HandleCheckForReady(ecode, p);
|
||||
});
|
||||
}
|
||||
|
||||
void ClientDestination::HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p)
|
||||
{
|
||||
if(ecode) // error happened
|
||||
p->set_value(nullptr);
|
||||
else if(IsReady()) // we are ready
|
||||
p->set_value(std::shared_ptr<ClientDestination>(this));
|
||||
else // we are not ready
|
||||
ScheduleCheckForReady(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint32_t length = bufbe32toh (buf);
|
||||
@@ -967,6 +962,13 @@ namespace client
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Missing datagram destination");
|
||||
break;
|
||||
case PROTOCOL_TYPE_RAW:
|
||||
// raw datagram
|
||||
if (m_DatagramDestination)
|
||||
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length, true);
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Missing raw datagram destination");
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]);
|
||||
}
|
||||
@@ -1105,8 +1107,8 @@ namespace client
|
||||
LogPrint (eLogInfo, "Destination: Creating new temporary keys of type for address ", ident, ".b32.i2p");
|
||||
memset (m_EncryptionPrivateKey, 0, 256);
|
||||
memset (m_EncryptionPublicKey, 0, 256);
|
||||
i2p::data::PrivateKeys::GenerateCryptoKeyPair (GetIdentity ()->GetCryptoKeyType (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
|
||||
i2p::data::PrivateKeys::GenerateCryptoKeyPair (m_EncryptionKeyType, m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
// TODO:: persist crypto key type
|
||||
std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out);
|
||||
if (f1) {
|
||||
f1.write ((char *)m_EncryptionPublicKey, 256);
|
||||
@@ -1128,11 +1130,13 @@ 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);
|
||||
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) // encrypt if type 5
|
||||
ls2 = std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (ls2, m_Keys);
|
||||
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;
|
||||
}
|
||||
SetLeaseSet (leaseSet);
|
||||
@@ -1143,7 +1147,7 @@ namespace client
|
||||
if (m_DatagramDestination) m_DatagramDestination->CleanUp ();
|
||||
}
|
||||
|
||||
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const
|
||||
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const
|
||||
{
|
||||
if (m_Decryptor)
|
||||
return m_Decryptor->Decrypt (encrypted, data, ctx, true);
|
||||
@@ -1151,5 +1155,63 @@ namespace client
|
||||
LogPrint (eLogError, "Destinations: decryptor is not set");
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClientDestination::ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params)
|
||||
{
|
||||
for (auto it: *params)
|
||||
if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group))
|
||||
{
|
||||
auto pos = it.second.find (':');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
i2p::data::AuthPublicKey pubKey;
|
||||
if (pubKey.FromBase64 (it.second.substr (pos+1)))
|
||||
m_AuthKeys->push_back (pubKey);
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Unexpected auth key ", it.second.substr (pos+1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
RunnableClientDestination::~RunnableClientDestination ()
|
||||
{
|
||||
if (IsRunning ())
|
||||
Stop ();
|
||||
}
|
||||
|
||||
void RunnableClientDestination::Start ()
|
||||
{
|
||||
if (!IsRunning ())
|
||||
{
|
||||
ClientDestination::Start ();
|
||||
StartIOService ();
|
||||
}
|
||||
}
|
||||
|
||||
void RunnableClientDestination::Stop ()
|
||||
{
|
||||
if (IsRunning ())
|
||||
{
|
||||
ClientDestination::Stop ();
|
||||
StopIOService ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#ifdef I2LUA
|
||||
#include <future>
|
||||
#endif
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
#include "TunnelPool.h"
|
||||
@@ -20,6 +17,7 @@
|
||||
#include "NetDb.hpp"
|
||||
#include "Streaming.h"
|
||||
#include "Datagram.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -55,7 +53,11 @@ namespace client
|
||||
const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType";
|
||||
const int DEFAULT_LEASESET_TYPE = 1;
|
||||
const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType";
|
||||
|
||||
const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64
|
||||
const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType";
|
||||
const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn
|
||||
const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn
|
||||
|
||||
// latency
|
||||
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
|
||||
const int DEFAULT_MIN_TUNNEL_LATENCY = 0;
|
||||
@@ -94,18 +96,17 @@ namespace client
|
||||
|
||||
public:
|
||||
|
||||
LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
LeaseSetDestination (boost::asio::io_service& service, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~LeaseSetDestination ();
|
||||
const std::string& GetNickname () const { return m_Nickname; };
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
|
||||
virtual bool Start ();
|
||||
virtual bool Stop ();
|
||||
virtual void Start ();
|
||||
virtual void Stop ();
|
||||
|
||||
/** i2cp reconfigure */
|
||||
virtual bool Reconfigure(std::map<std::string, std::string> i2cpOpts);
|
||||
|
||||
bool IsRunning () const { return m_IsRunning; };
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
|
||||
bool IsReady () const { return m_LeaseSet && !m_LeaseSet->IsExpired () && m_Pool->GetOutboundTunnels ().size () > 0; };
|
||||
std::shared_ptr<i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
|
||||
@@ -117,7 +118,6 @@ namespace client
|
||||
// implements GarlicDestination
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
|
||||
// override GarlicDestination
|
||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
||||
@@ -127,9 +127,15 @@ namespace client
|
||||
|
||||
protected:
|
||||
|
||||
// implements GarlicDestination
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len);
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len);
|
||||
|
||||
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
|
||||
int GetLeaseSetType () const { return m_LeaseSetType; };
|
||||
void SetLeaseSetType (int leaseSetType) { m_LeaseSetType = leaseSetType; };
|
||||
int GetAuthType () const { return m_AuthType; };
|
||||
bool IsPublic () const { return m_IsPublic; };
|
||||
virtual void CleanupDestination () {}; // additional clean up in derived classes
|
||||
// I2CP
|
||||
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
|
||||
@@ -137,7 +143,6 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void UpdateLeaseSet ();
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSetMt ();
|
||||
void Publish ();
|
||||
@@ -146,19 +151,18 @@ namespace client
|
||||
void HandlePublishDelayTimer (const boost::system::error_code& ecode);
|
||||
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
|
||||
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
|
||||
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleDeliveryStatusMessage (uint32_t msgID);
|
||||
|
||||
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> requestedBlindedKey = nullptr);
|
||||
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, std::shared_ptr<LeaseSetRequest> request);
|
||||
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
||||
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
||||
void CleanupRemoteLeaseSets ();
|
||||
i2p::data::CryptoKeyType GetPreferredCryptoType () const;
|
||||
|
||||
private:
|
||||
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service& m_Service;
|
||||
mutable std::mutex m_RemoteLeaseSetsMutex;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
|
||||
@@ -174,7 +178,8 @@ namespace client
|
||||
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer,
|
||||
m_PublishDelayTimer, m_CleanupTimer;
|
||||
std::string m_Nickname;
|
||||
int m_LeaseSetType;
|
||||
int m_LeaseSetType, m_AuthType;
|
||||
std::unique_ptr<i2p::data::Tag<32> > m_LeaseSetPrivKey; // non-null if presented
|
||||
|
||||
public:
|
||||
|
||||
@@ -182,24 +187,19 @@ namespace client
|
||||
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
|
||||
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; };
|
||||
bool IsEncryptedLeaseSet () const { return m_LeaseSetType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2; };
|
||||
bool IsPerClientAuth () const { return m_AuthType > 0; };
|
||||
};
|
||||
|
||||
class ClientDestination: public LeaseSetDestination
|
||||
{
|
||||
public:
|
||||
#ifdef I2LUA
|
||||
// type for informing that a client destination is ready
|
||||
typedef std::promise<std::shared_ptr<ClientDestination> > ReadyPromise;
|
||||
// informs promise with shared_from_this() when this destination is ready to use
|
||||
// if cancelled before ready, informs promise with nullptr
|
||||
void Ready(ReadyPromise & p);
|
||||
#endif
|
||||
|
||||
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys,
|
||||
bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~ClientDestination ();
|
||||
|
||||
virtual bool Start ();
|
||||
virtual bool Stop ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||
@@ -223,12 +223,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;
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
|
||||
bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const { return m_EncryptionKeyType == keyType; };
|
||||
const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const { return m_EncryptionPublicKey; };
|
||||
|
||||
protected:
|
||||
|
||||
@@ -239,13 +241,12 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<ClientDestination> GetSharedFromThis ()
|
||||
{ return std::static_pointer_cast<ClientDestination>(shared_from_this ()); }
|
||||
std::shared_ptr<ClientDestination> GetSharedFromThis () {
|
||||
return std::static_pointer_cast<ClientDestination>(shared_from_this ());
|
||||
}
|
||||
void PersistTemporaryKeys ();
|
||||
#ifdef I2LUA
|
||||
void ScheduleCheckForReady(ReadyPromise * p);
|
||||
void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p);
|
||||
#endif
|
||||
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
|
||||
|
||||
private:
|
||||
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
@@ -261,11 +262,26 @@ namespace client
|
||||
|
||||
boost::asio::deadline_timer m_ReadyChecker;
|
||||
|
||||
std::shared_ptr<std::vector<i2p::data::AuthPublicKey> > m_AuthKeys; // we don't need them for I2CP
|
||||
|
||||
public:
|
||||
|
||||
// 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
|
||||
{
|
||||
public:
|
||||
|
||||
RunnableClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~RunnableClientDestination ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
664
libi2pd/ECIESX25519AEADRatchetSession.cpp
Normal file
664
libi2pd/ECIESX25519AEADRatchetSession.cpp
Normal file
@@ -0,0 +1,664 @@
|
||||
#include <string.h>
|
||||
#include <openssl/sha.h>
|
||||
#include "Log.h"
|
||||
#include "Crypto.h"
|
||||
#include "Elligator.h"
|
||||
#include "Tag.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace garlic
|
||||
{
|
||||
|
||||
void RatchetTagSet::DHInitialize (const uint8_t * rootKey, const uint8_t * k)
|
||||
{
|
||||
// DH_INITIALIZE(rootKey, k)
|
||||
uint8_t keydata[64];
|
||||
i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64)
|
||||
// nextRootKey = keydata[0:31]
|
||||
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_KeyData.buf);
|
||||
// [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64)
|
||||
memcpy (m_SymmKeyCK, m_KeyData.buf + 32, 32);
|
||||
m_NextSymmKeyIndex = 0;
|
||||
}
|
||||
|
||||
void RatchetTagSet::NextSessionTagRatchet ()
|
||||
{
|
||||
i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), nullptr, 0, "STInitialization", m_KeyData.buf); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64)
|
||||
memcpy (m_SessTagConstant, m_KeyData.GetSessTagConstant (), 32);
|
||||
m_NextIndex = 0;
|
||||
}
|
||||
|
||||
uint64_t RatchetTagSet::GetNextSessionTag ()
|
||||
{
|
||||
i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
|
||||
m_NextIndex++;
|
||||
if (m_NextIndex >= 65535) m_NextIndex = 0; // TODO: dirty hack, should create new tagset
|
||||
return m_KeyData.GetTag ();
|
||||
}
|
||||
|
||||
void RatchetTagSet::GetSymmKey (int index, uint8_t * key)
|
||||
{
|
||||
if (m_NextSymmKeyIndex > 0 && index >= m_NextSymmKeyIndex)
|
||||
{
|
||||
auto num = index + 1 - m_NextSymmKeyIndex;
|
||||
for (int i = 0; i < num; i++)
|
||||
i2p::crypto::HKDF (m_CurrentSymmKeyCK, nullptr, 0, "SymmetricRatchet", m_CurrentSymmKeyCK);
|
||||
m_NextSymmKeyIndex += num;
|
||||
memcpy (key, m_CurrentSymmKeyCK + 32, 32);
|
||||
}
|
||||
else
|
||||
CalculateSymmKeyCK (index, key);
|
||||
}
|
||||
|
||||
void RatchetTagSet::CalculateSymmKeyCK (int index, uint8_t * key)
|
||||
{
|
||||
// TODO: store intermediate keys
|
||||
uint8_t currentSymmKeyCK[64];
|
||||
i2p::crypto::HKDF (m_SymmKeyCK, nullptr, 0, "SymmetricRatchet", currentSymmKeyCK); // keydata_0 = HKDF(symmKey_ck, SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
|
||||
for (int i = 0; i < index; i++)
|
||||
i2p::crypto::HKDF (currentSymmKeyCK, nullptr, 0, "SymmetricRatchet", currentSymmKeyCK); // keydata_n = HKDF(symmKey_chainKey_(n-1), SYMMKEY_CONSTANT, "SymmetricRatchet", 64)
|
||||
memcpy (key, currentSymmKeyCK + 32, 32);
|
||||
}
|
||||
|
||||
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner):
|
||||
GarlicRoutingSession (owner, true)
|
||||
{
|
||||
ResetKeys ();
|
||||
}
|
||||
|
||||
ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession ()
|
||||
{
|
||||
}
|
||||
|
||||
void ECIESX25519AEADRatchetSession::ResetKeys ()
|
||||
{
|
||||
// TODO : use precalculated hashes
|
||||
static const char protocolName[41] = "Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"; // 40 bytes
|
||||
SHA256 ((const uint8_t *)protocolName, 40, m_H);
|
||||
memcpy (m_CK, m_H, 32);
|
||||
SHA256 (m_H, 32, m_H);
|
||||
}
|
||||
|
||||
void ECIESX25519AEADRatchetSession::MixHash (const uint8_t * buf, size_t len)
|
||||
{
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init (&ctx);
|
||||
SHA256_Update (&ctx, m_H, 32);
|
||||
SHA256_Update (&ctx, buf, len);
|
||||
SHA256_Final (m_H, &ctx);
|
||||
}
|
||||
|
||||
void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce)
|
||||
{
|
||||
memset (nonce, 0, 4);
|
||||
htole64buf (nonce + 4, seqn);
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::GenerateEphemeralKeysAndEncode (uint8_t * buf)
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
m_EphemeralKeys.GenerateKeys ();
|
||||
if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys.GetPublicKey (), buf))
|
||||
return true; // success
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t ECIESX25519AEADRatchetSession::CreateNewSessionTag () const
|
||||
{
|
||||
uint8_t tagsetKey[32];
|
||||
i2p::crypto::HKDF (m_CK, nullptr, 0, "SessionReplyTags", tagsetKey, 32); // tagsetKey = HKDF(chainKey, ZEROLEN, "SessionReplyTags", 32)
|
||||
// Session Tag Ratchet
|
||||
RatchetTagSet tagsetNsr;
|
||||
tagsetNsr.DHInitialize (m_CK, tagsetKey); // tagset_nsr = DH_INITIALIZE(chainKey, tagsetKey)
|
||||
tagsetNsr.NextSessionTagRatchet ();
|
||||
return tagsetNsr.GetNextSessionTag ();
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::HandleNewIncomingSession (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (!GetOwner ()) return false;
|
||||
// we are Bob
|
||||
// KDF1
|
||||
MixHash (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET), 32); // h = SHA256(h || bpk)
|
||||
|
||||
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: Can't decode elligator");
|
||||
return false;
|
||||
}
|
||||
buf += 32; len -= 32;
|
||||
MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
|
||||
|
||||
uint8_t sharedSecret[32];
|
||||
GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519(bsk, aepk)
|
||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
||||
|
||||
// decrypt flags/static
|
||||
uint8_t nonce[12], fs[32];
|
||||
CreateNonce (0, nonce);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 32, m_H, 32, m_CK + 32, nonce, fs, 32, false)) // decrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD verification failed ");
|
||||
return false;
|
||||
}
|
||||
MixHash (buf, 48); // h = SHA256(h || ciphertext)
|
||||
buf += 48; len -= 48; // 32 data + 16 poly
|
||||
|
||||
// decrypt payload
|
||||
std::vector<uint8_t> payload (len - 16);
|
||||
// KDF2 for payload
|
||||
bool isStatic = !i2p::data::Tag<32> (fs).IsZero ();
|
||||
if (isStatic)
|
||||
{
|
||||
// static key, fs is apk
|
||||
memcpy (m_RemoteStaticKey, fs, 32);
|
||||
GetOwner ()->Decrypt (fs, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519(bsk, apk)
|
||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
||||
}
|
||||
else // all zeros flags
|
||||
CreateNonce (1, nonce);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed");
|
||||
return false;
|
||||
}
|
||||
if (isStatic) MixHash (buf, len); // h = SHA256(h || ciphertext)
|
||||
m_State = eSessionStateNewSessionReceived;
|
||||
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
|
||||
|
||||
HandlePayload (payload.data (), len - 16);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ECIESX25519AEADRatchetSession::HandlePayload (const uint8_t * buf, size_t len, int index)
|
||||
{
|
||||
size_t offset = 0;
|
||||
while (offset < len)
|
||||
{
|
||||
uint8_t blk = buf[offset];
|
||||
offset++;
|
||||
auto size = bufbe16toh (buf + offset);
|
||||
offset += 2;
|
||||
LogPrint (eLogDebug, "Garlic: Block type ", (int)blk, " of size ", size);
|
||||
if (size > len)
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: Unexpected block length ", size);
|
||||
break;
|
||||
}
|
||||
switch (blk)
|
||||
{
|
||||
case eECIESx25519BlkGalicClove:
|
||||
GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size);
|
||||
break;
|
||||
case eECIESx25519BlkDateTime:
|
||||
LogPrint (eLogDebug, "Garlic: datetime");
|
||||
break;
|
||||
case eECIESx25519BlkOptions:
|
||||
LogPrint (eLogDebug, "Garlic: options");
|
||||
break;
|
||||
case eECIESx25519BlkPadding:
|
||||
LogPrint (eLogDebug, "Garlic: padding");
|
||||
break;
|
||||
case eECIESx25519BlkAckRequest:
|
||||
{
|
||||
LogPrint (eLogDebug, "Garlic: ack request");
|
||||
m_AckRequests.push_back ({0, index}); // TODO: use actual tagsetid
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk);
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
|
||||
{
|
||||
ResetKeys ();
|
||||
// we are Alice, bpk is m_RemoteStaticKey
|
||||
size_t offset = 0;
|
||||
if (!GenerateEphemeralKeysAndEncode (out + offset))
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: Can't encode elligator");
|
||||
return false;
|
||||
}
|
||||
offset += 32;
|
||||
|
||||
// KDF1
|
||||
MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk)
|
||||
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || aepk)
|
||||
uint8_t sharedSecret[32];
|
||||
m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk)
|
||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
||||
// encrypt static key section
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Static section AEAD encryption failed ");
|
||||
return false;
|
||||
}
|
||||
MixHash (out + offset, 48); // h = SHA256(h || ciphertext)
|
||||
offset += 48;
|
||||
// KDF2
|
||||
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519 (ask, bpk)
|
||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
||||
// encrypt payload
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext)
|
||||
|
||||
m_State = eSessionStateNewSessionSent;
|
||||
if (GetOwner ())
|
||||
GetOwner ()->AddECIESx25519SessionTag (0, CreateNewSessionTag (), shared_from_this ());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
|
||||
{
|
||||
// we are Bob
|
||||
uint64_t tag = CreateNewSessionTag ();
|
||||
|
||||
size_t offset = 0;
|
||||
memcpy (out + offset, &tag, 8);
|
||||
offset += 8;
|
||||
if (!GenerateEphemeralKeysAndEncode (out + offset)) // bepk
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: Can't encode elligator");
|
||||
return false;
|
||||
}
|
||||
offset += 32;
|
||||
// KDF for Reply Key Section
|
||||
MixHash ((const uint8_t *)&tag, 8); // h = SHA256(h || tag)
|
||||
MixHash (m_EphemeralKeys.GetPublicKey (), 32); // h = SHA256(h || bepk)
|
||||
uint8_t sharedSecret[32];
|
||||
m_EphemeralKeys.Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk)
|
||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
|
||||
m_EphemeralKeys.Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk)
|
||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
// calulate hash for zero length
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (sharedSecret /* can be anything */, 0, m_H, 32, m_CK + 32, nonce, out + offset, 16, true)) // encrypt, ciphertext = ENCRYPT(k, n, ZEROLEN, ad)
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Reply key section AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
MixHash (out + offset, 16); // h = SHA256(h || ciphertext)
|
||||
offset += 16;
|
||||
memcpy (m_NSRHeader, out, 56); // for possible next NSR
|
||||
// KDF for payload
|
||||
uint8_t keydata[64];
|
||||
i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64)
|
||||
// k_ab = keydata[0:31], k_ba = keydata[32:63]
|
||||
m_ReceiveTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
|
||||
m_ReceiveTagset.NextSessionTagRatchet ();
|
||||
m_SendTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
|
||||
m_SendTagset.NextSessionTagRatchet ();
|
||||
GenerateMoreReceiveTags (GetOwner ()->GetNumTags ());
|
||||
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", m_NSRKey, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
|
||||
// encrypt payload
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + offset, len + 16, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: NSR payload section AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
m_State = eSessionStateNewSessionReplySent;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
|
||||
{
|
||||
// we are Bob and sent NSR already
|
||||
memcpy (out, m_NSRHeader, 56);
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
// encrypt payload
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_NSRKey, nonce, out + 56, len + 16, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Next NSR payload section AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len)
|
||||
{
|
||||
// we are Alice
|
||||
LogPrint (eLogDebug, "Garlic: reply received");
|
||||
const uint8_t * tag = buf;
|
||||
buf += 8; len -= 8; // tag
|
||||
uint8_t bepk[32]; // Bob's ephemeral key
|
||||
if (!i2p::crypto::GetElligator ()->Decode (buf, bepk))
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: Can't decode elligator");
|
||||
return false;
|
||||
}
|
||||
buf += 32; len -= 32;
|
||||
// KDF for Reply Key Section
|
||||
MixHash (tag, 8); // h = SHA256(h || tag)
|
||||
MixHash (bepk, 32); // h = SHA256(h || bepk)
|
||||
uint8_t sharedSecret[32];
|
||||
m_EphemeralKeys.Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk)
|
||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32)
|
||||
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET); // x25519 (ask, bepk)
|
||||
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64)
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (0, nonce);
|
||||
// calulate hash for zero length
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, 0, m_H, 32, m_CK + 32, nonce, sharedSecret/* can be anyting */, 0, false)) // decrypt, DECRYPT(k, n, ZEROLEN, ad) verification only
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Reply key section AEAD decryption failed");
|
||||
return false;
|
||||
}
|
||||
MixHash (buf, 16); // h = SHA256(h || ciphertext)
|
||||
buf += 16; len -= 16;
|
||||
// KDF for payload
|
||||
uint8_t keydata[64];
|
||||
i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64)
|
||||
// k_ab = keydata[0:31], k_ba = keydata[32:63]
|
||||
m_SendTagset.DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
|
||||
m_SendTagset.NextSessionTagRatchet ();
|
||||
m_ReceiveTagset.DHInitialize (m_CK, keydata + 32); // tagset_ba = DH_INITIALIZE(chainKey, k_ba)
|
||||
m_ReceiveTagset.NextSessionTagRatchet ();
|
||||
GenerateMoreReceiveTags (GetOwner ()->GetNumTags ());
|
||||
i2p::crypto::HKDF (keydata + 32, nullptr, 0, "AttachPayloadKDF", keydata, 32); // k = HKDF(k_ba, ZEROLEN, "AttachPayloadKDF", 32)
|
||||
// decrypt payload
|
||||
std::vector<uint8_t> payload (len - 16);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, keydata, nonce, payload.data (), len - 16, false)) // decrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_State = eSessionStateEstablished;
|
||||
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
|
||||
HandlePayload (payload.data (), len - 16);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
|
||||
{
|
||||
uint8_t nonce[12];
|
||||
auto index = m_SendTagset.GetNextIndex ();
|
||||
CreateNonce (index, nonce); // tag's index
|
||||
uint64_t tag = m_SendTagset.GetNextSessionTag ();
|
||||
memcpy (out, &tag, 8);
|
||||
// ad = The session tag, 8 bytes
|
||||
// ciphertext = ENCRYPT(k, n, payload, ad)
|
||||
uint8_t key[32];
|
||||
m_SendTagset.GetSymmKey (index, key);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, out, 8, key, nonce, out + 8, outLen - 8, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index)
|
||||
{
|
||||
uint8_t nonce[12];
|
||||
CreateNonce (index, nonce); // tag's index
|
||||
len -= 8; // tag
|
||||
std::vector<uint8_t> payload (len - 16);
|
||||
uint8_t key[32];
|
||||
m_ReceiveTagset.GetSymmKey (index, key);
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 8, len - 16, buf, 8, key, nonce, payload.data (), len - 16, false)) // decrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD decryption failed");
|
||||
return false;
|
||||
}
|
||||
HandlePayload (payload.data (), len - 16, index);
|
||||
if (m_ReceiveTagset.GetNextIndex () - index <= GetOwner ()->GetNumTags ()*2/3)
|
||||
GenerateMoreReceiveTags (GetOwner ()->GetNumTags ());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len, int index)
|
||||
{
|
||||
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
switch (m_State)
|
||||
{
|
||||
case eSessionStateNewSessionReplySent:
|
||||
m_State = eSessionStateEstablished;
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
case eSessionStateEstablished:
|
||||
return HandleExistingSessionMessage (buf, len, index);
|
||||
case eSessionStateNew:
|
||||
return HandleNewIncomingSession (buf, len);
|
||||
case eSessionStateNewSessionSent:
|
||||
return HandleNewOutgoingSessionReply (buf, len);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
auto m = NewI2NPMessage ();
|
||||
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
||||
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
|
||||
auto payload = CreatePayload (msg);
|
||||
size_t len = payload.size ();
|
||||
|
||||
switch (m_State)
|
||||
{
|
||||
case eSessionStateEstablished:
|
||||
if (!NewExistingSessionMessage (payload.data (), payload.size (), buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 24;
|
||||
break;
|
||||
case eSessionStateNew:
|
||||
if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 96;
|
||||
break;
|
||||
case eSessionStateNewSessionReceived:
|
||||
if (!NewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 72;
|
||||
break;
|
||||
case eSessionStateNewSessionReplySent:
|
||||
if (!NextNewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen))
|
||||
return nullptr;
|
||||
len += 72;
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
htobe32buf (m->GetPayload (), len);
|
||||
m->len += len + 4;
|
||||
m->FillI2NPMessageHeader (eI2NPGarlic);
|
||||
return m;
|
||||
}
|
||||
|
||||
std::vector<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 = (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
|
||||
payloadLen += paddingSize + 3;
|
||||
std::vector<uint8_t> v(payloadLen);
|
||||
size_t offset = 0;
|
||||
// DateTime
|
||||
v[offset] = eECIESx25519BlkDateTime; offset++;
|
||||
htobe16buf (v.data () + offset, 4); offset += 2;
|
||||
htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds
|
||||
// LeaseSet
|
||||
if (leaseSet)
|
||||
offset += CreateGarlicClove (leaseSet, v.data () + offset, payloadLen - offset);
|
||||
// DeliveryStatus
|
||||
if (deliveryStatus)
|
||||
offset += CreateDeliveryStatusClove (deliveryStatus, v.data () + offset, payloadLen - offset);
|
||||
// msg
|
||||
if (msg && m_Destination)
|
||||
offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset, true);
|
||||
// ack
|
||||
if (m_AckRequests.size () > 0)
|
||||
{
|
||||
v[offset] = eECIESx25519BlkAck; offset++;
|
||||
htobe16buf (v.data () + offset, m_AckRequests.size ()*4); offset += 2;
|
||||
for (auto& it: m_AckRequests)
|
||||
{
|
||||
htobe16buf (v.data () + offset, it.first); offset += 2;
|
||||
htobe16buf (v.data () + offset, it.second); offset += 2;
|
||||
}
|
||||
m_AckRequests.clear ();
|
||||
}
|
||||
// padding
|
||||
v[offset] = eECIESx25519BlkPadding; offset++;
|
||||
htobe16buf (v.data () + offset, paddingSize); offset += 2;
|
||||
memset (v.data () + offset, 0, paddingSize); offset += paddingSize;
|
||||
return v;
|
||||
}
|
||||
|
||||
size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len, bool isDestination)
|
||||
{
|
||||
if (!msg) return 0;
|
||||
uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1;
|
||||
if (isDestination) cloveSize += 32;
|
||||
if ((int)len < cloveSize + 3) return 0;
|
||||
buf[0] = eECIESx25519BlkGalicClove; // clove type
|
||||
htobe16buf (buf + 1, cloveSize); // size
|
||||
buf += 3;
|
||||
if (isDestination)
|
||||
{
|
||||
*buf = (eGarlicDeliveryTypeDestination << 5);
|
||||
memcpy (buf + 1, *m_Destination, 32); buf += 32;
|
||||
}
|
||||
else
|
||||
*buf = 0;
|
||||
buf++; // flag and delivery instructions
|
||||
*buf = msg->GetTypeID (); // I2NP msg type
|
||||
htobe32buf (buf + 1, msg->GetMsgID ()); // msgID
|
||||
htobe32buf (buf + 5, msg->GetExpiration ()/1000); // expiration in seconds
|
||||
memcpy (buf + 9, msg->GetPayload (), msg->GetPayloadLength ());
|
||||
return cloveSize + 3;
|
||||
}
|
||||
|
||||
size_t ECIESX25519AEADRatchetSession::CreateDeliveryStatusClove (std::shared_ptr<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++)
|
||||
{
|
||||
auto index = m_ReceiveTagset.GetNextIndex ();
|
||||
uint64_t tag = m_ReceiveTagset.GetNextSessionTag ();
|
||||
GetOwner ()->AddECIESx25519SessionTag (index, tag, shared_from_this ());
|
||||
}
|
||||
}
|
||||
|
||||
bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts)
|
||||
{
|
||||
CleanupUnconfirmedLeaseSet (ts);
|
||||
return ts > m_LastActivityTimestamp + ECIESX25519_EXPIRATION_TIMEOUT;
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag)
|
||||
{
|
||||
auto m = NewI2NPMessage ();
|
||||
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
||||
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
|
||||
uint8_t nonce[12];
|
||||
memset (nonce, 0, 12); // n = 0
|
||||
size_t offset = 0;
|
||||
memcpy (buf + offset, &tag, 8); offset += 8;
|
||||
auto payload = buf + offset;
|
||||
uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1;
|
||||
size_t len = cloveSize + 3;
|
||||
payload[0] = eECIESx25519BlkGalicClove; // clove type
|
||||
htobe16buf (payload + 1, cloveSize); // size
|
||||
payload += 3;
|
||||
*payload = 0; payload++; // flag and delivery instructions
|
||||
*payload = msg->GetTypeID (); // I2NP msg type
|
||||
htobe32buf (payload + 1, msg->GetMsgID ()); // msgID
|
||||
htobe32buf (payload + 5, msg->GetExpiration ()/1000); // expiration in seconds
|
||||
memcpy (payload + 9, msg->GetPayload (), msg->GetPayloadLength ());
|
||||
|
||||
if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len, buf, 8, key, nonce, buf + offset, len + 16, true)) // encrypt
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
|
||||
return nullptr;
|
||||
}
|
||||
offset += len + 16;
|
||||
|
||||
htobe32buf (m->GetPayload (), offset);
|
||||
m->len += offset + 4;
|
||||
m->FillI2NPMessageHeader (eI2NPGarlic);
|
||||
return m;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
136
libi2pd/ECIESX25519AEADRatchetSession.h
Normal file
136
libi2pd/ECIESX25519AEADRatchetSession.h
Normal file
@@ -0,0 +1,136 @@
|
||||
#ifndef ECIES_X25519_AEAD_RATCHET_SESSION_H__
|
||||
#define ECIES_X25519_AEAD_RATCHET_SESSION_H__
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include "Identity.h"
|
||||
#include "Crypto.h"
|
||||
#include "Garlic.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace garlic
|
||||
{
|
||||
class RatchetTagSet
|
||||
{
|
||||
public:
|
||||
|
||||
void DHInitialize (const uint8_t * rootKey, const uint8_t * k);
|
||||
void NextSessionTagRatchet ();
|
||||
uint64_t GetNextSessionTag ();
|
||||
int GetNextIndex () const { return m_NextIndex; };
|
||||
void GetSymmKey (int index, uint8_t * key);
|
||||
|
||||
private:
|
||||
|
||||
void CalculateSymmKeyCK (int index, uint8_t * key);
|
||||
|
||||
private:
|
||||
|
||||
union
|
||||
{
|
||||
uint64_t ll[8];
|
||||
uint8_t buf[64];
|
||||
|
||||
const uint8_t * GetSessTagCK () const { return buf; }; // sessTag_chainKey = keydata[0:31]
|
||||
const uint8_t * GetSessTagConstant () const { return buf + 32; }; // SESSTAG_CONSTANT = keydata[32:63]
|
||||
uint64_t GetTag () const { return ll[4]; }; // tag = keydata[32:39]
|
||||
|
||||
} m_KeyData;
|
||||
uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64];
|
||||
int m_NextIndex, m_NextSymmKeyIndex;
|
||||
};
|
||||
|
||||
enum ECIESx25519BlockType
|
||||
{
|
||||
eECIESx25519BlkDateTime = 0,
|
||||
eECIESx25519BlkSessionID = 1,
|
||||
eECIESx25519BlkTermination = 4,
|
||||
eECIESx25519BlkOptions = 5,
|
||||
eECIESx25519BlkNextSessionKey = 7,
|
||||
eECIESx25519BlkAck = 8,
|
||||
eECIESx25519BlkAckRequest = 9,
|
||||
eECIESx25519BlkGalicClove = 11,
|
||||
eECIESx25519BlkPadding = 254
|
||||
};
|
||||
|
||||
|
||||
const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second of inactivity we should restart after
|
||||
const int ECIESX25519_EXPIRATION_TIMEOUT = 600; // in seconds
|
||||
|
||||
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, public std::enable_shared_from_this<ECIESX25519AEADRatchetSession>
|
||||
{
|
||||
enum SessionState
|
||||
{
|
||||
eSessionStateNew =0,
|
||||
eSessionStateNewSessionReceived,
|
||||
eSessionStateNewSessionSent,
|
||||
eSessionStateNewSessionReplySent,
|
||||
eSessionStateEstablished
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
ECIESX25519AEADRatchetSession (GarlicDestination * owner);
|
||||
~ECIESX25519AEADRatchetSession ();
|
||||
|
||||
bool HandleNextMessage (const uint8_t * buf, size_t len, int index = 0);
|
||||
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
|
||||
|
||||
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
|
||||
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
|
||||
|
||||
void SetDestination (const i2p::data::IdentHash& dest) // TODO:
|
||||
{
|
||||
if (!m_Destination) m_Destination.reset (new i2p::data::IdentHash (dest));
|
||||
}
|
||||
|
||||
bool CheckExpired (uint64_t ts); // true is expired
|
||||
bool CanBeRestarted (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_RESTART_TIMEOUT; }
|
||||
|
||||
private:
|
||||
|
||||
void ResetKeys ();
|
||||
void MixHash (const uint8_t * buf, size_t len);
|
||||
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
||||
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
|
||||
uint64_t CreateNewSessionTag () const;
|
||||
|
||||
bool HandleNewIncomingSession (const uint8_t * buf, size_t len);
|
||||
bool HandleNewOutgoingSessionReply (const uint8_t * buf, size_t len);
|
||||
bool HandleExistingSessionMessage (const uint8_t * buf, size_t len, int index);
|
||||
void HandlePayload (const uint8_t * buf, size_t len, int index = 0);
|
||||
|
||||
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
|
||||
bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
|
||||
bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
|
||||
bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
|
||||
|
||||
std::vector<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);
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32];
|
||||
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
|
||||
uint8_t m_NSRHeader[56], m_NSRKey[32]; // new session reply, for incoming only
|
||||
i2p::crypto::X25519Keys m_EphemeralKeys;
|
||||
SessionState m_State = eSessionStateNew;
|
||||
uint64_t m_LastActivityTimestamp = 0; // incoming
|
||||
RatchetTagSet m_SendTagset, m_ReceiveTagset;
|
||||
std::unique_ptr<i2p::data::IdentHash> m_Destination;// TODO: might not need it
|
||||
std::list<std::pair<uint16_t, int> > m_AckRequests; // (tagsetid, index)
|
||||
};
|
||||
|
||||
std::shared_ptr<I2NPMessage> WrapECIESX25519AEADRatchetMessage (std::shared_ptr<const I2NPMessage> msg, const uint8_t * key, uint64_t tag);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -31,7 +31,7 @@ namespace crypto
|
||||
BN_mod_inverse (tmp, tmp, q, ctx);
|
||||
BN_set_word (d, 121665);
|
||||
BN_set_negative (d, 1);
|
||||
BN_mul (d, d, tmp, ctx);
|
||||
BN_mod_mul (d, d, tmp, q, ctx);
|
||||
|
||||
// 2^((q-1)/4)
|
||||
I = BN_new ();
|
||||
|
||||
207
libi2pd/Elligator.cpp
Normal file
207
libi2pd/Elligator.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
#include <openssl/rand.h>
|
||||
#include "Crypto.h"
|
||||
#include "Elligator.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
Elligator2::Elligator2 ()
|
||||
{
|
||||
// TODO: share with Ed22519
|
||||
p = BN_new ();
|
||||
// 2^255-19
|
||||
BN_set_bit (p, 255); // 2^255
|
||||
BN_sub_word (p, 19);
|
||||
p38 = BN_dup (p); BN_add_word (p38, 3); BN_div_word (p38, 8); // (p+3)/8
|
||||
p12 = BN_dup (p); BN_sub_word (p12, 1); BN_div_word (p12, 2); // (p-1)/2
|
||||
p14 = BN_dup (p); BN_sub_word (p14, 1); BN_div_word (p14, 4); // (p-1)/4
|
||||
|
||||
A = BN_new (); BN_set_word (A, 486662);
|
||||
nA = BN_new (); BN_sub (nA, p, A);
|
||||
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
// calculate sqrt(-1)
|
||||
sqrtn1 = BN_new ();
|
||||
BN_set_word (sqrtn1, 2);
|
||||
BN_mod_exp (sqrtn1, sqrtn1, p14, p, ctx); // 2^((p-1)/4
|
||||
|
||||
u = BN_new (); BN_set_word (u, 2);
|
||||
iu = BN_new (); BN_mod_inverse (iu, u, p, ctx);
|
||||
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
Elligator2::~Elligator2 ()
|
||||
{
|
||||
BN_free (p); BN_free (p38); BN_free (p12); BN_free (p14);
|
||||
BN_free (sqrtn1); BN_free (A); BN_free (nA);
|
||||
BN_free (u); BN_free (iu);
|
||||
}
|
||||
|
||||
bool Elligator2::Encode (const uint8_t * key, uint8_t * encoded, bool highY, bool random) const
|
||||
{
|
||||
bool ret = true;
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
BN_CTX_start (ctx);
|
||||
|
||||
uint8_t key1[32];
|
||||
for (size_t i = 0; i < 16; i++) // from Little Endian
|
||||
{
|
||||
key1[i] = key[31 - i];
|
||||
key1[31 - i] = key[i];
|
||||
}
|
||||
|
||||
BIGNUM * x = BN_CTX_get (ctx); BN_bin2bn (key1, 32, x);
|
||||
BIGNUM * xA = BN_CTX_get (ctx); BN_add (xA, x, A); // x + A
|
||||
BN_sub (xA, p, xA); // p - (x + A)
|
||||
|
||||
BIGNUM * uxxA = BN_CTX_get (ctx); // u*x*xA
|
||||
BN_mod_mul (uxxA, u, x, p, ctx);
|
||||
BN_mod_mul (uxxA, uxxA, xA, p, ctx);
|
||||
|
||||
if (Legendre (uxxA, ctx) != -1)
|
||||
{
|
||||
uint8_t randByte = 0; // random highest bits and high y
|
||||
if (random)
|
||||
{
|
||||
RAND_bytes (&randByte, 1);
|
||||
highY = randByte & 0x01;
|
||||
}
|
||||
|
||||
BIGNUM * r = BN_CTX_get (ctx);
|
||||
if (highY)
|
||||
{
|
||||
BN_mod_inverse (r, x, p, ctx);
|
||||
BN_mod_mul (r, r, xA, p, ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
BN_mod_inverse (r, xA, p, ctx);
|
||||
BN_mod_mul (r, r, x, p, ctx);
|
||||
}
|
||||
BN_mod_mul (r, r, iu, p, ctx);
|
||||
|
||||
SquareRoot (r, r, ctx);
|
||||
bn2buf (r, encoded, 32);
|
||||
|
||||
if (random)
|
||||
encoded[0] |= (randByte & 0xC0); // copy two highest bits from randByte
|
||||
for (size_t i = 0; i < 16; i++) // To Little Endian
|
||||
{
|
||||
uint8_t tmp = encoded[i];
|
||||
encoded[i] = encoded[31 - i];
|
||||
encoded[31 - i] = tmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
ret = false;
|
||||
|
||||
BN_CTX_end (ctx);
|
||||
BN_CTX_free (ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Elligator2::Decode (const uint8_t * encoded, uint8_t * key) const
|
||||
{
|
||||
bool ret = true;
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
BN_CTX_start (ctx);
|
||||
|
||||
uint8_t encoded1[32];
|
||||
for (size_t i = 0; i < 16; i++) // from Little Endian
|
||||
{
|
||||
encoded1[i] = encoded[31 - i];
|
||||
encoded1[31 - i] = encoded[i];
|
||||
}
|
||||
encoded1[0] &= 0x3F; // drop two highest bits
|
||||
|
||||
BIGNUM * r = BN_CTX_get (ctx); BN_bin2bn (encoded1, 32, r);
|
||||
|
||||
if (BN_cmp (r, p12) <= 0) // r < (p-1)/2
|
||||
{
|
||||
// v = -A/(1+u*r^2)
|
||||
BIGNUM * v = BN_CTX_get (ctx); BN_mod_sqr (v, r, p, ctx);
|
||||
BN_mod_mul (v, v, u, p, ctx);
|
||||
BN_add_word (v, 1);
|
||||
BN_mod_inverse (v, v, p, ctx);
|
||||
BN_mod_mul (v, v, nA, p, ctx);
|
||||
|
||||
BIGNUM * vpA = BN_CTX_get (ctx);
|
||||
BN_add (vpA, v, A); // v + A
|
||||
// t = v^3+A*v^2+v = v^2*(v+A)+v
|
||||
BIGNUM * t = BN_CTX_get (ctx); BN_mod_sqr (t, v, p, ctx);
|
||||
BN_mod_mul (t, t, vpA, p, ctx);
|
||||
BN_mod_add (t, t, v, p, ctx);
|
||||
|
||||
int legendre = Legendre (t, ctx);
|
||||
BIGNUM * x = BN_CTX_get (ctx);
|
||||
if (legendre == 1)
|
||||
BN_copy (x, v);
|
||||
else
|
||||
{
|
||||
BN_sub (x, p, v);
|
||||
BN_mod_sub (x, x, A, p, ctx);
|
||||
}
|
||||
|
||||
bn2buf (x, key, 32);
|
||||
for (size_t i = 0; i < 16; i++) // To Little Endian
|
||||
{
|
||||
uint8_t tmp = key[i];
|
||||
key[i] = key[31 - i];
|
||||
key[31 - i] = tmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
ret = false;
|
||||
|
||||
BN_CTX_end (ctx);
|
||||
BN_CTX_free (ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Elligator2::SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const
|
||||
{
|
||||
BIGNUM * t = BN_CTX_get (ctx);
|
||||
BN_mod_exp (t, x, p14, p, ctx); // t = x^((p-1)/4)
|
||||
BN_mod_exp (r, x, p38, p, ctx); // r = x^((p+3)/8)
|
||||
BN_add_word (t, 1);
|
||||
|
||||
if (!BN_cmp (t, p))
|
||||
BN_mod_mul (r, r, sqrtn1, p, ctx);
|
||||
|
||||
if (BN_cmp (r, p12) > 0) // r > (p-1)/2
|
||||
BN_sub (r, p, r);
|
||||
}
|
||||
|
||||
int Elligator2::Legendre (const BIGNUM * a, BN_CTX * ctx) const
|
||||
{
|
||||
// assume a < p, so don't check for a % p = 0, but a = 0 only
|
||||
if (BN_is_zero(a)) return 0;
|
||||
BIGNUM * r = BN_CTX_get (ctx);
|
||||
BN_mod_exp (r, a, p12, p, ctx); // r = a^((p-1)/2) mod p
|
||||
if (BN_is_word(r, 1))
|
||||
return 1;
|
||||
else if (BN_is_zero(r))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Elligator2> g_Elligator;
|
||||
std::unique_ptr<Elligator2>& GetElligator ()
|
||||
{
|
||||
if (!g_Elligator)
|
||||
{
|
||||
auto el = new Elligator2();
|
||||
if (!g_Elligator) // make sure it was not created already
|
||||
g_Elligator.reset (el);
|
||||
else
|
||||
delete el;
|
||||
}
|
||||
return g_Elligator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
39
libi2pd/Elligator.h
Normal file
39
libi2pd/Elligator.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef ELLIGATOR_H__
|
||||
#define ELLIGATOR_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <memory>
|
||||
#include <openssl/bn.h>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
class Elligator2
|
||||
{
|
||||
public:
|
||||
|
||||
Elligator2 ();
|
||||
~Elligator2 ();
|
||||
|
||||
bool Encode (const uint8_t * key, uint8_t * encoded, bool highY = false, bool random = true) const;
|
||||
bool Decode (const uint8_t * encoded, uint8_t * key) const;
|
||||
|
||||
private:
|
||||
|
||||
void SquareRoot (const BIGNUM * x, BIGNUM * r, BN_CTX * ctx) const;
|
||||
int Legendre (const BIGNUM * a, BN_CTX * ctx) const; // a/p
|
||||
|
||||
private:
|
||||
|
||||
BIGNUM * p, * p38, * p12, * p14, * sqrtn1, * A, * nA, * u, * iu;
|
||||
};
|
||||
|
||||
std::unique_ptr<Elligator2>& GetElligator ();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -11,30 +11,22 @@
|
||||
#include "Timestamp.h"
|
||||
#include "Log.h"
|
||||
#include "FS.h"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
#include "Garlic.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace garlic
|
||||
{
|
||||
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner,
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
|
||||
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
|
||||
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
|
||||
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet):
|
||||
m_Owner (owner), m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend),
|
||||
m_LeaseSetUpdateMsgID (0)
|
||||
{
|
||||
// create new session tags and session key
|
||||
RAND_bytes (m_SessionKey, 32);
|
||||
m_Encryption.SetKey (m_SessionKey);
|
||||
}
|
||||
|
||||
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
|
||||
m_Owner (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
|
||||
GarlicRoutingSession::GarlicRoutingSession ():
|
||||
m_Owner (nullptr), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
|
||||
{
|
||||
memcpy (m_SessionKey, sessionKey, 32);
|
||||
m_Encryption.SetKey (m_SessionKey);
|
||||
m_SessionTags.push_back (sessionTag);
|
||||
m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
GarlicRoutingSession::~GarlicRoutingSession ()
|
||||
@@ -66,88 +58,64 @@ namespace garlic
|
||||
m_SharedRoutingPath = path;
|
||||
}
|
||||
|
||||
GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags ()
|
||||
bool GarlicRoutingSession::MessageConfirmed (uint32_t msgID)
|
||||
{
|
||||
auto tags = new UnconfirmedTags (m_NumTags);
|
||||
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (int i = 0; i < m_NumTags; i++)
|
||||
if (msgID == GetLeaseSetUpdateMsgID ())
|
||||
{
|
||||
RAND_bytes (tags->sessionTags[i], 32);
|
||||
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
void GarlicRoutingSession::MessageConfirmed (uint32_t msgID)
|
||||
{
|
||||
TagsConfirmed (msgID);
|
||||
if (msgID == m_LeaseSetUpdateMsgID)
|
||||
{
|
||||
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
|
||||
m_LeaseSetUpdateMsgID = 0;
|
||||
SetLeaseSetUpdateStatus (eLeaseSetUpToDate);
|
||||
SetLeaseSetUpdateMsgID (0);
|
||||
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
CleanupExpiredTags ();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
|
||||
void GarlicRoutingSession::CleanupUnconfirmedLeaseSet (uint64_t ts)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
auto it = m_UnconfirmedTagsMsgs.find (msgID);
|
||||
if (it != m_UnconfirmedTagsMsgs.end ())
|
||||
{
|
||||
auto& tags = it->second;
|
||||
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
for (int i = 0; i < tags->numTags; i++)
|
||||
m_SessionTags.push_back (tags->sessionTags[i]);
|
||||
}
|
||||
m_UnconfirmedTagsMsgs.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
bool GarlicRoutingSession::CleanupExpiredTags ()
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
|
||||
{
|
||||
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
it = m_SessionTags.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
CleanupUnconfirmedTags ();
|
||||
if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
|
||||
if (GetOwner ())
|
||||
GetOwner ()->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
|
||||
m_LeaseSetUpdateMsgID = 0;
|
||||
}
|
||||
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
|
||||
}
|
||||
|
||||
bool GarlicRoutingSession::CleanupUnconfirmedTags ()
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> GarlicRoutingSession::CreateEncryptedDeliveryStatusMsg (uint32_t msgID)
|
||||
{
|
||||
bool ret = false;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
// delete expired unconfirmed tags
|
||||
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
|
||||
auto msg = CreateDeliveryStatusMsg (msgID);
|
||||
if (GetOwner ())
|
||||
{
|
||||
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveDeliveryStatusSession (it->first);
|
||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
//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 ret;
|
||||
return msg;
|
||||
}
|
||||
|
||||
ElGamalAESSession::ElGamalAESSession (GarlicDestination * owner,
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
|
||||
GarlicRoutingSession (owner, attachLeaseSet),
|
||||
m_Destination (destination), m_NumTags (numTags)
|
||||
{
|
||||
// create new session tags and session key
|
||||
RAND_bytes (m_SessionKey, 32);
|
||||
m_Encryption.SetKey (m_SessionKey);
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
|
||||
ElGamalAESSession::ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
|
||||
m_NumTags(1)
|
||||
{
|
||||
memcpy (m_SessionKey, sessionKey, 32);
|
||||
m_Encryption.SetKey (m_SessionKey);
|
||||
m_SessionTags.push_back (sessionTag);
|
||||
m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> ElGamalAESSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
auto m = NewI2NPMessage ();
|
||||
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
||||
@@ -213,10 +181,10 @@ namespace garlic
|
||||
return m;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
|
||||
size_t ElGamalAESSession::CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg)
|
||||
{
|
||||
size_t blockSize = 0;
|
||||
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
|
||||
bool createNewTags = GetOwner () && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags*2/3);
|
||||
UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr;
|
||||
htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count
|
||||
blockSize += 2;
|
||||
@@ -245,7 +213,7 @@ namespace garlic
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
|
||||
size_t ElGamalAESSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
|
||||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
uint32_t msgID;
|
||||
@@ -255,17 +223,17 @@ namespace garlic
|
||||
*numCloves = 0;
|
||||
size++;
|
||||
|
||||
if (m_Owner)
|
||||
if (GetOwner ())
|
||||
{
|
||||
// resubmit non-confirmed LeaseSet
|
||||
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && ts > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
|
||||
if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
||||
SetLeaseSetUpdateStatus (eLeaseSetUpdated);
|
||||
SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed
|
||||
}
|
||||
|
||||
// attach DeviveryStatus if necessary
|
||||
if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated
|
||||
if (newTags || GetLeaseSetUpdateStatus () == eLeaseSetUpdated) // new tags created or leaseset updated
|
||||
{
|
||||
// clove is DeliveryStatus
|
||||
auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID);
|
||||
@@ -279,20 +247,20 @@ namespace garlic
|
||||
m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr<UnconfirmedTags>(newTags)));
|
||||
newTags = nullptr; // got acquired
|
||||
}
|
||||
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
|
||||
GetOwner ()->DeliveryStatusSent (shared_from_this (), msgID);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Garlic: DeliveryStatus clove was not created");
|
||||
}
|
||||
// attach LeaseSet
|
||||
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
|
||||
if (GetLeaseSetUpdateStatus () == eLeaseSetUpdated)
|
||||
{
|
||||
if (m_LeaseSetUpdateMsgID) m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); // remove previous
|
||||
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
|
||||
m_LeaseSetUpdateMsgID = msgID;
|
||||
m_LeaseSetSubmissionTime = ts;
|
||||
if (GetLeaseSetUpdateMsgID ()) GetOwner ()->RemoveDeliveryStatusSession (GetLeaseSetUpdateMsgID ()); // remove previous
|
||||
SetLeaseSetUpdateStatus (eLeaseSetSubmitted);
|
||||
SetLeaseSetUpdateMsgID (msgID);
|
||||
SetLeaseSetSubmissionTime (ts);
|
||||
// clove if our leaseSet must be attached
|
||||
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
|
||||
auto leaseSet = CreateDatabaseStoreMsg (GetOwner ()->GetLeaseSet ());
|
||||
size += CreateGarlicClove (payload + size, leaseSet, false);
|
||||
(*numCloves)++;
|
||||
}
|
||||
@@ -313,7 +281,7 @@ namespace garlic
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
|
||||
size_t ElGamalAESSession::CreateGarlicClove (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg, bool isDestination)
|
||||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
|
||||
size_t size = 0;
|
||||
@@ -343,12 +311,12 @@ namespace garlic
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID)
|
||||
size_t ElGamalAESSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID)
|
||||
{
|
||||
size_t size = 0;
|
||||
if (m_Owner)
|
||||
if (GetOwner ())
|
||||
{
|
||||
auto inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel ();
|
||||
auto inboundTunnel = GetOwner ()->GetTunnelPool ()->GetNextInboundTunnel ();
|
||||
if (inboundTunnel)
|
||||
{
|
||||
buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
|
||||
@@ -359,19 +327,12 @@ namespace garlic
|
||||
htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID
|
||||
size += 4;
|
||||
// create msg
|
||||
auto msg = CreateDeliveryStatusMsg (msgID);
|
||||
if (m_Owner)
|
||||
{
|
||||
//encrypt
|
||||
uint8_t key[32], tag[32];
|
||||
RAND_bytes (key, 32); // random session key
|
||||
RAND_bytes (tag, 32); // random session tag
|
||||
m_Owner->SubmitSessionKey (key, tag);
|
||||
GarlicRoutingSession garlic (key, tag);
|
||||
msg = garlic.WrapSingleMessage (msg);
|
||||
}
|
||||
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
|
||||
size += msg->GetLength ();
|
||||
auto msg = CreateEncryptedDeliveryStatusMsg (msgID);
|
||||
if (msg)
|
||||
{
|
||||
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
|
||||
size += msg->GetLength ();
|
||||
}
|
||||
// fill clove
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
|
||||
uint32_t cloveID;
|
||||
@@ -392,6 +353,77 @@ namespace garlic
|
||||
return size;
|
||||
}
|
||||
|
||||
ElGamalAESSession::UnconfirmedTags * ElGamalAESSession::GenerateSessionTags ()
|
||||
{
|
||||
auto tags = new UnconfirmedTags (m_NumTags);
|
||||
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (int i = 0; i < m_NumTags; i++)
|
||||
{
|
||||
RAND_bytes (tags->sessionTags[i], 32);
|
||||
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
bool ElGamalAESSession::MessageConfirmed (uint32_t msgID)
|
||||
{
|
||||
TagsConfirmed (msgID);
|
||||
if (!GarlicRoutingSession::MessageConfirmed (msgID))
|
||||
CleanupExpiredTags ();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ElGamalAESSession::TagsConfirmed (uint32_t msgID)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
auto it = m_UnconfirmedTagsMsgs.find (msgID);
|
||||
if (it != m_UnconfirmedTagsMsgs.end ())
|
||||
{
|
||||
auto& tags = it->second;
|
||||
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
for (int i = 0; i < tags->numTags; i++)
|
||||
m_SessionTags.push_back (tags->sessionTags[i]);
|
||||
}
|
||||
m_UnconfirmedTagsMsgs.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
bool ElGamalAESSession::CleanupExpiredTags ()
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
|
||||
{
|
||||
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
it = m_SessionTags.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
CleanupUnconfirmedTags ();
|
||||
CleanupUnconfirmedLeaseSet (ts);
|
||||
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
|
||||
}
|
||||
|
||||
bool ElGamalAESSession::CleanupUnconfirmedTags ()
|
||||
{
|
||||
bool ret = false;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
// delete expired unconfirmed tags
|
||||
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
|
||||
{
|
||||
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (GetOwner ())
|
||||
GetOwner ()->RemoveDeliveryStatusSession (it->first);
|
||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
GarlicDestination::GarlicDestination (): m_NumTags (32) // 32 tags by default
|
||||
{
|
||||
m_Ctx = BN_CTX_new ();
|
||||
@@ -407,6 +439,8 @@ namespace garlic
|
||||
m_Sessions.clear ();
|
||||
m_DeliveryStatusSessions.clear ();
|
||||
m_Tags.clear ();
|
||||
m_ECIESx25519Sessions.clear ();
|
||||
m_ECIESx25519Tags.clear ();
|
||||
}
|
||||
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
|
||||
{
|
||||
@@ -434,6 +468,7 @@ namespace garlic
|
||||
}
|
||||
buf += 4; // length
|
||||
auto it = m_Tags.find (SessionTag(buf));
|
||||
// AES tag might be used even if encryption type is not ElGamal/AES
|
||||
if (it != m_Tags.end ())
|
||||
{
|
||||
// tag found. Use AES
|
||||
@@ -452,9 +487,15 @@ namespace garlic
|
||||
}
|
||||
else
|
||||
{
|
||||
// tag not found. Use ElGamal
|
||||
// tag not found. Handle depending on encryption type
|
||||
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET))
|
||||
{
|
||||
HandleECIESx25519 (buf, length);
|
||||
return;
|
||||
}
|
||||
// otherwise assume ElGamal/AES
|
||||
ElGamalBlock elGamal;
|
||||
if (length >= 514 && Decrypt (buf, (uint8_t *)&elGamal, m_Ctx))
|
||||
if (length >= 514 && Decrypt (buf, (uint8_t *)&elGamal, m_Ctx, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL))
|
||||
{
|
||||
auto decryption = std::make_shared<AESDecryption>(elGamal.sessionKey);
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
@@ -543,7 +584,7 @@ namespace garlic
|
||||
LogPrint (eLogError, "Garlic: message is too short");
|
||||
break;
|
||||
}
|
||||
HandleI2NPMessage (buf, len - offset, from);
|
||||
HandleI2NPMessage (buf, len - offset);
|
||||
break;
|
||||
case eGarlicDeliveryTypeDestination:
|
||||
LogPrint (eLogDebug, "Garlic: type destination");
|
||||
@@ -554,7 +595,7 @@ namespace garlic
|
||||
LogPrint (eLogError, "Garlic: message is too short");
|
||||
break;
|
||||
}
|
||||
HandleI2NPMessage (buf, len - offset, from);
|
||||
HandleI2NPMessage (buf, len - offset);
|
||||
break;
|
||||
case eGarlicDeliveryTypeTunnel:
|
||||
{
|
||||
@@ -638,21 +679,41 @@ namespace garlic
|
||||
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
|
||||
{
|
||||
GarlicRoutingSessionPtr session;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto it = m_Sessions.find (destination->GetIdentHash ());
|
||||
if (it != m_Sessions.end ())
|
||||
session = it->second;
|
||||
}
|
||||
if (!session)
|
||||
{
|
||||
session = std::make_shared<GarlicRoutingSession> (this, destination,
|
||||
attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
m_Sessions[destination->GetIdentHash ()] = session;
|
||||
}
|
||||
return session;
|
||||
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET &&
|
||||
SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET))
|
||||
{
|
||||
ECIESX25519AEADRatchetSessionPtr session;
|
||||
uint8_t staticKey[32];
|
||||
destination->Encrypt (nullptr, staticKey, nullptr); // we are supposed to get static key
|
||||
auto it = m_ECIESx25519Sessions.find (staticKey);
|
||||
if (it != m_ECIESx25519Sessions.end ())
|
||||
session = it->second;
|
||||
if (!session)
|
||||
{
|
||||
session = std::make_shared<ECIESX25519AEADRatchetSession> (this);
|
||||
session->SetRemoteStaticKey (staticKey);
|
||||
}
|
||||
session->SetDestination (destination->GetIdentHash ()); // TODO: remove
|
||||
return session;
|
||||
}
|
||||
else
|
||||
{
|
||||
ElGamalAESSessionPtr session;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto it = m_Sessions.find (destination->GetIdentHash ());
|
||||
if (it != m_Sessions.end ())
|
||||
session = it->second;
|
||||
}
|
||||
if (!session)
|
||||
{
|
||||
session = std::make_shared<ElGamalAESSession> (this, destination,
|
||||
attachLeaseSet ? m_NumTags : 4, attachLeaseSet); // specified num tags for connections and 4 for LS requests
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
m_Sessions[destination->GetIdentHash ()] = session;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::CleanupExpiredTags ()
|
||||
@@ -700,6 +761,25 @@ namespace garlic
|
||||
++it;
|
||||
}
|
||||
}
|
||||
// ECIESx25519
|
||||
for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();)
|
||||
{
|
||||
if (ts > it->second.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
||||
it = m_ECIESx25519Tags.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
for (auto it = m_ECIESx25519Sessions.begin (); it != m_ECIESx25519Sessions.end ();)
|
||||
{
|
||||
if (it->second->CheckExpired (ts))
|
||||
{
|
||||
it->second->SetOwner (nullptr);
|
||||
it = m_ECIESx25519Sessions.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
|
||||
@@ -714,9 +794,8 @@ namespace garlic
|
||||
m_DeliveryStatusSessions[msgID] = session;
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
void GarlicDestination::HandleDeliveryStatusMessage (uint32_t msgID)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (msg->GetPayload ());
|
||||
GarlicRoutingSessionPtr session;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
||||
@@ -736,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 ();
|
||||
}
|
||||
|
||||
@@ -748,7 +831,8 @@ namespace garlic
|
||||
|
||||
void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
HandleDeliveryStatusMessage (msg);
|
||||
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
|
||||
HandleDeliveryStatusMessage (msgID);
|
||||
}
|
||||
|
||||
void GarlicDestination::SaveTags ()
|
||||
@@ -806,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);
|
||||
@@ -821,5 +905,107 @@ namespace garlic
|
||||
if (ts >= i2p::fs::GetLastUpdateTime (it) + INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
||||
i2p::fs::Remove (it);
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleECIESx25519 (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint64_t tag;
|
||||
memcpy (&tag, buf, 8);
|
||||
ECIESX25519AEADRatchetSessionPtr session;
|
||||
int index = 0;
|
||||
auto it = m_ECIESx25519Tags.find (tag);
|
||||
if (it != m_ECIESx25519Tags.end ())
|
||||
{
|
||||
session = it->second.session;
|
||||
index = it->second.index;
|
||||
m_ECIESx25519Tags.erase (tag);
|
||||
}
|
||||
else
|
||||
session = std::make_shared<ECIESX25519AEADRatchetSession> (this); // incoming
|
||||
|
||||
if (!session->HandleNextMessage (buf, len, index))
|
||||
LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message");
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len)
|
||||
{
|
||||
const uint8_t * buf1 = buf;
|
||||
uint8_t flag = buf[0]; buf++; // flag
|
||||
GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03);
|
||||
switch (deliveryType)
|
||||
{
|
||||
case eGarlicDeliveryTypeDestination:
|
||||
LogPrint (eLogDebug, "Garlic: type destination");
|
||||
buf += 32; // TODO: check destination
|
||||
#if (__cplusplus >= 201703L) // C++ 17 or higher
|
||||
[[fallthrough]];
|
||||
#endif
|
||||
// no break here
|
||||
case eGarlicDeliveryTypeLocal:
|
||||
{
|
||||
LogPrint (eLogDebug, "Garlic: type local");
|
||||
I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
|
||||
buf += (4 + 4); // msgID + expiration
|
||||
ptrdiff_t offset = buf - buf1;
|
||||
if (offset <= (int)len)
|
||||
HandleCloveI2NPMessage (typeID, buf, len - offset);
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: clove is too long");
|
||||
break;
|
||||
}
|
||||
case eGarlicDeliveryTypeTunnel:
|
||||
{
|
||||
LogPrint (eLogDebug, "Garlic: type tunnel");
|
||||
// gwHash and gwTunnel sequence is reverted
|
||||
const uint8_t * gwHash = buf;
|
||||
buf += 32;
|
||||
ptrdiff_t offset = buf - buf1;
|
||||
if (offset + 13 > (int)len)
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: message is too short");
|
||||
break;
|
||||
}
|
||||
uint32_t gwTunnel = bufbe32toh (buf); buf += 4;
|
||||
I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid
|
||||
buf += (4 + 4); // msgID + expiration
|
||||
offset += 13;
|
||||
if (GetTunnelPool ())
|
||||
{
|
||||
auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||
if (tunnel)
|
||||
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset));
|
||||
else
|
||||
LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Garlic: Tunnel pool is not set for inbound tunnel");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint (eLogWarning, "Garlic: unexpected delivery type ", (int)deliveryType);
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session)
|
||||
{
|
||||
m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexSession{index, session, i2p::util::GetSecondsSinceEpoch ()});
|
||||
}
|
||||
|
||||
void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session)
|
||||
{
|
||||
i2p::data::Tag<32> staticKeyTag (staticKey);
|
||||
auto it = m_ECIESx25519Sessions.find (staticKeyTag);
|
||||
if (it != m_ECIESx25519Sessions.end ())
|
||||
{
|
||||
if (it->second->CanBeRestarted (i2p::util::GetSecondsSinceEpoch ()))
|
||||
m_ECIESx25519Sessions.erase (it);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64 (), " already exists");
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_ECIESx25519Sessions.emplace (staticKeyTag, session);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
146
libi2pd/Garlic.h
146
libi2pd/Garlic.h
@@ -2,7 +2,7 @@
|
||||
#define GARLIC_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -85,8 +85,10 @@ namespace garlic
|
||||
};
|
||||
|
||||
class GarlicDestination;
|
||||
class GarlicRoutingSession: public std::enable_shared_from_this<GarlicRoutingSession>
|
||||
class GarlicRoutingSession
|
||||
{
|
||||
protected:
|
||||
|
||||
enum LeaseSetUpdateStatus
|
||||
{
|
||||
eLeaseSetUpToDate = 0,
|
||||
@@ -95,7 +97,60 @@ namespace garlic
|
||||
eLeaseSetDoNotSend
|
||||
};
|
||||
|
||||
struct UnconfirmedTags
|
||||
public:
|
||||
|
||||
GarlicRoutingSession (GarlicDestination * owner, bool attachLeaseSet);
|
||||
GarlicRoutingSession ();
|
||||
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 ()
|
||||
{
|
||||
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
||||
};
|
||||
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);
|
||||
|
||||
GarlicDestination * GetOwner () const { return m_Owner; }
|
||||
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
|
||||
|
||||
protected:
|
||||
|
||||
LeaseSetUpdateStatus GetLeaseSetUpdateStatus () const { return m_LeaseSetUpdateStatus; }
|
||||
void SetLeaseSetUpdateStatus (LeaseSetUpdateStatus status) { m_LeaseSetUpdateStatus = status; }
|
||||
uint32_t GetLeaseSetUpdateMsgID () const { return m_LeaseSetUpdateMsgID; }
|
||||
void SetLeaseSetUpdateMsgID (uint32_t msgID) { m_LeaseSetUpdateMsgID = msgID; }
|
||||
void SetLeaseSetSubmissionTime (uint64_t ts) { m_LeaseSetSubmissionTime = ts; }
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateEncryptedDeliveryStatusMsg (uint32_t msgID);
|
||||
|
||||
private:
|
||||
|
||||
GarlicDestination * m_Owner;
|
||||
|
||||
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
|
||||
uint32_t m_LeaseSetUpdateMsgID;
|
||||
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
|
||||
|
||||
public:
|
||||
// for HTTP only
|
||||
virtual size_t GetNumOutgoingTags () const { return 0; };
|
||||
};
|
||||
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
|
||||
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
|
||||
|
||||
class ElGamalAESSession: public GarlicRoutingSession, public std::enable_shared_from_this<ElGamalAESSession>
|
||||
{
|
||||
struct UnconfirmedTags
|
||||
{
|
||||
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
|
||||
~UnconfirmedTags () { delete[] sessionTags; };
|
||||
@@ -105,32 +160,20 @@ namespace garlic
|
||||
uint32_t tagsCreationTime;
|
||||
};
|
||||
|
||||
public:
|
||||
public:
|
||||
|
||||
GarlicRoutingSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
||||
ElGamalAESSession (GarlicDestination * owner, std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
||||
int numTags, bool attachLeaseSet);
|
||||
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
|
||||
~GarlicRoutingSession ();
|
||||
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
|
||||
void MessageConfirmed (uint32_t msgID);
|
||||
ElGamalAESSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
|
||||
~ElGamalAESSession () {};
|
||||
|
||||
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
|
||||
|
||||
bool MessageConfirmed (uint32_t msgID);
|
||||
bool CleanupExpiredTags (); // returns true if something left
|
||||
bool CleanupUnconfirmedTags (); // returns true if something has been deleted
|
||||
|
||||
void SetLeaseSetUpdated ()
|
||||
{
|
||||
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
||||
};
|
||||
bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
|
||||
bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
|
||||
uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; }
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
|
||||
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
|
||||
|
||||
const GarlicDestination * GetOwner () const { return m_Owner; }
|
||||
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
|
||||
|
||||
private:
|
||||
private:
|
||||
|
||||
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
|
||||
size_t CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags);
|
||||
@@ -139,31 +182,32 @@ namespace garlic
|
||||
|
||||
void TagsConfirmed (uint32_t msgID);
|
||||
UnconfirmedTags * GenerateSessionTags ();
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
|
||||
|
||||
private:
|
||||
|
||||
GarlicDestination * m_Owner;
|
||||
std::shared_ptr<const i2p::data::RoutingDestination> m_Destination;
|
||||
|
||||
i2p::crypto::AESKey m_SessionKey;
|
||||
i2p::crypto::AESKey m_SessionKey;
|
||||
std::list<SessionTag> m_SessionTags;
|
||||
int m_NumTags;
|
||||
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
|
||||
|
||||
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
|
||||
uint32_t m_LeaseSetUpdateMsgID;
|
||||
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
|
||||
|
||||
public:
|
||||
public:
|
||||
// for HTTP only
|
||||
size_t GetNumOutgoingTags () const { return m_SessionTags.size (); };
|
||||
size_t GetNumOutgoingTags () const { return m_SessionTags.size (); };
|
||||
};
|
||||
typedef std::shared_ptr<ElGamalAESSession> ElGamalAESSessionPtr;
|
||||
|
||||
class ECIESX25519AEADRatchetSession;
|
||||
typedef std::shared_ptr<ECIESX25519AEADRatchetSession> ECIESX25519AEADRatchetSessionPtr;
|
||||
struct ECIESX25519AEADRatchetIndexSession
|
||||
{
|
||||
int index;
|
||||
ECIESX25519AEADRatchetSessionPtr session;
|
||||
uint64_t creationTime; // seconds since epoch
|
||||
};
|
||||
//using GarlicRoutingSessionPtr = std::shared_ptr<GarlicRoutingSession>;
|
||||
typedef std::shared_ptr<GarlicRoutingSession> GarlicRoutingSessionPtr; // TODO: replace to using after switch to 4.8
|
||||
|
||||
class GarlicDestination: public i2p::data::LocalDestination
|
||||
{
|
||||
@@ -174,6 +218,7 @@ namespace garlic
|
||||
|
||||
void CleanUp ();
|
||||
void SetNumTags (int numTags) { m_NumTags = numTags; };
|
||||
int GetNumTags () const { return m_NumTags; };
|
||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
||||
void CleanupExpiredTags ();
|
||||
void RemoveDeliveryStatusSession (uint32_t msgID);
|
||||
@@ -183,6 +228,9 @@ 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 (GarlicRoutingSessionPtr session, uint32_t msgID);
|
||||
void AddECIESx25519SessionTag (int index, uint64_t tag, ECIESX25519AEADRatchetSessionPtr session);
|
||||
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
|
||||
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
|
||||
|
||||
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
@@ -190,12 +238,13 @@ namespace garlic
|
||||
|
||||
virtual std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () = 0; // TODO
|
||||
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
|
||||
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only
|
||||
virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) = 0;
|
||||
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void HandleDeliveryStatusMessage (uint32_t msgID);
|
||||
|
||||
void SaveTags ();
|
||||
void LoadTags ();
|
||||
@@ -206,18 +255,23 @@ namespace garlic
|
||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
|
||||
// ECIES-X25519-AEAD-Ratchet
|
||||
void HandleECIESx25519 (const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
BN_CTX * m_Ctx; // incoming
|
||||
// outgoing sessions
|
||||
int m_NumTags;
|
||||
std::mutex m_SessionsMutex;
|
||||
std::map<i2p::data::IdentHash, GarlicRoutingSessionPtr> m_Sessions;
|
||||
std::unordered_map<i2p::data::IdentHash, ElGamalAESSessionPtr> m_Sessions;
|
||||
std::unordered_map<i2p::data::Tag<32>, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session
|
||||
// incoming
|
||||
std::map<SessionTag, std::shared_ptr<AESDecryption> > m_Tags;
|
||||
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
|
||||
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexSession> m_ECIESx25519Tags; // session tag -> session
|
||||
// DeliveryStatus
|
||||
std::mutex m_DeliveryStatusSessionsMutex;
|
||||
std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
|
||||
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -258,12 +258,12 @@ namespace i2p
|
||||
return m;
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
|
||||
{
|
||||
if (!leaseSet) return nullptr;
|
||||
auto m = NewI2NPShortMessage ();
|
||||
uint8_t * payload = m->GetPayload ();
|
||||
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
|
||||
memcpy (payload + DATABASE_STORE_KEY_OFFSET, storeHash, 32);
|
||||
payload[DATABASE_STORE_TYPE_OFFSET] = leaseSet->GetStoreType (); // 1 for LeaseSet
|
||||
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
|
||||
size_t size = DATABASE_STORE_HEADER_SIZE;
|
||||
|
||||
@@ -95,6 +95,7 @@ namespace i2p
|
||||
// DatabaseLookup flags
|
||||
const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01;
|
||||
const uint8_t DATABASE_LOOKUP_ENCRYPTION_FLAG = 0x02;
|
||||
const uint8_t DATABASE_LOOKUP_ECIES_FLAG = 0x10;
|
||||
const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C;
|
||||
const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0;
|
||||
const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100
|
||||
@@ -247,7 +248,7 @@ namespace tunnel
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (const i2p::data::IdentHash& storeHash, std::shared_ptr<const i2p::data::LeaseSet> leaseSet); // for floodfill only
|
||||
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LocalLeaseSet> leaseSet, uint32_t replyToken = 0, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel = nullptr);
|
||||
bool IsRouterInfoMsg (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
|
||||
@@ -34,12 +34,11 @@ namespace data
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx ():
|
||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType):
|
||||
m_IsVerifierCreated (false)
|
||||
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType)
|
||||
{
|
||||
memcpy (m_StandardIdentity.publicKey, publicKey, 256); // publicKey in awlays assumed 256 regardless actual size, padding must be taken care of
|
||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
@@ -141,19 +140,19 @@ namespace data
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
|
||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
FromBuffer (buf, len);
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const IdentityEx& other):
|
||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const Identity& standard):
|
||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
*this = standard;
|
||||
}
|
||||
@@ -161,6 +160,7 @@ namespace data
|
||||
IdentityEx::~IdentityEx ()
|
||||
{
|
||||
delete[] m_ExtendedBuffer;
|
||||
delete m_Verifier;
|
||||
}
|
||||
|
||||
IdentityEx& IdentityEx::operator=(const IdentityEx& other)
|
||||
@@ -178,8 +178,8 @@ namespace data
|
||||
else
|
||||
m_ExtendedBuffer = nullptr;
|
||||
|
||||
delete m_Verifier;
|
||||
m_Verifier = nullptr;
|
||||
m_IsVerifierCreated = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -193,8 +193,8 @@ namespace data
|
||||
m_ExtendedBuffer = nullptr;
|
||||
m_ExtendedLen = 0;
|
||||
|
||||
delete m_Verifier;
|
||||
m_Verifier = nullptr;
|
||||
m_IsVerifierCreated = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -233,6 +233,7 @@ namespace data
|
||||
}
|
||||
SHA256(buf, GetFullLen (), m_IdentHash);
|
||||
|
||||
delete m_Verifier;
|
||||
m_Verifier = nullptr;
|
||||
|
||||
return GetFullLen ();
|
||||
@@ -381,33 +382,27 @@ namespace data
|
||||
|
||||
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
|
||||
{
|
||||
if (!m_Verifier)
|
||||
bool del = false;
|
||||
{
|
||||
auto created = m_IsVerifierCreated.exchange (true);
|
||||
if (!created)
|
||||
m_Verifier.reset (verifier);
|
||||
std::lock_guard<std::mutex> l(m_VerifierMutex);
|
||||
if (!m_Verifier)
|
||||
m_Verifier = verifier;
|
||||
else
|
||||
{
|
||||
delete verifier;
|
||||
int count = 0;
|
||||
while (!m_Verifier && count < 500) // 5 seconds
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds(10));
|
||||
count++;
|
||||
}
|
||||
if (!m_Verifier)
|
||||
LogPrint (eLogError, "Identity: couldn't get verifier in 5 seconds");
|
||||
}
|
||||
del = true;
|
||||
}
|
||||
else
|
||||
if (del)
|
||||
delete verifier;
|
||||
}
|
||||
|
||||
void IdentityEx::DropVerifier () const
|
||||
{
|
||||
// TODO: potential race condition with Verify
|
||||
m_IsVerifierCreated = false;
|
||||
m_Verifier = nullptr;
|
||||
i2p::crypto::Verifier * verifier;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_VerifierMutex);
|
||||
verifier = m_Verifier;
|
||||
m_Verifier = nullptr;
|
||||
}
|
||||
delete verifier;
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> IdentityEx::CreateEncryptor (CryptoKeyType keyType, const uint8_t * key)
|
||||
@@ -417,6 +412,9 @@ namespace data
|
||||
case CRYPTO_KEY_TYPE_ELGAMAL:
|
||||
return std::make_shared<i2p::crypto::ElGamalEncryptor>(key);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
|
||||
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetEncryptor>(key);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
|
||||
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
|
||||
return std::make_shared<i2p::crypto::ECIESP256Encryptor>(key);
|
||||
@@ -674,6 +672,9 @@ namespace data
|
||||
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
|
||||
return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
|
||||
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType);
|
||||
};
|
||||
@@ -717,7 +718,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;
|
||||
@@ -750,6 +754,9 @@ namespace data
|
||||
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
|
||||
i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub);
|
||||
break;
|
||||
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET:
|
||||
i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub);
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Crypto key type ", (int)type, " is not supported");
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include "Base.h"
|
||||
#include "Signature.h"
|
||||
#include "CryptoKey.h"
|
||||
@@ -55,6 +56,7 @@ namespace data
|
||||
|
||||
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RARCHET = 4;
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later
|
||||
const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES
|
||||
|
||||
@@ -124,8 +126,8 @@ namespace data
|
||||
|
||||
Identity m_StandardIdentity;
|
||||
IdentHash m_IdentHash;
|
||||
mutable std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
|
||||
mutable std::atomic_bool m_IsVerifierCreated; // make sure we don't create twice
|
||||
mutable i2p::crypto::Verifier * m_Verifier = nullptr;
|
||||
mutable std::mutex m_VerifierMutex;
|
||||
size_t m_ExtendedLen;
|
||||
uint8_t * m_ExtendedBuffer;
|
||||
};
|
||||
@@ -215,6 +217,7 @@ namespace data
|
||||
virtual bool IsDestination () const = 0; // for garlic
|
||||
|
||||
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
|
||||
virtual CryptoKeyType GetEncryptionType () const { return GetIdentity ()->GetCryptoKeyType (); }; // override in LeaseSet2
|
||||
};
|
||||
|
||||
class LocalDestination
|
||||
@@ -222,10 +225,12 @@ namespace data
|
||||
public:
|
||||
|
||||
virtual ~LocalDestination() {};
|
||||
virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const = 0;
|
||||
virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0;
|
||||
virtual std::shared_ptr<const IdentityEx> GetIdentity () const = 0;
|
||||
|
||||
const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); };
|
||||
virtual bool SupportsEncryptionType (CryptoKeyType keyType) const { return GetIdentity ()->GetCryptoKeyType () == keyType; }; // override for LeaseSet
|
||||
virtual const uint8_t * GetEncryptionPublicKey (CryptoKeyType keyType) const { return GetIdentity ()->GetEncryptionPublicKey (); }; // override for LeaseSet
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,18 +251,26 @@ namespace data
|
||||
memcpy (m_Buffer, buf, len);
|
||||
}
|
||||
|
||||
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases):
|
||||
LeaseSet (storeLeases), m_StoreType (storeType), m_OrigStoreType (storeType)
|
||||
void LeaseSet::SetBufferLen (size_t len)
|
||||
{
|
||||
if (len <= m_BufferLen) m_BufferLen = len;
|
||||
else
|
||||
LogPrint (eLogError, "LeaseSet2: actual buffer size ", len , " exceeds full buffer size ", m_BufferLen);
|
||||
}
|
||||
|
||||
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto):
|
||||
LeaseSet (storeLeases), m_StoreType (storeType), m_EncryptionType (preferredCrypto)
|
||||
{
|
||||
SetBuffer (buf, len);
|
||||
SetBuffer (buf, len);
|
||||
if (storeType == NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
||||
ReadFromBufferEncrypted (buf, len, nullptr, nullptr);
|
||||
else
|
||||
ReadFromBuffer (buf, len);
|
||||
}
|
||||
|
||||
LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret):
|
||||
LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_OrigStoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
||||
LeaseSet2::LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key,
|
||||
const uint8_t * secret, CryptoKeyType preferredCrypto):
|
||||
LeaseSet (true), m_StoreType (NETDB_STORE_TYPE_ENCRYPTED_LEASESET2), m_EncryptionType (preferredCrypto)
|
||||
{
|
||||
ReadFromBufferEncrypted (buf, len, key, secret);
|
||||
}
|
||||
@@ -302,6 +310,12 @@ namespace data
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (flags & LEASESET2_FLAG_UNPUBLISHED_LEASESET) m_IsPublic = false;
|
||||
if (flags & LEASESET2_FLAG_PUBLISHED_ENCRYPTED)
|
||||
{
|
||||
m_IsPublishedEncrypted = true;
|
||||
m_IsPublic = true;
|
||||
}
|
||||
// type specific part
|
||||
size_t s = 0;
|
||||
switch (m_StoreType)
|
||||
@@ -324,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>
|
||||
@@ -349,7 +365,8 @@ namespace data
|
||||
offset += propertiesLen; // skip for now. TODO: implement properties
|
||||
if (offset + 1 >= len) return 0;
|
||||
// key sections
|
||||
uint16_t currentKeyType = 0;
|
||||
CryptoKeyType preferredKeyType = m_EncryptionType;
|
||||
bool preferredKeyFound = false;
|
||||
int numKeySections = buf[offset]; offset++;
|
||||
for (int i = 0; i < numKeySections; i++)
|
||||
{
|
||||
@@ -357,15 +374,15 @@ namespace data
|
||||
if (offset + 2 >= len) return 0;
|
||||
uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2;
|
||||
if (offset + encryptionKeyLen >= len) return 0;
|
||||
if (IsStoreLeases ()) // create encryptor with leases only
|
||||
if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only
|
||||
{
|
||||
// we pick first valid key, higher key type has higher priority 4-1-0
|
||||
// if two keys with of the same type, pick first
|
||||
// we pick first valid key if preferred not found
|
||||
auto encryptor = i2p::data::IdentityEx::CreateEncryptor (keyType, buf + offset);
|
||||
if (encryptor && (!m_Encryptor || keyType > currentKeyType))
|
||||
if (encryptor && (!m_Encryptor || keyType == preferredKeyType))
|
||||
{
|
||||
m_Encryptor = encryptor; // TODO: atomic
|
||||
currentKeyType = keyType;
|
||||
m_EncryptionType = keyType;
|
||||
if (keyType == preferredKeyType) preferredKeyFound = true;
|
||||
}
|
||||
}
|
||||
offset += encryptionKeyLen;
|
||||
@@ -479,7 +496,7 @@ namespace data
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet2: Unexpected blinded key type ", blindedKeyType, " instread ", key->GetBlindedSigType ());
|
||||
LogPrint (eLogError, "LeaseSet2: Unexpected blinded key type ", blindedKeyType, " instead ", key->GetBlindedSigType ());
|
||||
return;
|
||||
}
|
||||
// outer key
|
||||
@@ -506,7 +523,7 @@ namespace data
|
||||
if (authDataLen > 0)
|
||||
{
|
||||
memcpy (innerInput + 32, subcredential, 36);
|
||||
i2p::crypto::HKDF (outerPlainText.data () + 1, innerInput, 68, "ELS2_L2K", keys);
|
||||
i2p::crypto::HKDF (outerPlainText.data () + 1 + authDataLen, innerInput, 68, "ELS2_L2K", keys);
|
||||
}
|
||||
else
|
||||
// no authData presented, innerInput = subcredential || publishedTimestamp
|
||||
@@ -529,42 +546,83 @@ 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
|
||||
static inline bool GetAuthCookie (const uint8_t * authClients, int numClients, const uint8_t * okm, uint8_t * authCookie)
|
||||
{
|
||||
// try to find clientCookie_i for clientID_i = okm[44:51]
|
||||
for (int i = 0; i < numClients; i++)
|
||||
{
|
||||
if (!memcmp (okm + 44, authClients + i*40, 8)) // clientID_i
|
||||
{
|
||||
// clientKey_i = okm[0:31]
|
||||
// clientIV_i = okm[32:43]
|
||||
i2p::crypto::ChaCha20 (authClients + i*40 + 8, 32, okm, okm + 32, authCookie); // clientCookie_i
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t LeaseSet2::ExtractClientAuthData (const uint8_t * buf, size_t len, const uint8_t * secret, const uint8_t * subcredential, uint8_t * authCookie) const
|
||||
{
|
||||
size_t offset = 0;
|
||||
uint8_t flag = buf[offset]; offset++; // flag
|
||||
if (flag & 0x01) // client auth
|
||||
{
|
||||
if (flag & 0x02) // PSK, bit 1 is set to 1
|
||||
if (!(flag & 0x0E)) // DH, bit 1-3 all zeroes
|
||||
{
|
||||
const uint8_t * ephemeralPublicKey = buf + offset; offset += 32; // ephemeralPublicKey
|
||||
uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients
|
||||
const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients
|
||||
if (offset > len)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in DH auth data");
|
||||
return 0;
|
||||
}
|
||||
// calculate authCookie
|
||||
if (secret)
|
||||
{
|
||||
i2p::crypto::X25519Keys ck (secret, nullptr); // derive cpk_i from csk_i
|
||||
uint8_t authInput[100];
|
||||
ck.Agree (ephemeralPublicKey, authInput); // sharedSecret is first 32 bytes of authInput
|
||||
memcpy (authInput + 32, ck.GetPublicKey (), 32); // cpk_i
|
||||
memcpy (authInput + 64, subcredential, 36);
|
||||
uint8_t okm[64]; // 52 actual data
|
||||
i2p::crypto::HKDF (ephemeralPublicKey, authInput, 100, "ELS2_XCA", okm);
|
||||
if (!GetAuthCookie (authClients, numClients, okm, authCookie))
|
||||
LogPrint (eLogError, "LeaseSet2: Client cookie DH not found");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: csk_i is not provided");
|
||||
}
|
||||
else if (flag & 0x02) // PSK, bit 1 is set to 1
|
||||
{
|
||||
const uint8_t * authSalt = buf + offset; offset += 32; // authSalt
|
||||
uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients
|
||||
const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients
|
||||
if (offset > len)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in PSK auth data");
|
||||
return 0;
|
||||
}
|
||||
// calculate authCookie
|
||||
if (secret)
|
||||
{
|
||||
uint8_t authInput[68];
|
||||
memcpy (authInput, secret, 32);
|
||||
memcpy (authInput, subcredential, 36);
|
||||
memcpy (authInput + 32, subcredential, 36);
|
||||
uint8_t okm[64]; // 52 actual data
|
||||
i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm);
|
||||
// try to find clientCookie_i for clientID_i = okm[44:51]
|
||||
bool found = false;
|
||||
for (int i = 0; i < numClients; i++)
|
||||
{
|
||||
if (!memcmp (okm + 44, authClients + i*40, 8)) // clientID_i
|
||||
{
|
||||
// clientKey_i = okm[0:31]
|
||||
// clientIV_i = okm[32:43]
|
||||
i2p::crypto::ChaCha20 (authClients + i*40 + 8, 32, okm, okm + 32, authCookie); // clientCookie_i
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
LogPrint (eLogError, "LeaseSet2: Client cookie not found");
|
||||
if (!GetAuthCookie (authClients, numClients, okm, authCookie))
|
||||
LogPrint (eLogError, "LeaseSet2: Client cookie PSK not found");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: psk_i is not provided");
|
||||
@@ -705,22 +763,32 @@ namespace data
|
||||
}
|
||||
|
||||
LocalLeaseSet2::LocalLeaseSet2 (uint8_t storeType, const i2p::data::PrivateKeys& keys,
|
||||
uint16_t keyType, uint16_t keyLen, const uint8_t * encryptionPublicKey,
|
||||
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels):
|
||||
const KeySections& encryptionKeys,
|
||||
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels,
|
||||
bool isPublic, bool isPublishedEncrypted):
|
||||
LocalLeaseSet (keys.GetPublic (), nullptr, 0)
|
||||
{
|
||||
auto identity = keys.GetPublic ();
|
||||
// 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 ())
|
||||
{
|
||||
flags |= LEASESET2_FLAG_OFFLINE_KEYS;
|
||||
m_BufferLen += keys.GetOfflineSignature ().size ();
|
||||
}
|
||||
if (isPublishedEncrypted)
|
||||
{
|
||||
flags |= LEASESET2_FLAG_PUBLISHED_ENCRYPTED;
|
||||
isPublic = true;
|
||||
}
|
||||
if (!isPublic) flags |= LEASESET2_FLAG_UNPUBLISHED_LEASESET;
|
||||
|
||||
m_Buffer = new uint8_t[m_BufferLen + 1];
|
||||
m_Buffer[0] = storeType;
|
||||
@@ -739,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
|
||||
@@ -774,12 +845,22 @@ namespace data
|
||||
m_Buffer[0] = storeType;
|
||||
}
|
||||
|
||||
LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType):
|
||||
LocalEncryptedLeaseSet2::LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys,
|
||||
int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys):
|
||||
LocalLeaseSet2 (ls->GetIdentity ()), m_InnerLeaseSet (ls)
|
||||
{
|
||||
size_t lenInnerPlaintext = ls->GetBufferLen () + 1, lenOuterPlaintext = lenInnerPlaintext + 32 + 1,
|
||||
lenOuterCiphertext = lenOuterPlaintext + 32;
|
||||
m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/;
|
||||
size_t lenInnerPlaintext = ls->GetBufferLen () + 1, lenOuterPlaintext = lenInnerPlaintext + 32 + 1;
|
||||
uint8_t layer1Flags = 0;
|
||||
if (authKeys)
|
||||
{
|
||||
if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH) layer1Flags |= 0x01; // DH, authentication scheme 0, auth bit 1
|
||||
else if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_PSK) layer1Flags |= 0x03; // PSK, authentication scheme 1, auth bit 1
|
||||
if (layer1Flags)
|
||||
lenOuterPlaintext += 32 + 2 + authKeys->size ()*40; // auth data len
|
||||
}
|
||||
size_t lenOuterCiphertext = lenOuterPlaintext + 32;
|
||||
|
||||
m_BufferLen = 2/*blinded sig type*/ + 32/*blinded pub key*/ + 4/*published*/ + 2/*expires*/ + 2/*flags*/ + 2/*lenOuterCiphertext*/ + lenOuterCiphertext + 64/*signature*/;
|
||||
m_Buffer = new uint8_t[m_BufferLen + 1];
|
||||
m_Buffer[0] = NETDB_STORE_TYPE_ENCRYPTED_LEASESET2;
|
||||
BlindedPublicKey blindedKey (ls->GetIdentity ());
|
||||
@@ -788,9 +869,9 @@ namespace data
|
||||
i2p::util::GetDateString (timestamp, date);
|
||||
uint8_t blindedPriv[64], blindedPub[128]; // 64 and 128 max
|
||||
size_t publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub);
|
||||
std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKeyType, blindedPriv));
|
||||
std::unique_ptr<i2p::crypto::Signer> blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv));
|
||||
auto offset = 1;
|
||||
htobe16buf (m_Buffer + offset, blindedKeyType); offset += 2; // Blinded Public Key Sig Type
|
||||
htobe16buf (m_Buffer + offset, blindedKey.GetBlindedSigType ()); offset += 2; // Blinded Public Key Sig Type
|
||||
memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key
|
||||
htobe32buf (m_Buffer + offset, timestamp); offset += 4; // published timestamp (seconds)
|
||||
auto nextMidnight = (timestamp/86400LL + 1)*86400LL; // 86400 = 24*3600 seconds
|
||||
@@ -812,12 +893,26 @@ namespace data
|
||||
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L1K", keys1);
|
||||
offset += 32; // outerSalt
|
||||
uint8_t * outerPlainText = m_Buffer + offset;
|
||||
m_Buffer[offset] = 0; offset++; // flag
|
||||
m_Buffer[offset] = layer1Flags; offset++; // layer 1 flags
|
||||
// auth data
|
||||
uint8_t innerInput[68]; // authCookie || subcredential || publishedTimestamp
|
||||
if (layer1Flags)
|
||||
{
|
||||
RAND_bytes (innerInput, 32); // authCookie
|
||||
CreateClientAuthData (subcredential, authType, authKeys, innerInput, m_Buffer + offset);
|
||||
offset += 32 + 2 + authKeys->size ()*40; // auth clients
|
||||
}
|
||||
// Layer 2
|
||||
// keys = HKDF(outerSalt, outerInput, "ELS2_L2K", 44)
|
||||
uint8_t keys2[64]; // 44 bytes actual data
|
||||
RAND_bytes (m_Buffer + offset, 32); // innerSalt = CSRNG(32)
|
||||
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2);
|
||||
if (layer1Flags)
|
||||
{
|
||||
memcpy (innerInput + 32, subcredential, 36); // + subcredential || publishedTimestamp
|
||||
i2p::crypto::HKDF (m_Buffer + offset, innerInput, 68, "ELS2_L2K", keys2);
|
||||
}
|
||||
else
|
||||
i2p::crypto::HKDF (m_Buffer + offset, subcredential, 36, "ELS2_L2K", keys2); // no authCookie
|
||||
offset += 32; // innerSalt
|
||||
m_Buffer[offset] = ls->GetStoreType ();
|
||||
memcpy (m_Buffer + offset + 1, ls->GetBuffer (), ls->GetBufferLen ());
|
||||
@@ -844,6 +939,44 @@ namespace data
|
||||
else
|
||||
LogPrint (eLogError, "LeaseSet2: couldn't extract inner layer");
|
||||
}
|
||||
|
||||
|
||||
void LocalEncryptedLeaseSet2::CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t * authCookie, uint8_t * authData) const
|
||||
{
|
||||
if (authType == ENCRYPTED_LEASESET_AUTH_TYPE_DH)
|
||||
{
|
||||
i2p::crypto::X25519Keys ek;
|
||||
ek.GenerateKeys (); // esk and epk
|
||||
memcpy (authData, ek.GetPublicKey (), 32); authData += 32; // epk
|
||||
htobe16buf (authData, authKeys->size ()); authData += 2; // num clients
|
||||
uint8_t authInput[100]; // sharedSecret || cpk_i || subcredential || publishedTimestamp
|
||||
memcpy (authInput + 64, subcredential, 36);
|
||||
for (auto& it: *authKeys)
|
||||
{
|
||||
ek.Agree (it, authInput); // sharedSecret = DH(esk, cpk_i)
|
||||
memcpy (authInput + 32, it, 32);
|
||||
uint8_t okm[64]; // 52 actual data
|
||||
i2p::crypto::HKDF (ek.GetPublicKey (), authInput, 100, "ELS2_XCA", okm);
|
||||
memcpy (authData, okm + 44, 8); authData += 8; // clientID_i
|
||||
i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i
|
||||
}
|
||||
}
|
||||
else // assume PSK
|
||||
{
|
||||
uint8_t authSalt[32];
|
||||
RAND_bytes (authSalt, 32);
|
||||
memcpy (authData, authSalt, 32); authData += 32; // authSalt
|
||||
htobe16buf (authData, authKeys->size ()); authData += 2; // num clients
|
||||
uint8_t authInput[68]; // authInput = psk_i || subcredential || publishedTimestamp
|
||||
memcpy (authInput + 32, subcredential, 36);
|
||||
for (auto& it: *authKeys)
|
||||
{
|
||||
memcpy (authInput, it, 32);
|
||||
uint8_t okm[64]; // 52 actual data
|
||||
i2p::crypto::HKDF (authSalt, authInput, 68, "ELS2PSKA", okm);
|
||||
memcpy (authData, okm + 44, 8); authData += 8; // clientID_i
|
||||
i2p::crypto::ChaCha20 (authCookie, 32, okm, okm + 32, authData); authData += 32; // clientCookie_i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@ namespace data
|
||||
bool operator== (const LeaseSet& other) const
|
||||
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };
|
||||
virtual uint8_t GetStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
|
||||
virtual uint8_t GetOrigStoreType () const { return NETDB_STORE_TYPE_LEASESET; };
|
||||
virtual uint32_t GetPublishedTimestamp () const { return 0; }; // should be set for LeaseSet2 only
|
||||
virtual std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return nullptr; };
|
||||
virtual bool IsPublishedEncrypted () const { return false; };
|
||||
|
||||
// implements RoutingDestination
|
||||
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_Identity; };
|
||||
@@ -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; };
|
||||
@@ -129,21 +130,25 @@ namespace data
|
||||
const uint8_t NETDB_STORE_TYPE_META_LEASESET2 = 7;
|
||||
|
||||
const uint16_t LEASESET2_FLAG_OFFLINE_KEYS = 0x0001;
|
||||
|
||||
const uint16_t LEASESET2_FLAG_UNPUBLISHED_LEASESET = 0x0002;
|
||||
const uint16_t LEASESET2_FLAG_PUBLISHED_ENCRYPTED = 0x0004;
|
||||
|
||||
class LeaseSet2: public LeaseSet
|
||||
{
|
||||
public:
|
||||
|
||||
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true);
|
||||
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr); // store type 5, called from local netdb only
|
||||
LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases = true, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL);
|
||||
LeaseSet2 (const uint8_t * buf, size_t len, std::shared_ptr<const BlindedPublicKey> key, const uint8_t * secret = nullptr, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL); // store type 5, called from local netdb only
|
||||
uint8_t GetStoreType () const { return m_StoreType; };
|
||||
uint8_t GetOrigStoreType () const { return m_OrigStoreType; };
|
||||
uint32_t GetPublishedTimestamp () const { return m_PublishedTimestamp; };
|
||||
bool IsPublic () const { return m_IsPublic; };
|
||||
bool IsPublishedEncrypted () const { return m_IsPublishedEncrypted; };
|
||||
std::shared_ptr<const i2p::crypto::Verifier> GetTransientVerifier () const { return m_TransientVerifier; };
|
||||
void Update (const uint8_t * buf, size_t len, bool verifySignature);
|
||||
|
||||
// implements RoutingDestination
|
||||
void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const;
|
||||
CryptoKeyType GetEncryptionType () const { return m_EncryptionType; };
|
||||
|
||||
private:
|
||||
|
||||
@@ -160,9 +165,11 @@ namespace data
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_StoreType, m_OrigStoreType;
|
||||
uint8_t m_StoreType;
|
||||
uint32_t m_PublishedTimestamp = 0;
|
||||
bool m_IsPublic = true, m_IsPublishedEncrypted = false;
|
||||
std::shared_ptr<i2p::crypto::Verifier> m_TransientVerifier;
|
||||
CryptoKeyType m_EncryptionType;
|
||||
std::shared_ptr<i2p::crypto::CryptoKeyEncryptor> m_Encryptor; // for standardLS2
|
||||
};
|
||||
|
||||
@@ -225,9 +232,17 @@ 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,
|
||||
std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||
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
|
||||
|
||||
virtual ~LocalLeaseSet2 () { delete[] m_Buffer; };
|
||||
@@ -247,17 +262,28 @@ namespace data
|
||||
size_t m_BufferLen;
|
||||
};
|
||||
|
||||
|
||||
const int ENCRYPTED_LEASESET_AUTH_TYPE_NONE = 0;
|
||||
const int ENCRYPTED_LEASESET_AUTH_TYPE_DH = 1;
|
||||
const int ENCRYPTED_LEASESET_AUTH_TYPE_PSK = 2;
|
||||
|
||||
typedef i2p::data::Tag<32> AuthPublicKey;
|
||||
|
||||
class LocalEncryptedLeaseSet2: public LocalLeaseSet2
|
||||
{
|
||||
public:
|
||||
|
||||
LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, i2p::data::SigningKeyType blindedKeyType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519);
|
||||
LocalEncryptedLeaseSet2 (std::shared_ptr<const LocalLeaseSet2> ls, const i2p::data::PrivateKeys& keys, int authType = ENCRYPTED_LEASESET_AUTH_TYPE_NONE, std::shared_ptr<std::vector<AuthPublicKey> > authKeys = nullptr);
|
||||
|
||||
LocalEncryptedLeaseSet2 (std::shared_ptr<const IdentityEx> identity, const uint8_t * buf, size_t len); // from I2CP
|
||||
|
||||
const IdentHash& GetStoreHash () const { return m_StoreHash; };
|
||||
std::shared_ptr<const LocalLeaseSet> GetInnerLeaseSet () const { return m_InnerLeaseSet; };
|
||||
|
||||
private:
|
||||
|
||||
void CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr<std::vector<AuthPublicKey> > authKeys, const uint8_t * authCookie, uint8_t * authData) const;
|
||||
|
||||
private:
|
||||
|
||||
IdentHash m_StoreHash;
|
||||
|
||||
@@ -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();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2018, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*
|
||||
* Kovri go write your own code
|
||||
*
|
||||
*/
|
||||
#ifndef NTCP2_H__
|
||||
#define NTCP2_H__
|
||||
@@ -30,7 +28,7 @@ namespace i2p
|
||||
namespace transport
|
||||
{
|
||||
|
||||
const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519;
|
||||
const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519;
|
||||
const int NTCP2_MAX_PADDING_RATIO = 6; // in %
|
||||
|
||||
const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||
@@ -38,7 +36,7 @@ namespace transport
|
||||
const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes
|
||||
const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
||||
|
||||
const int NTCP2_CLOCK_SKEW = 60; // in seconds
|
||||
const int NTCP2_CLOCK_SKEW = 60; // in seconds
|
||||
const int NTCP2_MAX_OUTGOING_QUEUE_SIZE = 500; // how many messages we can queue up
|
||||
|
||||
enum NTCP2BlockType
|
||||
@@ -48,8 +46,8 @@ namespace transport
|
||||
eNTCP2BlkRouterInfo, // 2
|
||||
eNTCP2BlkI2NPMessage, // 3
|
||||
eNTCP2BlkTermination, // 4
|
||||
eNTCP2BlkPadding = 254
|
||||
};
|
||||
eNTCP2BlkPadding = 254
|
||||
};
|
||||
|
||||
enum NTCP2TerminationReason
|
||||
{
|
||||
@@ -71,21 +69,21 @@ namespace transport
|
||||
eNTCP2RouterInfoSignatureVerificationFail, // 15
|
||||
eNTCP2IncorrectSParameter, // 16
|
||||
eNTCP2Banned, // 17
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
// RouterInfo flags
|
||||
const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
|
||||
const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
|
||||
|
||||
struct NTCP2Establisher
|
||||
{
|
||||
NTCP2Establisher ();
|
||||
~NTCP2Establisher ();
|
||||
|
||||
|
||||
const uint8_t * GetPub () const { return m_EphemeralKeys.GetPublicKey (); };
|
||||
const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
|
||||
uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set
|
||||
|
||||
const uint8_t * GetK () const { return m_K; };
|
||||
const uint8_t * GetK () const { return m_CK + 32; };
|
||||
const uint8_t * GetCK () const { return m_CK; };
|
||||
const uint8_t * GetH () const { return m_H; };
|
||||
|
||||
@@ -114,34 +112,36 @@ namespace transport
|
||||
|
||||
i2p::crypto::X25519Keys m_EphemeralKeys;
|
||||
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
|
||||
uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /*k*/;
|
||||
uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[64] /* [ck, k]*/;
|
||||
i2p::data::IdentHash m_RemoteIdentHash;
|
||||
uint16_t m3p2Len;
|
||||
uint16_t m3p2Len;
|
||||
|
||||
uint8_t * m_SessionRequestBuffer, * m_SessionCreatedBuffer, * m_SessionConfirmedBuffer;
|
||||
size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
class NTCP2Server;
|
||||
class NTCP2Session: public TransportSession, public std::enable_shared_from_this<NTCP2Session>
|
||||
{
|
||||
public:
|
||||
|
||||
NTCP2Session (NTCP2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
|
||||
|
||||
NTCP2Session (NTCP2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
|
||||
~NTCP2Session ();
|
||||
void Terminate ();
|
||||
void TerminateByTimeout ();
|
||||
void Done ();
|
||||
void Close () { m_Socket.close (); }; // for accept
|
||||
|
||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
||||
|
||||
bool IsEstablished () const { return m_IsEstablished; };
|
||||
bool IsTerminated () const { return m_IsTerminated; };
|
||||
|
||||
void ClientLogin (); // Alice
|
||||
void ClientLogin (); // Alice
|
||||
void ServerLogin (); // Bob
|
||||
|
||||
|
||||
void SendLocalRouterInfo (); // after handshake
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||
|
||||
@@ -194,15 +194,15 @@ namespace transport
|
||||
|
||||
std::unique_ptr<NTCP2Establisher> m_Establisher;
|
||||
// data phase
|
||||
uint8_t m_Kab[33], m_Kba[32], m_Sipkeysab[33], m_Sipkeysba[32];
|
||||
uint8_t m_Kab[32], m_Kba[32], m_Sipkeysab[32], m_Sipkeysba[32];
|
||||
const uint8_t * m_SendKey, * m_ReceiveKey;
|
||||
#if OPENSSL_SIPHASH
|
||||
#if OPENSSL_SIPHASH
|
||||
EVP_PKEY * m_SendSipKey, * m_ReceiveSipKey;
|
||||
EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx;
|
||||
#else
|
||||
const uint8_t * m_SendSipKey, * m_ReceiveSipKey;
|
||||
#endif
|
||||
uint16_t m_NextReceivedLen;
|
||||
uint16_t m_NextReceivedLen;
|
||||
uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer;
|
||||
union
|
||||
{
|
||||
@@ -217,31 +217,51 @@ namespace transport
|
||||
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||
};
|
||||
|
||||
class NTCP2Server
|
||||
class NTCP2Server: private i2p::util::RunnableServiceWithWork
|
||||
{
|
||||
public:
|
||||
|
||||
enum RemoteAddressType
|
||||
{
|
||||
eIP4Address,
|
||||
eIP6Address,
|
||||
eHostname
|
||||
};
|
||||
|
||||
enum ProxyType
|
||||
{
|
||||
eNoProxy,
|
||||
eSocksProxy,
|
||||
eHTTPProxy
|
||||
};
|
||||
|
||||
NTCP2Server ();
|
||||
~NTCP2Server ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
|
||||
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session);
|
||||
bool AddNTCP2Session (std::shared_ptr<NTCP2Session> session, bool incoming = false);
|
||||
void RemoveNTCP2Session (std::shared_ptr<NTCP2Session> session);
|
||||
std::shared_ptr<NTCP2Session> FindNTCP2Session (const i2p::data::IdentHash& ident);
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
|
||||
void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<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 Run ();
|
||||
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 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 ();
|
||||
@@ -249,15 +269,17 @@ namespace transport
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::deadline_timer m_TerminationTimer;
|
||||
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_NTCP2Acceptor, m_NTCP2V6Acceptor;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<NTCP2Session> > m_NTCP2Sessions;
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
#include "NTCP2.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Garlic.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "ECIESX25519AEADRatchetSession.h"
|
||||
#include "Config.h"
|
||||
#include "NetDb.hpp"
|
||||
|
||||
using namespace i2p::transport;
|
||||
|
||||
@@ -307,10 +308,18 @@ namespace data
|
||||
if (it == m_LeaseSets.end () || it->second->GetStoreType () != storeType ||
|
||||
leaseSet->GetPublishedTimestamp () > it->second->GetPublishedTimestamp ())
|
||||
{
|
||||
// TODO: implement actual update
|
||||
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
|
||||
m_LeaseSets[ident] = leaseSet;
|
||||
return true;
|
||||
if (leaseSet->IsPublic ())
|
||||
{
|
||||
// TODO: implement actual update
|
||||
LogPrint (eLogInfo, "NetDb: LeaseSet2 updated: ", ident.ToBase32());
|
||||
m_LeaseSets[ident] = leaseSet;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: Unpublished LeaseSet2 received: ", ident.ToBase32());
|
||||
m_LeaseSets.erase (ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -523,9 +532,10 @@ namespace data
|
||||
auto total = m_RouterInfos.size ();
|
||||
uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL;
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch();
|
||||
auto uptime = i2p::context.GetUptime ();
|
||||
// routers don't expire if less than 90 or uptime is less than 1 hour
|
||||
bool checkForExpiration = total > NETDB_MIN_ROUTERS && ts > (i2p::context.GetStartupTime () + 600)*1000LL; // 10 minutes
|
||||
if (checkForExpiration && ts > (i2p::context.GetStartupTime () + 3600)*1000LL) // 1 hour
|
||||
bool checkForExpiration = total > NETDB_MIN_ROUTERS && uptime > 600; // 10 minutes
|
||||
if (checkForExpiration && uptime > 3600) // 1 hour
|
||||
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
|
||||
NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
|
||||
|
||||
@@ -909,7 +919,7 @@ namespace data
|
||||
else if (!leaseSet->IsExpired ()) // we don't send back our LeaseSets
|
||||
{
|
||||
LogPrint (eLogDebug, "NetDb: requested LeaseSet ", key, " found");
|
||||
replyMsg = CreateDatabaseStoreMsg (leaseSet);
|
||||
replyMsg = CreateDatabaseStoreMsg (ident, leaseSet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -940,10 +950,20 @@ namespace data
|
||||
const uint8_t numTags = excluded[32];
|
||||
if (numTags)
|
||||
{
|
||||
const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
|
||||
i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag);
|
||||
replyMsg = garlic.WrapSingleMessage (replyMsg);
|
||||
if(replyMsg == nullptr) LogPrint(eLogError, "NetDb: failed to wrap message");
|
||||
if (flag & DATABASE_LOOKUP_ECIES_FLAG)
|
||||
{
|
||||
uint64_t tag;
|
||||
memcpy (&tag, excluded + 33, 8);
|
||||
replyMsg = i2p::garlic::WrapECIESX25519AEADRatchetMessage (replyMsg, sessionKey, tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
const i2p::garlic::SessionTag sessionTag(excluded + 33); // take first tag
|
||||
i2p::garlic::ElGamalAESSession garlic (sessionKey, sessionTag);
|
||||
replyMsg = garlic.WrapSingleMessage (replyMsg);
|
||||
}
|
||||
if (!replyMsg)
|
||||
LogPrint (eLogError, "NetDb: failed to wrap message");
|
||||
}
|
||||
else
|
||||
LogPrint(eLogWarning, "NetDb: encrypted reply requested but no tags provided");
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace i2p
|
||||
|
||||
RouterContext::RouterContext ():
|
||||
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
|
||||
m_StartupTime (0), m_ShareRatio (100), m_Status (eRouterStatusOK),
|
||||
m_ShareRatio (100), m_Status (eRouterStatusOK),
|
||||
m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID)
|
||||
{
|
||||
}
|
||||
@@ -27,7 +27,8 @@ namespace i2p
|
||||
void RouterContext::Init ()
|
||||
{
|
||||
srand (i2p::util::GetMillisecondsSinceEpoch () % 1000);
|
||||
m_StartupTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
m_StartupTime = std::chrono::steady_clock::now();
|
||||
|
||||
if (!Load ())
|
||||
CreateNewRouter ();
|
||||
m_Decryptor = m_Keys.CreateDecryptor (nullptr);
|
||||
@@ -183,17 +184,18 @@ namespace i2p
|
||||
void RouterContext::PublishNTCP2Address (int port, bool publish, bool v4only)
|
||||
{
|
||||
if (!m_NTCP2Keys) return;
|
||||
if (!port)
|
||||
{
|
||||
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
|
||||
if (port == 9150) port = 9151; // Tor browser
|
||||
}
|
||||
bool updated = false;
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
if (address->IsNTCP2 () && (address->port != port || address->ntcp2->isPublished != publish) && (!v4only || address->host.is_v4 ()))
|
||||
{
|
||||
address->port = port;
|
||||
if (!port && !address->port)
|
||||
{
|
||||
// select random port only if address's port is not set
|
||||
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
|
||||
if (port == 9150) port = 9151; // Tor browser
|
||||
}
|
||||
if (port) address->port = port;
|
||||
address->cost = publish ? 3 : 14;
|
||||
address->ntcp2->isPublished = publish;
|
||||
address->ntcp2->iv = m_NTCP2Keys->iv;
|
||||
@@ -336,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);
|
||||
@@ -432,14 +438,14 @@ namespace i2p
|
||||
}
|
||||
// remove NTCP or NTCP2 v4 address
|
||||
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
|
||||
if (ntcp)
|
||||
if (ntcp)
|
||||
PublishNTCPAddress (false);
|
||||
else
|
||||
{
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
if (ntcp2)
|
||||
PublishNTCP2Address (port, false, true);
|
||||
}
|
||||
}
|
||||
// update
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
@@ -489,7 +495,7 @@ namespace i2p
|
||||
void RouterContext::SetSupportsV6 (bool supportsV6)
|
||||
{
|
||||
if (supportsV6)
|
||||
{
|
||||
{
|
||||
m_RouterInfo.EnableV6 ();
|
||||
// insert v6 addresses if necessary
|
||||
bool foundSSU = false, foundNTCP = false, foundNTCP2 = false;
|
||||
@@ -507,7 +513,7 @@ namespace i2p
|
||||
}
|
||||
else
|
||||
foundNTCP = true;
|
||||
}
|
||||
}
|
||||
port = addr->port;
|
||||
}
|
||||
if (!port) i2p::config::GetOption("port", port);
|
||||
@@ -519,7 +525,7 @@ namespace i2p
|
||||
{
|
||||
std::string host = "::1"; // TODO: read host
|
||||
m_RouterInfo.AddSSUAddress (host.c_str (), port, GetIdentHash ());
|
||||
}
|
||||
}
|
||||
}
|
||||
// NTCP2
|
||||
if (!foundNTCP2)
|
||||
@@ -528,11 +534,11 @@ namespace i2p
|
||||
bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published);
|
||||
if (ntcp2 && ntcp2Published)
|
||||
{
|
||||
std::string ntcp2Host;
|
||||
std::string ntcp2Host;
|
||||
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
|
||||
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
|
||||
else
|
||||
ntcp2Host = "::1";
|
||||
ntcp2Host = "::1";
|
||||
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
|
||||
if (!ntcp2Port) ntcp2Port = port;
|
||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
|
||||
@@ -544,10 +550,10 @@ namespace i2p
|
||||
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
|
||||
if (ntcp)
|
||||
{
|
||||
std::string host = "::1";
|
||||
std::string host = "::1";
|
||||
m_RouterInfo.AddNTCPAddress (host.c_str (), port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
m_RouterInfo.DisableV6 ();
|
||||
@@ -690,9 +696,9 @@ namespace i2p
|
||||
return i2p::tunnel::tunnels.GetExploratoryPool ();
|
||||
}
|
||||
|
||||
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
||||
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len), from));
|
||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
|
||||
}
|
||||
|
||||
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
@@ -715,10 +721,10 @@ namespace i2p
|
||||
|
||||
uint32_t RouterContext::GetUptime () const
|
||||
{
|
||||
return i2p::util::GetSecondsSinceEpoch () - m_StartupTime;
|
||||
return std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now() - m_StartupTime).count ();
|
||||
}
|
||||
|
||||
bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const
|
||||
bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const
|
||||
{
|
||||
return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
@@ -64,8 +65,7 @@ namespace i2p
|
||||
const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
|
||||
i2p::crypto::X25519Keys& GetStaticKeys ();
|
||||
|
||||
uint32_t GetUptime () const;
|
||||
uint32_t GetStartupTime () const { return m_StartupTime; };
|
||||
uint32_t GetUptime () const; // in seconds
|
||||
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
|
||||
uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; };
|
||||
uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; };
|
||||
@@ -108,19 +108,24 @@ namespace i2p
|
||||
|
||||
// implements LocalDestination
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const;
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const;
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||
void SetLeaseSetUpdated () {};
|
||||
|
||||
// implements GarlicDestination
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; };
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
|
||||
// override GarlicDestination
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
protected:
|
||||
|
||||
// implements GarlicDestination
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len);
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) { return false; }; // not implemented
|
||||
|
||||
private:
|
||||
|
||||
void CreateNewRouter ();
|
||||
@@ -136,8 +141,8 @@ namespace i2p
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> m_Decryptor;
|
||||
uint64_t m_LastUpdateTime; // in seconds
|
||||
bool m_AcceptsTunnels, m_IsFloodfill;
|
||||
uint64_t m_StartupTime; // in seconds since epoch
|
||||
bool m_AcceptsTunnels, m_IsFloodfill;
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_StartupTime;
|
||||
uint64_t m_BandwidthLimit; // allowed bandwidth
|
||||
int m_ShareRatio;
|
||||
RouterStatus m_Status;
|
||||
|
||||
@@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <boost/bind.hpp>
|
||||
#include "version.h"
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
@@ -729,7 +730,8 @@ namespace transport
|
||||
encryption.Encrypt (encrypted, encryptedLen, encrypted);
|
||||
// assume actual buffer size is 18 (16 + 2) bytes more
|
||||
memcpy (buf + len, iv, 16);
|
||||
htobe16buf (buf + len + 16, encryptedLen);
|
||||
uint16_t netid = i2p::context.GetNetID ();
|
||||
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
|
||||
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac);
|
||||
}
|
||||
|
||||
@@ -750,7 +752,8 @@ namespace transport
|
||||
m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted);
|
||||
// assume actual buffer size is 18 (16 + 2) bytes more
|
||||
memcpy (buf + len, header->iv, 16);
|
||||
htobe16buf (buf + len + 16, encryptedLen);
|
||||
uint16_t netid = i2p::context.GetNetID ();
|
||||
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
|
||||
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac);
|
||||
}
|
||||
|
||||
@@ -799,7 +802,8 @@ namespace transport
|
||||
uint16_t encryptedLen = len - (encrypted - buf);
|
||||
// assume actual buffer size is 18 (16 + 2) bytes more
|
||||
memcpy (buf + len, header->iv, 16);
|
||||
htobe16buf (buf + len + 16, encryptedLen);
|
||||
uint16_t netid = i2p::context.GetNetID ();
|
||||
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
|
||||
uint8_t digest[16];
|
||||
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest);
|
||||
return !memcmp (header->mac, digest, 16);
|
||||
|
||||
@@ -86,13 +86,14 @@ namespace stream
|
||||
LogPrint (eLogDebug, "Streaming: Stream deleted");
|
||||
}
|
||||
|
||||
void Stream::Terminate ()
|
||||
void Stream::Terminate (bool deleteFromDestination) // shoudl be called from StreamingDestination::Stop only
|
||||
{
|
||||
m_AckSendTimer.cancel ();
|
||||
m_ReceiveTimer.cancel ();
|
||||
m_ResendTimer.cancel ();
|
||||
//CleanUp (); /* Need to recheck - broke working on windows */
|
||||
m_LocalDestination.DeleteStream (shared_from_this ());
|
||||
if (deleteFromDestination)
|
||||
m_LocalDestination.DeleteStream (shared_from_this ());
|
||||
}
|
||||
|
||||
void Stream::CleanUp ()
|
||||
@@ -847,6 +848,9 @@ 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);
|
||||
@@ -918,7 +922,7 @@ namespace stream
|
||||
{
|
||||
expired = false;
|
||||
// time to request
|
||||
if (m_RemoteLeaseSet->GetOrigStoreType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
||||
if (m_RemoteLeaseSet->IsPublishedEncrypted ())
|
||||
m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet (
|
||||
std::make_shared<i2p::data::BlindedPublicKey>(m_RemoteIdentity));
|
||||
else
|
||||
@@ -964,7 +968,6 @@ namespace stream
|
||||
|
||||
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
|
||||
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
|
||||
m_LastIncomingReceiveStreamID (0),
|
||||
m_PendingIncomingTimer (m_Owner->GetService ())
|
||||
{
|
||||
}
|
||||
@@ -990,7 +993,10 @@ namespace stream
|
||||
m_PendingIncomingStreams.clear ();
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
for (auto it: m_Streams)
|
||||
it.second->Terminate (false); // we delete here
|
||||
m_Streams.clear ();
|
||||
m_IncomingStreams.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1013,18 +1019,17 @@ namespace stream
|
||||
if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream
|
||||
{
|
||||
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
|
||||
if (receiveStreamID == m_LastIncomingReceiveStreamID)
|
||||
auto it1 = m_IncomingStreams.find (receiveStreamID);
|
||||
if (it1 != m_IncomingStreams.end ())
|
||||
{
|
||||
// already pending
|
||||
LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists");
|
||||
DeletePacket (packet); // drop it, because previous should be connected
|
||||
return;
|
||||
}
|
||||
auto incomingStream = CreateNewIncomingStream ();
|
||||
auto incomingStream = CreateNewIncomingStream (receiveStreamID);
|
||||
incomingStream->HandleNextPacket (packet); // SYN
|
||||
auto ident = incomingStream->GetRemoteIdentity();
|
||||
|
||||
m_LastIncomingReceiveStreamID = receiveStreamID;
|
||||
|
||||
// handle saved packets if any
|
||||
{
|
||||
@@ -1062,13 +1067,13 @@ namespace stream
|
||||
else // follow on packet without SYN
|
||||
{
|
||||
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
|
||||
for (auto& it: m_Streams)
|
||||
if (it.second->GetSendStreamID () == receiveStreamID)
|
||||
{
|
||||
// found
|
||||
it.second->HandleNextPacket (packet);
|
||||
return;
|
||||
}
|
||||
auto it1 = m_IncomingStreams.find (receiveStreamID);
|
||||
if (it1 != m_IncomingStreams.end ())
|
||||
{
|
||||
// found
|
||||
it1->second->HandleNextPacket (packet);
|
||||
return;
|
||||
}
|
||||
// save follow on packet
|
||||
auto it = m_SavedPackets.find (receiveStreamID);
|
||||
if (it != m_SavedPackets.end ())
|
||||
@@ -1105,11 +1110,12 @@ namespace stream
|
||||
return s;
|
||||
}
|
||||
|
||||
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream ()
|
||||
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream (uint32_t receiveStreamID)
|
||||
{
|
||||
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
m_Streams[s->GetRecvStreamID ()] = s;
|
||||
m_IncomingStreams[receiveStreamID] = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1118,12 +1124,20 @@ namespace stream
|
||||
if (stream)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
auto it = m_Streams.find (stream->GetRecvStreamID ());
|
||||
if (it != m_Streams.end ())
|
||||
m_Streams.erase (it);
|
||||
m_Streams.erase (stream->GetRecvStreamID ());
|
||||
m_IncomingStreams.erase (stream->GetSendStreamID ());
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -180,8 +180,7 @@ namespace stream
|
||||
int GetWindowSize () const { return m_WindowSize; };
|
||||
int GetRTT () const { return m_RTT; };
|
||||
|
||||
/** don't call me */
|
||||
void Terminate ();
|
||||
void Terminate (bool deleteFromDestination = true);
|
||||
|
||||
private:
|
||||
|
||||
@@ -251,6 +250,7 @@ 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; };
|
||||
@@ -269,8 +269,10 @@ namespace stream
|
||||
|
||||
void AcceptOnceAcceptor (std::shared_ptr<Stream> stream, Acceptor acceptor, Acceptor prev);
|
||||
|
||||
private:
|
||||
|
||||
void HandleNextPacket (Packet * packet);
|
||||
std::shared_ptr<Stream> CreateNewIncomingStream ();
|
||||
std::shared_ptr<Stream> CreateNewIncomingStream (uint32_t receiveStreamID);
|
||||
void HandlePendingIncomingTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
@@ -280,8 +282,8 @@ namespace stream
|
||||
bool m_Gzip; // gzip compression of data messages
|
||||
std::mutex m_StreamsMutex;
|
||||
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
|
||||
std::map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
|
||||
Acceptor m_Acceptor;
|
||||
uint32_t m_LastIncomingReceiveStreamID;
|
||||
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
|
||||
boost::asio::deadline_timer m_PendingIncomingTimer;
|
||||
std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
|
||||
|
||||
@@ -71,14 +71,14 @@ public:
|
||||
return std::string (str, str + l);
|
||||
}
|
||||
|
||||
void FromBase32 (const std::string& s)
|
||||
size_t FromBase32 (const std::string& s)
|
||||
{
|
||||
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
|
||||
return i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
|
||||
}
|
||||
|
||||
void FromBase64 (const std::string& s)
|
||||
size_t FromBase64 (const std::string& s)
|
||||
{
|
||||
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
|
||||
return i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -93,4 +93,16 @@ private:
|
||||
} // data
|
||||
} // i2p
|
||||
|
||||
namespace std
|
||||
{
|
||||
// hash for std::unordered_map
|
||||
template<size_t sz> struct hash<i2p::data::Tag<sz> >
|
||||
{
|
||||
size_t operator()(const i2p::data::Tag<sz>& s) const
|
||||
{
|
||||
return s.GetLL ()[0];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* TAG_H__ */
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include "Identity.h"
|
||||
#include "Crypto.h"
|
||||
#include "RouterInfo.h"
|
||||
@@ -67,8 +68,16 @@ namespace transport
|
||||
|
||||
std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; }
|
||||
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
|
||||
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity ()
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
|
||||
return m_RemoteIdentity;
|
||||
}
|
||||
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_RemoteIdentityMutex);
|
||||
m_RemoteIdentity = ident;
|
||||
}
|
||||
|
||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
||||
@@ -85,6 +94,7 @@ namespace transport
|
||||
protected:
|
||||
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
|
||||
mutable std::mutex m_RemoteIdentityMutex;
|
||||
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
|
||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
||||
bool m_IsOutgoing;
|
||||
|
||||
@@ -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);
|
||||
@@ -196,13 +193,38 @@ namespace transport
|
||||
LogPrint(eLogError, "Transports: invalid NTCP proxy url ", ntcpproxy);
|
||||
return;
|
||||
}
|
||||
// create NTCP2. TODO: move to acceptor
|
||||
// create NTCP2. TODO: move to acceptor
|
||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||
if (ntcp2)
|
||||
{
|
||||
m_NTCP2Server = new NTCP2Server ();
|
||||
m_NTCP2Server->Start ();
|
||||
}
|
||||
if(!ntcp2proxy.empty())
|
||||
{
|
||||
if(proxyurl.parse(ntcp2proxy))
|
||||
{
|
||||
if(proxyurl.schema == "socks" || proxyurl.schema == "http")
|
||||
{
|
||||
m_NTCP2Server = new NTCP2Server ();
|
||||
NTCP2Server::ProxyType proxytype = NTCP2Server::eSocksProxy;
|
||||
|
||||
if (proxyurl.schema == "http")
|
||||
proxytype = NTCP2Server::eHTTPProxy;
|
||||
|
||||
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port) ;
|
||||
m_NTCP2Server->Start();
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Transports: invalid NTCP2 proxy url ", ntcp2proxy);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NTCP2Server = new NTCP2Server ();
|
||||
m_NTCP2Server->Start ();
|
||||
}
|
||||
}
|
||||
|
||||
// create acceptors
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
@@ -249,11 +271,11 @@ 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,24 +424,36 @@ 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++;
|
||||
peer.numAttempts++;
|
||||
auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ());
|
||||
if (address && m_NTCPServer)
|
||||
{
|
||||
@@ -465,6 +496,7 @@ namespace transport
|
||||
}
|
||||
}
|
||||
LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available");
|
||||
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed
|
||||
peer.Done ();
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (ident);
|
||||
@@ -503,28 +535,6 @@ namespace transport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
|
||||
{
|
||||
if (!router) return;
|
||||
m_Service->post (std::bind (&Transports::PostCloseSession, this, router));
|
||||
}
|
||||
|
||||
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
|
||||
{
|
||||
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr;
|
||||
if (ssuSession) // try SSU first
|
||||
{
|
||||
m_SSUServer->DeleteSession (ssuSession);
|
||||
LogPrint (eLogDebug, "Transports: SSU session closed");
|
||||
}
|
||||
auto ntcpSession = m_NTCPServer ? m_NTCPServer->FindNTCPSession(router->GetIdentHash()) : nullptr;
|
||||
if (ntcpSession) // try deleting ntcp session too
|
||||
{
|
||||
ntcpSession->Terminate ();
|
||||
LogPrint(eLogDebug, "Transports: NTCP session closed");
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::DetectExternalIP ()
|
||||
{
|
||||
@@ -562,7 +572,10 @@ namespace transport
|
||||
{
|
||||
auto addr = router->GetSSUV6Address ();
|
||||
if (addr)
|
||||
m_SSUServer->CreateDirectSession (router, { addr->host, (uint16_t)addr->port }, false);
|
||||
m_SSUServer->GetServiceV6 ().post ([this, router, addr]
|
||||
{
|
||||
m_SSUServer->CreateDirectSession (router, { addr->host, (uint16_t)addr->port }, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -576,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++)
|
||||
{
|
||||
@@ -591,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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,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)
|
||||
{
|
||||
@@ -643,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 (), {} }));
|
||||
@@ -660,17 +668,19 @@ 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 ())
|
||||
{
|
||||
auto before = it->second.sessions.size ();
|
||||
it->second.sessions.remove (session);
|
||||
if (it->second.sessions.empty ()) // TODO: why?
|
||||
if (it->second.sessions.empty ())
|
||||
{
|
||||
if (it->second.delayedMessages.size () > 0)
|
||||
{
|
||||
if (before > 0) // we had an active session before
|
||||
it->second.numAttempts = 0; // start over
|
||||
ConnectToPeer (ident, it->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
|
||||
@@ -92,7 +92,6 @@ namespace transport
|
||||
|
||||
void SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
|
||||
void CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
|
||||
|
||||
void PeerConnected (std::shared_ptr<TransportSession> session);
|
||||
void PeerDisconnected (std::shared_ptr<TransportSession> session);
|
||||
@@ -131,7 +130,6 @@ namespace transport
|
||||
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
|
||||
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
|
||||
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
|
||||
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
|
||||
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
|
||||
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
|
||||
void HandlePeerTestTimer (const boost::system::error_code& ecode);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <string.h>
|
||||
#include "I2PEndian.h"
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
@@ -13,9 +14,6 @@
|
||||
#include "Config.h"
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -34,9 +32,6 @@ 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 ();
|
||||
@@ -45,7 +40,7 @@ namespace tunnel
|
||||
// shuffle records
|
||||
std::vector<int> recordIndicies;
|
||||
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
|
||||
std::random_shuffle (recordIndicies.begin(), recordIndicies.end());
|
||||
std::shuffle (recordIndicies.begin(), recordIndicies.end(), std::mt19937(std::random_device()()));
|
||||
|
||||
// create real records
|
||||
uint8_t * records = msg->GetPayload () + 1;
|
||||
@@ -63,15 +58,9 @@ 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++)
|
||||
{
|
||||
@@ -206,9 +195,6 @@ namespace tunnel
|
||||
void Tunnel::SetState(TunnelState state)
|
||||
{
|
||||
m_State = state;
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnel.state", this, state);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -590,7 +576,6 @@ namespace tunnel
|
||||
for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();)
|
||||
{
|
||||
auto tunnel = it->second;
|
||||
auto pool = tunnel->GetTunnelPool();
|
||||
switch (tunnel->GetState ())
|
||||
{
|
||||
case eTunnelStatePending:
|
||||
@@ -613,11 +598,6 @@ namespace tunnel
|
||||
hop = hop->next;
|
||||
}
|
||||
}
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
|
||||
#endif
|
||||
// for i2lua
|
||||
if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultTimeout);
|
||||
// delete
|
||||
it = pendingTunnels.erase (it);
|
||||
m_NumFailedTunnelCreations++;
|
||||
@@ -627,12 +607,6 @@ 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);
|
||||
|
||||
it = pendingTunnels.erase (it);
|
||||
m_NumFailedTunnelCreations++;
|
||||
break;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include "I2PEndian.h"
|
||||
#include "Crypto.h"
|
||||
#include "Tunnel.h"
|
||||
@@ -10,9 +11,6 @@
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Destination.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -85,25 +83,17 @@ 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);
|
||||
}
|
||||
if (m_LocalDestination)
|
||||
m_LocalDestination->SetLeaseSetUpdated ();
|
||||
|
||||
OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
|
||||
}
|
||||
|
||||
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
|
||||
{
|
||||
if (expiredTunnel)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnels.expired", expiredTunnel);
|
||||
#endif
|
||||
expiredTunnel->SetTunnelPool (nullptr);
|
||||
for (auto& it: m_Tests)
|
||||
if (it.second.second == expiredTunnel) it.second.second = nullptr;
|
||||
@@ -117,14 +107,9 @@ 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);
|
||||
}
|
||||
OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
|
||||
|
||||
//CreatePairedInboundTunnel (createdTunnel);
|
||||
}
|
||||
|
||||
@@ -132,9 +117,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;
|
||||
@@ -441,7 +423,7 @@ namespace tunnel
|
||||
int size = m_ExplicitPeers->size ();
|
||||
std::vector<int> peerIndicies;
|
||||
for (int i = 0; i < size; i++) peerIndicies.push_back(i);
|
||||
std::random_shuffle (peerIndicies.begin(), peerIndicies.end());
|
||||
std::shuffle (peerIndicies.begin(), peerIndicies.end(), std::mt19937(std::random_device()()));
|
||||
|
||||
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
|
||||
for (int i = 0; i < numHops; i++)
|
||||
@@ -610,11 +592,5 @@ namespace tunnel
|
||||
}
|
||||
return tun;
|
||||
}
|
||||
|
||||
void TunnelPool::OnTunnelBuildResult(std::shared_ptr<Tunnel> tunnel, TunnelBuildResult result)
|
||||
{
|
||||
auto peers = tunnel->GetPeers();
|
||||
if(m_CustomPeerSelector) m_CustomPeerSelector->OnBuildResult(peers, tunnel->IsInbound(), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,6 @@ namespace tunnel
|
||||
class InboundTunnel;
|
||||
class OutboundTunnel;
|
||||
|
||||
|
||||
enum TunnelBuildResult {
|
||||
eBuildResultOkay, // tunnel was built okay
|
||||
eBuildResultRejected, // tunnel build was explicitly rejected
|
||||
eBuildResultTimeout // tunnel build timed out
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<const i2p::data::IdentityEx> Peer;
|
||||
typedef std::vector<Peer> Path;
|
||||
|
||||
@@ -38,7 +31,6 @@ namespace tunnel
|
||||
{
|
||||
virtual ~ITunnelPeerSelector() {};
|
||||
virtual bool SelectPeers(Path & peers, int hops, bool isInbound) = 0;
|
||||
virtual bool OnBuildResult(const Path & peers, bool isInbound, TunnelBuildResult result) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -98,8 +90,6 @@ namespace tunnel
|
||||
std::shared_ptr<InboundTunnel> GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude=nullptr) const;
|
||||
std::shared_ptr<OutboundTunnel> GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude=nullptr) const;
|
||||
|
||||
void OnTunnelBuildResult(std::shared_ptr<Tunnel> tunnel, TunnelBuildResult result);
|
||||
|
||||
// for overriding tunnel peer selection
|
||||
std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const;
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace api
|
||||
std::shared_ptr<i2p::client::ClientDestination> CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
auto localDestination = std::make_shared<i2p::client::ClientDestination> (keys, isPublic, params);
|
||||
auto localDestination = std::make_shared<i2p::client::RunnableClientDestination> (keys, isPublic, params);
|
||||
localDestination->Start ();
|
||||
return localDestination;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ namespace api
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
|
||||
auto localDestination = std::make_shared<i2p::client::ClientDestination> (keys, isPublic, params);
|
||||
auto localDestination = std::make_shared<i2p::client::RunnableClientDestination> (keys, isPublic, params);
|
||||
localDestination->Start ();
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
|
||||
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
|
||||
|
||||
// inet_pton exists Windows since Vista, but XP haven't that function!
|
||||
// inet_pton exists Windows since Vista, but XP doesn't have that function!
|
||||
// This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found
|
||||
int inet_pton_xp(int af, const char *src, void *dst)
|
||||
{
|
||||
@@ -57,21 +57,65 @@ namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
void RunnableService::StartIOService ()
|
||||
{
|
||||
if (!m_IsRunning)
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread.reset (new std::thread (std::bind (& RunnableService::Run, this)));
|
||||
}
|
||||
}
|
||||
|
||||
void RunnableService::StopIOService ()
|
||||
{
|
||||
if (m_IsRunning)
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunnableService::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, m_Name, ": runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace net
|
||||
{
|
||||
#ifdef WIN32
|
||||
bool IsWindowsXPorLater()
|
||||
{
|
||||
OSVERSIONINFO osvi;
|
||||
static bool isRequested = false;
|
||||
static bool isXP = false;
|
||||
if (!isRequested)
|
||||
{
|
||||
// request
|
||||
OSVERSIONINFO osvi;
|
||||
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
GetVersionEx(&osvi);
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
GetVersionEx(&osvi);
|
||||
|
||||
if (osvi.dwMajorVersion <= 5)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
isXP = osvi.dwMajorVersion <= 5;
|
||||
isRequested = true;
|
||||
}
|
||||
return isXP;
|
||||
}
|
||||
|
||||
int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback)
|
||||
@@ -202,21 +246,20 @@ namespace net
|
||||
std::string localAddressUniversal = localAddress.to_string();
|
||||
#endif
|
||||
|
||||
if (IsWindowsXPorLater())
|
||||
{
|
||||
#define inet_pton inet_pton_xp
|
||||
}
|
||||
typedef int (* IPN)(int af, const char *src, void *dst);
|
||||
IPN inetpton = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton");
|
||||
if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found
|
||||
|
||||
if(localAddress.is_v4())
|
||||
{
|
||||
sockaddr_in inputAddress;
|
||||
inet_pton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr));
|
||||
inetpton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr));
|
||||
return GetMTUWindowsIpv4(inputAddress, fallback);
|
||||
}
|
||||
else if(localAddress.is_v6())
|
||||
{
|
||||
sockaddr_in6 inputAddress;
|
||||
inet_pton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr));
|
||||
inetpton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr));
|
||||
return GetMTUWindowsIpv6(inputAddress, fallback);
|
||||
} else {
|
||||
LogPrint(eLogError, "NetIface: GetMTU(): address family is not supported");
|
||||
@@ -281,7 +324,7 @@ namespace net
|
||||
|
||||
int GetMTU(const boost::asio::ip::address& localAddress)
|
||||
{
|
||||
const int fallback = 576; // fallback MTU
|
||||
int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU
|
||||
|
||||
#ifdef WIN32
|
||||
return GetMTUWindows(localAddress, fallback);
|
||||
@@ -312,15 +355,14 @@ namespace net
|
||||
if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af)
|
||||
{
|
||||
// match
|
||||
char * addr = new char[INET6_ADDRSTRLEN];
|
||||
bzero(addr, INET6_ADDRSTRLEN);
|
||||
char addr[INET6_ADDRSTRLEN];
|
||||
memset (addr, 0, INET6_ADDRSTRLEN);
|
||||
if(af == AF_INET)
|
||||
inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN);
|
||||
else
|
||||
inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN);
|
||||
freeifaddrs(addrs);
|
||||
std::string cur_ifaddr(addr);
|
||||
delete[] addr;
|
||||
return boost::asio::ip::address::from_string(cur_ifaddr);
|
||||
}
|
||||
cur = cur->ifa_next;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
@@ -46,7 +47,7 @@ namespace util
|
||||
{
|
||||
auto tmp = m_Head;
|
||||
m_Head = static_cast<T*>(*(void * *)m_Head); // next
|
||||
delete tmp;
|
||||
::operator delete ((void *)tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +123,43 @@ namespace util
|
||||
std::mutex m_Mutex;
|
||||
};
|
||||
|
||||
class RunnableService
|
||||
{
|
||||
protected:
|
||||
|
||||
RunnableService (const std::string& name): m_Name (name), m_IsRunning (false) {}
|
||||
virtual ~RunnableService () {}
|
||||
|
||||
boost::asio::io_service& GetIOService () { return m_Service; }
|
||||
bool IsRunning () const { return m_IsRunning; };
|
||||
|
||||
void StartIOService ();
|
||||
void StopIOService ();
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
|
||||
private:
|
||||
|
||||
std::string m_Name;
|
||||
volatile bool m_IsRunning;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
};
|
||||
|
||||
class RunnableServiceWithWork: public RunnableService
|
||||
{
|
||||
protected:
|
||||
|
||||
RunnableServiceWithWork (const std::string& name):
|
||||
RunnableService (name), m_Work (GetIOService ()) {}
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::io_service::work m_Work;
|
||||
};
|
||||
|
||||
namespace net
|
||||
{
|
||||
int GetMTU (const boost::asio::ip::address& localAddress);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c)
|
||||
|
||||
#define I2PD_VERSION_MAJOR 2
|
||||
#define I2PD_VERSION_MINOR 26
|
||||
#define I2PD_VERSION_MINOR 31
|
||||
#define I2PD_VERSION_MICRO 0
|
||||
#define I2PD_VERSION_PATCH 0
|
||||
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#define I2P_VERSION_MAJOR 0
|
||||
#define I2P_VERSION_MINOR 9
|
||||
#define I2P_VERSION_MICRO 40
|
||||
#define I2P_VERSION_MICRO 45
|
||||
#define I2P_VERSION_PATCH 0
|
||||
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
||||
|
||||
|
||||
@@ -237,17 +237,19 @@ namespace client
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
Address::Address (const std::string& b32)
|
||||
Address::Address (const std::string& b32):
|
||||
addressType (eAddressInvalid)
|
||||
{
|
||||
if (b32.length () <= B33_ADDRESS_THRESHOLD)
|
||||
{
|
||||
addressType = eAddressIndentHash;
|
||||
identHash.FromBase32 (b32);
|
||||
if (identHash.FromBase32 (b32) > 0)
|
||||
addressType = eAddressIndentHash;
|
||||
}
|
||||
else
|
||||
{
|
||||
addressType = eAddressBlindedPublicKey;
|
||||
blindedPublicKey = std::make_shared<i2p::data::BlindedPublicKey>(b32);
|
||||
if (blindedPublicKey->IsValid ())
|
||||
addressType = eAddressBlindedPublicKey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,7 +322,10 @@ namespace client
|
||||
{
|
||||
auto pos = address.find(".b32.i2p");
|
||||
if (pos != std::string::npos)
|
||||
return std::make_shared<const Address>(address.substr (0, pos));
|
||||
{
|
||||
auto addr = std::make_shared<const Address>(address.substr (0, pos));
|
||||
return addr->IsValid () ? addr : nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = address.find (".i2p");
|
||||
@@ -422,9 +427,9 @@ namespace client
|
||||
std::string name = s.substr(0, pos++);
|
||||
std::string addr = s.substr(pos);
|
||||
|
||||
size_t pos = s.find('#');
|
||||
size_t pos = addr.find('#');
|
||||
if (pos != std::string::npos)
|
||||
addr = addr.substr(pos); // remove comments
|
||||
addr = addr.substr(0, pos); // remove comments
|
||||
|
||||
auto ident = std::make_shared<i2p::data::IdentityEx> ();
|
||||
if (!ident->FromBase64(addr)) {
|
||||
|
||||
@@ -33,13 +33,14 @@ namespace client
|
||||
|
||||
struct Address
|
||||
{
|
||||
enum { eAddressIndentHash, eAddressBlindedPublicKey } addressType;
|
||||
enum { eAddressIndentHash, eAddressBlindedPublicKey, eAddressInvalid } addressType;
|
||||
i2p::data::IdentHash identHash;
|
||||
std::shared_ptr<i2p::data::BlindedPublicKey> blindedPublicKey;
|
||||
|
||||
Address (const std::string& b32);
|
||||
Address (const i2p::data::IdentHash& hash);
|
||||
bool IsIdentHash () const { return addressType == eAddressIndentHash; };
|
||||
bool IsValid () const { return addressType != eAddressInvalid; };
|
||||
};
|
||||
|
||||
inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }
|
||||
|
||||
@@ -341,17 +341,17 @@ namespace client
|
||||
SendReplyOK();
|
||||
}
|
||||
|
||||
void BOBCommandSession::SendData (const char * data)
|
||||
void BOBCommandSession::SendRaw (const char * data)
|
||||
{
|
||||
std::ostream os(&m_SendBuffer);
|
||||
os << "DATA " << data << std::endl;
|
||||
os << data << std::endl;
|
||||
}
|
||||
|
||||
void BOBCommandSession::BuildStatusLine(bool currentTunnel, BOBDestination *dest, std::string &out)
|
||||
{
|
||||
// helper lambdas
|
||||
const auto isset = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost
|
||||
const auto issetNum = [&isset](const int p) { return isset(p == 0 ? "" : std::to_string(p)); }; // for inport, outport
|
||||
const auto issetStr = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost
|
||||
const auto issetNum = [&issetStr](const int p) { return issetStr(p == 0 ? "" : std::to_string(p)); }; // for inport, outport
|
||||
const auto destExists = [](const BOBDestination * const dest) { return dest != nullptr; };
|
||||
const auto destReady = [](const BOBDestination * const dest) { return dest->GetLocalDestination()->IsReady(); };
|
||||
const auto bool_str = [](const bool v) { return v ? "true" : "false"; }; // bool -> str
|
||||
@@ -359,8 +359,8 @@ namespace client
|
||||
// tunnel info
|
||||
const std::string nickname = currentTunnel ? m_Nickname : dest->GetNickname();
|
||||
const bool quiet = currentTunnel ? m_IsQuiet : dest->GetQuiet();
|
||||
const std::string inhost = isset(currentTunnel ? m_InHost : dest->GetInHost());
|
||||
const std::string outhost = isset(currentTunnel ? m_OutHost : dest->GetOutHost());
|
||||
const std::string inhost = issetStr(currentTunnel ? m_InHost : dest->GetInHost());
|
||||
const std::string outhost = issetStr(currentTunnel ? m_OutHost : dest->GetOutHost());
|
||||
const std::string inport = issetNum(currentTunnel ? m_InPort : dest->GetInPort());
|
||||
const std::string outport = issetNum(currentTunnel ? m_OutPort : dest->GetOutPort());
|
||||
const bool keys = destExists(dest); // key must exist when destination is created
|
||||
@@ -370,7 +370,8 @@ namespace client
|
||||
|
||||
// build line
|
||||
std::stringstream ss;
|
||||
ss << "NICKNAME: " << nickname << " " << "STARTING: " << bool_str(starting) << " "
|
||||
ss << "DATA "
|
||||
<< "NICKNAME: " << nickname << " " << "STARTING: " << bool_str(starting) << " "
|
||||
<< "RUNNING: " << bool_str(running) << " " << "STOPPING: " << bool_str(stopping) << " "
|
||||
<< "KEYS: " << bool_str(keys) << " " << "QUIET: " << bool_str(quiet) << " "
|
||||
<< "INPORT: " << inport << " " << "INHOST: " << inhost << " "
|
||||
@@ -654,16 +655,16 @@ namespace client
|
||||
for (const auto& it: destinations)
|
||||
{
|
||||
BuildStatusLine(false, it.second, statusLine);
|
||||
SendData (statusLine.c_str());
|
||||
SendRaw(statusLine.c_str());
|
||||
if(m_Nickname.compare(it.second->GetNickname()) == 0)
|
||||
sentCurrent = true;
|
||||
}
|
||||
if(!sentCurrent && !m_Nickname.empty())
|
||||
{
|
||||
// add the current tunnel to the list
|
||||
// add the current tunnel to the list.
|
||||
// this is for the incomplete tunnel which has not been started yet.
|
||||
BuildStatusLine(true, m_CurrentDestination, statusLine);
|
||||
LogPrint(eLogError, statusLine);
|
||||
SendData(statusLine.c_str());
|
||||
SendRaw(statusLine.c_str());
|
||||
}
|
||||
SendReplyOK ("Listing done");
|
||||
}
|
||||
@@ -690,21 +691,23 @@ namespace client
|
||||
void BOBCommandSession::StatusCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: status ", operand);
|
||||
const std::string name = operand;
|
||||
std::string statusLine;
|
||||
if (m_Nickname == operand)
|
||||
|
||||
// always prefer destination
|
||||
auto ptr = m_Owner.FindDestination(name);
|
||||
if(ptr != nullptr)
|
||||
{
|
||||
// check current tunnel
|
||||
BuildStatusLine(true, nullptr, statusLine);
|
||||
// tunnel destination exists
|
||||
BuildStatusLine(false, ptr, statusLine);
|
||||
SendReplyOK(statusLine.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// check other
|
||||
std::string name = operand;
|
||||
auto ptr = m_Owner.FindDestination(name);
|
||||
if(ptr != nullptr)
|
||||
if(m_Nickname == name && !name.empty())
|
||||
{
|
||||
BuildStatusLine(false, ptr, statusLine);
|
||||
// tunnel is incomplete / has not been started yet
|
||||
BuildStatusLine(true, nullptr, statusLine);
|
||||
SendReplyOK(statusLine.c_str());
|
||||
}
|
||||
else
|
||||
@@ -740,8 +743,8 @@ namespace client
|
||||
}
|
||||
|
||||
BOBCommandChannel::BOBCommandChannel (const std::string& address, int port):
|
||||
m_IsRunning (false), m_Thread (nullptr),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
|
||||
RunnableService ("BOB"),
|
||||
m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
|
||||
{
|
||||
// command -> handler
|
||||
m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler;
|
||||
@@ -791,7 +794,8 @@ namespace client
|
||||
|
||||
BOBCommandChannel::~BOBCommandChannel ()
|
||||
{
|
||||
Stop ();
|
||||
if (IsRunning ())
|
||||
Stop ();
|
||||
for (const auto& it: m_Destinations)
|
||||
delete it.second;
|
||||
}
|
||||
@@ -799,38 +803,15 @@ namespace client
|
||||
void BOBCommandChannel::Start ()
|
||||
{
|
||||
Accept ();
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&BOBCommandChannel::Run, this));
|
||||
StartIOService ();
|
||||
}
|
||||
|
||||
void BOBCommandChannel::Stop ()
|
||||
{
|
||||
m_IsRunning = false;
|
||||
for (auto& it: m_Destinations)
|
||||
it.second->Stop ();
|
||||
m_Acceptor.cancel ();
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void BOBCommandChannel::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "BOB: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
StopIOService ();
|
||||
}
|
||||
|
||||
void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <boost/asio.hpp>
|
||||
#include "util.h"
|
||||
#include "I2PTunnel.h"
|
||||
#include "I2PService.h"
|
||||
#include "Identity.h"
|
||||
@@ -213,7 +214,7 @@ namespace client
|
||||
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void SendReplyOK (const char * msg = nullptr);
|
||||
void SendReplyError (const char * msg);
|
||||
void SendData (const char * data);
|
||||
void SendRaw (const char * data);
|
||||
|
||||
void BuildStatusLine(bool currentTunnel, BOBDestination *destination, std::string &out);
|
||||
|
||||
@@ -231,7 +232,7 @@ namespace client
|
||||
};
|
||||
typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len);
|
||||
|
||||
class BOBCommandChannel
|
||||
class BOBCommandChannel: private i2p::util::RunnableService
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -241,22 +242,18 @@ namespace client
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
void AddDestination (const std::string& name, BOBDestination * dest);
|
||||
void DeleteDestination (const std::string& name);
|
||||
BOBDestination * FindDestination (const std::string& name);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void Accept ();
|
||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<BOBCommandSession> session);
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
std::map<std::string, BOBDestination *> m_Destinations;
|
||||
std::map<std::string, BOBCommandHandler> m_CommandHandlers;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "util.h"
|
||||
#include "ClientContext.h"
|
||||
#include "SOCKS.h"
|
||||
#include "WebSocks.h"
|
||||
#include "MatchedDestination.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -53,14 +52,19 @@ namespace client
|
||||
|
||||
// SAM
|
||||
bool sam; i2p::config::GetOption("sam.enabled", sam);
|
||||
if (sam) {
|
||||
if (sam)
|
||||
{
|
||||
std::string samAddr; i2p::config::GetOption("sam.address", samAddr);
|
||||
uint16_t samPort; i2p::config::GetOption("sam.port", samPort);
|
||||
bool singleThread; i2p::config::GetOption("sam.singlethread", singleThread);
|
||||
LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort);
|
||||
try {
|
||||
m_SamBridge = new SAMBridge (samAddr, samPort);
|
||||
m_SamBridge->Start ();
|
||||
} catch (std::exception& e) {
|
||||
try
|
||||
{
|
||||
m_SamBridge = new SAMBridge (samAddr, samPort, singleThread);
|
||||
m_SamBridge->Start ();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LogPrint(eLogError, "Clients: Exception in SAM bridge: ", e.what());
|
||||
}
|
||||
}
|
||||
@@ -305,21 +309,34 @@ namespace client
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
|
||||
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
|
||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
||||
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
|
||||
localDestination->Start ();
|
||||
auto localDestination = std::make_shared<RunnableClientDestination> (keys, isPublic, params);
|
||||
AddLocalDestination (localDestination);
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (
|
||||
boost::asio::io_service& service, bool isPublic,
|
||||
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType,
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
|
||||
auto localDestination = std::make_shared<ClientDestination> (service, keys, isPublic, params);
|
||||
AddLocalDestination (localDestination);
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map<std::string, std::string> * params)
|
||||
{
|
||||
MatchedTunnelDestination * cl = new MatchedTunnelDestination(keys, name, params);
|
||||
auto localDestination = std::shared_ptr<ClientDestination>(cl);
|
||||
auto localDestination = std::make_shared<MatchedTunnelDestination>(keys, name, params);
|
||||
AddLocalDestination (localDestination);
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
void ClientContext::AddLocalDestination (std::shared_ptr<ClientDestination> localDestination)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
||||
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
|
||||
localDestination->Start ();
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
void ClientContext::DeleteLocalDestination (std::shared_ptr<ClientDestination> destination)
|
||||
@@ -344,14 +361,26 @@ namespace client
|
||||
if (it != m_Destinations.end ())
|
||||
{
|
||||
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
|
||||
if (!it->second->IsRunning ())
|
||||
it->second->Start ();
|
||||
it->second->Start (); // make sure to start
|
||||
return it->second;
|
||||
}
|
||||
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
|
||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
||||
m_Destinations[keys.GetPublic ()->GetIdentHash ()] = localDestination;
|
||||
localDestination->Start ();
|
||||
auto localDestination = std::make_shared<RunnableClientDestination> (keys, isPublic, params);
|
||||
AddLocalDestination (localDestination);
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (boost::asio::io_service& service,
|
||||
const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params)
|
||||
{
|
||||
auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ());
|
||||
if (it != m_Destinations.end ())
|
||||
{
|
||||
LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists");
|
||||
it->second->Start (); // make sure to start
|
||||
return it->second;
|
||||
}
|
||||
auto localDestination = std::make_shared<ClientDestination> (service, keys, isPublic, params);
|
||||
AddLocalDestination (localDestination);
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
@@ -381,6 +410,16 @@ namespace client
|
||||
return section.second.get (boost::property_tree::ptree::path_type (name, '/'), value);
|
||||
}
|
||||
|
||||
template<typename Section>
|
||||
void ClientContext::ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map<std::string, std::string>& options) const
|
||||
{
|
||||
for (auto it: section.second)
|
||||
{
|
||||
if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group))
|
||||
options[it.first] = it.second.get_value ("");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Section>
|
||||
void ClientContext::ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const
|
||||
{
|
||||
@@ -395,6 +434,17 @@ namespace client
|
||||
options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
|
||||
std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "");
|
||||
if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType;
|
||||
std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, "");
|
||||
if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey;
|
||||
auto authType = GetI2CPOption(section, I2CP_PARAM_LEASESET_AUTH_TYPE, 0);
|
||||
if (authType != "0") // auth is set
|
||||
{
|
||||
options[I2CP_PARAM_LEASESET_AUTH_TYPE] = authType;
|
||||
if (authType == "1") // DH
|
||||
ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_DH, options);
|
||||
else if (authType == "2") // PSK
|
||||
ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_PSK, options);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const
|
||||
@@ -547,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
|
||||
{
|
||||
@@ -608,8 +656,8 @@ namespace client
|
||||
|
||||
// I2CP
|
||||
std::map<std::string, std::string> options;
|
||||
ReadI2CPOptions (section, options);
|
||||
|
||||
ReadI2CPOptions (section, options);
|
||||
|
||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
||||
i2p::data::PrivateKeys k;
|
||||
if(!LoadPrivateKeys (k, keys, sigType, cryptoType))
|
||||
|
||||
@@ -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";
|
||||
@@ -68,8 +68,15 @@ namespace client
|
||||
i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
||||
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
|
||||
const std::map<std::string, std::string> * params = nullptr); // used by SAM only
|
||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
|
||||
bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
||||
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
|
||||
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
|
||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
||||
const std::map<std::string, std::string> * params = nullptr);
|
||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
|
||||
const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
||||
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
|
||||
std::shared_ptr<ClientDestination> CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map<std::string, std::string> * params = nullptr);
|
||||
void DeleteLocalDestination (std::shared_ptr<ClientDestination> destination);
|
||||
std::shared_ptr<ClientDestination> FindLocalDestination (const i2p::data::IdentHash& destination) const;
|
||||
@@ -95,6 +102,8 @@ namespace client
|
||||
template<typename Section>
|
||||
std::string GetI2CPStringOption (const Section& section, const std::string& name, const std::string& value) const; // GetI2CPOption with string default value
|
||||
template<typename Section>
|
||||
void ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map<std::string, std::string>& options) const;
|
||||
template<typename Section>
|
||||
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const; // for tunnels
|
||||
void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const; // for HTTP and SOCKS proxy
|
||||
|
||||
@@ -105,6 +114,7 @@ namespace client
|
||||
void VisitTunnels (Visitor v); // Visitor: (I2PService *) -> bool, true means retain
|
||||
|
||||
void CreateNewSharedLocalDestination ();
|
||||
void AddLocalDestination (std::shared_ptr<ClientDestination> localDestination);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user