Merge pull request #1583 from PurpleI2P/openssl

2.35.0
This commit is contained in:
orignal 2020-11-30 14:53:45 -05:00 committed by GitHub
commit 2ff6f9d346
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 1781 additions and 1241 deletions

20
.github/workflows/build-osx.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: Build on OSX
on: [push, pull_request]
jobs:
build:
name: With USE_UPNP=${{ matrix.with_upnp }}
runs-on: macOS-latest
strategy:
fail-fast: true
matrix:
with_upnp: ['yes', 'no']
steps:
- uses: actions/checkout@v2
- name: install packages
run: |
brew update
brew install boost miniupnpc openssl@1.1
- name: build application
run: make HOMEBREW=1 USE_UPNP=${{ matrix.with_upnp }} PREFIX=$GITHUB_WORKSPACE/output -j3

View File

@ -26,4 +26,6 @@ jobs:
install: base-devel mingw-w64-${{ matrix.arch }}-gcc mingw-w64-${{ matrix.arch }}-boost mingw-w64-${{ matrix.arch }}-openssl mingw-w64-${{ matrix.arch }}-miniupnpc install: base-devel mingw-w64-${{ matrix.arch }}-gcc mingw-w64-${{ matrix.arch }}-boost mingw-w64-${{ matrix.arch }}-openssl mingw-w64-${{ matrix.arch }}-miniupnpc
update: true update: true
- name: build application - name: build application
run: make USE_UPNP=yes DEBUG=no -j3 run: |
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
make USE_UPNP=yes DEBUG=no -j3

View File

@ -1,10 +1,10 @@
name: Build on Ubuntu with make name: Build on Ubuntu
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
build: build:
name: Building with USE_UPNP=${{ matrix.with_upnp }} flag name: With USE_UPNP=${{ matrix.with_upnp }}
runs-on: ubuntu-16.04 runs-on: ubuntu-16.04
strategy: strategy:
fail-fast: true fail-fast: true
@ -18,4 +18,20 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install build-essential libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev sudo apt-get install build-essential libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application - name: build application
run: make USE_AVX=no USE_AESNI=no USE_UPNP=${{ matrix.with_upnp }} -j3 run: make USE_UPNP=${{ matrix.with_upnp }} -j3
build_qt:
name: With QT GUI
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v2
- name: install packages
run: |
sudo add-apt-repository ppa:mhier/libboost-latest
sudo apt-get update
sudo apt-get install build-essential qt5-default libqt5gui5 libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application
run: |
cd qt/i2pd_qt
qmake
make -j3

3
.gitignore vendored
View File

@ -3,11 +3,12 @@
router.info router.info
router.keys router.keys
i2p i2p
libi2pd.so
netDb netDb
/i2pd /i2pd
/libi2pd.a /libi2pd.a
/libi2pdclient.a /libi2pdclient.a
/libi2pd.so
/libi2pdclient.so
*.exe *.exe

View File

@ -1,6 +1,57 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.35.0] - 2020-11-30
### Added
- ECIES-x25519 routers
- Random intro keys for SSU
- Graceful shutdown timer for windows
- Send queue for I2CP messages
- Update DSA router keys to EdDSA
- TCP_QUICKACK for NTCP2 sockets on Linux
### Changed
- Exclude floodfills with DSA signatures and < 0.9.28
- Random intervals between tunnel tests and manage for tunnel pools
- Don't replace an addressbook record by one with DSA signature
- Publish RouterInfo after update
- Create paired inbound tunnels if no inbound tunnels yet
- Reseed servers list
### Fixed
- Transient signature length, if different from identity
- Terminate I2CP session if destroyed
- RouterInfo publishing confirmation
- Check if ECIES-X25519-AEAD-Ratchet session expired before generating more tags
- Correct block size for delivery type local for ECIES-X25519-AEAD-Ratchet
## [2.34.0] - 2020-10-27
### Added
- Ping responses for streaming
- STREAM FORWARD for SAM
- Tunnels through ECIES-x25519 routers
- Single thread for I2CP
- Shared transient destination between proxies
- Database lookups from ECIES destinations with ratchets response
- Handle WebDAV HTTP methods
- Don't try to connect or build tunnels if offline
- Validate IP when trying connect to remote peer
- Handle ICMP responses and WinAPI errors for SSU
### Changed
- Removed NTCP
- Dropped gcc 4.7 support
- Encyption type 0,4 by default for client tunnels
- Stripped out some HTTP header for HTTP server response
- HTTP 1.1 addressbook requests
- Set LeaseSet type to 3 for ratchets if not specified
- Handle SSU v4 and v6 messages in one thread
- Eliminate DH keys thread
### Fixed
- Random crashes on I2CP session disconnect
- Stream through racthets hangs if first SYN was not acked
- Check "Last-Modified" instead "If-Modified-Since" for addressbook reponse
- Trim behind ECIESx25519 tags
- Few bugs with Android main activity
- QT visual and layout issues
## [2.33.0] - 2020-08-24 ## [2.33.0] - 2020-08-24
### Added ### Added
- Shared transient addresses - Shared transient addresses

View File

@ -1,4 +1,4 @@
Copyright (c) 2013-2015, The PurpleI2P Project Copyright (c) 2013-2020, The PurpleI2P Project
All rights reserved. All rights reserved.

View File

@ -4,17 +4,15 @@ ARLIB := libi2pd.a
SHLIB_CLIENT := libi2pdclient.so SHLIB_CLIENT := libi2pdclient.so
ARLIB_CLIENT := libi2pdclient.a ARLIB_CLIENT := libi2pdclient.a
I2PD := i2pd I2PD := i2pd
GREP := grep
DEPS := obj/make.dep
LIB_SRC_DIR := libi2pd LIB_SRC_DIR := libi2pd
LIB_CLIENT_SRC_DIR := libi2pd_client LIB_CLIENT_SRC_DIR := libi2pd_client
DAEMON_SRC_DIR := daemon DAEMON_SRC_DIR := daemon
# import source files lists
include filelist.mk include filelist.mk
USE_AESNI := yes USE_AESNI := yes
USE_AVX := yes
USE_STATIC := no USE_STATIC := no
USE_MESHNET := no USE_MESHNET := no
USE_UPNP := no USE_UPNP := no
@ -51,7 +49,12 @@ ifeq ($(USE_MESHNET),yes)
NEEDED_CXXFLAGS += -DMESHNET NEEDED_CXXFLAGS += -DMESHNET
endif endif
NEEDED_CXXFLAGS += -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR)
LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d)
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD) all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
@ -72,32 +75,29 @@ api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
## -std=c++11. If you want to remove this variable please do so in a way that allows setting ## -std=c++11. If you want to remove this variable please do so in a way that allows setting
## custom FLAGS to work at build-time. ## custom FLAGS to work at build-time.
deps: mk_obj_dir
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
obj/%.o: %.cpp obj/%.o: %.cpp
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $< $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -c -o $@ $<
# '-' is 'ignore if missing' on first run # '-' is 'ignore if missing' on first run
-include $(DEPS) -include $(DEPS)
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
$(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS) $(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC)) $(SHLIB): $(LIB_OBJS)
ifneq ($(USE_STATIC),yes) ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^ $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
endif endif
$(SHLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC)) $(SHLIB_CLIENT): $(LIB_CLIENT_OBJS)
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^ ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB)
endif
$(ARLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC)) $(ARLIB): $(LIB_OBJS)
$(AR) -r $@ $^ $(AR) -r $@ $^
$(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC)) $(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
$(AR) -r $@ $^ $(AR) -r $@ $^
clean: clean:
@ -123,7 +123,6 @@ doxygen:
.PHONY: all .PHONY: all
.PHONY: clean .PHONY: clean
.PHONY: deps
.PHONY: doxygen .PHONY: doxygen
.PHONY: dist .PHONY: dist
.PHONY: last-dist .PHONY: last-dist
@ -131,3 +130,4 @@ doxygen:
.PHONY: api_client .PHONY: api_client
.PHONY: mk_obj_dir .PHONY: mk_obj_dir
.PHONY: install .PHONY: install
.PHONY: strip

View File

@ -35,10 +35,7 @@ endif
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2 # Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic # Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
ifeq ($(USE_AESNI),yes) ifeq ($(USE_AESNI),yes)
CXXFLAGS += -maes CXXFLAGS += -D__AES__ -maes
endif
ifeq ($(USE_AVX),1)
CXXFLAGS += -mavx
endif endif
install: all install: all
@ -51,4 +48,4 @@ install: all
@ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/ @ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/
@ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf @ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf
@ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt @ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf @ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf

View File

@ -1,5 +1,5 @@
# set defaults instead redefine # set defaults instead redefine
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation -Wno-psabi CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-psabi
LDFLAGS ?= ${LD_DEBUG} LDFLAGS ?= ${LD_DEBUG}
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time ## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
@ -33,7 +33,7 @@ ifeq ($(USE_STATIC),yes)
# NOTE: on glibc you will get this warning: # NOTE: on glibc you will get this warning:
# Using 'getaddrinfo' in statically linked applications requires at runtime # Using 'getaddrinfo' in statically linked applications requires at runtime
# the shared libraries from the glibc version used for linking # the shared libraries from the glibc version used for linking
LIBDIR := /usr/lib LIBDIR := /usr/lib/$(SYS)
LDLIBS += $(LIBDIR)/libboost_system.a LDLIBS += $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_date_time.a LDLIBS += $(LIBDIR)/libboost_date_time.a
LDLIBS += $(LIBDIR)/libboost_filesystem.a LDLIBS += $(LIBDIR)/libboost_filesystem.a
@ -41,37 +41,24 @@ ifeq ($(USE_STATIC),yes)
LDLIBS += $(LIBDIR)/libssl.a LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libz.a LDLIBS += $(LIBDIR)/libz.a
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt -ldl ifeq ($(USE_UPNP),yes)
USE_AESNI := no LDLIBS += $(LIBDIR)/libminiupnpc.a
endif
LDLIBS += -lpthread -ldl
else else
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
endif
# UPNP Support (miniupnpc 1.5 and higher)
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
CXXFLAGS += -DUSE_UPNP
ifeq ($(USE_STATIC),yes)
LDLIBS += $(LIBDIR)/libminiupnpc.a
else
LDLIBS += -lminiupnpc LDLIBS += -lminiupnpc
endif endif
endif endif
ifeq ($(USE_AESNI),yes) # UPNP Support (miniupnpc 1.5 and higher)
#check if AES-NI is supported by CPU ifeq ($(USE_UPNP),yes)
ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0) NEEDED_CXXFLAGS += -DUSE_UPNP
machine := $(shell uname -m)
ifeq ($(machine), aarch64)
CXXFLAGS += -DARM64AES
else
CPU_FLAGS += -maes
endif
endif
endif endif
ifeq ($(USE_AVX),yes) ifeq ($(USE_AESNI),yes)
#check if AVX supported by CPU ifeq (, $(findstring arm, $(SYS))$(findstring aarch64, $(SYS))) # no arm and aarch64 in dumpmachine
ifneq ($(shell $(GREP) -c avx /proc/cpuinfo),0) NEEDED_CXXFLAGS += -D__AES__ -maes
CPU_FLAGS += -mavx
endif endif
endif endif

View File

@ -1,9 +1,11 @@
USE_WIN32_APP=yes # Build application with GUI (tray, main window)
CXX = g++ USE_WIN32_APP := yes
WINDRES = windres WINDRES = windres
CXXFLAGS := ${CXX_DEBUG} -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN
INCFLAGS = -Idaemon -I. CXXFLAGS := $(CXX_DEBUG) -D_MT -DWIN32_LEAN_AND_MEAN -fPIC -msse
LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc -static-libstdc++ INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32
LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc
# detect proper flag for c++11 support by compilers # detect proper flag for c++11 support by compilers
CXXVER := $(shell $(CXX) -dumpversion) CXXVER := $(shell $(CXX) -dumpversion)
@ -38,29 +40,21 @@ LDLIBS += \
-liphlpapi \ -liphlpapi \
-lole32 \ -lole32 \
-luuid \ -luuid \
-lstdc++ \
-lpthread -lpthread
ifeq ($(USE_WIN32_APP), yes) ifeq ($(USE_WIN32_APP), yes)
CXXFLAGS += -DWIN32_APP NEEDED_CXXFLAGS += -DWIN32_APP
LDFLAGS += -mwindows LDFLAGS += -mwindows
DAEMON_RC += Win32/Resource.rc DAEMON_RC += Win32/Resource.rc
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
endif endif
ifeq ($(USE_WINXP_FLAGS), yes) ifeq ($(USE_WINXP_FLAGS), yes)
CXXFLAGS += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501 NEEDED_CXXFLAGS += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
endif endif
# don't change following line to ifeq ($(USE_AESNI),yes) !!! ifeq ($(USE_AESNI),yes)
ifeq ($(USE_AESNI),1) NEEDED_CXXFLAGS += -D__AES__ -maes
CPU_FLAGS += -maes
else
CPU_FLAGS += -msse
endif
ifeq ($(USE_AVX),1)
CPU_FLAGS += -mavx
endif endif
ifeq ($(USE_ASLR),yes) ifeq ($(USE_ASLR),yes)

View File

@ -22,12 +22,8 @@ ifeq ($(USE_UPNP),yes)
endif endif
endif endif
ifeq ($(USE_AESNI),1) ifeq ($(USE_AESNI),yes)
CXXFLAGS += -maes CXXFLAGS += -D__AES__ -maes
else else
CXXFLAGS += -msse CXXFLAGS += -msse
endif endif
ifeq ($(USE_AVX),1)
CXXFLAGS += -mavx
endif

View File

@ -14,10 +14,10 @@
#include "Log.h" #include "Log.h"
#ifdef _WIN32 #ifdef _WIN32
#include "Win32/Win32Service.h" #include "Win32Service.h"
#ifdef WIN32_APP #ifdef WIN32_APP
#include <windows.h> #include <windows.h>
#include "Win32/Win32App.h" #include "Win32App.h"
#endif #endif
namespace i2p namespace i2p

View File

@ -25,7 +25,7 @@ BEGIN
VALUE "FileDescription", "C++ I2P daemon" VALUE "FileDescription", "C++ I2P daemon"
VALUE "FileVersion", I2PD_VERSION VALUE "FileVersion", I2PD_VERSION
VALUE "InternalName", CODENAME VALUE "InternalName", CODENAME
VALUE "LegalCopyright", "Copyright (C) 2013-2017, The PurpleI2P Project" VALUE "LegalCopyright", "Copyright (C) 2013-2020, The PurpleI2P Project"
VALUE "OriginalFilename", "i2pd" VALUE "OriginalFilename", "i2pd"
VALUE "ProductName", "Purple I2P" VALUE "ProductName", "Purple I2P"
VALUE "ProductVersion", I2P_VERSION VALUE "ProductVersion", I2P_VERSION

View File

@ -43,10 +43,7 @@ namespace i2p
{ {
namespace win32 namespace win32
{ {
static DWORD GracefulShutdownEndtime = 0; DWORD g_GracefulShutdownEndtime = 0;
typedef DWORD (* IPN)();
IPN GetTickCountLocal = (IPN)GetProcAddress (GetModuleHandle ("KERNEL32.dll"), "GetTickCount");
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem) static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
{ {
@ -167,9 +164,9 @@ namespace win32
s << "; "; s << "; ";
s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n"; s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";
s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
if (GracefulShutdownEndtime != 0) if (g_GracefulShutdownEndtime != 0)
{ {
DWORD GracefulTimeLeft = (GracefulShutdownEndtime - GetTickCountLocal()) / 1000; DWORD GracefulTimeLeft = (g_GracefulShutdownEndtime - GetTickCount()) / 1000;
s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft); s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft);
} }
else else
@ -247,7 +244,7 @@ namespace win32
i2p::context.SetAcceptsTunnels (false); i2p::context.SetAcceptsTunnels (false);
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
GracefulShutdownEndtime = GetTickCountLocal() + 10*60*1000; g_GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
i2p::util::DaemonWin32::Instance ().isGraceful = true; i2p::util::DaemonWin32::Instance ().isGraceful = true;
return 0; return 0;
} }
@ -256,7 +253,7 @@ namespace win32
i2p::context.SetAcceptsTunnels (true); i2p::context.SetAcceptsTunnels (true);
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER); KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER); KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
GracefulShutdownEndtime = 0; g_GracefulShutdownEndtime = 0;
i2p::util::DaemonWin32::Instance ().isGraceful = false; i2p::util::DaemonWin32::Instance ().isGraceful = false;
return 0; return 0;
} }
@ -343,7 +340,7 @@ namespace win32
{ {
case IDT_GRACEFUL_SHUTDOWN_TIMER: case IDT_GRACEFUL_SHUTDOWN_TIMER:
{ {
GracefulShutdownEndtime = 0; g_GracefulShutdownEndtime = 0;
PostMessage (hWnd, WM_CLOSE, 0, 0); // exit PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
return 0; return 0;
} }

View File

@ -15,6 +15,8 @@ namespace i2p
{ {
namespace win32 namespace win32
{ {
extern DWORD g_GracefulShutdownEndtime;
bool StartWin32App (); bool StartWin32App ();
void StopWin32App (); void StopWin32App ();
int RunWin32App (); int RunWin32App ();

View File

@ -15,10 +15,10 @@
android:allowBackup="true" android:allowBackup="true"
android:icon="@drawable/icon" android:icon="@drawable/icon"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light.DarkActionBar"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true" android:theme="@android:style/Theme.Holo.Light.DarkActionBar"
> android:usesCleartextTraffic="true">
<activity android:name=".WebConsoleActivity"></activity>
<receiver android:name=".NetworkStateChangeReceiver"> <receiver android:name=".NetworkStateChangeReceiver">
<intent-filter> <intent-filter>
@ -31,10 +31,10 @@
android:label="@string/app_name"> android:label="@string/app_name">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".I2PDActivity" android:name=".I2PDActivity"
android:label="@string/app_name" /> android:label="@string/app_name" />
@ -52,4 +52,5 @@
android:value="org.purplei2p.i2pd.I2PDPermsAskerActivity" /> android:value="org.purplei2p.i2pd.I2PDPermsAskerActivity" />
</activity> </activity>
</application> </application>
</manifest>
</manifest>

View File

@ -30,8 +30,8 @@ android {
applicationId "org.purplei2p.i2pd" applicationId "org.purplei2p.i2pd"
targetSdkVersion 29 targetSdkVersion 29
minSdkVersion 14 minSdkVersion 14
versionCode 2330 versionCode 2350
versionName "2.33.0" versionName "2.35.0"
setProperty("archivesBaseName", archivesBaseName + "-" + versionName) setProperty("archivesBaseName", archivesBaseName + "-" + versionName)
ndk { ndk {
abiFilters 'armeabi-v7a' abiFilters 'armeabi-v7a'
@ -90,7 +90,7 @@ android {
} }
} }
ext.abiCodes = ['armeabi-v7a':1, 'x86':2, 'arm64-v8a':3, 'x86_64':4] ext.abiCodes = ['armeabi-v7a': 1, 'x86': 2, 'arm64-v8a': 3, 'x86_64': 4]
import com.android.build.OutputFile import com.android.build.OutputFile
android.applicationVariants.all { variant -> android.applicationVariants.all { variant ->

View File

@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/layout_prompt" <LinearLayout android:id="@+id/layout_prompt"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
tools:context=".I2PDActivity"> tools:context=".WebConsoleActivity">
<WebView <WebView
android:id="@+id/webview1" android:id="@+id/webview1"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" /> android:layout_height="fill_parent" />
</LinearLayout> </LinearLayout>

View File

@ -1,181 +0,0 @@
package org.purplei2p.i2pd;
import java.util.HashSet;
import java.util.Set;
import android.os.Environment;
import android.util.Log;
import org.purplei2p.i2pd.R;
public class DaemonSingleton {
private static final String TAG = "i2pd";
private static final DaemonSingleton instance = new DaemonSingleton();
public interface StateUpdateListener {
void daemonStateUpdate();
}
private final Set<StateUpdateListener> stateUpdateListeners = new HashSet<>();
public static DaemonSingleton getInstance() {
return instance;
}
public synchronized void addStateChangeListener(StateUpdateListener listener) {
stateUpdateListeners.add(listener);
}
public synchronized void removeStateChangeListener(StateUpdateListener listener) {
stateUpdateListeners.remove(listener);
}
private synchronized void setState(State newState) {
if (newState == null)
throw new NullPointerException();
State oldState = state;
if (oldState == null)
throw new NullPointerException();
if (oldState.equals(newState))
return;
state = newState;
fireStateUpdate1();
}
public synchronized void stopAcceptingTunnels() {
if (isStartedOkay()) {
setState(State.gracefulShutdownInProgress);
I2PD_JNI.stopAcceptingTunnels();
}
}
public synchronized void startAcceptingTunnels() {
if (isStartedOkay()) {
setState(State.startedOkay);
I2PD_JNI.startAcceptingTunnels();
}
}
public synchronized void reloadTunnelsConfigs() {
if (isStartedOkay()) {
I2PD_JNI.reloadTunnelsConfigs();
}
}
public synchronized int GetTransitTunnelsCount() {
return I2PD_JNI.GetTransitTunnelsCount();
}
private volatile boolean startedOkay;
public enum State {
uninitialized(R.string.uninitialized),
starting(R.string.starting),
jniLibraryLoaded(R.string.jniLibraryLoaded),
startedOkay(R.string.startedOkay),
startFailed(R.string.startFailed),
gracefulShutdownInProgress(R.string.gracefulShutdownInProgress),
stopped(R.string.stopped);
State(int statusStringResourceId) {
this.statusStringResourceId = statusStringResourceId;
}
private final int statusStringResourceId;
public int getStatusStringResourceId() {
return statusStringResourceId;
}
};
private volatile State state = State.uninitialized;
public State getState() {
return state;
}
{
setState(State.starting);
new Thread(new Runnable() {
@Override
public void run() {
try {
I2PD_JNI.loadLibraries();
setState(State.jniLibraryLoaded);
} catch (Throwable tr) {
lastThrowable = tr;
setState(State.startFailed);
return;
}
try {
synchronized (DaemonSingleton.this) {
I2PD_JNI.setDataDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd");
daemonStartResult = I2PD_JNI.startDaemon();
if ("ok".equals(daemonStartResult)) {
setState(State.startedOkay);
setStartedOkay(true);
} else
setState(State.startFailed);
}
} catch (Throwable tr) {
lastThrowable = tr;
setState(State.startFailed);
}
}
}, "i2pdDaemonStart").start();
}
private Throwable lastThrowable;
private String daemonStartResult = "N/A";
private void fireStateUpdate1() {
Log.i(TAG, "daemon state change: " + state);
for (StateUpdateListener listener : stateUpdateListeners) {
try {
listener.daemonStateUpdate();
} catch (Throwable tr) {
Log.e(TAG, "exception in listener ignored", tr);
}
}
}
public Throwable getLastThrowable() {
return lastThrowable;
}
public String getDaemonStartResult() {
return daemonStartResult;
}
private final Object startedOkayLock = new Object();
public boolean isStartedOkay() {
synchronized (startedOkayLock) {
return startedOkay;
}
}
private void setStartedOkay(boolean startedOkay) {
synchronized (startedOkayLock) {
this.startedOkay = startedOkay;
}
}
public synchronized void stopDaemon() {
if (isStartedOkay()) {
try {
I2PD_JNI.stopDaemon();
} catch(Throwable tr) {
Log.e(TAG, "", tr);
}
setStartedOkay(false);
setState(State.stopped);
}
}
}

View File

@ -0,0 +1,374 @@
package org.purplei2p.i2pd;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
import android.annotation.TargetApi;
import android.content.res.AssetManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.annotation.RequiresApi;
public class DaemonWrapper {
private static final String TAG = "i2pd";
private final AssetManager assetManager;
private final ConnectivityManager connectivityManager;
private String i2pdpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/";
private boolean assetsCopied;
public interface StateUpdateListener {
void daemonStateUpdate(State oldValue, State newValue);
}
private final Set<StateUpdateListener> stateUpdateListeners = new HashSet<>();
public synchronized void addStateChangeListener(StateUpdateListener listener) {
stateUpdateListeners.add(listener);
}
public synchronized void removeStateChangeListener(StateUpdateListener listener) {
stateUpdateListeners.remove(listener);
}
private synchronized void setState(State newState) {
if (newState == null)
throw new NullPointerException();
State oldState = state;
if (oldState == null)
throw new NullPointerException();
if (oldState.equals(newState))
return;
state = newState;
fireStateUpdate1(oldState, newState);
}
public synchronized void stopAcceptingTunnels() {
if (isStartedOkay()) {
setState(State.gracefulShutdownInProgress);
I2PD_JNI.stopAcceptingTunnels();
}
}
public synchronized void startAcceptingTunnels() {
if (isStartedOkay()) {
setState(State.startedOkay);
I2PD_JNI.startAcceptingTunnels();
}
}
public synchronized void reloadTunnelsConfigs() {
if (isStartedOkay()) {
I2PD_JNI.reloadTunnelsConfigs();
}
}
public int getTransitTunnelsCount() {
return I2PD_JNI.GetTransitTunnelsCount();
}
public enum State {
uninitialized(R.string.uninitialized),
starting(R.string.starting),
jniLibraryLoaded(R.string.jniLibraryLoaded),
startedOkay(R.string.startedOkay),
startFailed(R.string.startFailed),
gracefulShutdownInProgress(R.string.gracefulShutdownInProgress),
stopped(R.string.stopped);
State(int statusStringResourceId) {
this.statusStringResourceId = statusStringResourceId;
}
private final int statusStringResourceId;
public int getStatusStringResourceId() {
return statusStringResourceId;
}
public boolean isStartedOkay() {
return equals(State.startedOkay) || equals(State.gracefulShutdownInProgress);
}
}
private volatile State state = State.uninitialized;
public State getState() {
return state;
}
public DaemonWrapper(AssetManager assetManager, ConnectivityManager connectivityManager){
this.assetManager = assetManager;
this.connectivityManager = connectivityManager;
setState(State.starting);
new Thread(() -> {
try {
processAssets();
I2PD_JNI.loadLibraries();
setState(State.jniLibraryLoaded);
registerNetworkCallback();
} catch (Throwable tr) {
lastThrowable = tr;
setState(State.startFailed);
return;
}
try {
synchronized (DaemonWrapper.this) {
I2PD_JNI.setDataDir(Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd");
daemonStartResult = I2PD_JNI.startDaemon();
if ("ok".equals(daemonStartResult)) {
setState(State.startedOkay);
} else
setState(State.startFailed);
}
} catch (Throwable tr) {
lastThrowable = tr;
setState(State.startFailed);
}
}, "i2pdDaemonStart").start();
}
private Throwable lastThrowable;
private String daemonStartResult = "N/A";
private void fireStateUpdate1(State oldValue, State newValue) {
Log.i(TAG, "daemon state change: " + state);
for (StateUpdateListener listener : stateUpdateListeners) {
try {
listener.daemonStateUpdate(oldValue, newValue);
} catch (Throwable tr) {
Log.e(TAG, "exception in listener ignored", tr);
}
}
}
public Throwable getLastThrowable() {
return lastThrowable;
}
public String getDaemonStartResult() {
return daemonStartResult;
}
public boolean isStartedOkay() {
return getState().isStartedOkay();
}
public synchronized void stopDaemon() {
if (isStartedOkay()) {
try {
I2PD_JNI.stopDaemon();
} catch(Throwable tr) {
Log.e(TAG, "", tr);
}
setState(State.stopped);
}
}
private void processAssets() {
if (!assetsCopied) {
try {
assetsCopied = true;
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
FileReader fileReader = new FileReader(holderFile);
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 {
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 overwritten
copyAsset("addressbook");
copyAsset("certificates");
copyAsset("tunnels.d");
copyAsset("i2pd.conf");
copyAsset("subscriptions.txt");
copyAsset("tunnels.conf");
// update holder file about successful copying
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,"on assets copying", tr);
}
}
}
/**
* Copy the asset at the specified path to this app's data directory. If the
* asset is a directory, its contents are also copied.
*
* @param path
* Path to asset, relative to app's assets directory.
*/
private void copyAsset(String path) {
// If we have a directory, we make it and recurse. If a file, we copy its
// contents.
try {
String[] contents = assetManager.list(path);
// The documentation suggests that list throws an IOException, but doesn't
// say under what conditions. It'd be nice if it did so when the path was
// 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) {
copyFileAsset(path);
return;
}
// Make the directory.
File dir = new File(i2pdpath, path);
boolean result = dir.mkdirs();
Log.d(TAG, "dir.mkdirs() returned " + result);
// Recurse on the contents.
for (String entry : contents) {
copyAsset(path + '/' + entry);
}
} catch (IOException e) {
Log.e(TAG, "ex ignored for path='" + path + "'", e);
}
}
/**
* Copy the asset file specified by path to app's data directory. Assumes
* parent directories have already been created.
*
* @param path
* Path to asset, relative to app's assets directory.
*/
private void copyFileAsset(String path) {
File file = new File(i2pdpath, path);
if (!file.exists()) {
try {
try (InputStream in = assetManager.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);
}
}
}
private void deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
File[] files = fileOrDirectory.listFiles();
if (files != null) {
for (File child : files) {
deleteRecursive(child);
}
}
}
boolean deleteResult = fileOrDirectory.delete();
if (!deleteResult)
Log.e(TAG, "fileOrDirectory.delete() returned " + deleteResult + ", absolute path='" + fileOrDirectory.getAbsolutePath() + "'");
}
private void registerNetworkCallback(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) registerNetworkCallback0();
}
@TargetApi(Build.VERSION_CODES.M)
private void registerNetworkCallback0() {
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
.build();
NetworkStateCallbackImpl networkCallback = new NetworkStateCallbackImpl();
connectivityManager.registerNetworkCallback(request, networkCallback);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private static final class NetworkStateCallbackImpl extends ConnectivityManager.NetworkCallback {
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
I2PD_JNI.onNetworkStateChanged(true);
Log.i(TAG, "NetworkCallback.onAvailable");
}
@Override
public void onLost(Network network) {
super.onLost(network);
I2PD_JNI.onNetworkStateChanged(false);
Log.i(TAG, " NetworkCallback.onLost");
}
}
}

View File

@ -19,28 +19,38 @@ public class ForegroundService extends Service {
private volatile boolean shown; private volatile boolean shown;
private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener = private static ForegroundService instance;
new DaemonSingleton.StateUpdateListener() {
private static volatile DaemonWrapper daemon;
private static final Object initDeinitLock = new Object();
private final DaemonWrapper.StateUpdateListener daemonStateUpdatedListener =
new DaemonWrapper.StateUpdateListener() {
@Override @Override
public void daemonStateUpdate() { public void daemonStateUpdate(DaemonWrapper.State oldValue, DaemonWrapper.State newValue) {
try { updateNotificationText();
synchronized (ForegroundService.this) {
if (shown) cancelNotification();
showNotification();
}
} catch (Throwable tr) {
Log.e(TAG,"error ignored",tr);
}
} }
}; };
private void updateNotificationText() {
try {
synchronized (initDeinitLock) {
if (shown) cancelNotification();
showNotification();
}
} catch (Throwable tr) {
Log.e(TAG,"error ignored",tr);
}
}
private NotificationManager notificationManager; private NotificationManager notificationManager;
// Unique Identification Number for the Notification. // Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it. // We use it on Notification start, and to cancel it.
private int NOTIFICATION = 1; private static final int NOTIFICATION = 1;
/** /**
* Class for clients to access. Because we know this service always * Class for clients to access. Because we know this service always
@ -53,16 +63,27 @@ public class ForegroundService extends Service {
} }
} }
public static void init(DaemonWrapper daemon) {
ForegroundService.daemon = daemon;
initCheck();
}
private static void initCheck() {
synchronized (initDeinitLock) {
if (instance != null && daemon != null) instance.setListener();
}
}
@Override @Override
public void onCreate() { public void onCreate() {
notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
instance = this;
initCheck();
}
synchronized (this) { private void setListener() {
DaemonSingleton.getInstance().addStateChangeListener(daemonStateUpdatedListener); daemon.addStateChangeListener(daemonStateUpdatedListener);
if (!shown) daemonStateUpdatedListener.daemonStateUpdate(); updateNotificationText();
}
// Tell the user we started.
// Toast.makeText(this, R.string.i2pd_service_started, Toast.LENGTH_SHORT).show();
} }
@Override @Override
@ -73,19 +94,33 @@ public class ForegroundService extends Service {
@Override @Override
public void onDestroy() { public void onDestroy() {
DaemonSingleton.getInstance().removeStateChangeListener(daemonStateUpdatedListener);
cancelNotification(); cancelNotification();
deinitCheck();
instance=null;
} }
private synchronized void cancelNotification() { public static void deinit() {
// Cancel the persistent notification. deinitCheck();
notificationManager.cancel(NOTIFICATION); }
stopForeground(true); private static void deinitCheck() {
synchronized (initDeinitLock) {
if (daemon != null && instance != null)
daemon.removeStateChangeListener(instance.daemonStateUpdatedListener);
}
}
// Tell the user we stopped. private void cancelNotification() {
//Toast.makeText(this, R.string.i2pd_service_stopped, Toast.LENGTH_SHORT).show(); synchronized (initDeinitLock) {
shown=false; // Cancel the persistent notification.
notificationManager.cancel(NOTIFICATION);
stopForeground(true);
// Tell the user we stopped.
//Toast.makeText(this, R.string.i2pd_service_stopped, Toast.LENGTH_SHORT).show();
shown = false;
}
} }
@Override @Override
@ -100,36 +135,42 @@ public class ForegroundService extends Service {
/** /**
* Show a notification while this service is running. * Show a notification while this service is running.
*/ */
private synchronized void showNotification() { private void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification synchronized (initDeinitLock) {
CharSequence text = getText(DaemonSingleton.getInstance().getState().getStatusStringResourceId()); if (daemon != null) {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(daemon.getState().getStatusStringResourceId());
// The PendingIntent to launch our activity if the user selects this notification // The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, I2PDActivity.class), 0); new Intent(this, I2PDActivity.class), 0);
// If earlier version channel ID is not used // If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
String channelId = Build.VERSION.SDK_INT >= 26 ? createNotificationChannel() : ""; String channelId = Build.VERSION.SDK_INT >= 26 ? createNotificationChannel() : "";
// Set the info for the views that show in the notification panel. // Set the info for the views that show in the notification panel.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId) NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
.setOngoing(true) .setOngoing(true)
.setSmallIcon(R.drawable.itoopie_notification_icon); // the status icon .setSmallIcon(R.drawable.itoopie_notification_icon); // the status icon
if(Build.VERSION.SDK_INT >= 16) builder = builder.setPriority(Notification.PRIORITY_DEFAULT); if (Build.VERSION.SDK_INT >= 16)
if(Build.VERSION.SDK_INT >= 21) builder = builder.setCategory(Notification.CATEGORY_SERVICE); builder = builder.setPriority(Notification.PRIORITY_DEFAULT);
Notification notification = builder if (Build.VERSION.SDK_INT >= 21)
.setTicker(text) // the status text builder = builder.setCategory(Notification.CATEGORY_SERVICE);
.setWhen(System.currentTimeMillis()) // the time stamp Notification notification = builder
.setContentTitle(getText(R.string.app_name)) // the label of the entry .setTicker(text) // the status text
.setContentText(text) // the contents of the entry .setWhen(System.currentTimeMillis()) // the time stamp
.setContentIntent(contentIntent) // The intent to send when the entry is clicked .setContentTitle(getText(R.string.app_name)) // the label of the entry
.build(); .setContentText(text) // the contents of the entry
.setContentIntent(contentIntent) // The intent to send when the entry is clicked
.build();
// Send the notification. // Send the notification.
//mNM.notify(NOTIFICATION, notification); //mNM.notify(NOTIFICATION, notification);
startForeground(NOTIFICATION, notification); startForeground(NOTIFICATION, notification);
shown = true; shown = true;
}
}
} }
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
@ -144,6 +185,4 @@ public class ForegroundService extends Service {
else Log.e(TAG, "error: NOTIFICATION_SERVICE is null"); else Log.e(TAG, "error: NOTIFICATION_SERVICE is null");
return channelId; return channelId;
} }
private static final DaemonSingleton daemon = DaemonSingleton.getInstance();
} }

View File

@ -1,13 +1,5 @@
package org.purplei2p.i2pd; package org.purplei2p.i2pd;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.Timer; import java.util.Timer;
@ -15,7 +7,6 @@ import java.util.TimerTask;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
@ -24,16 +15,11 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Build; import android.os.Build;
import android.os.Environment;
import android.os.IBinder; import android.os.IBinder;
import android.os.PowerManager; import android.os.PowerManager;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
@ -46,7 +32,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
@ -60,44 +45,43 @@ import android.webkit.WebViewClient;
import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS; import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS;
public class I2PDActivity extends Activity { public class I2PDActivity extends Activity {
private WebView webView;
private static final String TAG = "i2pdActvt"; private static final String TAG = "i2pdActvt";
private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1; private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000; public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000;
public static final String PACKAGE_URI_SCHEME = "package:"; public static final String PACKAGE_URI_SCHEME = "package:";
private TextView textView; private TextView textView;
private boolean assetsCopied; //private ConfigParser parser = new ConfigParser(i2pdpath); // TODO
private NetworkStateCallback networkCallback;
private String i2pdpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd/";
//private ConfigParser parser = new ConfigParser(i2pdpath); // TODO:
private static final DaemonSingleton daemon = DaemonSingleton.getInstance(); private static volatile DaemonWrapper daemon;
private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener = new DaemonSingleton.StateUpdateListener() { private final DaemonWrapper.StateUpdateListener daemonStateUpdatedListener = new DaemonWrapper.StateUpdateListener() {
@Override @Override
public void daemonStateUpdate() { public void daemonStateUpdate(DaemonWrapper.State oldValue, DaemonWrapper.State newValue) {
processAssets(); updateStatusText();
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 void updateStatusText() {
runOnUiThread(() -> {
try {
if (textView == null)
return;
Throwable tr = daemon.getLastThrowable();
if (tr!=null) {
textView.setText(throwableToString(tr));
return;
}
DaemonWrapper.State state = daemon.getState();
String startResultStr = DaemonWrapper.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : "";
String graceStr = DaemonWrapper.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 volatile long graceStartedMillis;
private static final Object graceStartedMillis_LOCK = new Object(); private static final Object graceStartedMillis_LOCK = new Object();
private Menu optionsMenu; private Menu optionsMenu;
@ -117,10 +101,16 @@ public class I2PDActivity extends Activity {
Log.i(TAG, "onCreate"); Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (daemon==null) {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
daemon = new DaemonWrapper(getAssets(), connectivityManager);
}
ForegroundService.init(daemon);
textView = new TextView(this); textView = new TextView(this);
setContentView(textView); setContentView(textView);
daemon.addStateChangeListener(daemonStateUpdatedListener); daemon.addStateChangeListener(daemonStateUpdatedListener);
daemonStateUpdatedListener.daemonStateUpdate(); daemonStateUpdatedListener.daemonStateUpdate(DaemonWrapper.State.uninitialized, daemon.getState());
// request permissions // request permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@ -145,15 +135,13 @@ public class I2PDActivity extends Activity {
openBatteryOptimizationDialogIfNeeded(); openBatteryOptimizationDialogIfNeeded();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
registerNetworkCallback();
}
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
textView = null; textView = null;
ForegroundService.deinit();
daemon.removeStateChangeListener(daemonStateUpdatedListener); daemon.removeStateChangeListener(daemonStateUpdatedListener);
//cancelGracefulStop0(); //cancelGracefulStop0();
try { try {
@ -288,15 +276,8 @@ public class I2PDActivity extends Activity {
return true; return true;
case R.id.action_start_webview: case R.id.action_start_webview:
setContentView(R.layout.webview); startActivity(new Intent(getApplicationContext(), WebConsoleActivity.class));
this.webView = (WebView) findViewById(R.id.webview1); return true;
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); return super.onOptionsItemSelected(item);
@ -335,7 +316,7 @@ public class I2PDActivity extends Activity {
private static volatile Timer gracefulQuitTimer; private static volatile Timer gracefulQuitTimer;
private void i2pdGracefulStop() { private void i2pdGracefulStop() {
if (daemon.getState() == DaemonSingleton.State.stopped) { if (daemon.getState() == DaemonWrapper.State.stopped) {
Toast.makeText(this, R.string.already_stopped, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.already_stopped, Toast.LENGTH_SHORT).show();
return; return;
} }
@ -384,9 +365,10 @@ public class I2PDActivity extends Activity {
if (gracefulQuitTimerOld != null) if (gracefulQuitTimerOld != null)
gracefulQuitTimerOld.cancel(); gracefulQuitTimerOld.cancel();
if(daemon.GetTransitTunnelsCount() <= 0) { // no tunnels left if(daemon.getTransitTunnelsCount() <= 0) { // no tunnels left
Log.d(TAG, "no transit tunnels left, stopping"); Log.d(TAG, "no transit tunnels left, stopping");
i2pdStop(); i2pdStop();
return;
} }
final Timer gracefulQuitTimer = new Timer(true); final Timer gracefulQuitTimer = new Timer(true);
@ -402,7 +384,7 @@ public class I2PDActivity extends Activity {
final TimerTask tickerTask = new TimerTask() { final TimerTask tickerTask = new TimerTask() {
@Override @Override
public void run() { public void run() {
daemonStateUpdatedListener.daemonStateUpdate(); updateStatusText();
} }
}; };
gracefulQuitTimer.scheduleAtFixedRate(tickerTask, 0/*start delay*/, 1000/*millis period*/); gracefulQuitTimer.scheduleAtFixedRate(tickerTask, 0/*start delay*/, 1000/*millis period*/);
@ -427,167 +409,6 @@ public class I2PDActivity extends Activity {
}); });
} }
/**
* Copy the asset at the specified path to this app's data directory. If the
* asset is a directory, its contents are also copied.
*
* @param path
* Path to asset, relative to app's assets directory.
*/
private void copyAsset(String path) {
AssetManager manager = getAssets();
// If we have a directory, we make it and recurse. If a file, we copy its
// contents.
try {
String[] contents = manager.list(path);
// The documentation suggests that list throws an IOException, but doesn't
// say under what conditions. It'd be nice if it did so when the path was
// 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) {
copyFileAsset(path);
return;
}
// Make the directory.
File dir = new File(i2pdpath, path);
boolean result = dir.mkdirs();
Log.d(TAG, "dir.mkdirs() returned " + result);
// Recurse on the contents.
for (String entry : contents) {
copyAsset(path + '/' + entry);
}
} catch (IOException e) {
Log.e(TAG, "ex ignored for path='" + path + "'", e);
}
}
/**
* Copy the asset file specified by path to app's data directory. Assumes
* parent directories have already been created.
*
* @param path
* Path to asset, relative to app's assets directory.
*/
private void copyFileAsset(String path) {
File file = new File(i2pdpath, path);
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);
}
}
}
private void deleteRecursive(File fileOrDirectory) {
if (fileOrDirectory.isDirectory()) {
File[] files = fileOrDirectory.listFiles();
if (files != null) {
for (File child : files) {
deleteRecursive(child);
}
}
}
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");
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
FileReader fileReader = new FileReader(holderFile);
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 {
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 overwritten
copyAsset("addressbook");
copyAsset("certificates");
copyAsset("tunnels.d");
copyAsset("i2pd.conf");
copyAsset("subscriptions.txt");
copyAsset("tunnels.conf");
// update holder file about successful copying
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,"on assets copying", tr);
}
}
}
@SuppressLint("BatteryLife") @SuppressLint("BatteryLife")
private void openBatteryOptimizationDialogIfNeeded() { private void openBatteryOptimizationDialogIfNeeded() {
boolean questionEnabled = getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true); boolean questionEnabled = getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true);
@ -642,33 +463,6 @@ public class I2PDActivity extends Activity {
return "show_battery_optimization" + (device == null ? "" : device); return "show_battery_optimization" + (device == null ? "" : device);
} }
@TargetApi(Build.VERSION_CODES.M)
private void registerNetworkCallback() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
.build();
networkCallback = new NetworkStateCallback();
connectivityManager.registerNetworkCallback(request, networkCallback);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private final class NetworkStateCallback extends ConnectivityManager.NetworkCallback {
@Override
public void onAvailable(Network network) {
super.onAvailable(network);
I2PD_JNI.onNetworkStateChanged(true);
Log.i(TAG, "NetworkCallback.onAvailable");
}
@Override
public void onLost(Network network) {
super.onLost(network);
I2PD_JNI.onNetworkStateChanged(false);
Log.i(TAG, " NetworkCallback.onLost");
}
}
private void quit() { private void quit() {
try { try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

View File

@ -0,0 +1,39 @@
package org.purplei2p.i2pd;
import android.app.Activity;
import android.os.Bundle;
import android.view.MenuItem;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.util.Objects;
public class WebConsoleActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_console);
Objects.requireNonNull(getActionBar()).setDisplayHomeAsUpEnabled(true);
final WebView webView = findViewById(R.id.webview1);
webView.setWebViewClient(new WebViewClient());
final WebSettings webSettings = webView.getSettings();
webSettings.setBuiltInZoomControls(true);
webSettings.setJavaScriptEnabled(false);
webView.loadUrl("http://127.0.0.1:7070"); // TODO: instead 7070 I2Pd....HttpPort
}
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id==android.R.id.home) {
finish();
return true;
}
return false;
}
}

View File

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

View File

@ -11,9 +11,8 @@ if(WIN32 OR MSVC OR MSYS OR MINGW)
message(SEND_ERROR "cmake build for windows is not supported. Please use MSYS2 with makefiles in project root.") message(SEND_ERROR "cmake build for windows is not supported. Please use MSYS2 with makefiles in project root.")
endif() endif()
# configurale options # configurable options
option(WITH_AESNI "Use AES-NI instructions set" OFF) option(WITH_AESNI "Use AES-NI instructions set" ON)
option(WITH_AVX "Use AVX instructions" OFF)
option(WITH_HARDENING "Use hardening compiler flags" OFF) option(WITH_HARDENING "Use hardening compiler flags" OFF)
option(WITH_LIBRARY "Build library" ON) option(WITH_LIBRARY "Build library" ON)
option(WITH_BINARY "Build binary" ON) option(WITH_BINARY "Build binary" ON)
@ -189,13 +188,11 @@ if(UNIX)
endif() endif()
endif() endif()
if(WITH_AESNI) # Note: AES-NI and AVX is available on x86-based CPU's.
# Here also ARM64 implementation, but currently we don't support it.
if(WITH_AESNI AND (ARCHITECTURE MATCHES "x86_64" OR ARCHITECTURE MATCHES "i386"))
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes")
add_definitions(-DAESNI) add_definitions(-D__AES__)
endif()
if(WITH_AVX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx")
endif() endif()
if(WITH_ADDRSANITIZER) if(WITH_ADDRSANITIZER)
@ -309,7 +306,6 @@ message(STATUS "Architecture : ${ARCHITECTURE}")
message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}") message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}")
message(STATUS "Options:") message(STATUS "Options:")
message(STATUS " AESNI : ${WITH_AESNI}") message(STATUS " AESNI : ${WITH_AESNI}")
message(STATUS " AVX : ${WITH_AVX}")
message(STATUS " HARDENING : ${WITH_HARDENING}") message(STATUS " HARDENING : ${WITH_HARDENING}")
message(STATUS " LIBRARY : ${WITH_LIBRARY}") message(STATUS " LIBRARY : ${WITH_LIBRARY}")
message(STATUS " BINARY : ${WITH_BINARY}") message(STATUS " BINARY : ${WITH_BINARY}")

View File

@ -2,7 +2,7 @@
setlocal enableextensions enabledelayedexpansion setlocal enableextensions enabledelayedexpansion
title Building i2pd title Building i2pd
REM Copyright (c) 2013-2017, The PurpleI2P Project REM Copyright (c) 2013-2020, The PurpleI2P Project
REM This file is part of Purple i2pd project and licensed under BSD3 REM This file is part of Purple i2pd project and licensed under BSD3
REM See full license text in LICENSE file at top of project tree REM See full license text in LICENSE file at top of project tree
@ -23,17 +23,17 @@ set "xSH=%WD%bash -lc"
set "FILELIST=i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates contrib/tunnels.d" set "FILELIST=i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates contrib/tunnels.d"
REM detecting number of processors and subtract 1. REM detecting number of processors
set /a threads=%NUMBER_OF_PROCESSORS%-1 set /a threads=%NUMBER_OF_PROCESSORS%
REM we must work in root of repo REM we must work in root of repo
cd .. cd ..
REM deleting old log files REM deleting old log files
del /S build_*.log >> nul del /S build_*.log >> nul 2>&1
echo Receiving latest commit and cleaning up... echo Receiving latest commit and cleaning up...
%xSH% "git pull && make clean" > build/build_git.log 2>&1 %xSH% "git checkout contrib/* && git pull && make clean" > build\build.log 2>&1
echo. echo.
REM set to variable current commit hash REM set to variable current commit hash
@ -43,16 +43,17 @@ FOR /F "usebackq" %%a IN (`%xSH% 'git describe --tags'`) DO (
%xSH% "echo To use configs and certificates, move all files and certificates folder from contrib directory here. > README.txt" >> nul %xSH% "echo To use configs and certificates, move all files and certificates folder from contrib directory here. > README.txt" >> nul
REM converting configuration files to DOS format (usable in default notepad)
%xSH% "unix2dos contrib/i2pd.conf contrib/tunnels.conf contrib/tunnels.d/*" >> build\build.log 2>&1
REM starting building REM starting building
set MSYSTEM=MINGW32 set MSYSTEM=MINGW32
set bitness=32 set bitness=32
call :BUILDING call :BUILDING
echo.
set MSYSTEM=MINGW64 set MSYSTEM=MINGW64
set bitness=64 set bitness=64
call :BUILDING call :BUILDING
echo.
REM building for WinXP REM building for WinXP
set "WD=C:\msys64-xp\usr\bin\" set "WD=C:\msys64-xp\usr\bin\"
@ -62,7 +63,10 @@ set "xSH=%WD%bash -lc"
call :BUILDING_XP call :BUILDING_XP
echo. echo.
del README.txt >> nul REM compile installer
C:\PROGRA~2\INNOSE~1\ISCC.exe /dI2Pd_ver="%tag%" build\win_installer.iss >> build\build.log 2>&1
del README.txt i2pd_x32.exe i2pd_x64.exe i2pd_xp.exe >> nul
echo Build complete... echo Build complete...
pause pause
@ -70,20 +74,13 @@ exit /b 0
:BUILDING :BUILDING
%xSH% "make clean" >> nul %xSH% "make clean" >> nul
echo Building i2pd %tag% for win%bitness%: echo Building i2pd %tag% for win%bitness%
echo Build AVX+AESNI... %xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && cp i2pd.exe i2pd_x%bitness%.exe && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST% && make clean" > build\build_win%bitness%_%tag%.log 2>&1
%xSH% "make DEBUG=no USE_UPNP=yes USE_AVX=1 USE_AESNI=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_avx_aesni.zip %FILELIST% && make clean" > build/build_win%bitness%_avx_aesni_%tag%.log 2>&1
echo Build AVX...
%xSH% "make DEBUG=no USE_UPNP=yes USE_AVX=1 -j%threads% && zip -r9 build/i2pd_%tag%_win%bitness%_mingw_avx.zip %FILELIST% && make clean" > build/build_win%bitness%_avx_%tag%.log 2>&1
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 goto EOF
:BUILDING_XP :BUILDING_XP
%xSH% "make clean" >> nul %xSH% "make clean" >> nul
echo Building i2pd %tag% for winxp... 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 %xSH% "make DEBUG=no USE_UPNP=yes USE_WINXP_FLAGS=yes -j%threads% && cp i2pd.exe i2pd_xp.exe && zip -r9 build/i2pd_%tag%_winxp_mingw.zip %FILELIST% && make clean" > build\build_winxp_%tag%.log 2>&1
:EOF :EOF

View File

@ -1,6 +1,8 @@
#define I2Pd_AppName "i2pd" #define I2Pd_AppName "i2pd"
#define I2Pd_ver "2.33.0"
#define I2Pd_Publisher "PurpleI2P" #define I2Pd_Publisher "PurpleI2P"
; Get application version from compiled binary
; Disabled to use definition from command line
;#define I2Pd_ver GetFileVersionString(AddBackslash(SourcePath) + "..\i2pd_x64.exe")
[Setup] [Setup]
AppName={#I2Pd_AppName} AppName={#I2Pd_AppName}
@ -10,9 +12,9 @@ DefaultDirName={pf}\I2Pd
DefaultGroupName=I2Pd DefaultGroupName=I2Pd
UninstallDisplayIcon={app}\I2Pd.exe UninstallDisplayIcon={app}\I2Pd.exe
OutputDir=. OutputDir=.
LicenseFile=../LICENSE LicenseFile=..\LICENSE
OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_ver} OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_ver}
SetupIconFile=mask.ico SetupIconFile=..\Win32\mask.ico
InternalCompressLevel=ultra64 InternalCompressLevel=ultra64
Compression=lzma/ultra64 Compression=lzma/ultra64
SolidCompression=true SolidCompression=true
@ -23,10 +25,12 @@ AppID={{621A23E0-3CF4-4BD6-97BC-4835EA5206A2}
AppPublisherURL=http://i2pd.website/ AppPublisherURL=http://i2pd.website/
AppSupportURL=https://github.com/PurpleI2P/i2pd/issues AppSupportURL=https://github.com/PurpleI2P/i2pd/issues
AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases
CloseApplications=yes
[Files] [Files]
Source: ..\i2pd_x86.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: not IsWin64 Source: ..\i2pd_x32.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: not IsWin64; MinVersion: 6.0
Source: ..\i2pd_x64.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: IsWin64 Source: ..\i2pd_x64.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: IsWin64; MinVersion: 6.0
Source: ..\i2pd_xp.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: IsWin64; OnlyBelowVersion: 6.0
Source: ..\README.md; DestDir: {app}; DestName: Readme.txt; Flags: onlyifdoesntexist Source: ..\README.md; DestDir: {app}; DestName: Readme.txt; Flags: onlyifdoesntexist
Source: ..\contrib\i2pd.conf; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist Source: ..\contrib\i2pd.conf; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist
Source: ..\contrib\subscriptions.txt; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist Source: ..\contrib\subscriptions.txt; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# Copyright (c) 2013-2017, The PurpleI2P Project # Copyright (c) 2013-2020, The PurpleI2P Project
# #
# This file is part of Purple i2pd project and licensed under BSD3 # This file is part of Purple i2pd project and licensed under BSD3
# #

View File

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# Copyright (c) 2013-2019, The PurpleI2P Project # Copyright (c) 2013-2020, The PurpleI2P Project
# #
# This file is part of Purple i2pd project and licensed under BSD3 # This file is part of Purple i2pd project and licensed under BSD3
# #
@ -21,13 +21,13 @@ arch=$(uname -m)
screenfind=$(which screen) screenfind=$(which screen)
if [ -z $screenfind ]; then if [ -z $screenfind ]; then
echo "Can't find 'screen' installed. That script needs it!"; echo "Can't find 'screen' installed. That script needs it!";
exit 1; exit 1;
fi fi
if [ -z i2pd-$arch ]; then if [ -z i2pd-$arch ]; then
echo "Can't find i2pd binary for your archtecture."; echo "Can't find i2pd binary for your archtecture.";
exit 1; exit 1;
fi fi
screen -AmdS i2pd ./i2pd-$arch --datadir=$DIR screen -AmdS i2pd ./i2pd-$arch --datadir=$DIR

View File

@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFeTCCA2GgAwIBAgIEZZozujANBgkqhkiG9w0BAQ0FADBtMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEWMBQGA1UEAwwNbWVlaEBtYWlsLmky
cDAeFw0xNDA2MjgyMjQ5MDlaFw0yNDA2MjcyMjQ5MDlaMG0xCzAJBgNVBAYTAlhY
MQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9ueW1v
dXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRYwFAYDVQQDDA1tZWVoQG1haWwuaTJw
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnVnmPE4uUvCky0yCnnVH
cJEDqzwDPupx0zr0YDlhZk5VOPPecx5haayJ/V6nXPc1aVVWn+CHfedcF2aBgN4K
5aBueS/l6l5WHcv02DofAqlTmyAws3oQeR1qoTuW24cKRtLR7h5bxv63f6bgp6e+
RihFNez6UxErnRPuJOJEO2Im6EgVp6fz7tQ7R35zxAUeES2YILPySvzy2vYm/EEG
jXX7Ap2A5svVo90xCMOeUZ/55vLsjyIshN+tV87U4xwvAkUmwsmWVHm3BQpHkI6z
zMJie6epB8Bqm0GYm0EcElJH4OCxGTvDLoghpswbuUO7iy3JSfoL7ZCnoiQdK9K4
yVVChj8lG+r7KaTowK96iZep+sZefjOt5VFGuW2Fi/WBv3ldiLlJAo/ZfrUM4+vG
fyNBXbl6bX87uTCGOT1p3dazo+zJMsAZ+Y93DlM/mDEWFa1kKNrs74syzaWEqF4L
KQE6VoYn80OOzafSigTVQgSwUtQtB0XGhMzJhyxU2XHWe1LFIy7Pta0B+lDiZj7c
I8nXxYjsDfEu/Elj/Ra9N6bH0awmgB5JDa+Tbir+oEM5SyDfpSaCGuatdGxjweGI
kVmFU0SqCZV/8TXbIu6MUVzTZMZVT94edifFSRad4fqw7eZbSXlPu++3d1/btn6h
ibM04nkv0mm+FxCKB/wdAkECAwEAAaMhMB8wHQYDVR0OBBYEFO7jIkSRkoXyJcho
9/Q0gDOINa5EMA0GCSqGSIb3DQEBDQUAA4ICAQBzfWO7+8HWOKLaYWToJ6XZbpNF
3wXv1yC4W/HRR80m4JSsq9r0d7838Nvd7vLVP6MY6MaVb/JnV76FdQ5WQ6ticD0Y
o3zmpqqbKVSspN0lrkig4surT88AjfVQz/vEIzKNQEbpzc3hC2LCiE2u+cK/ix4j
b9RohnaPvwLnew5RNQRpcmk+XejaNITISr2yQIwXL7TEYy8HdGCfzFSSFhKe9vkb
GsWS5ASrUzRoprswmlgRe8gEHI+d51Z7mWgna0/5mBz9bH/3QXtpxlLWm3bVV+kt
pZjQDTHE0GqG2YsD1Gmp4LU/JFhCojMTtiPCXmr9KFtpiVlx06DuKm5PC8Ak+5w+
m/DQYYfv9z+AA5Y430bjnzwg67bhqVyyek4wcDQinFswv3h4bIB7CJujDcEqXXza
lhG1ufPPCUTMrVjh7AShohZraqlSlyQPY9vEppLwD4W1d+MqDHM7ljOH7gQYaUPi
wE30AdXEOxLZcT3aRKxkKf2esNofSuUC/+NXQvPjpuI4UJKO3eegi+M9dbnKoNWs
MPPLPpycecWPheFYM5K6Ao63cjlUY2wYwCfDTFgjA5q8i/Rp7i6Z6fLE3YWJ4VdR
WOFB7hlluQ//jMW6M1qz6IYXmlUjcXl81VEvlOH/QBNrPvX3I3SYXYgVRnVGUudB
o3eNsanvTU+TIFBh2Q==
-----END CERTIFICATE-----

View File

@ -229,3 +229,12 @@ verify = true
[persist] [persist]
## Save peer profiles on disk (default: true) ## Save peer profiles on disk (default: true)
# profiles = true # profiles = true
[cpuext]
## Use CPU AES-NI instructions set when work with cryptography when available (default: true)
# aesni = true
## Use CPU AVX instructions set when work with cryptography when available (default: true)
# avx = true
## Force usage of CPU instructions set, even if they not found
## DO NOT TOUCH that option if you really don't know what are you doing!
# force = false

View File

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.33.0 Version: 2.35.0
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@ -56,18 +56,31 @@ cd build
%endif %endif
%endif %endif
%if 0%{?mageia} > 7 %if 0%{?fedora} >= 33
pushd build pushd %{_target_platform}
make %{?_smp_mflags}
popd
%else
make %{?_smp_mflags}
%endif %endif
%if 0%{?mageia} > 7
pushd build
%endif
make %{?_smp_mflags}
%if 0%{?fedora} >= 33
popd
%endif
%if 0%{?mageia} > 7
popd
%endif
%install %install
pushd build pushd build
%if 0%{?fedora} >= 33
pushd %{_target_platform}
%endif
%if 0%{?mageia} %if 0%{?mageia}
pushd build pushd build
%endif %endif
@ -124,6 +137,12 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Nov 30 2020 orignal <i2porignal@yandex.ru> - 2.35.0
- update to 2.35.0
* Tue Oct 27 2020 orignal <i2porignal@yandex.ru> - 2.34.0
- update to 2.34.0
* Mon Aug 24 2020 orignal <i2porignal@yandex.ru> - 2.33.0 * Mon Aug 24 2020 orignal <i2porignal@yandex.ru> - 2.33.0
- update to 2.33.0 - update to 2.33.0

View File

@ -1,5 +1,5 @@
Name: i2pd Name: i2pd
Version: 2.33.0 Version: 2.35.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@ -54,18 +54,31 @@ cd build
%endif %endif
%endif %endif
%if 0%{?mageia} > 7 %if 0%{?fedora} >= 33
pushd build pushd %{_target_platform}
make %{?_smp_mflags}
popd
%else
make %{?_smp_mflags}
%endif %endif
%if 0%{?mageia} > 7
pushd build
%endif
make %{?_smp_mflags}
%if 0%{?fedora} >= 33
popd
%endif
%if 0%{?mageia} > 7
popd
%endif
%install %install
pushd build pushd build
%if 0%{?fedora} >= 33
pushd %{_target_platform}
%endif
%if 0%{?mageia} %if 0%{?mageia}
pushd build pushd build
%endif %endif
@ -122,6 +135,12 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Nov 30 2020 orignal <i2porignal@yandex.ru> - 2.35.0
- update to 2.35.0
* Tue Oct 27 2020 orignal <i2porignal@yandex.ru> - 2.34.0
- update to 2.34.0
* Mon Aug 24 2020 orignal <i2porignal@yandex.ru> - 2.33.0 * Mon Aug 24 2020 orignal <i2porignal@yandex.ru> - 2.33.0
- update to 2.33.0 - update to 2.33.0

View File

@ -128,7 +128,10 @@ namespace i2p
LogPrint(eLogDebug, "FS: data directory: ", datadir); LogPrint(eLogDebug, "FS: data directory: ", datadir);
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
i2p::crypto::InitCrypto (precomputation); bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
bool avx; i2p::config::GetOption("cpuext.avx", avx);
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt);
int netID; i2p::config::GetOption("netid", netID); int netID; i2p::config::GetOption("netid", netID);
i2p::context.SetNetID (netID); i2p::context.SetNetID (netID);

View File

@ -30,8 +30,9 @@
#include "Daemon.h" #include "Daemon.h"
#include "util.h" #include "util.h"
#include "ECIESX25519AEADRatchetSession.h" #include "ECIESX25519AEADRatchetSession.h"
#ifdef WIN32_APP #ifdef WIN32_APP
#include "Win32/Win32App.h" #include "Win32App.h"
#endif #endif
// For image and info // For image and info
@ -69,7 +70,7 @@ namespace http {
" .menu { float: left; } .menu a, .commands a { display: block; }\r\n" " .menu { float: left; } .menu a, .commands a { display: block; }\r\n"
" .listitem { display: block; font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n" " .listitem { display: block; font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
" .tableitem { font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n" " .tableitem { font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
" .content { float: left; font-size: 1em; margin-left: 4em; max-width: 46em; overflow: auto; }\r\n" " .content { float: left; font-size: 1em; margin-left: 4em; max-width: 45em; overflow: auto; }\r\n"
" .tunnel.established { color: #56B734; } .tunnel.expiring { color: #D3AE3F; }\r\n" " .tunnel.established { color: #56B734; } .tunnel.expiring { color: #D3AE3F; }\r\n"
" .tunnel.failed { color: #D33F3F; } .tunnel.building { color: #434343; }\r\n" " .tunnel.failed { color: #D33F3F; } .tunnel.building { color: #434343; }\r\n"
" caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n" " caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
@ -270,8 +271,18 @@ namespace http {
} }
s << "<br>\r\n"; s << "<br>\r\n";
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
if (auto remains = Daemon.gracefulShutdownInterval) if (auto remains = Daemon.gracefulShutdownInterval) {
s << "<b>Stopping in:</b> " << remains << " seconds<br>\r\n"; s << "<b>Stopping in:</b> ";
ShowUptime(s, remains);
s << "<br>\r\n";
}
#elif defined(WIN32_APP)
if (i2p::win32::g_GracefulShutdownEndtime != 0) {
uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000;
s << "<b>Stopping in:</b> ";
ShowUptime(s, remains);
s << "<br>\r\n";
}
#endif #endif
auto family = i2p::context.GetFamily (); auto family = i2p::context.GetFamily ();
if (family.length () > 0) if (family.length () > 0)
@ -838,6 +849,7 @@ namespace http {
case i2p::client::eSAMSocketTypeSession : s << "session"; break; case i2p::client::eSAMSocketTypeSession : s << "session"; break;
case i2p::client::eSAMSocketTypeStream : s << "stream"; break; case i2p::client::eSAMSocketTypeStream : s << "stream"; break;
case i2p::client::eSAMSocketTypeAcceptor : s << "acceptor"; break; case i2p::client::eSAMSocketTypeAcceptor : s << "acceptor"; break;
case i2p::client::eSAMSocketTypeForward : s << "forward"; break;
default: s << "unknown"; break; default: s << "unknown"; break;
} }
s << " [" << it->GetSocket ().remote_endpoint() << "]"; s << " [" << it->GetSocket ().remote_endpoint() << "]";

12
debian/changelog vendored
View File

@ -1,3 +1,15 @@
i2pd (2.35.0-1) unstable; urgency=high
* updated to version 2.35.0/0.9.48
-- orignal <orignal@i2pmail.org> Mon, 30 Nov 2020 16:00:00 +0000
i2pd (2.34.0-1) unstable; urgency=medium
* updated to version 2.34.0
-- orignal <orignal@i2pmail.org> Tue, 27 Oct 2020 16:00:00 +0000
i2pd (2.33.0-1) unstable; urgency=medium i2pd (2.33.0-1) unstable; urgency=medium
* updated to version 2.33.0/0.9.47 * updated to version 2.33.0/0.9.47

2
debian/compat vendored
View File

@ -1 +1 @@
9 10

View File

@ -1,15 +0,0 @@
Index: i2pd/Makefile
===================================================================
--- i2pd.orig/Makefile
+++ i2pd/Makefile
@@ -13,8 +13,8 @@ DAEMON_SRC_DIR := daemon
include filelist.mk
-USE_AESNI := yes
-USE_AVX := yes
+USE_AESNI := no
+USE_AVX := no
USE_STATIC := no
USE_MESHNET := no
USE_UPNP := no

View File

@ -1,2 +1 @@
01-tune-build-opts.patch 01-fix-1210.patch
02-fix-1210.patch

View File

@ -27,37 +27,32 @@ namespace cpu
bool aesni = false; bool aesni = false;
bool avx = false; bool avx = false;
void Detect() void Detect(bool AesSwitch, bool AvxSwitch, bool force)
{ {
#if defined(__AES__) || defined(__AVX__)
#if defined(__x86_64__) || defined(__i386__) #if defined(__x86_64__) || defined(__i386__)
int info[4]; int info[4];
__cpuid(0, info[0], info[1], info[2], info[3]); __cpuid(0, info[0], info[1], info[2], info[3]);
if (info[0] >= 0x00000001) { if (info[0] >= 0x00000001) {
__cpuid(0x00000001, info[0], info[1], info[2], info[3]); __cpuid(0x00000001, info[0], info[1], info[2], info[3]);
#ifdef __AES__ #if defined (_WIN32) && (WINVER == 0x0501) // WinXP
aesni = info[2] & bit_AES; // AESNI if (AesSwitch && force) { // only if forced
#endif // __AES__ #else
#ifdef __AVX__ if ((info[2] & bit_AES && AesSwitch) || (AesSwitch && force)) {
avx = info[2] & bit_AVX; // AVX #endif
#endif // __AVX__ aesni = true;
}
#if defined (_WIN32) && (WINVER == 0x0501) // WinXP
if (AvxSwitch && force) { // only if forced
#else
if ((info[2] & bit_AVX && AvxSwitch) || (AvxSwitch && force)) {
#endif
avx = true;
}
} }
#endif // defined(__x86_64__) || defined(__i386__) #endif // defined(__x86_64__) || defined(__i386__)
#ifdef __AES__ LogPrint(eLogInfo, "AESNI ", (aesni ? "enabled" : "disabled"));
if(aesni) LogPrint(eLogInfo, "AVX ", (avx ? "enabled" : "disabled"));
{
LogPrint(eLogInfo, "AESNI enabled");
}
#endif // __AES__
#ifdef __AVX__
if(avx)
{
LogPrint(eLogInfo, "AVX enabled");
}
#endif // __AVX__
#endif // defined(__AES__) || defined(__AVX__)
} }
} }
} }

View File

@ -16,7 +16,7 @@ namespace cpu
extern bool aesni; extern bool aesni;
extern bool avx; extern bool avx;
void Detect(); void Detect(bool AesSwitch, bool AvxSwitch, bool force);
} }
} }

View File

@ -47,11 +47,11 @@ namespace config {
("ifname", value<std::string>()->default_value(""), "Network interface to bind to") ("ifname", value<std::string>()->default_value(""), "Network interface to bind to")
("ifname4", value<std::string>()->default_value(""), "Network interface to bind to for ipv4") ("ifname4", value<std::string>()->default_value(""), "Network interface to bind to for ipv4")
("ifname6", value<std::string>()->default_value(""), "Network interface to bind to for ipv6") ("ifname6", value<std::string>()->default_value(""), "Network interface to bind to for ipv6")
("nat", value<bool>()->default_value(true), "Should we assume we are behind NAT? (default: enabled)") ("nat", bool_switch()->default_value(true), "Should we assume we are behind NAT? (default: enabled)")
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)") ("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
("ipv4", value<bool>()->default_value(true), "Enable communication through ipv4 (default: enabled)") ("ipv4", bool_switch()->default_value(true), "Enable communication through ipv4 (default: enabled)")
("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)") ("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)")
("reservedrange", value<bool>()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)") ("reservedrange", bool_switch()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)")
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2") ("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)") ("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)")
("service", bool_switch()->default_value(false), "Router will use system folders like '/var/lib/i2pd' (default: disabled)") ("service", bool_switch()->default_value(false), "Router will use system folders like '/var/lib/i2pd' (default: disabled)")
@ -59,8 +59,8 @@ namespace config {
("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)") ("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)")
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") ("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") ("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
("ntcp", value<bool>()->default_value(false), "Ignored. Always false") ("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
("ssu", value<bool>()->default_value(true), "Enable SSU transport (default: enabled)") ("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Ignored") ("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
#ifdef _WIN32 #ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')") ("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
@ -197,13 +197,12 @@ namespace config {
("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks") ("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks")
("reseed.urls", value<std::string>()->default_value( ("reseed.urls", value<std::string>()->default_value(
"https://reseed.i2p-projekt.de/," "https://reseed.i2p-projekt.de/,"
"https://reseed.diva.exchange/," "https://reseed.diva.exchange/,"
"https://reseed.i2p2.no/,"
"https://reseed-fr.i2pd.xyz/," "https://reseed-fr.i2pd.xyz/,"
"https://reseed.memcpy.io/," "https://reseed.memcpy.io/,"
"https://reseed.onion.im/," "https://reseed.onion.im/,"
"https://i2pseed.creativecowpat.net:8443/," "https://i2pseed.creativecowpat.net:8443/,"
"https://reseed.i2pgit.org/," "https://reseed.i2pgit.org/,"
"https://i2p.novg.net/" "https://i2p.novg.net/"
), "Reseed URLs, separated by comma") ), "Reseed URLs, separated by comma")
; ;
@ -266,6 +265,13 @@ namespace config {
("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)") ("persist.addressbook", value<bool>()->default_value(true), "Persist full addresses (default: true)")
; ;
options_description cpuext("CPU encryption extensions options");
cpuext.add_options()
("cpuext.aesni", bool_switch()->default_value(true), "Use auto detection for AESNI CPU extensions. If false, AESNI will be not used")
("cpuext.avx", bool_switch()->default_value(true), "Use auto detection for AVX CPU extensions. If false, AVX will be not used")
("cpuext.force", bool_switch()->default_value(false), "Force usage of CPU extensions. Useful when cpuinfo is not available on virtual machines")
;
m_OptionsDesc m_OptionsDesc
.add(general) .add(general)
.add(limits) .add(limits)
@ -286,6 +292,7 @@ namespace config {
.add(ntcp2) .add(ntcp2)
.add(nettime) .add(nettime)
.add(persist) .add(persist)
.add(cpuext)
; ;
} }

View File

@ -119,7 +119,7 @@ namespace crypto
~CryptoConstants () ~CryptoConstants ()
{ {
BN_free (elgp); BN_free (elgg); BN_free (dsap); BN_free (dsaq); BN_free (dsag); BN_free (rsae); BN_free (elgp); BN_free (elgg); BN_free (dsap); BN_free (dsaq); BN_free (dsag); BN_free (rsae);
} }
}; };
@ -522,7 +522,7 @@ namespace crypto
bn2buf (y, encrypted + len, len); bn2buf (y, encrypted + len, len);
RAND_bytes (encrypted + 2*len, 256 - 2*len); RAND_bytes (encrypted + 2*len, 256 - 2*len);
} }
// ecryption key and iv // encryption key and iv
EC_POINT_mul (curve, p, nullptr, key, k, ctx); EC_POINT_mul (curve, p, nullptr, key, k, ctx);
EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr); EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr);
uint8_t keyBuf[64], iv[64], shared[32]; uint8_t keyBuf[64], iv[64], shared[32];
@ -638,7 +638,7 @@ namespace crypto
{ {
uint64_t buf[256]; uint64_t buf[256];
uint64_t hash[12]; // 96 bytes uint64_t hash[12]; // 96 bytes
#ifdef __AVX__ #if defined(__x86_64__) || defined(__i386__)
if(i2p::cpu::avx) if(i2p::cpu::avx)
{ {
__asm__ __asm__
@ -657,7 +657,7 @@ namespace crypto
: :
: [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads), : [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads),
[buf]"r"(buf), [hash]"r"(hash) [buf]"r"(buf), [hash]"r"(hash)
: "memory", "%xmm0" // TODO: change to %ymm0 later : "memory", "%xmm0" // TODO: change to %ymm0 later
); );
} }
else else
@ -688,7 +688,7 @@ namespace crypto
// concatenate with msg // concatenate with msg
memcpy (buf + 8, msg, len); memcpy (buf + 8, msg, len);
// calculate first hash // calculate first hash
MD5((uint8_t *)buf, len + 64, (uint8_t *)(hash + 8)); // 16 bytes MD5((uint8_t *)buf, len + 64, (uint8_t *)(hash + 8)); // 16 bytes
// calculate digest // calculate digest
MD5((uint8_t *)hash, 96, digest); MD5((uint8_t *)hash, 96, digest);
@ -696,35 +696,28 @@ namespace crypto
// AES // AES
#ifdef __AES__ #ifdef __AES__
#ifdef ARM64AES
void init_aesenc(void){
// TODO: Implementation
}
#endif
#define KeyExpansion256(round0,round1) \ #define KeyExpansion256(round0,round1) \
"pshufd $0xff, %%xmm2, %%xmm2 \n" \ "pshufd $0xff, %%xmm2, %%xmm2 \n" \
"movaps %%xmm1, %%xmm4 \n" \ "movaps %%xmm1, %%xmm4 \n" \
"pslldq $4, %%xmm4 \n" \ "pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm1 \n" \ "pxor %%xmm4, %%xmm1 \n" \
"pslldq $4, %%xmm4 \n" \ "pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm1 \n" \ "pxor %%xmm4, %%xmm1 \n" \
"pslldq $4, %%xmm4 \n" \ "pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm1 \n" \ "pxor %%xmm4, %%xmm1 \n" \
"pxor %%xmm2, %%xmm1 \n" \ "pxor %%xmm2, %%xmm1 \n" \
"movaps %%xmm1, "#round0"(%[sched]) \n" \ "movaps %%xmm1, "#round0"(%[sched]) \n" \
"aeskeygenassist $0, %%xmm1, %%xmm4 \n" \ "aeskeygenassist $0, %%xmm1, %%xmm4 \n" \
"pshufd $0xaa, %%xmm4, %%xmm2 \n" \ "pshufd $0xaa, %%xmm4, %%xmm2 \n" \
"movaps %%xmm3, %%xmm4 \n" \ "movaps %%xmm3, %%xmm4 \n" \
"pslldq $4, %%xmm4 \n" \ "pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm3 \n" \ "pxor %%xmm4, %%xmm3 \n" \
"pslldq $4, %%xmm4 \n" \ "pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm3 \n" \ "pxor %%xmm4, %%xmm3 \n" \
"pslldq $4, %%xmm4 \n" \ "pslldq $4, %%xmm4 \n" \
"pxor %%xmm4, %%xmm3 \n" \ "pxor %%xmm4, %%xmm3 \n" \
"pxor %%xmm2, %%xmm3 \n" \ "pxor %%xmm2, %%xmm3 \n" \
"movaps %%xmm3, "#round1"(%[sched]) \n" "movaps %%xmm3, "#round1"(%[sched]) \n"
#endif #endif
#ifdef __AES__ #ifdef __AES__
@ -750,16 +743,16 @@ namespace crypto
KeyExpansion256(192,208) KeyExpansion256(192,208)
"aeskeygenassist $64, %%xmm3, %%xmm2 \n" "aeskeygenassist $64, %%xmm3, %%xmm2 \n"
// key expansion final // key expansion final
"pshufd $0xff, %%xmm2, %%xmm2 \n" "pshufd $0xff, %%xmm2, %%xmm2 \n"
"movaps %%xmm1, %%xmm4 \n" "movaps %%xmm1, %%xmm4 \n"
"pslldq $4, %%xmm4 \n" "pslldq $4, %%xmm4 \n"
"pxor %%xmm4, %%xmm1 \n" "pxor %%xmm4, %%xmm1 \n"
"pslldq $4, %%xmm4 \n" "pslldq $4, %%xmm4 \n"
"pxor %%xmm4, %%xmm1 \n" "pxor %%xmm4, %%xmm1 \n"
"pslldq $4, %%xmm4 \n" "pslldq $4, %%xmm4 \n"
"pxor %%xmm4, %%xmm1 \n" "pxor %%xmm4, %%xmm1 \n"
"pxor %%xmm2, %%xmm1 \n" "pxor %%xmm2, %%xmm1 \n"
"movups %%xmm1, 224(%[sched]) \n" "movups %%xmm1, 224(%[sched]) \n"
: // output : // output
: [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input : [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input
: "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged
@ -794,9 +787,9 @@ namespace crypto
{ {
__asm__ __asm__
( (
"movups (%[in]), %%xmm0 \n" "movups (%[in]), %%xmm0 \n"
EncryptAES256(sched) EncryptAES256(sched)
"movups %%xmm0, (%[out]) \n" "movups %%xmm0, (%[out]) \n"
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory"
); );
} }
@ -833,9 +826,9 @@ namespace crypto
{ {
__asm__ __asm__
( (
"movups (%[in]), %%xmm0 \n" "movups (%[in]), %%xmm0 \n"
DecryptAES256(sched) DecryptAES256(sched)
"movups %%xmm0, (%[out]) \n" "movups %%xmm0, (%[out]) \n"
: : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory"
); );
} }
@ -848,7 +841,7 @@ namespace crypto
#ifdef __AES__ #ifdef __AES__
#define CallAESIMC(offset) \ #define CallAESIMC(offset) \
"movaps "#offset"(%[shed]), %%xmm0 \n" \ "movaps "#offset"(%[shed]), %%xmm0 \n" \
"aesimc %%xmm0, %%xmm0 \n" \ "aesimc %%xmm0, %%xmm0 \n" \
"movaps %%xmm0, "#offset"(%[shed]) \n" "movaps %%xmm0, "#offset"(%[shed]) \n"
#endif #endif
@ -873,7 +866,7 @@ namespace crypto
if(i2p::cpu::aesni) if(i2p::cpu::aesni)
{ {
ExpandKey (key); // expand encryption key first ExpandKey (key); // expand encryption key first
// then invert it using aesimc // then invert it using aesimc
__asm__ __asm__
( (
CallAESIMC(16) CallAESIMC(16)
@ -906,18 +899,18 @@ namespace crypto
{ {
__asm__ __asm__
( (
"movups (%[iv]), %%xmm1 \n" "movups (%[iv]), %%xmm1 \n"
"1: \n" "1: \n"
"movups (%[in]), %%xmm0 \n" "movups (%[in]), %%xmm0 \n"
"pxor %%xmm1, %%xmm0 \n" "pxor %%xmm1, %%xmm0 \n"
EncryptAES256(sched) EncryptAES256(sched)
"movaps %%xmm0, %%xmm1 \n" "movaps %%xmm0, %%xmm1 \n"
"movups %%xmm0, (%[out]) \n" "movups %%xmm0, (%[out]) \n"
"add $16, %[in] \n" "add $16, %[in] \n"
"add $16, %[out] \n" "add $16, %[out] \n"
"dec %[num] \n" "dec %[num] \n"
"jnz 1b \n" "jnz 1b \n"
"movups %%xmm1, (%[iv]) \n" "movups %%xmm1, (%[iv]) \n"
: :
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
@ -951,12 +944,12 @@ namespace crypto
{ {
__asm__ __asm__
( (
"movups (%[iv]), %%xmm1 \n" "movups (%[iv]), %%xmm1 \n"
"movups (%[in]), %%xmm0 \n" "movups (%[in]), %%xmm0 \n"
"pxor %%xmm1, %%xmm0 \n" "pxor %%xmm1, %%xmm0 \n"
EncryptAES256(sched) EncryptAES256(sched)
"movups %%xmm0, (%[out]) \n" "movups %%xmm0, (%[out]) \n"
"movups %%xmm0, (%[iv]) \n" "movups %%xmm0, (%[iv]) \n"
: :
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), : [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out) [in]"r"(in), [out]"r"(out)
@ -975,19 +968,19 @@ namespace crypto
{ {
__asm__ __asm__
( (
"movups (%[iv]), %%xmm1 \n" "movups (%[iv]), %%xmm1 \n"
"1: \n" "1: \n"
"movups (%[in]), %%xmm0 \n" "movups (%[in]), %%xmm0 \n"
"movaps %%xmm0, %%xmm2 \n" "movaps %%xmm0, %%xmm2 \n"
DecryptAES256(sched) DecryptAES256(sched)
"pxor %%xmm1, %%xmm0 \n" "pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[out]) \n" "movups %%xmm0, (%[out]) \n"
"movaps %%xmm2, %%xmm1 \n" "movaps %%xmm2, %%xmm1 \n"
"add $16, %[in] \n" "add $16, %[in] \n"
"add $16, %[out] \n" "add $16, %[out] \n"
"dec %[num] \n" "dec %[num] \n"
"jnz 1b \n" "jnz 1b \n"
"movups %%xmm1, (%[iv]) \n" "movups %%xmm1, (%[iv]) \n"
: :
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
@ -1021,12 +1014,12 @@ namespace crypto
{ {
__asm__ __asm__
( (
"movups (%[iv]), %%xmm1 \n" "movups (%[iv]), %%xmm1 \n"
"movups (%[in]), %%xmm0 \n" "movups (%[in]), %%xmm0 \n"
"movups %%xmm0, (%[iv]) \n" "movups %%xmm0, (%[iv]) \n"
DecryptAES256(sched) DecryptAES256(sched)
"pxor %%xmm1, %%xmm0 \n" "pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[out]) \n" "movups %%xmm0, (%[out]) \n"
: :
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), : [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
[in]"r"(in), [out]"r"(out) [in]"r"(in), [out]"r"(out)
@ -1046,7 +1039,7 @@ namespace crypto
__asm__ __asm__
( (
// encrypt IV // encrypt IV
"movups (%[in]), %%xmm0 \n" "movups (%[in]), %%xmm0 \n"
EncryptAES256(sched_iv) EncryptAES256(sched_iv)
"movaps %%xmm0, %%xmm1 \n" "movaps %%xmm0, %%xmm1 \n"
// double IV encryption // double IV encryption
@ -1056,11 +1049,11 @@ namespace crypto
"1: \n" "1: \n"
"add $16, %[in] \n" "add $16, %[in] \n"
"add $16, %[out] \n" "add $16, %[out] \n"
"movups (%[in]), %%xmm0 \n" "movups (%[in]), %%xmm0 \n"
"pxor %%xmm1, %%xmm0 \n" "pxor %%xmm1, %%xmm0 \n"
EncryptAES256(sched_l) EncryptAES256(sched_l)
"movaps %%xmm0, %%xmm1 \n" "movaps %%xmm0, %%xmm1 \n"
"movups %%xmm0, (%[out]) \n" "movups %%xmm0, (%[out]) \n"
"dec %[num] \n" "dec %[num] \n"
"jnz 1b \n" "jnz 1b \n"
: :
@ -1097,11 +1090,11 @@ namespace crypto
"1: \n" "1: \n"
"add $16, %[in] \n" "add $16, %[in] \n"
"add $16, %[out] \n" "add $16, %[out] \n"
"movups (%[in]), %%xmm0 \n" "movups (%[in]), %%xmm0 \n"
"movaps %%xmm0, %%xmm2 \n" "movaps %%xmm0, %%xmm2 \n"
DecryptAES256(sched_l) DecryptAES256(sched_l)
"pxor %%xmm1, %%xmm0 \n" "pxor %%xmm1, %%xmm0 \n"
"movups %%xmm0, (%[out]) \n" "movups %%xmm0, (%[out]) \n"
"movaps %%xmm2, %%xmm1 \n" "movaps %%xmm2, %%xmm1 \n"
"dec %[num] \n" "dec %[num] \n"
"jnz 1b \n" "jnz 1b \n"
@ -1323,9 +1316,24 @@ namespace crypto
#endif #endif
} }
void NoiseSymmetricState::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 NoiseSymmetricState::MixKey (const uint8_t * sharedSecret)
{
HKDF (m_CK, sharedSecret, 32, "", m_CK);
// new ck is m_CK[0:31], key is m_CK[32:63]
}
// init and terminate // init and terminate
/* std::vector <std::unique_ptr<std::mutex> > m_OpenSSLMutexes; /* std::vector <std::unique_ptr<std::mutex> > m_OpenSSLMutexes;
static void OpensslLockingCallback(int mode, int type, const char * file, int line) static void OpensslLockingCallback(int mode, int type, const char * file, int line)
{ {
if (type > 0 && (size_t)type < m_OpenSSLMutexes.size ()) if (type > 0 && (size_t)type < m_OpenSSLMutexes.size ())
@ -1337,10 +1345,9 @@ namespace crypto
} }
}*/ }*/
void InitCrypto (bool precomputation, bool aesni, bool avx, bool force)
void InitCrypto (bool precomputation)
{ {
i2p::cpu::Detect (); i2p::cpu::Detect (aesni, avx, force);
#if LEGACY_OPENSSL #if LEGACY_OPENSSL
SSL_library_init (); SSL_library_init ();
#endif #endif

View File

@ -169,9 +169,6 @@ namespace crypto
#ifdef __AES__ #ifdef __AES__
#ifdef ARM64AES
void init_aesenc(void) __attribute__((constructor));
#endif
class ECBCryptoAESNI class ECBCryptoAESNI
{ {
public: public:
@ -311,8 +308,18 @@ namespace crypto
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 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
// Noise
struct NoiseSymmetricState
{
uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/;
void MixHash (const uint8_t * buf, size_t len);
void MixKey (const uint8_t * sharedSecret);
};
// init and terminate // init and terminate
void InitCrypto (bool precomputation); void InitCrypto (bool precomputation, bool aesni, bool avx, bool force);
void TerminateCrypto (); void TerminateCrypto ();
} }
} }

View File

@ -560,8 +560,8 @@ namespace client
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4); RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
auto msg = i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound); auto msg = i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound);
if (floodfill->GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) // TODO: remove when implemented if (floodfill->GetIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // TODO: remove whan implemented
msg = WrapMessage (floodfill, msg); msg = WrapMessageForRouter (floodfill, msg);
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
@ -746,7 +746,7 @@ namespace client
request->excluded.insert (nextFloodfill->GetIdentHash ()); request->excluded.insert (nextFloodfill->GetIdentHash ());
request->requestTimeoutTimer.cancel (); request->requestTimeoutTimer.cancel ();
bool isECIES = SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) && bool isECIES = SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) &&
nextFloodfill->GetVersion () >= MAKE_VERSION_NUMBER(0, 9, 46); // >= 0.9.46; nextFloodfill->GetVersion () >= MAKE_VERSION_NUMBER(0, 9, 46); // >= 0.9.46;
uint8_t replyKey[32], replyTag[32]; uint8_t replyKey[32], replyTag[32];
RAND_bytes (replyKey, 32); // random session key RAND_bytes (replyKey, 32); // random session key
@ -756,10 +756,9 @@ namespace client
else else
AddSessionKey (replyKey, replyTag); AddSessionKey (replyKey, replyTag);
auto msg = CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, auto msg = CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES);
request->replyTunnel, replyKey, replyTag, isECIES); if (nextFloodfill->GetIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // TODO: remove whan implemented
if (nextFloodfill->GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) // TODO: remove when implemented msg = WrapMessageForRouter (nextFloodfill, msg);
msg = WrapMessage (nextFloodfill, msg);
request->outboundTunnel->SendTunnelDataMsg ( request->outboundTunnel->SendTunnelDataMsg (
{ {
i2p::tunnel::TunnelMessageBlock i2p::tunnel::TunnelMessageBlock
@ -846,8 +845,8 @@ namespace client
i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType () const i2p::data::CryptoKeyType LeaseSetDestination::GetPreferredCryptoType () const
{ {
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)) if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET; return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD;
return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
} }
@ -902,7 +901,7 @@ namespace client
else else
encryptionKey->GenerateKeys (); encryptionKey->GenerateKeys ();
encryptionKey->CreateDecryptor (); encryptionKey->CreateDecryptor ();
if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
{ {
m_ECIESx25519EncryptionKey.reset (encryptionKey); m_ECIESx25519EncryptionKey.reset (encryptionKey);
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET)
@ -1219,7 +1218,7 @@ namespace client
bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const
{ {
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor) if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor)
return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data, ctx, true); return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data, ctx, true);
if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor) if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor)
@ -1231,12 +1230,12 @@ namespace client
bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const bool ClientDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const
{ {
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ? (bool)m_ECIESx25519EncryptionKey : (bool)m_StandardEncryptionKey; return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519EncryptionKey : (bool)m_StandardEncryptionKey;
} }
const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const const uint8_t * ClientDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const
{ {
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
return m_ECIESx25519EncryptionKey ? m_ECIESx25519EncryptionKey->pub : nullptr; return m_ECIESx25519EncryptionKey ? m_ECIESx25519EncryptionKey->pub : nullptr;
return m_StandardEncryptionKey ? m_StandardEncryptionKey->pub : nullptr; return m_StandardEncryptionKey ? m_StandardEncryptionKey->pub : nullptr;
} }

View File

@ -176,15 +176,6 @@ namespace garlic
memcpy (m_H, hh, 32); memcpy (m_H, hh, 32);
} }
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) void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce)
{ {
memset (nonce, 0, 4); memset (nonce, 0, 4);
@ -245,7 +236,7 @@ namespace garlic
if (!GetOwner ()) return false; if (!GetOwner ()) return false;
// we are Bob // we are Bob
// KDF1 // KDF1
MixHash (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET), 32); // h = SHA256(h || bpk) MixHash (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD), 32); // h = SHA256(h || bpk)
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
{ {
@ -256,8 +247,8 @@ namespace garlic
MixHash (m_Aepk, 32); // h = SHA256(h || aepk) MixHash (m_Aepk, 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519(bsk, aepk) GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519(bsk, aepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) MixKey (sharedSecret);
// decrypt flags/static // decrypt flags/static
uint8_t nonce[12], fs[32]; uint8_t nonce[12], fs[32];
@ -276,8 +267,8 @@ namespace garlic
{ {
// static key, fs is apk // static key, fs is apk
memcpy (m_RemoteStaticKey, fs, 32); memcpy (m_RemoteStaticKey, fs, 32);
GetOwner ()->Decrypt (fs, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519(bsk, apk) GetOwner ()->Decrypt (fs, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519(bsk, apk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) MixKey (sharedSecret);
} }
else // all zeros flags else // all zeros flags
CreateNonce (1, nonce); CreateNonce (1, nonce);
@ -289,10 +280,13 @@ namespace garlic
LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed"); LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed");
return false; return false;
} }
if (isStatic) MixHash (buf, len); // h = SHA256(h || ciphertext)
m_State = eSessionStateNewSessionReceived; m_State = eSessionStateNewSessionReceived;
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); if (isStatic)
{
MixHash (buf, len); // h = SHA256(h || ciphertext)
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
}
HandlePayload (payload.data (), len - 16, nullptr, 0); HandlePayload (payload.data (), len - 16, nullptr, 0);
return true; return true;
@ -452,7 +446,7 @@ namespace garlic
LogPrint (eLogDebug, "Garlic: new send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created"); LogPrint (eLogDebug, "Garlic: new send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created");
} }
bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic)
{ {
ResetKeys (); ResetKeys ();
// we are Alice, bpk is m_RemoteStaticKey // we are Alice, bpk is m_RemoteStaticKey
@ -469,32 +463,48 @@ namespace garlic
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk) MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk) m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) MixKey (sharedSecret);
// encrypt static key section // encrypt flags/static key section
uint8_t nonce[12]; uint8_t nonce[12];
CreateNonce (0, nonce); CreateNonce (0, nonce);
if (!i2p::crypto::AEADChaCha20Poly1305 (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET), 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt const uint8_t * fs;
if (isStatic)
fs = GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
else
{ {
LogPrint (eLogWarning, "Garlic: Static section AEAD encryption failed "); memset (out + offset, 0, 32); // all zeros flags section
fs = out + offset;
}
if (!i2p::crypto::AEADChaCha20Poly1305 (fs, 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed ");
return false; return false;
} }
MixHash (out + offset, 48); // h = SHA256(h || ciphertext) MixHash (out + offset, 48); // h = SHA256(h || ciphertext)
offset += 48; offset += 48;
// KDF2 // KDF2
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bpk) if (isStatic)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) {
GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk)
MixKey (sharedSecret);
}
else
CreateNonce (1, nonce);
// encrypt payload // encrypt payload
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt 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"); LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed");
return false; return false;
} }
MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext)
m_State = eSessionStateNewSessionSent; m_State = eSessionStateNewSessionSent;
if (GetOwner ()) if (isStatic)
GenerateMoreReceiveTags (CreateNewSessionTagset (), ECIESX25519_NSR_NUM_GENERATED_TAGS); {
MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext)
if (GetOwner ())
GenerateMoreReceiveTags (CreateNewSessionTagset (), ECIESX25519_NSR_NUM_GENERATED_TAGS);
}
return true; return true;
} }
@ -520,9 +530,9 @@ namespace garlic
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk) MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || bepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
m_EphemeralKeys->Agree (m_Aepk, sharedSecret); // sharedSecret = x25519(besk, aepk) 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) MixKey (sharedSecret);
m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // sharedSecret = x25519(besk, apk) 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) MixKey (sharedSecret);
uint8_t nonce[12]; uint8_t nonce[12];
CreateNonce (0, nonce); CreateNonce (0, nonce);
// calculate hash for zero length // calculate hash for zero length
@ -607,9 +617,9 @@ namespace garlic
{ {
// only fist time, we assume ephemeral keys the same // only fist time, we assume ephemeral keys the same
m_EphemeralKeys->Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk) m_EphemeralKeys->Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK, 32); // chainKey = HKDF(chainKey, sharedSecret, "", 32) MixKey (sharedSecret);
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET); // x25519 (ask, bepk) GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk)
i2p::crypto::HKDF (m_CK, sharedSecret, 32, "", m_CK); // [chainKey, key] = HKDF(chainKey, sharedSecret, "", 64) MixKey (sharedSecret);
} }
uint8_t nonce[12]; uint8_t nonce[12];
CreateNonce (0, nonce); CreateNonce (0, nonce);
@ -784,6 +794,11 @@ namespace garlic
return nullptr; return nullptr;
len += 72; len += 72;
break; break;
case eSessionStateOneTime:
if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen, false))
return nullptr;
len += 96;
break;
default: default:
return nullptr; return nullptr;
} }
@ -794,6 +809,12 @@ namespace garlic
return m; return m;
} }
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg)
{
m_State = eSessionStateOneTime;
return WrapSingleMessage (msg);
}
std::vector<uint8_t> ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first) std::vector<uint8_t> ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first)
{ {
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
@ -878,7 +899,7 @@ namespace garlic
} }
} }
// msg // msg
if (msg && m_Destination) if (msg)
offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset); offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset);
// ack // ack
if (m_AckRequests.size () > 0) if (m_AckRequests.size () > 0)
@ -990,8 +1011,11 @@ namespace garlic
void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (std::shared_ptr<RatchetTagSet> receiveTagset, int numTags) void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (std::shared_ptr<RatchetTagSet> receiveTagset, int numTags)
{ {
for (int i = 0; i < numTags; i++) if (GetOwner ())
GetOwner ()->AddECIESx25519SessionNextTag (receiveTagset); {
for (int i = 0; i < numTags; i++)
GetOwner ()->AddECIESx25519SessionNextTag (receiveTagset);
}
} }
bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts) bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts)

View File

@ -31,7 +31,7 @@ namespace garlic
const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after
const int ECIESX25519_INCOMING_TAGS_EXPIRATION_TIMEOUT = 600; // in seconds const int ECIESX25519_INCOMING_TAGS_EXPIRATION_TIMEOUT = 600; // in seconds
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180 const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 4096; // number of tags we request new tagset after const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24; const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 160; const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 160;
const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12; const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
@ -129,7 +129,9 @@ namespace garlic
const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02; const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02;
const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04; const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04;
class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, public std::enable_shared_from_this<ECIESX25519AEADRatchetSession> class ECIESX25519AEADRatchetSession: public GarlicRoutingSession,
private i2p::crypto::NoiseSymmetricState,
public std::enable_shared_from_this<ECIESX25519AEADRatchetSession>
{ {
enum SessionState enum SessionState
{ {
@ -137,7 +139,8 @@ namespace garlic
eSessionStateNewSessionReceived, eSessionStateNewSessionReceived,
eSessionStateNewSessionSent, eSessionStateNewSessionSent,
eSessionStateNewSessionReplySent, eSessionStateNewSessionReplySent,
eSessionStateEstablished eSessionStateEstablished,
eSessionStateOneTime
}; };
struct DHRatchet struct DHRatchet
@ -155,7 +158,8 @@ namespace garlic
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<RatchetTagSet> receiveTagset, int index = 0); bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<RatchetTagSet> receiveTagset, int index = 0);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg); std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg);
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
@ -169,12 +173,12 @@ namespace garlic
bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); } bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); }
bool IsRatchets () const { return true; }; bool IsRatchets () const { return true; };
bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; };
uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; }; uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; };
private: private:
void ResetKeys (); void ResetKeys ();
void MixHash (const uint8_t * buf, size_t len);
void CreateNonce (uint64_t seqn, uint8_t * nonce); void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
std::shared_ptr<RatchetTagSet> CreateNewSessionTagset (); std::shared_ptr<RatchetTagSet> CreateNewSessionTagset ();
@ -185,7 +189,7 @@ namespace garlic
void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<RatchetTagSet>& receiveTagset, int index); void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr<RatchetTagSet>& receiveTagset, int index);
void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<RatchetTagSet>& receiveTagset); void HandleNextKey (const uint8_t * buf, size_t len, const std::shared_ptr<RatchetTagSet>& receiveTagset);
bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic = true);
bool NewSessionReplyMessage (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 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); bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
@ -199,7 +203,7 @@ namespace garlic
private: private:
uint8_t m_H[32], m_CK[64] /* [chainkey, key] */, m_RemoteStaticKey[32]; uint8_t m_RemoteStaticKey[32];
uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only uint8_t m_Aepk[32]; // Alice's ephemeral keys, for incoming only
uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys; std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;

View File

@ -46,13 +46,13 @@ namespace fs {
dataDir = cmdline_param; dataDir = cmdline_param;
return; return;
} }
#if defined(WIN32) || defined(_WIN32) #ifdef _WIN32
char localAppData[MAX_PATH]; char localAppData[MAX_PATH];
// check executable directory first // check executable directory first
if(!GetModuleFileName(NULL, localAppData, MAX_PATH)) if(!GetModuleFileName(NULL, localAppData, MAX_PATH))
{ {
#if defined(WIN32_APP) #ifdef WIN32_APP
MessageBox(NULL, TEXT("Unable to get application path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); MessageBox(NULL, TEXT("Unable to get application path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
#else #else
fprintf(stderr, "Error: Unable to get application path!"); fprintf(stderr, "Error: Unable to get application path!");
@ -70,7 +70,7 @@ namespace fs {
{ {
if(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK) if(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK)
{ {
#if defined(WIN32_APP) #ifdef WIN32_APP
MessageBox(NULL, TEXT("Unable to get AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); MessageBox(NULL, TEXT("Unable to get AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
#else #else
fprintf(stderr, "Error: Unable to get AppData path!"); fprintf(stderr, "Error: Unable to get AppData path!");

View File

@ -506,7 +506,7 @@ namespace garlic
else else
{ {
bool found = false; bool found = false;
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)) if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
{ {
// try ECIESx25519 tag // try ECIESx25519 tag
uint64_t tag; uint64_t tag;
@ -536,7 +536,7 @@ namespace garlic
decryption->Decrypt(buf + 514, length - 514, buf + 514); decryption->Decrypt(buf + 514, length - 514, buf + 514);
HandleAESBlock (buf + 514, length - 514, decryption, msg->from); HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
} }
else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)) else if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
{ {
// otherwise ECIESx25519 // otherwise ECIESx25519
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
@ -709,18 +709,27 @@ namespace garlic
} }
} }
std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination, std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet) std::shared_ptr<I2NPMessage> msg)
{ {
auto session = GetRoutingSession (destination, attachLeaseSet); if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
return session->WrapSingleMessage (msg); {
auto session = std::make_shared<ECIESX25519AEADRatchetSession>(this, false);
session->SetRemoteStaticKey (router->GetIdentity ()->GetEncryptionPublicKey ());
return session->WrapOneTimeMessage (msg);
}
else
{
auto session = GetRoutingSession (router, false);
return session->WrapSingleMessage (msg);
}
} }
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession ( std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet) std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
{ {
if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET && if (destination->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD &&
SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET)) SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
{ {
ECIESX25519AEADRatchetSessionPtr session; ECIESX25519AEADRatchetSessionPtr session;
uint8_t staticKey[32]; uint8_t staticKey[32];
@ -762,6 +771,7 @@ namespace garlic
} }
return session; return session;
} }
return nullptr;
} }
void GarlicDestination::CleanupExpiredTags () void GarlicDestination::CleanupExpiredTags ()

View File

@ -114,6 +114,7 @@ namespace garlic
virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession virtual bool CleanupUnconfirmedTags () { return false; }; // for I2CP, override in ElGamalAESSession
virtual bool MessageConfirmed (uint32_t msgID); virtual bool MessageConfirmed (uint32_t msgID);
virtual bool IsRatchets () const { return false; }; virtual bool IsRatchets () const { return false; };
virtual bool IsReadyToSend () const { return true; };
virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only
void SetLeaseSetUpdated () void SetLeaseSetUpdated ()
@ -237,8 +238,8 @@ namespace garlic
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet); std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
void CleanupExpiredTags (); void CleanupExpiredTags ();
void RemoveDeliveryStatusSession (uint32_t msgID); void RemoveDeliveryStatusSession (uint32_t msgID);
std::shared_ptr<I2NPMessage> WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination, std::shared_ptr<I2NPMessage> WrapMessageForRouter (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet = false); std::shared_ptr<I2NPMessage> msg);
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag

View File

@ -361,39 +361,82 @@ namespace i2p
{ {
LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours"); LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours");
BN_CTX * ctx = BN_CTX_new (); BN_CTX * ctx = BN_CTX_new ();
i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText, ctx); bool success = i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText, ctx);
BN_CTX_free (ctx); BN_CTX_free (ctx);
if(!success) return false;
uint8_t retCode = 0;
bool isECIES = i2p::context.IsECIES ();
// replace record to reply // replace record to reply
if (i2p::context.AcceptsTunnels () && if (i2p::context.AcceptsTunnels () &&
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels && i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels &&
!i2p::transport::transports.IsBandwidthExceeded () && !i2p::transport::transports.IsBandwidthExceeded () &&
!i2p::transport::transports.IsTransitBandwidthExceeded ()) !i2p::transport::transports.IsTransitBandwidthExceeded ())
{ {
auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( auto transitTunnel = isECIES ?
i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80,
clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) :
i2p::tunnel::CreateTransitTunnel (
bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80, clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80,
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40); clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40);
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0;
} }
else else
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30) retCode = 30; // always reject with bandwidth reason (30)
//TODO: fill filler if (isECIES)
SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret {
record + BUILD_RESPONSE_RECORD_HASH_OFFSET); memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options
record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
}
else
{
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode;
SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret
record + BUILD_RESPONSE_RECORD_HASH_OFFSET);
}
// encrypt reply // encrypt reply
i2p::crypto::CBCEncryption encryption; i2p::crypto::CBCEncryption encryption;
for (int j = 0; j < num; j++) for (int j = 0; j < num; j++)
{ {
encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); if (isECIES)
{
if (j == i)
{
uint8_t nonce[12];
memset (nonce, 0, 12);
auto noiseState = std::move (i2p::context.GetCurrentNoiseState ());
if (!noiseState || !i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16,
noiseState->m_H, 32, noiseState->m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt
{
LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed");
return false;
}
}
else
{
encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
}
}
else
{
encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
}
} }
return true; return true;
} }
@ -405,7 +448,7 @@ namespace i2p
{ {
int num = buf[0]; int num = buf[0];
LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records"); LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records");
if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1) if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1)
{ {
LogPrint (eLogError, "VaribleTunnelBuild message of ", num, " records is too short ", len); LogPrint (eLogError, "VaribleTunnelBuild message of ", num, " records is too short ", len);
return; return;
@ -430,28 +473,55 @@ namespace i2p
} }
else else
{ {
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; if (i2p::context.IsECIES ())
if (HandleBuildRequestRecords (num, buf + 1, clearText))
{ {
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText))
{ {
// so we send it to reply tunnel if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, {
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), // so we send it to reply tunnel
eI2NPVariableTunnelBuildReply, buf, len, transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPVariableTunnelBuildReply, buf, len,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
} }
else
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
} }
else
{
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
if (HandleBuildRequestRecords (num, buf + 1, clearText))
{
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel
{
// so we send it to reply tunnel
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
eI2NPVariableTunnelBuildReply, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
else
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET)));
}
}
} }
} }
void HandleTunnelBuildMsg (uint8_t * buf, size_t len) void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
{ {
if (len < NUM_TUNNEL_BUILD_RECORDS*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE) if (i2p::context.IsECIES ())
{
LogPrint (eLogWarning, "TunnelBuild is too old for ECIES router");
return;
}
if (len < NUM_TUNNEL_BUILD_RECORDS*TUNNEL_BUILD_RECORD_SIZE)
{ {
LogPrint (eLogError, "TunnelBuild message is too short ", len); LogPrint (eLogError, "TunnelBuild message is too short ", len);
return; return;

View File

@ -98,6 +98,7 @@ namespace i2p
const size_t ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 464; const size_t ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 464;
// ECIES BuildResponseRecord // ECIES BuildResponseRecord
const size_t ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET = 0;
const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511; const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511;
enum I2NPMessageType enum I2NPMessageType

View File

@ -48,10 +48,10 @@ namespace data
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType) IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType)
{ {
if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
{ {
memcpy (m_StandardIdentity.publicKey, publicKey, 32); memcpy (m_StandardIdentity.publicKey, publicKey, 32);
RAND_bytes (m_StandardIdentity.publicKey, 224); RAND_bytes (m_StandardIdentity.publicKey + 32, 224);
} }
else else
memcpy (m_StandardIdentity.publicKey, publicKey, 256); memcpy (m_StandardIdentity.publicKey, publicKey, 256);
@ -426,7 +426,7 @@ namespace data
case CRYPTO_KEY_TYPE_ELGAMAL: case CRYPTO_KEY_TYPE_ELGAMAL:
return std::make_shared<i2p::crypto::ElGamalEncryptor>(key); return std::make_shared<i2p::crypto::ElGamalEncryptor>(key);
break; break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET: case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetEncryptor>(key); return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetEncryptor>(key);
break; break;
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
@ -476,7 +476,7 @@ namespace data
size_t PrivateKeys::GetFullLen () const size_t PrivateKeys::GetFullLen () const
{ {
size_t ret = m_Public->GetFullLen () + 256 + m_Public->GetSigningPrivateKeyLen (); size_t ret = m_Public->GetFullLen () + GetPrivateKeyLen () + m_Public->GetSigningPrivateKeyLen ();
if (IsOfflineSignature ()) if (IsOfflineSignature ())
ret += m_OfflineSignature.size () + m_TransientSigningPrivateKeyLen; ret += m_OfflineSignature.size () + m_TransientSigningPrivateKeyLen;
return ret; return ret;
@ -486,9 +486,10 @@ namespace data
{ {
m_Public = std::make_shared<IdentityEx>(); m_Public = std::make_shared<IdentityEx>();
size_t ret = m_Public->FromBuffer (buf, len); size_t ret = m_Public->FromBuffer (buf, len);
if (!ret || ret + 256 > len) return 0; // overflow auto cryptoKeyLen = GetPrivateKeyLen ();
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256 if (!ret || ret + cryptoKeyLen > len) return 0; // overflow
ret += 256; memcpy (m_PrivateKey, buf + ret, cryptoKeyLen);
ret += cryptoKeyLen;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen (); size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(signingPrivateKeySize + ret > len || signingPrivateKeySize > 128) return 0; // overflow if(signingPrivateKeySize + ret > len || signingPrivateKeySize > 128) return 0; // overflow
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize); memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
@ -540,8 +541,9 @@ namespace data
size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const
{ {
size_t ret = m_Public->ToBuffer (buf, len); size_t ret = m_Public->ToBuffer (buf, len);
memcpy (buf + ret, m_PrivateKey, 256); // private key always 256 auto cryptoKeyLen = GetPrivateKeyLen ();
ret += 256; memcpy (buf + ret, m_PrivateKey, cryptoKeyLen);
ret += cryptoKeyLen;
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen (); size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
if(ret + signingPrivateKeySize > len) return 0; // overflow if(ret + signingPrivateKeySize > len) return 0; // overflow
if (IsOfflineSignature ()) if (IsOfflineSignature ())
@ -657,6 +659,12 @@ namespace data
return IsOfflineSignature () ? m_TransientSignatureLen : m_Public->GetSignatureLen (); return IsOfflineSignature () ? m_TransientSignatureLen : m_Public->GetSignatureLen ();
} }
size_t PrivateKeys::GetPrivateKeyLen () const
{
// private key length always 256, but type 4
return (m_Public->GetCryptoKeyType () == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) ? 32 : 256;
}
uint8_t * PrivateKeys::GetPadding() uint8_t * PrivateKeys::GetPadding()
{ {
if(m_Public->GetSigningKeyType () == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519) if(m_Public->GetSigningKeyType () == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519)
@ -679,6 +687,9 @@ namespace data
case CRYPTO_KEY_TYPE_ELGAMAL: case CRYPTO_KEY_TYPE_ELGAMAL:
return std::make_shared<i2p::crypto::ElGamalDecryptor>(key); return std::make_shared<i2p::crypto::ElGamalDecryptor>(key);
break; break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
break;
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
return std::make_shared<i2p::crypto::ECIESP256Decryptor>(key); return std::make_shared<i2p::crypto::ECIESP256Decryptor>(key);
@ -686,9 +697,6 @@ namespace data
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key); return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key);
break; break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET:
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
break;
default: default:
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType); LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType);
}; };
@ -768,7 +776,7 @@ namespace data
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub); i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub);
break; break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET: case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub); i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub);
break; break;
default: default:
@ -820,7 +828,7 @@ namespace data
XORMetric operator^(const IdentHash& key1, const IdentHash& key2) XORMetric operator^(const IdentHash& key1, const IdentHash& key2)
{ {
XORMetric m; XORMetric m;
#ifdef __AVX__ #if defined(__x86_64__) || defined(__i386__)
if(i2p::cpu::avx) if(i2p::cpu::avx)
{ {
__asm__ __asm__

View File

@ -64,7 +64,7 @@ namespace data
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0; 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_P256_SHA256_AES256CBC = 1;
const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET = 4; const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD = 4;
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later 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 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
@ -183,6 +183,7 @@ namespace data
void CreateSigner () const; void CreateSigner () const;
void CreateSigner (SigningKeyType keyType) const; void CreateSigner (SigningKeyType keyType) const;
size_t GetPrivateKeyLen () const;
private: private:

View File

@ -39,21 +39,6 @@ namespace transport
delete[] m_SessionConfirmedBuffer; delete[] m_SessionConfirmedBuffer;
} }
void NTCP2Establisher::MixKey (const uint8_t * inputKeyMaterial)
{
i2p::crypto::HKDF (m_CK, inputKeyMaterial, 32, "", m_CK);
// ck is m_CK[0:31], k is m_CK[32:63]
}
void NTCP2Establisher::MixHash (const uint8_t * buf, size_t len)
{
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, m_H, 32);
SHA256_Update (&ctx, buf, len);
SHA256_Final (m_H, &ctx);
}
void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub) void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub)
{ {
static const uint8_t protocolNameHash[] = static const uint8_t protocolNameHash[] =
@ -757,6 +742,10 @@ namespace transport
void NTCP2Session::ReceiveLength () void NTCP2Session::ReceiveLength ()
{ {
if (IsTerminated ()) return; if (IsTerminated ()) return;
#ifdef __linux__
const int one = 1;
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
#endif
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_NextReceivedLen, 2), boost::asio::transfer_all (), boost::asio::async_read (m_Socket, boost::asio::buffer(&m_NextReceivedLen, 2), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleReceivedLength, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); std::bind(&NTCP2Session::HandleReceivedLength, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
} }
@ -808,6 +797,10 @@ namespace transport
void NTCP2Session::Receive () void NTCP2Session::Receive ()
{ {
if (IsTerminated ()) return; if (IsTerminated ()) return;
#ifdef __linux__
const int one = 1;
setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
#endif
boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (),
std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
} }

View File

@ -74,7 +74,7 @@ namespace transport
// RouterInfo flags // RouterInfo flags
const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; const uint8_t NTCP2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
struct NTCP2Establisher struct NTCP2Establisher: private i2p::crypto::NoiseSymmetricState
{ {
NTCP2Establisher (); NTCP2Establisher ();
~NTCP2Establisher (); ~NTCP2Establisher ();
@ -94,8 +94,6 @@ namespace transport
void KDF3Alice (); // for SessionConfirmed part 2 void KDF3Alice (); // for SessionConfirmed part 2
void KDF3Bob (); void KDF3Bob ();
void MixKey (const uint8_t * inputKeyMaterial);
void MixHash (const uint8_t * buf, size_t len);
void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
void CreateEphemeralKey (); void CreateEphemeralKey ();
@ -112,7 +110,7 @@ namespace transport
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys; std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
uint8_t m_RemoteEphemeralPublicKey[32]; // x25519 uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[64] /* [ck, k]*/; uint8_t m_RemoteStaticKey[32], m_IV[16];
i2p::data::IdentHash m_RemoteIdentHash; i2p::data::IdentHash m_RemoteIdentHash;
uint16_t m3p2Len; uint16_t m3p2Len;

View File

@ -111,6 +111,9 @@ namespace data
case eI2NPDatabaseLookup: case eI2NPDatabaseLookup:
HandleDatabaseLookupMsg (msg); HandleDatabaseLookupMsg (msg);
break; break;
case eI2NPDeliveryStatus:
HandleDeliveryStatusMsg (msg);
break;
case eI2NPDummyMsg: case eI2NPDummyMsg:
// plain RouterInfo from NTCP2 with flags for now // plain RouterInfo from NTCP2 with flags for now
HandleNTCP2RouterInfoMsg (msg); HandleNTCP2RouterInfoMsg (msg);
@ -148,11 +151,22 @@ namespace data
lastDestinationCleanup = ts; lastDestinationCleanup = ts;
} }
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL) // update timestamp and publish // publish
if (!m_HiddenMode && i2p::transport::transports.IsOnline ())
{ {
i2p::context.UpdateTimestamp (ts); bool publish = false;
if (!m_HiddenMode) Publish (); if (m_PublishReplyToken)
lastPublish = ts; {
if (ts - lastPublish >= NETDB_PUBLISH_CONFIRMATION_TIMEOUT) publish = true;
}
else if (i2p::context.GetLastUpdateTime () > lastPublish ||
ts - lastPublish >= NETDB_PUBLISH_INTERVAL) publish = true;
if (publish) // update timestamp and publish
{
i2p::context.UpdateTimestamp (ts);
Publish ();
lastPublish = ts;
}
} }
if (ts - lastExploratory >= 30) // exploratory every 30 seconds if (ts - lastExploratory >= 30) // exploratory every 30 seconds
{ {
@ -226,7 +240,7 @@ namespace data
std::unique_lock<std::mutex> l(m_FloodfillsMutex); std::unique_lock<std::mutex> l(m_FloodfillsMutex);
if (wasFloodfill) if (wasFloodfill)
m_Floodfills.remove (r); m_Floodfills.remove (r);
else else if (r->IsEligibleFloodfill ())
m_Floodfills.push_back (r); m_Floodfills.push_back (r);
} }
} }
@ -249,7 +263,7 @@ namespace data
if (inserted) if (inserted)
{ {
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64()); LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64());
if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable if (r->IsFloodfill () && r->IsEligibleFloodfill ())
{ {
std::unique_lock<std::mutex> l(m_FloodfillsMutex); std::unique_lock<std::mutex> l(m_FloodfillsMutex);
m_Floodfills.push_back (r); m_Floodfills.push_back (r);
@ -992,6 +1006,16 @@ namespace data
} }
} }
void NetDb::HandleDeliveryStatusMsg (std::shared_ptr<const I2NPMessage> msg)
{
if (m_PublishReplyToken == bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET))
{
LogPrint (eLogInfo, "NetDb: Publishing confirmed. reply token=", m_PublishReplyToken);
m_PublishExcluded.clear ();
m_PublishReplyToken = 0;
}
}
void NetDb::Explore (int numDestinations) void NetDb::Explore (int numDestinations)
{ {
// new requests // new requests
@ -1045,18 +1069,22 @@ namespace data
void NetDb::Publish () void NetDb::Publish ()
{ {
i2p::context.UpdateStats (); // for floodfill i2p::context.UpdateStats (); // for floodfill
std::set<IdentHash> excluded; // TODO: fill up later
for (int i = 0; i < 2; i++) if (m_PublishExcluded.size () > NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS)
{ {
auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded); LogPrint (eLogError, "NetDb: Couldn't publish our RouterInfo to ", NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS, " closest routers. Try again");
if (floodfill) m_PublishExcluded.clear ();
{ }
uint32_t replyToken;
RAND_bytes ((uint8_t *)&replyToken, 4); auto floodfill = GetClosestFloodfill (i2p::context.GetIdentHash (), m_PublishExcluded);
LogPrint (eLogInfo, "NetDb: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken); if (floodfill)
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken)); {
excluded.insert (floodfill->GetIdentHash ()); uint32_t replyToken;
} RAND_bytes ((uint8_t *)&replyToken, 4);
LogPrint (eLogInfo, "NetDb: Publishing our RouterInfo to ", i2p::data::GetIdentHashAbbreviation(floodfill->GetIdentHash ()), ". reply token=", replyToken);
m_PublishExcluded.insert (floodfill->GetIdentHash ());
m_PublishReplyToken = replyToken;
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken));
} }
} }

View File

@ -41,7 +41,10 @@ namespace data
const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours
const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours
const int NETDB_PUBLISH_INTERVAL = 60 * 40; const int NETDB_PUBLISH_INTERVAL = 60 * 40;
const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15;
const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36 const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36
const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 28); // 0.9.28
/** function for visiting a leaseset stored in a floodfill */ /** function for visiting a leaseset stored in a floodfill */
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor; typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
@ -77,6 +80,7 @@ namespace data
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg); void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg); void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
void HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m); void HandleNTCP2RouterInfoMsg (std::shared_ptr<const I2NPMessage> m);
void HandleDeliveryStatusMsg (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<const RouterInfo> GetRandomRouter () const; std::shared_ptr<const RouterInfo> GetRandomRouter () const;
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const; std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
@ -115,6 +119,8 @@ namespace data
void ClearRouterInfos () { m_RouterInfos.clear (); }; void ClearRouterInfos () { m_RouterInfos.clear (); };
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; };
private: private:
void Load (); void Load ();
@ -162,9 +168,11 @@ namespace data
/** router info we are bootstrapping from or nullptr if we are not currently doing that*/ /** router info we are bootstrapping from or nullptr if we are not currently doing that*/
std::shared_ptr<RouterInfo> m_FloodfillBootstrap; std::shared_ptr<RouterInfo> m_FloodfillBootstrap;
/** true if in hidden mode */ /** true if in hidden mode */
bool m_HiddenMode; bool m_HiddenMode;
std::set<IdentHash> m_PublishExcluded;
uint32_t m_PublishReplyToken = 0;
}; };
extern NetDb netdb; extern NetDb netdb;

View File

@ -19,6 +19,7 @@
#include "version.h" #include "version.h"
#include "Log.h" #include "Log.h"
#include "Family.h" #include "Family.h"
#include "TunnelConfig.h"
#include "RouterContext.h" #include "RouterContext.h"
namespace i2p namespace i2p
@ -41,6 +42,13 @@ namespace i2p
CreateNewRouter (); CreateNewRouter ();
m_Decryptor = m_Keys.CreateDecryptor (nullptr); m_Decryptor = m_Keys.CreateDecryptor (nullptr);
UpdateRouterInfo (); UpdateRouterInfo ();
if (IsECIES ())
{
auto initState = new i2p::crypto::NoiseSymmetricState ();
i2p::tunnel::InitBuildRequestRecordNoiseState (*initState);
initState->MixHash (GetIdentity ()->GetEncryptionPublicKey (), 32); // h = SHA256(h || hepk)
m_InitialNoiseState.reset (initState);
}
} }
void RouterContext::CreateNewRouter () void RouterContext::CreateNewRouter ()
@ -81,7 +89,7 @@ namespace i2p
host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string(); host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string();
if (ssu) if (ssu)
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
} }
if (ipv6) if (ipv6)
{ {
@ -95,7 +103,7 @@ namespace i2p
host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string(); host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string();
if (ssu) if (ssu)
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
} }
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
@ -478,7 +486,7 @@ namespace i2p
if (ssu) if (ssu)
{ {
std::string host = "::1"; // TODO: read host std::string host = "::1"; // TODO: read host
m_RouterInfo.AddSSUAddress (host.c_str (), port, GetIdentHash ()); m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
} }
} }
// NTCP2 // NTCP2
@ -556,31 +564,42 @@ namespace i2p
bool RouterContext::Load () bool RouterContext::Load ()
{ {
std::ifstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ifstream::in | std::ifstream::binary); {
if (!fk.is_open ()) return false; std::ifstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ifstream::in | std::ifstream::binary);
fk.seekg (0, std::ios::end); if (!fk.is_open ()) return false;
size_t len = fk.tellg(); fk.seekg (0, std::ios::end);
fk.seekg (0, std::ios::beg); size_t len = fk.tellg();
fk.seekg (0, std::ios::beg);
if (len == sizeof (i2p::data::Keys)) // old keys file format if (len == sizeof (i2p::data::Keys)) // old keys file format
{ {
i2p::data::Keys keys; i2p::data::Keys keys;
fk.read ((char *)&keys, sizeof (keys)); fk.read ((char *)&keys, sizeof (keys));
m_Keys = keys; m_Keys = keys;
}
else // new keys file format
{
uint8_t * buf = new uint8_t[len];
fk.read ((char *)buf, len);
m_Keys.FromBuffer (buf, len);
delete[] buf;
}
} }
else // new keys file format std::shared_ptr<const i2p::data::IdentityEx> oldIdentity;
if (m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
{ {
uint8_t * buf = new uint8_t[len]; // update keys
fk.read ((char *)buf, len); LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new");
m_Keys.FromBuffer (buf, len); oldIdentity = m_Keys.GetPublic ();
delete[] buf; m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519);
} SaveKeys ();
}
// read NTCP2 keys if available // read NTCP2 keys if available
std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary); std::ifstream n2k (i2p::fs::DataDirPath (NTCP2_KEYS), std::ifstream::in | std::ifstream::binary);
if (n2k) if (n2k)
{ {
n2k.seekg (0, std::ios::end); n2k.seekg (0, std::ios::end);
len = n2k.tellg(); size_t len = n2k.tellg();
n2k.seekg (0, std::ios::beg); n2k.seekg (0, std::ios::beg);
if (len == sizeof (NTCP2PrivateKeys)) if (len == sizeof (NTCP2PrivateKeys))
{ {
@ -590,17 +609,15 @@ namespace i2p
n2k.close (); n2k.close ();
} }
// read RouterInfo // read RouterInfo
m_RouterInfo.SetRouterIdentity (GetIdentity ()); m_RouterInfo.SetRouterIdentity (oldIdentity ? oldIdentity : GetIdentity ());
i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO)); i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO));
if (!routerInfo.IsUnreachable ()) // router.info looks good if (!routerInfo.IsUnreachable ()) // router.info looks good
{ {
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
if (oldIdentity)
m_RouterInfo.SetRouterIdentity (GetIdentity ()); // from new keys
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION); m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
m_RouterInfo.SetProperty ("router.version", I2P_VERSION); m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
// Migration to 0.9.24. TODO: remove later
m_RouterInfo.DeleteProperty ("coreVersion");
m_RouterInfo.DeleteProperty ("stat_uptime");
} }
else else
{ {
@ -645,6 +662,15 @@ namespace i2p
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len))); i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len)));
} }
bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len)
{
auto msg = CreateI2NPMessage (typeID, payload, len);
if (!msg) return false;
i2p::HandleI2NPMessage (msg);
return true;
}
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{ {
std::unique_lock<std::mutex> l(m_GarlicMutex); std::unique_lock<std::mutex> l(m_GarlicMutex);
@ -653,8 +679,13 @@ namespace i2p
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
{ {
std::unique_lock<std::mutex> l(m_GarlicMutex); if (i2p::data::netdb.GetPublishReplyToken () == bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET))
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg); i2p::data::netdb.PostI2NPMsg (msg);
else
{
std::unique_lock<std::mutex> l(m_GarlicMutex);
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
}
} }
void RouterContext::CleanupDestination () void RouterContext::CleanupDestination ()
@ -673,9 +704,32 @@ namespace i2p
return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false; return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false;
} }
bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx)
{ {
return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, false) : false; if (!m_Decryptor) return false;
if (IsECIES ())
{
if (!m_InitialNoiseState) return false;
// m_InitialNoiseState is h = SHA256(h || hepk)
m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState));
m_CurrentNoiseState->MixHash (encrypted, 32); // h = SHA256(h || sepk)
uint8_t sharedSecret[32];
m_Decryptor->Decrypt (encrypted, sharedSecret, ctx, false);
m_CurrentNoiseState->MixKey (sharedSecret);
encrypted += 32;
uint8_t nonce[12];
memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE,
m_CurrentNoiseState->m_H, 32, m_CurrentNoiseState->m_CK + 32, nonce, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, false)) // decrypt
{
LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed");
return false;
}
m_CurrentNoiseState->MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
return true;
}
else
return m_Decryptor->Decrypt (encrypted, data, ctx, false);
} }
i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()

View File

@ -84,7 +84,7 @@ namespace i2p
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; }; void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
int GetNetID () const { return m_NetID; }; int GetNetID () const { return m_NetID; };
void SetNetID (int netID) { m_NetID = netID; }; void SetNetID (int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const; bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx);
void UpdatePort (int port); // called from Daemon void UpdatePort (int port); // called from Daemon
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
@ -109,7 +109,9 @@ namespace i2p
bool SupportsV4 () const { return m_RouterInfo.IsV4 (); }; bool SupportsV4 () const { return m_RouterInfo.IsV4 (); };
void SetSupportsV6 (bool supportsV6); void SetSupportsV6 (bool supportsV6);
void SetSupportsV4 (bool supportsV4); void SetSupportsV4 (bool supportsV4);
bool IsECIES () const { return GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
std::unique_ptr<i2p::crypto::NoiseSymmetricState>& GetCurrentNoiseState () { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
void UpdateStats (); void UpdateStats ();
void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing
@ -133,7 +135,7 @@ namespace i2p
// implements GarlicDestination // implements GarlicDestination
void HandleI2NPMessage (const uint8_t * buf, size_t len); void HandleI2NPMessage (const uint8_t * buf, size_t len);
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) { return false; }; // not implemented bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len);
private: private:
@ -160,6 +162,8 @@ namespace i2p
std::mutex m_GarlicMutex; std::mutex m_GarlicMutex;
std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys; std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;
std::unique_ptr<i2p::crypto::X25519Keys> m_StaticKeys; std::unique_ptr<i2p::crypto::X25519Keys> m_StaticKeys;
// for ECIESx25519
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_InitialNoiseState, m_CurrentNoiseState;
}; };
extern RouterContext context; extern RouterContext context;

View File

@ -719,7 +719,10 @@ namespace data
addr->date = 0; addr->date = 0;
addr->ssu.reset (new SSUExt ()); addr->ssu.reset (new SSUExt ());
addr->ssu->mtu = mtu; addr->ssu->mtu = mtu;
memcpy (addr->ssu->key, key, 32); if (key)
memcpy (addr->ssu->key, key, 32);
else
RAND_bytes (addr->ssu->key, 32);
for (const auto& it: *m_Addresses) // don't insert same address twice for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return; if (*it == *addr) return;
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4; m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
@ -945,5 +948,12 @@ namespace data
if (encryptor) if (encryptor)
encryptor->Encrypt (data, encrypted, ctx, true); encryptor->Encrypt (data, encrypted, ctx, true);
} }
bool RouterInfo::IsEligibleFloodfill () const
{
// floodfill must be reachable, >= 0.9.28 and not DSA
return IsReachable () && m_Version >= NETDB_MIN_FLOODFILL_VERSION &&
GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1;
}
} }
} }

View File

@ -184,7 +184,8 @@ namespace data
bool IsHidden () const { return m_Caps & eHidden; }; bool IsHidden () const { return m_Caps & eHidden; };
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; }; bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; }; bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
bool IsEligibleFloodfill () const;
uint8_t GetCaps () const { return m_Caps; }; uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps); void SetCaps (uint8_t caps);
void SetCaps (const char * caps); void SetCaps (const char * caps);

View File

@ -13,6 +13,10 @@
#include "NetDb.hpp" #include "NetDb.hpp"
#include "SSU.h" #include "SSU.h"
#ifdef _WIN32
#include <boost/winapi/error_codes.hpp>
#endif
namespace i2p namespace i2p
{ {
namespace transport namespace transport
@ -247,11 +251,17 @@ namespace transport
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
{ {
if (!ecode || if (!ecode
ecode == boost::asio::error::connection_refused || || ecode == boost::asio::error::connection_refused
ecode == boost::asio::error::connection_reset || || ecode == boost::asio::error::connection_reset
ecode == boost::asio::error::network_unreachable || || ecode == boost::asio::error::network_unreachable
ecode == boost::asio::error::host_unreachable) || ecode == boost::asio::error::host_unreachable
#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO
|| ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_
|| ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_
|| ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_
#endif
)
// just try continue reading when received ICMP response otherwise socket can crash, // just try continue reading when received ICMP response otherwise socket can crash,
// but better to find out which host were sent it and mark that router as unreachable // but better to find out which host were sent it and mark that router as unreachable
{ {
@ -300,11 +310,17 @@ namespace transport
void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
{ {
if (!ecode || if (!ecode
ecode == boost::asio::error::connection_refused || || ecode == boost::asio::error::connection_refused
ecode == boost::asio::error::connection_reset || || ecode == boost::asio::error::connection_reset
ecode == boost::asio::error::network_unreachable || || ecode == boost::asio::error::network_unreachable
ecode == boost::asio::error::host_unreachable) || ecode == boost::asio::error::host_unreachable
#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO
|| ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_
|| ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_
|| ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_
#endif
)
// just try continue reading when received ICMP response otherwise socket can crash, // just try continue reading when received ICMP response otherwise socket can crash,
// but better to find out which host were sent it and mark that router as unreachable // but better to find out which host were sent it and mark that router as unreachable
{ {

View File

@ -30,14 +30,15 @@ namespace transport
if (router) if (router)
{ {
// we are client // we are client
auto address = router->GetSSUAddress (false); auto address = IsV6 () ? router->GetSSUV6Address () : router->GetSSUAddress (true);
if (address) m_IntroKey = address->ssu->key; if (address) m_IntroKey = address->ssu->key;
m_Data.AdjustPacketSize (router); // mtu m_Data.AdjustPacketSize (router); // mtu
} }
else else
{ {
// we are server // we are server
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false); auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () :
i2p::context.GetRouterInfo ().GetSSUAddress (true);
if (address) m_IntroKey = address->ssu->key; if (address) m_IntroKey = address->ssu->key;
} }
m_CreationTime = i2p::util::GetSecondsSinceEpoch (); m_CreationTime = i2p::util::GetSecondsSinceEpoch ();

View File

@ -21,10 +21,18 @@ namespace stream
{ {
void SendBufferQueue::Add (const uint8_t * buf, size_t len, SendHandler handler) void SendBufferQueue::Add (const uint8_t * buf, size_t len, SendHandler handler)
{ {
m_Buffers.push_back (std::make_shared<SendBuffer>(buf, len, handler)); Add (std::make_shared<SendBuffer>(buf, len, handler));
m_Size += len;
} }
void SendBufferQueue::Add (std::shared_ptr<SendBuffer> buf)
{
if (buf)
{
m_Buffers.push_back (buf);
m_Size += buf->len;
}
}
size_t SendBufferQueue::Get (uint8_t * buf, size_t len) size_t SendBufferQueue::Get (uint8_t * buf, size_t len)
{ {
size_t offset = 0; size_t offset = 0;
@ -325,7 +333,7 @@ namespace stream
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
{ {
uint8_t signature[256]; uint8_t signature[256];
auto signatureLen = m_RemoteIdentity->GetSignatureLen (); auto signatureLen = m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : m_RemoteIdentity->GetSignatureLen ();
if(signatureLen <= sizeof(signature)) if(signatureLen <= sizeof(signature))
{ {
memcpy (signature, optionData, signatureLen); memcpy (signature, optionData, signatureLen);
@ -756,7 +764,7 @@ namespace stream
return; return;
} }
} }
if (!m_RoutingSession || !m_RoutingSession->GetOwner ()) // expired and detached if (!m_RoutingSession || !m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ()) // expired and detached or new session sent
m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true);
if (!m_CurrentOutboundTunnel && m_RoutingSession) // first message to send if (!m_CurrentOutboundTunnel && m_RoutingSession) // first message to send
{ {

View File

@ -111,6 +111,11 @@ namespace stream
buf = new uint8_t[len]; buf = new uint8_t[len];
memcpy (buf, b, len); memcpy (buf, b, len);
} }
SendBuffer (size_t l): // creat empty buffer
len(l), offset (0)
{
buf = new uint8_t[len];
}
~SendBuffer () ~SendBuffer ()
{ {
delete[] buf; delete[] buf;
@ -129,6 +134,7 @@ namespace stream
~SendBufferQueue () { CleanUp (); }; ~SendBufferQueue () { CleanUp (); };
void Add (const uint8_t * buf, size_t len, SendHandler handler); void Add (const uint8_t * buf, size_t len, SendHandler handler);
void Add (std::shared_ptr<SendBuffer> buf);
size_t Get (uint8_t * buf, size_t len); size_t Get (uint8_t * buf, size_t len);
size_t GetSize () const { return m_Size; }; size_t GetSize () const { return m_Size; };
bool IsEmpty () const { return m_Buffers.empty (); }; bool IsEmpty () const { return m_Buffers.empty (); };

View File

@ -19,7 +19,7 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Timestamp.h" #include "Timestamp.h"
#ifdef WIN32 #ifdef _WIN32
#ifndef _WIN64 #ifndef _WIN64
#define _USE_32BIT_TIME_T #define _USE_32BIT_TIME_T
#endif #endif

View File

@ -124,7 +124,7 @@ namespace tunnel
uint8_t nonce[12]; uint8_t nonce[12];
memset (nonce, 0, 12); memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (record, TUNNEL_BUILD_RECORD_SIZE - 16, if (!i2p::crypto::AEADChaCha20Poly1305 (record, TUNNEL_BUILD_RECORD_SIZE - 16,
hop->h, 32, hop->ck, nonce, record, TUNNEL_BUILD_RECORD_SIZE - 16, false)) // decrypt hop->m_H, 32, hop->m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE - 16, false)) // decrypt
{ {
LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed");
return false; return false;
@ -474,7 +474,7 @@ namespace tunnel
{ {
std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready
uint64_t lastTs = 0; uint64_t lastTs = 0, lastPoolsTs = 0;
while (m_IsRunning) while (m_IsRunning)
{ {
try try
@ -535,12 +535,20 @@ namespace tunnel
while (msg); while (msg);
} }
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); if (i2p::transport::transports.IsOnline())
if (ts - lastTs >= 15 && i2p::transport::transports.IsOnline()) // manage tunnels every 15 seconds
{ {
ManageTunnels (); uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
lastTs = ts; if (ts - lastTs >= 15) // manage tunnels every 15 seconds
} {
ManageTunnels ();
lastTs = ts;
}
if (ts - lastPoolsTs >= 5) // manage pools every 5 seconds
{
ManageTunnelPools (ts);
lastPoolsTs = ts;
}
}
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
@ -582,7 +590,6 @@ namespace tunnel
ManageInboundTunnels (); ManageInboundTunnels ();
ManageOutboundTunnels (); ManageOutboundTunnels ();
ManageTransitTunnels (); ManageTransitTunnels ();
ManageTunnelPools ();
} }
void Tunnels::ManagePendingTunnels () void Tunnels::ManagePendingTunnels ()
@ -794,16 +801,13 @@ namespace tunnel
} }
} }
void Tunnels::ManageTunnelPools () void Tunnels::ManageTunnelPools (uint64_t ts)
{ {
std::unique_lock<std::mutex> l(m_PoolsMutex); std::unique_lock<std::mutex> l(m_PoolsMutex);
for (auto& pool : m_Pools) for (auto& pool : m_Pools)
{ {
if (pool && pool->IsActive ()) if (pool && pool->IsActive ())
{ pool->ManageTunnels (ts);
pool->CreateTunnels ();
pool->TestTunnels ();
}
} }
} }

View File

@ -230,7 +230,7 @@ namespace tunnel
void ManagePendingTunnels (); void ManagePendingTunnels ();
template<class PendingTunnels> template<class PendingTunnels>
void ManagePendingTunnels (PendingTunnels& pendingTunnels); void ManagePendingTunnels (PendingTunnels& pendingTunnels);
void ManageTunnelPools (); void ManageTunnelPools (uint64_t ts);
std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel (); std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel ();
std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel (); std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel ();
@ -249,7 +249,7 @@ namespace tunnel
std::list<std::shared_ptr<TunnelPool>> m_Pools; std::list<std::shared_ptr<TunnelPool>> m_Pools;
std::shared_ptr<TunnelPool> m_ExploratoryPool; std::shared_ptr<TunnelPool> m_ExploratoryPool;
i2p::util::Queue<std::shared_ptr<I2NPMessage> > m_Queue; i2p::util::Queue<std::shared_ptr<I2NPMessage> > m_Queue;
// some stats // some stats
int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations; int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations;

View File

@ -10,7 +10,6 @@
#include <memory> #include <memory>
#include <openssl/rand.h> #include <openssl/rand.h>
#include <openssl/sha.h> #include <openssl/sha.h>
#include "Crypto.h"
#include "Log.h" #include "Log.h"
#include "Transports.h" #include "Transports.h"
#include "Timestamp.h" #include "Timestamp.h"
@ -128,9 +127,7 @@ namespace tunnel
void TunnelHopConfig::EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor, void TunnelHopConfig::EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx) const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx)
{ {
static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars InitBuildRequestRecordNoiseState (*this);
memcpy (ck, protocolName, 32); // ck = h = protocol_name || 0
SHA256 (ck, 32, h); // h = SHA256(h);
uint8_t hepk[32]; uint8_t hepk[32];
encryptor->Encrypt (nullptr, hepk, nullptr, false); encryptor->Encrypt (nullptr, hepk, nullptr, false);
MixHash (hepk, 32); // h = SHA256(h || hepk) MixHash (hepk, 32); // h = SHA256(h || hepk)
@ -140,13 +137,11 @@ namespace tunnel
encrypted += 32; encrypted += 32;
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
ephemeralKeys->Agree (hepk, sharedSecret); // x25519(sesk, hepk) ephemeralKeys->Agree (hepk, sharedSecret); // x25519(sesk, hepk)
uint8_t keydata[64]; MixKey (sharedSecret);
i2p::crypto::HKDF (ck, sharedSecret, 32, "", keydata);
memcpy (ck, keydata, 32);
uint8_t nonce[12]; uint8_t nonce[12];
memset (nonce, 0, 12); memset (nonce, 0, 12);
if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, h, 32, if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, m_H, 32,
keydata + 32, nonce, encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16, true)) // encrypt m_CK + 32, nonce, encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16, true)) // encrypt
{ {
LogPrint (eLogWarning, "Tunnel: Plaintext AEAD encryption failed"); LogPrint (eLogWarning, "Tunnel: Plaintext AEAD encryption failed");
return; return;
@ -154,13 +149,16 @@ namespace tunnel
MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext) MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
} }
void TunnelHopConfig::MixHash (const uint8_t * buf, size_t len) void InitBuildRequestRecordNoiseState (i2p::crypto::NoiseSymmetricState& state)
{ {
SHA256_CTX ctx; static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars
SHA256_Init (&ctx); static const uint8_t hh[32] =
SHA256_Update (&ctx, h, 32); {
SHA256_Update (&ctx, buf, len); 0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1,
SHA256_Final (h, &ctx); 0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, 0x54
} }; // SHA256 (protocol_name || 0)
memcpy (state.m_CK, protocolName, 32); // ck = h = protocol_name || 0
memcpy (state.m_H, hh, 32); // h = SHA256(h)
}
} }
} }

View File

@ -12,12 +12,13 @@
#include <vector> #include <vector>
#include "Identity.h" #include "Identity.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "Crypto.h"
namespace i2p namespace i2p
{ {
namespace tunnel namespace tunnel
{ {
struct TunnelHopConfig struct TunnelHopConfig: public i2p::crypto::NoiseSymmetricState
{ {
std::shared_ptr<const i2p::data::IdentityEx> ident; std::shared_ptr<const i2p::data::IdentityEx> ident;
i2p::data::IdentHash nextIdent; i2p::data::IdentHash nextIdent;
@ -30,7 +31,6 @@ namespace tunnel
TunnelHopConfig * next, * prev; TunnelHopConfig * next, * prev;
int recordIndex; // record # in tunnel build message int recordIndex; // record # in tunnel build message
uint8_t ck[32], h[32]; // for ECIES
TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r); TunnelHopConfig (std::shared_ptr<const i2p::data::IdentityEx> r);
@ -39,13 +39,14 @@ namespace tunnel
void SetNext (TunnelHopConfig * n); void SetNext (TunnelHopConfig * n);
void SetPrev (TunnelHopConfig * p); void SetPrev (TunnelHopConfig * p);
bool IsECIES () const { return ident->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET; }; bool IsECIES () const { return ident->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx); void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx);
void EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor, void EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx); const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx);
void MixHash (const uint8_t * buf, size_t len);
}; };
void InitBuildRequestRecordNoiseState (i2p::crypto::NoiseSymmetricState& state);
class TunnelConfig class TunnelConfig
{ {
public: public:

View File

@ -26,9 +26,10 @@ namespace tunnel
{ {
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels): TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true), m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
m_CustomPeerSelector(nullptr) m_IsActive (true), m_CustomPeerSelector(nullptr)
{ {
m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + rand () % TUNNEL_POOL_MANAGE_INTERVAL;
} }
TunnelPool::~TunnelPool () TunnelPool::~TunnelPool ()
@ -118,7 +119,6 @@ namespace tunnel
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
m_OutboundTunnels.insert (createdTunnel); m_OutboundTunnels.insert (createdTunnel);
} }
//CreatePairedInboundTunnel (createdTunnel);
} }
void TunnelPool::TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel) void TunnelPool::TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel)
@ -235,6 +235,15 @@ namespace tunnel
for (const auto& it : m_InboundTunnels) for (const auto& it : m_InboundTunnels)
if (it->IsEstablished ()) num++; if (it->IsEstablished ()) num++;
} }
if (!num && !m_OutboundTunnels.empty ())
{
for (auto it: m_OutboundTunnels)
{
CreatePairedInboundTunnel (it);
num++;
if (num >= m_NumInboundTunnels) break;
}
}
for (int i = num; i < m_NumInboundTunnels; i++) for (int i = num; i < m_NumInboundTunnels; i++)
CreateInboundTunnel (); CreateInboundTunnel ();
@ -313,6 +322,16 @@ namespace tunnel
} }
} }
void TunnelPool::ManageTunnels (uint64_t ts)
{
if (ts > m_NextManageTime)
{
CreateTunnels ();
TestTunnels ();
m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (rand () % TUNNEL_POOL_MANAGE_INTERVAL)/2;
}
}
void TunnelPool::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) void TunnelPool::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{ {
if (m_LocalDestination) if (m_LocalDestination)
@ -342,17 +361,24 @@ namespace tunnel
} }
if (found) if (found)
{ {
// restore from test failed state if any
if (test.first->GetState () == eTunnelStateTestFailed)
test.first->SetState (eTunnelStateEstablished);
if (test.second->GetState () == eTunnelStateTestFailed)
test.second->SetState (eTunnelStateEstablished);
uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp; uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp;
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", dlt, " milliseconds"); LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", dlt, " milliseconds");
// update latency
uint64_t latency = dlt / 2; uint64_t latency = dlt / 2;
test.first->AddLatencySample(latency); // restore from test failed state if any
test.second->AddLatencySample(latency); if (test.first)
{
if (test.first->GetState () == eTunnelStateTestFailed)
test.first->SetState (eTunnelStateEstablished);
// update latency
test.first->AddLatencySample(latency);
}
if (test.second)
{
if (test.second->GetState () == eTunnelStateTestFailed)
test.second->SetState (eTunnelStateEstablished);
// update latency
test.second->AddLatencySample(latency);
}
} }
else else
{ {

View File

@ -27,6 +27,8 @@ namespace i2p
{ {
namespace tunnel namespace tunnel
{ {
const int TUNNEL_POOL_MANAGE_INTERVAL = 10; // in seconds
class Tunnel; class Tunnel;
class InboundTunnel; class InboundTunnel;
class OutboundTunnel; class OutboundTunnel;
@ -69,6 +71,7 @@ namespace tunnel
std::shared_ptr<InboundTunnel> GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded = nullptr) const; std::shared_ptr<InboundTunnel> GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded = nullptr) const;
std::shared_ptr<OutboundTunnel> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const; std::shared_ptr<OutboundTunnel> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const;
void TestTunnels (); void TestTunnels ();
void ManageTunnels (uint64_t ts);
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg); void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg); void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg);
@ -123,6 +126,7 @@ namespace tunnel
mutable std::mutex m_TestsMutex; mutable std::mutex m_TestsMutex;
std::map<uint32_t, std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > m_Tests; std::map<uint32_t, std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > m_Tests;
bool m_IsActive; bool m_IsActive;
uint64_t m_NextManageTime; // in seconds
std::mutex m_CustomPeerSelectorMutex; std::mutex m_CustomPeerSelectorMutex;
ITunnelPeerSelector * m_CustomPeerSelector; ITunnelPeerSelector * m_CustomPeerSelector;

View File

@ -37,7 +37,10 @@ namespace api
i2p::fs::Init(); i2p::fs::Init();
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
i2p::crypto::InitCrypto (precomputation); bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
bool avx; i2p::config::GetOption("cpuext.avx", avx);
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt);
int netID; i2p::config::GetOption("netid", netID); int netID; i2p::config::GetOption("netid", netID);
i2p::context.SetNetID (netID); i2p::context.SetNetID (netID);

View File

@ -13,7 +13,7 @@
#include "util.h" #include "util.h"
#include "Log.h" #include "Log.h"
#ifdef WIN32 #ifdef _WIN32
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
@ -56,7 +56,7 @@ int inet_pton_xp(int af, const char *src, void *dst)
} }
return 0; return 0;
} }
#else /* !WIN32 => UNIX */ #else /* !_WIN32 => UNIX */
#include <sys/types.h> #include <sys/types.h>
#include <ifaddrs.h> #include <ifaddrs.h>
#endif #endif
@ -109,7 +109,7 @@ namespace util
namespace net namespace net
{ {
#ifdef WIN32 #ifdef _WIN32
bool IsWindowsXPorLater() bool IsWindowsXPorLater()
{ {
static bool isRequested = false; static bool isRequested = false;
@ -333,13 +333,13 @@ namespace net
return mtu; return mtu;
} }
#endif // WIN32 #endif // _WIN32
int GetMTU(const boost::asio::ip::address& localAddress) int GetMTU(const boost::asio::ip::address& localAddress)
{ {
int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU
#ifdef WIN32 #ifdef _WIN32
return GetMTUWindows(localAddress, fallback); return GetMTUWindows(localAddress, fallback);
#else #else
return GetMTUUnix(localAddress, fallback); return GetMTUUnix(localAddress, fallback);
@ -349,7 +349,7 @@ namespace net
const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6) const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6)
{ {
#ifdef WIN32 #ifdef _WIN32
LogPrint(eLogError, "NetIface: cannot get address by interface name, not implemented on WIN32"); LogPrint(eLogError, "NetIface: cannot get address by interface name, not implemented on WIN32");
if(ipv6) if(ipv6)
return boost::asio::ip::address::from_string("::1"); return boost::asio::ip::address::from_string("::1");

View File

@ -16,7 +16,7 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 33 #define I2PD_VERSION_MINOR 35
#define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
@ -30,7 +30,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 47 #define I2P_VERSION_MICRO 48
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@ -454,17 +454,18 @@ namespace client
auto it = m_Addresses.find (name); auto it = m_Addresses.find (name);
if (it != m_Addresses.end ()) // already exists ? if (it != m_Addresses.end ()) // already exists ?
{ {
if (it->second->IsIdentHash () && it->second->identHash != ident->GetIdentHash ()) // address changed? if (it->second->IsIdentHash () && it->second->identHash != ident->GetIdentHash () && // address changed?
ident->GetSigningKeyType () != i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) // don't replace by DSA
{ {
it->second->identHash = ident->GetIdentHash (); it->second->identHash = ident->GetIdentHash ();
m_Storage->AddAddress (ident); m_Storage->AddAddress (ident);
m_Storage->RemoveAddress (it->second->identHash);
LogPrint (eLogInfo, "Addressbook: updated host: ", name); LogPrint (eLogInfo, "Addressbook: updated host: ", name);
} }
} }
else else
{ {
//m_Addresses.emplace (name, std::make_shared<Address>(ident->GetIdentHash ())); m_Addresses.emplace (name, std::make_shared<Address>(ident->GetIdentHash ()));
m_Addresses[name] = std::make_shared<Address>(ident->GetIdentHash ()); // for gcc 4.7
m_Storage->AddAddress (ident); m_Storage->AddAddress (ident);
if (is_update) if (is_update)
LogPrint (eLogInfo, "Addressbook: added new host: ", name); LogPrint (eLogInfo, "Addressbook: added new host: ", name);

View File

@ -590,7 +590,8 @@ namespace client
localDestination = CreateNewMatchedTunnelDestination(k, dest, &options); localDestination = CreateNewMatchedTunnelDestination(k, dest, &options);
else else
localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options); localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options);
destinations[keys] = localDestination; if (keys != "transient")
destinations[keys] = localDestination;
} }
} }
} }

View File

@ -30,6 +30,12 @@ namespace client
{ {
} }
void I2CPDestination::Stop ()
{
LeaseSetDestination::Stop ();
m_Owner = nullptr;
}
void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key) void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key)
{ {
m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_Identity->GetCryptoKeyType (), key); m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_Identity->GetCryptoKeyType (), key);
@ -46,7 +52,7 @@ namespace client
bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const
{ {
if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET && m_ECIESx25519Decryptor) if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor)
return m_ECIESx25519Decryptor->Decrypt (encrypted, data, ctx, true); return m_ECIESx25519Decryptor->Decrypt (encrypted, data, ctx, true);
if (m_Decryptor) if (m_Decryptor)
return m_Decryptor->Decrypt (encrypted, data, ctx, true); return m_Decryptor->Decrypt (encrypted, data, ctx, true);
@ -57,14 +63,14 @@ namespace client
const uint8_t * I2CPDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const const uint8_t * I2CPDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const
{ {
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET && m_ECIESx25519Decryptor) if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor)
return m_ECIESx25519Decryptor->GetPubicKey (); return m_ECIESx25519Decryptor->GetPubicKey ();
return nullptr; return nullptr;
} }
bool I2CPDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const bool I2CPDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const
{ {
return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET ? (bool)m_ECIESx25519Decryptor : m_EncryptionKeyType == keyType; return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519Decryptor : m_EncryptionKeyType == keyType;
} }
@ -72,7 +78,8 @@ namespace client
{ {
uint32_t length = bufbe32toh (buf); uint32_t length = bufbe32toh (buf);
if (length > len - 4) length = len - 4; if (length > len - 4) length = len - 4;
m_Owner->SendMessagePayloadMessage (buf + 4, length); if (m_Owner)
m_Owner->SendMessagePayloadMessage (buf + 4, length);
} }
void I2CPDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels) void I2CPDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
@ -82,9 +89,16 @@ namespace client
m_LeaseSetExpirationTime = ls.GetExpirationTime (); m_LeaseSetExpirationTime = ls.GetExpirationTime ();
uint8_t * leases = ls.GetLeases (); uint8_t * leases = ls.GetLeases ();
leases[-1] = tunnels.size (); leases[-1] = tunnels.size ();
htobe16buf (leases - 3, m_Owner->GetSessionID ()); if (m_Owner)
size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size (); {
m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l); uint16_t sessionID = m_Owner->GetSessionID ();
if (sessionID != 0xFFFF)
{
htobe16buf (leases - 3, sessionID);
size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size ();
m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l);
}
}
} }
void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len) void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len)
@ -119,7 +133,8 @@ namespace client
[s, msg, remote, nonce]() [s, msg, remote, nonce]()
{ {
bool sent = s->SendMsg (msg, remote); bool sent = s->SendMsg (msg, remote);
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure); if (s->m_Owner)
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
}); });
} }
else else
@ -130,9 +145,10 @@ namespace client
if (ls) if (ls)
{ {
bool sent = s->SendMsg (msg, ls); bool sent = s->SendMsg (msg, ls);
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure); if (s->m_Owner)
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
} }
else else if (s->m_Owner)
s->m_Owner->SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLeaseSet); s->m_Owner->SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLeaseSet);
}); });
} }
@ -227,12 +243,13 @@ namespace client
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket): I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket):
m_Owner (owner), m_Socket (socket), m_SessionID (0xFFFF), m_Owner (owner), m_Socket (socket), m_SessionID (0xFFFF),
m_MessageID (0), m_IsSendAccepted (true) m_MessageID (0), m_IsSendAccepted (true), m_IsSending (false)
{ {
} }
I2CPSession::~I2CPSession () I2CPSession::~I2CPSession ()
{ {
Terminate ();
} }
void I2CPSession::Start () void I2CPSession::Start ()
@ -343,40 +360,76 @@ namespace client
m_Socket->close (); m_Socket->close ();
m_Socket = nullptr; m_Socket = nullptr;
} }
m_Owner.RemoveSession (GetSessionID ()); if (!m_SendQueue.IsEmpty ())
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " terminated"); m_SendQueue.CleanUp ();
if (m_SessionID != 0xFFFF)
{
m_Owner.RemoveSession (GetSessionID ());
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " terminated");
m_SessionID = 0xFFFF;
}
} }
void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len) void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len)
{ {
if (len > I2CP_MAX_MESSAGE_LENGTH) auto l = len + I2CP_HEADER_SIZE;
if (l > I2CP_MAX_MESSAGE_LENGTH)
{ {
LogPrint (eLogError, "I2CP: Message to send is too long ", len); LogPrint (eLogError, "I2CP: Message to send is too long ", l);
return; return;
} }
auto socket = m_Socket; auto sendBuf = m_IsSending ? std::make_shared<i2p::stream::SendBuffer> (l) : nullptr;
if (socket) uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer;
htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len);
buf[I2CP_HEADER_TYPE_OFFSET] = type;
memcpy (buf + I2CP_HEADER_SIZE, payload, len);
if (sendBuf)
{
if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE)
m_SendQueue.Add (sendBuf);
else
{
LogPrint (eLogWarning, "I2CP: send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE);
return;
}
}
else
{ {
auto l = len + I2CP_HEADER_SIZE; auto socket = m_Socket;
uint8_t * buf = new uint8_t[l]; if (socket)
htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len); {
buf[I2CP_HEADER_TYPE_OFFSET] = type; m_IsSending = true;
memcpy (buf + I2CP_HEADER_SIZE, payload, len); boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l),
boost::asio::async_write (*socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (), boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent,
std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (), shared_from_this (), std::placeholders::_1, std::placeholders::_2));
std::placeholders::_1, std::placeholders::_2, buf)); }
}
}
void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
else if (!m_SendQueue.IsEmpty ())
{
auto socket = m_Socket;
if (socket)
{
auto len = m_SendQueue.Get (m_SendBuffer, I2CP_MAX_MESSAGE_LENGTH);
boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, len),
boost::asio::transfer_all (),std::bind(&I2CPSession::HandleI2CPMessageSent,
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
else
m_IsSending = false;
} }
else else
LogPrint (eLogError, "I2CP: Can't write to the socket"); m_IsSending = false;
} }
void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf)
{
delete[] buf;
if (ecode && ecode != boost::asio::error::operation_aborted)
Terminate ();
}
std::string I2CPSession::ExtractString (const uint8_t * buf, size_t len) std::string I2CPSession::ExtractString (const uint8_t * buf, size_t len)
{ {
uint8_t l = buf[0]; uint8_t l = buf[0];
@ -492,11 +545,7 @@ namespace client
{ {
SendSessionStatusMessage (0); // destroy SendSessionStatusMessage (0); // destroy
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " destroyed"); LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " destroyed");
if (m_Destination) Terminate ();
{
m_Destination->Stop ();
m_Destination = 0;
}
} }
void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len) void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len)
@ -621,7 +670,7 @@ namespace client
uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type
uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length
if (offset + keyLen > len) return; if (offset + keyLen > len) return;
if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
m_Destination->SetECIESx25519EncryptionPrivateKey (buf + offset); m_Destination->SetECIESx25519EncryptionPrivateKey (buf + offset);
else else
{ {
@ -807,16 +856,40 @@ namespace client
{ {
// we don't use SendI2CPMessage to eliminate additional copy // we don't use SendI2CPMessage to eliminate additional copy
auto l = len + 10 + I2CP_HEADER_SIZE; auto l = len + 10 + I2CP_HEADER_SIZE;
uint8_t * buf = new uint8_t[l]; if (l > I2CP_MAX_MESSAGE_LENGTH)
{
LogPrint (eLogError, "I2CP: Message to send is too long ", l);
return;
}
auto sendBuf = m_IsSending ? std::make_shared<i2p::stream::SendBuffer> (l) : nullptr;
uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer;
htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len + 10); htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len + 10);
buf[I2CP_HEADER_TYPE_OFFSET] = I2CP_MESSAGE_PAYLOAD_MESSAGE; buf[I2CP_HEADER_TYPE_OFFSET] = I2CP_MESSAGE_PAYLOAD_MESSAGE;
htobe16buf (buf + I2CP_HEADER_SIZE, m_SessionID); htobe16buf (buf + I2CP_HEADER_SIZE, m_SessionID);
htobe32buf (buf + I2CP_HEADER_SIZE + 2, m_MessageID++); htobe32buf (buf + I2CP_HEADER_SIZE + 2, m_MessageID++);
htobe32buf (buf + I2CP_HEADER_SIZE + 6, len); htobe32buf (buf + I2CP_HEADER_SIZE + 6, len);
memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len); memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len);
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, l), boost::asio::transfer_all (), if (sendBuf)
std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (), {
std::placeholders::_1, std::placeholders::_2, buf)); if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE)
m_SendQueue.Add (sendBuf);
else
{
LogPrint (eLogWarning, "I2CP: send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE);
return;
}
}
else
{
auto socket = m_Socket;
if (socket)
{
m_IsSending = true;
boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l),
boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent,
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
}
}
} }
I2CPServer::I2CPServer (const std::string& interface, int port, bool isSingleThread): I2CPServer::I2CPServer (const std::string& interface, int port, bool isSingleThread):

View File

@ -17,6 +17,7 @@
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "util.h" #include "util.h"
#include "Destination.h" #include "Destination.h"
#include "Streaming.h"
namespace i2p namespace i2p
{ {
@ -25,6 +26,7 @@ namespace client
const uint8_t I2CP_PROTOCOL_BYTE = 0x2A; const uint8_t I2CP_PROTOCOL_BYTE = 0x2A;
const size_t I2CP_SESSION_BUFFER_SIZE = 4096; const size_t I2CP_SESSION_BUFFER_SIZE = 4096;
const size_t I2CP_MAX_MESSAGE_LENGTH = 65535; const size_t I2CP_MAX_MESSAGE_LENGTH = 65535;
const size_t I2CP_MAX_SEND_QUEUE_SIZE = 1024*1024; // in bytes, 1M
const size_t I2CP_HEADER_LENGTH_OFFSET = 0; const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4; const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4;
@ -70,6 +72,8 @@ namespace client
I2CPDestination (boost::asio::io_service& service, std::shared_ptr<I2CPSession> owner, I2CPDestination (boost::asio::io_service& service, std::shared_ptr<I2CPSession> owner,
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params); std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params);
~I2CPDestination () {}; ~I2CPDestination () {};
void Stop ();
void SetEncryptionPrivateKey (const uint8_t * key); void SetEncryptionPrivateKey (const uint8_t * key);
void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; }; void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; };
@ -166,12 +170,12 @@ namespace client
void HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleMessage (); void HandleMessage ();
void Terminate (); void Terminate ();
void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf); void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
std::string ExtractString (const uint8_t * buf, size_t len); std::string ExtractString (const uint8_t * buf, size_t len);
size_t PutString (uint8_t * buf, size_t len, const std::string& str); size_t PutString (uint8_t * buf, size_t len, const std::string& str);
void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping); void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping);
void SendSessionStatusMessage (uint8_t status); void SendSessionStatusMessage (uint8_t status);
void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity); void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity);
@ -186,6 +190,11 @@ namespace client
uint16_t m_SessionID; uint16_t m_SessionID;
uint32_t m_MessageID; uint32_t m_MessageID;
bool m_IsSendAccepted; bool m_IsSendAccepted;
// to client
bool m_IsSending;
uint8_t m_SendBuffer[I2CP_MAX_MESSAGE_LENGTH];
i2p::stream::SendBufferQueue m_SendQueue;
}; };
typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len); typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len);

View File

@ -54,6 +54,7 @@ namespace client
break; break;
} }
case eSAMSocketTypeAcceptor: case eSAMSocketTypeAcceptor:
case eSAMSocketTypeForward:
{ {
if (Session) if (Session)
{ {
@ -198,7 +199,7 @@ namespace client
{ {
LogPrint (eLogDebug, "SAMSocket::SendMessageReply, close=",close?"true":"false", " reason: ", msg); LogPrint (eLogDebug, "SAMSocket::SendMessageReply, close=",close?"true":"false", " reason: ", msg);
if (!m_IsSilent) if (!m_IsSilent || m_SocketType == eSAMSocketTypeForward)
boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (),
std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (), std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
std::placeholders::_1, std::placeholders::_2, close)); std::placeholders::_1, std::placeholders::_2, close));
@ -263,6 +264,8 @@ namespace client
ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1, bytes_transferred - (eol - m_Buffer) - 1); ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1, bytes_transferred - (eol - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT)) else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT))
ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_STREAM_FORWARD))
ProcessStreamForward (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_DEST_GENERATE)) else if (!strcmp (m_Buffer, SAM_DEST_GENERATE))
ProcessDestGenerate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); ProcessDestGenerate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP)) else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
@ -358,12 +361,12 @@ namespace client
std::shared_ptr<boost::asio::ip::udp::endpoint> forward = nullptr; std::shared_ptr<boost::asio::ip::udp::endpoint> forward = nullptr;
if ((type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) && if ((type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) &&
params.find(SAM_VALUE_HOST) != params.end() && params.find(SAM_VALUE_PORT) != params.end()) params.find(SAM_PARAM_HOST) != params.end() && params.find(SAM_PARAM_PORT) != params.end())
{ {
// udp forward selected // udp forward selected
boost::system::error_code e; boost::system::error_code e;
// TODO: support hostnames in udp forward // TODO: support hostnames in udp forward
auto addr = boost::asio::ip::address::from_string(params[SAM_VALUE_HOST], e); auto addr = boost::asio::ip::address::from_string(params[SAM_PARAM_HOST], e);
if (e) if (e)
{ {
// not an ip address // not an ip address
@ -371,7 +374,7 @@ namespace client
return; return;
} }
auto port = std::stoi(params[SAM_VALUE_PORT]); auto port = std::stoi(params[SAM_PARAM_PORT]);
if (port == -1) if (port == -1)
{ {
SendI2PError("Invalid port"); SendI2PError("Invalid port");
@ -469,6 +472,11 @@ namespace client
void SAMSocket::ProcessStreamConnect (char * buf, size_t len, size_t rem) void SAMSocket::ProcessStreamConnect (char * buf, size_t len, size_t rem)
{ {
LogPrint (eLogDebug, "SAM: stream connect: ", buf); LogPrint (eLogDebug, "SAM: stream connect: ", buf);
if ( m_SocketType != eSAMSocketTypeUnknown)
{
SendI2PError ("Socket already in use");
return;
}
std::map<std::string, std::string> params; std::map<std::string, std::string> params;
ExtractParams (buf, params); ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID]; std::string& id = params[SAM_PARAM_ID];
@ -544,6 +552,11 @@ namespace client
void SAMSocket::ProcessStreamAccept (char * buf, size_t len) void SAMSocket::ProcessStreamAccept (char * buf, size_t len)
{ {
LogPrint (eLogDebug, "SAM: stream accept: ", buf); LogPrint (eLogDebug, "SAM: stream accept: ", buf);
if ( m_SocketType != eSAMSocketTypeUnknown)
{
SendI2PError ("Socket already in use");
return;
}
std::map<std::string, std::string> params; std::map<std::string, std::string> params;
ExtractParams (buf, params); ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID]; std::string& id = params[SAM_PARAM_ID];
@ -565,6 +578,53 @@ namespace client
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
} }
void SAMSocket::ProcessStreamForward (char * buf, size_t len)
{
LogPrint (eLogDebug, "SAM: stream forward: ", buf);
std::map<std::string, std::string> params;
ExtractParams (buf, params);
std::string& id = params[SAM_PARAM_ID];
auto session = m_Owner.FindSession (id);
if (!session)
{
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
return;
}
if (session->localDestination->IsAcceptingStreams ())
{
SendI2PError ("Already accepting");
return;
}
auto it = params.find (SAM_PARAM_PORT);
if (it == params.end ())
{
SendI2PError ("PORT is missing");
return;
}
auto port = std::stoi (it->second);
if (port <= 0 || port >= 0xFFFF)
{
SendI2PError ("Invalid PORT");
return;
}
boost::system::error_code ec;
auto ep = m_Socket.remote_endpoint (ec);
if (ec)
{
SendI2PError ("Socket error");
return;
}
ep.port (port);
m_SocketType = eSAMSocketTypeForward;
m_ID = id;
m_IsAccepting = true;
std::string& silent = params[SAM_PARAM_SILENT];
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PForward,
shared_from_this (), std::placeholders::_1, ep));
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
}
size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data) size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data)
{ {
LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len); LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len);
@ -917,6 +977,43 @@ namespace client
LogPrint (eLogWarning, "SAM: I2P acceptor has been reset"); LogPrint (eLogWarning, "SAM: I2P acceptor has been reset");
} }
void SAMSocket::HandleI2PForward (std::shared_ptr<i2p::stream::Stream> stream,
boost::asio::ip::tcp::endpoint ep)
{
if (stream)
{
LogPrint (eLogDebug, "SAM: incoming forward I2P connection for session ", m_ID);
auto newSocket = std::make_shared<SAMSocket>(m_Owner);
newSocket->SetSocketType (eSAMSocketTypeStream);
auto s = shared_from_this ();
newSocket->GetSocket ().async_connect (ep,
[s, newSocket, stream](const boost::system::error_code& ecode)
{
if (!ecode)
{
s->m_Owner.AddSocket (newSocket);
newSocket->Receive ();
newSocket->m_Stream = stream;
newSocket->m_ID = s->m_ID;
if (!s->m_IsSilent)
{
// get remote peer address
auto dest = stream->GetRemoteIdentity()->ToBase64 ();
memcpy (newSocket->m_StreamBuffer, dest.c_str (), dest.length ());
newSocket->m_StreamBuffer[dest.length ()] = '\n';
newSocket->HandleI2PReceive (boost::system::error_code (),dest.length () + 1); // we send identity like it has been received from stream
}
else
newSocket->I2PReceive ();
}
else
stream->AsyncClose ();
});
}
else
LogPrint (eLogWarning, "SAM: I2P forward acceptor has been reset");
}
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{ {
LogPrint (eLogDebug, "SAM: datagram received ", len); LogPrint (eLogDebug, "SAM: datagram received ", len);
@ -1072,6 +1169,12 @@ namespace client
std::placeholders::_1, newSocket)); std::placeholders::_1, newSocket));
} }
void SAMBridge::AddSocket(std::shared_ptr<SAMSocket> socket)
{
std::unique_lock<std::mutex> lock(m_OpenSocketsMutex);
m_OpenSockets.push_back(socket);
}
void SAMBridge::RemoveSocket(const std::shared_ptr<SAMSocket> & socket) void SAMBridge::RemoveSocket(const std::shared_ptr<SAMSocket> & socket)
{ {
std::unique_lock<std::mutex> lock(m_OpenSocketsMutex); std::unique_lock<std::mutex> lock(m_OpenSocketsMutex);
@ -1087,10 +1190,7 @@ namespace client
if (!ec) if (!ec)
{ {
LogPrint (eLogDebug, "SAM: new connection from ", ep); LogPrint (eLogDebug, "SAM: new connection from ", ep);
{ AddSocket (socket);
std::unique_lock<std::mutex> l(m_OpenSocketsMutex);
m_OpenSockets.push_back(socket);
}
socket->ReceiveHandshake (); socket->ReceiveHandshake ();
} }
else else

View File

@ -48,6 +48,7 @@ namespace client
const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n"; const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n";
const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n"; const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n";
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
const char SAM_STREAM_FORWARD[] = "STREAM FORWARD";
const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND"; const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND";
const char SAM_RAW_SEND[] = "RAW SEND"; const char SAM_RAW_SEND[] = "RAW SEND";
const char SAM_DEST_GENERATE[] = "DEST GENERATE"; const char SAM_DEST_GENERATE[] = "DEST GENERATE";
@ -69,14 +70,14 @@ namespace client
const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE"; const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
const char SAM_PARAM_CRYPTO_TYPE[] = "CRYPTO_TYPE"; const char SAM_PARAM_CRYPTO_TYPE[] = "CRYPTO_TYPE";
const char SAM_PARAM_SIZE[] = "SIZE"; const char SAM_PARAM_SIZE[] = "SIZE";
const char SAM_PARAM_HOST[] = "HOST";
const char SAM_PARAM_PORT[] = "PORT";
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT"; const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
const char SAM_VALUE_STREAM[] = "STREAM"; const char SAM_VALUE_STREAM[] = "STREAM";
const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; const char SAM_VALUE_DATAGRAM[] = "DATAGRAM";
const char SAM_VALUE_RAW[] = "RAW"; const char SAM_VALUE_RAW[] = "RAW";
const char SAM_VALUE_TRUE[] = "true"; const char SAM_VALUE_TRUE[] = "true";
const char SAM_VALUE_FALSE[] = "false"; const char SAM_VALUE_FALSE[] = "false";
const char SAM_VALUE_HOST[] = "HOST";
const char SAM_VALUE_PORT[] = "PORT";
enum SAMSocketType enum SAMSocketType
{ {
@ -84,6 +85,7 @@ namespace client
eSAMSocketTypeSession, eSAMSocketTypeSession,
eSAMSocketTypeStream, eSAMSocketTypeStream,
eSAMSocketTypeAcceptor, eSAMSocketTypeAcceptor,
eSAMSocketTypeForward,
eSAMSocketTypeTerminated eSAMSocketTypeTerminated
}; };
@ -121,6 +123,7 @@ namespace client
void I2PReceive (); void I2PReceive ();
void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream); void HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream);
void HandleI2PForward (std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::endpoint ep);
void HandleWriteI2PData (const boost::system::error_code& ecode, size_t sz); void HandleWriteI2PData (const boost::system::error_code& ecode, size_t sz);
void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
@ -128,6 +131,7 @@ namespace client
void ProcessSessionCreate (char * buf, size_t len); void ProcessSessionCreate (char * buf, size_t len);
void ProcessStreamConnect (char * buf, size_t len, size_t rem); void ProcessStreamConnect (char * buf, size_t len, size_t rem);
void ProcessStreamAccept (char * buf, size_t len); void ProcessStreamAccept (char * buf, size_t len);
void ProcessStreamForward (char * buf, size_t len);
void ProcessDestGenerate (char * buf, size_t len); void ProcessDestGenerate (char * buf, size_t len);
void ProcessNamingLookup (char * buf, size_t len); void ProcessNamingLookup (char * buf, size_t len);
void SendI2PError(const std::string & msg); void SendI2PError(const std::string & msg);
@ -205,6 +209,7 @@ namespace client
/** send raw data to remote endpoint from our UDP Socket */ /** send raw data to remote endpoint from our UDP Socket */
void SendTo(const uint8_t * buf, size_t len, std::shared_ptr<boost::asio::ip::udp::endpoint> remote); void SendTo(const uint8_t * buf, size_t len, std::shared_ptr<boost::asio::ip::udp::endpoint> remote);
void AddSocket(std::shared_ptr<SAMSocket> socket);
void RemoveSocket(const std::shared_ptr<SAMSocket> & socket); void RemoveSocket(const std::shared_ptr<SAMSocket> & socket);
bool ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const; bool ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const;

View File

@ -35,6 +35,8 @@
<translation type="qt" /> <translation type="qt" />
<releases> <releases>
<release version="2.35.0" date="2020-11-30" />
<release version="2.34.0" date="2020-10-27" />
<release version="2.33.0" date="2020-08-24" /> <release version="2.33.0" date="2020-08-24" />
<release version="2.32.1" date="2020-06-02" /> <release version="2.32.1" date="2020-06-02" />
<release version="2.32.0" date="2020-05-25" /> <release version="2.32.0" date="2020-05-25" />

View File

@ -17,7 +17,7 @@ BEGIN
VALUE "FileDescription", "I2Pd Qt" VALUE "FileDescription", "I2Pd Qt"
VALUE "FileVersion", I2PD_VERSION VALUE "FileVersion", I2PD_VERSION
VALUE "InternalName", "i2pd-qt" VALUE "InternalName", "i2pd-qt"
VALUE "LegalCopyright", "Copyright (C) 2013-2018, The PurpleI2P Project" VALUE "LegalCopyright", "Copyright (C) 2013-2020, The PurpleI2P Project"
VALUE "LegalTrademarks1", "Distributed under the BSD 3-Clause software license, see the accompanying file COPYING or https://opensource.org/licenses/BSD-3-Clause." VALUE "LegalTrademarks1", "Distributed under the BSD 3-Clause software license, see the accompanying file COPYING or https://opensource.org/licenses/BSD-3-Clause."
VALUE "OriginalFilename", "i2pd_qt.exe" VALUE "OriginalFilename", "i2pd_qt.exe"
VALUE "ProductName", "i2pd-qt" VALUE "ProductName", "i2pd-qt"

View File

@ -4,81 +4,19 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = i2pd_qt TARGET = i2pd_qt
TEMPLATE = app TEMPLATE = app
QMAKE_CXXFLAGS *= -Wno-unused-parameter -Wno-maybe-uninitialized QMAKE_CXXFLAGS *= -Wno-unused-parameter -Wno-maybe-uninitialized -Wno-deprecated-copy
CONFIG += strict_c++ c++11 CONFIG += strict_c++ c++11
DEFINES += USE_UPNP
CONFIG(debug, debug|release) { CONFIG(debug, debug|release) {
message(Debug build) message(Debug build)
DEFINES += DEBUG_WITH_DEFAULT_LOGGING DEFINES += DEBUG_WITH_DEFAULT_LOGGING
I2PDMAKE += DEBUG=yes
} else { } else {
message(Release build) message(Release build)
I2PDMAKE += DEBUG=no
} }
SOURCES += DaemonQT.cpp mainwindow.cpp \ SOURCES += DaemonQT.cpp mainwindow.cpp \
../../libi2pd/api.cpp \
../../libi2pd/Base.cpp \
../../libi2pd/Blinding.cpp \
../../libi2pd/BloomFilter.cpp \
../../libi2pd/ChaCha20.cpp \
../../libi2pd/Config.cpp \
../../libi2pd/CPU.cpp \
../../libi2pd/Crypto.cpp \
../../libi2pd/CryptoKey.cpp \
../../libi2pd/Datagram.cpp \
../../libi2pd/Destination.cpp \
../../libi2pd/Ed25519.cpp \
../../libi2pd/Family.cpp \
../../libi2pd/FS.cpp \
../../libi2pd/Garlic.cpp \
../../libi2pd/Gost.cpp \
../../libi2pd/Gzip.cpp \
../../libi2pd/HTTP.cpp \
../../libi2pd/I2NPProtocol.cpp \
../../libi2pd/I2PEndian.cpp \
../../libi2pd/Identity.cpp \
../../libi2pd/LeaseSet.cpp \
../../libi2pd/Log.cpp \
../../libi2pd/NetDb.cpp \
../../libi2pd/NetDbRequests.cpp \
../../libi2pd/NTCP2.cpp \
../../libi2pd/Poly1305.cpp \
../../libi2pd/Profiling.cpp \
../../libi2pd/Reseed.cpp \
../../libi2pd/RouterContext.cpp \
../../libi2pd/RouterInfo.cpp \
../../libi2pd/Signature.cpp \
../../libi2pd/SSU.cpp \
../../libi2pd/SSUData.cpp \
../../libi2pd/SSUSession.cpp \
../../libi2pd/Streaming.cpp \
../../libi2pd/Timestamp.cpp \
../../libi2pd/TransitTunnel.cpp \
../../libi2pd/Transports.cpp \
../../libi2pd/Tunnel.cpp \
../../libi2pd/TunnelEndpoint.cpp \
../../libi2pd/TunnelGateway.cpp \
../../libi2pd/TunnelPool.cpp \
../../libi2pd/TunnelConfig.cpp \
../../libi2pd/util.cpp \
../../libi2pd/Elligator.cpp \
../../libi2pd/ECIESX25519AEADRatchetSession.cpp \
../../libi2pd_client/AddressBook.cpp \
../../libi2pd_client/BOB.cpp \
../../libi2pd_client/ClientContext.cpp \
../../libi2pd_client/HTTPProxy.cpp \
../../libi2pd_client/I2CP.cpp \
../../libi2pd_client/I2PService.cpp \
../../libi2pd_client/I2PTunnel.cpp \
../../libi2pd_client/MatchedDestination.cpp \
../../libi2pd_client/SAM.cpp \
../../libi2pd_client/SOCKS.cpp \
../../daemon/Daemon.cpp \
../../daemon/HTTPServer.cpp \
../../daemon/I2PControl.cpp \
../../daemon/i2pd.cpp \
../../daemon/UPnP.cpp \
ClientTunnelPane.cpp \ ClientTunnelPane.cpp \
MainWindowItems.cpp \ MainWindowItems.cpp \
ServerTunnelPane.cpp \ ServerTunnelPane.cpp \
@ -93,77 +31,14 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \
DelayedSaveManager.cpp \ DelayedSaveManager.cpp \
Saver.cpp \ Saver.cpp \
DelayedSaveManagerImpl.cpp \ DelayedSaveManagerImpl.cpp \
SaverImpl.cpp SaverImpl.cpp \
../../daemon/Daemon.cpp \
../../daemon/HTTPServer.cpp \
../../daemon/I2PControl.cpp \
../../daemon/i2pd.cpp \
../../daemon/UPnP.cpp
HEADERS += DaemonQT.h mainwindow.h \ HEADERS += DaemonQT.h mainwindow.h \
../../libi2pd/api.h \
../../libi2pd/Base.h \
../../libi2pd/Blinding.h \
../../libi2pd/BloomFilter.h \
../../libi2pd/ChaCha20.h \
../../libi2pd/Config.h \
../../libi2pd/CPU.h \
../../libi2pd/Crypto.h \
../../libi2pd/CryptoKey.h \
../../libi2pd/Datagram.h \
../../libi2pd/Destination.h \
../../libi2pd/Ed25519.h \
../../libi2pd/Family.h \
../../libi2pd/FS.h \
../../libi2pd/Garlic.h \
../../libi2pd/Gost.h \
../../libi2pd/Gzip.h \
../../libi2pd/HTTP.h \
../../libi2pd/I2NPProtocol.h \
../../libi2pd/I2PEndian.h \
../../libi2pd/Identity.h \
../../libi2pd/LeaseSet.h \
../../libi2pd/LittleBigEndian.h \
../../libi2pd/Log.h \
../../libi2pd/NetDb.hpp \
../../libi2pd/NetDbRequests.h \
../../libi2pd/NTCP2.h \
../../libi2pd/Poly1305.h \
../../libi2pd/Profiling.h \
../../libi2pd/Queue.h \
../../libi2pd/Reseed.h \
../../libi2pd/RouterContext.h \
../../libi2pd/RouterInfo.h \
../../libi2pd/Signature.h \
../../libi2pd/Siphash.h \
../../libi2pd/SSU.h \
../../libi2pd/SSUData.h \
../../libi2pd/SSUSession.h \
../../libi2pd/Streaming.h \
../../libi2pd/Tag.h \
../../libi2pd/Timestamp.h \
../../libi2pd/TransitTunnel.h \
../../libi2pd/Transports.h \
../../libi2pd/TransportSession.h \
../../libi2pd/Tunnel.h \
../../libi2pd/TunnelBase.h \
../../libi2pd/TunnelConfig.h \
../../libi2pd/TunnelEndpoint.h \
../../libi2pd/TunnelGateway.h \
../../libi2pd/TunnelPool.h \
../../libi2pd/util.h \
../../libi2pd/version.h \
../../libi2pd/Elligator.h \
../../libi2pd/ECIESX25519AEADRatchetSession.h \
../../libi2pd_client/AddressBook.h \
../../libi2pd_client/BOB.h \
../../libi2pd_client/ClientContext.h \
../../libi2pd_client/HTTPProxy.h \
../../libi2pd_client/I2CP.h \
../../libi2pd_client/I2PService.h \
../../libi2pd_client/I2PTunnel.h \
../../libi2pd_client/MatchedDestination.h \
../../libi2pd_client/SAM.h \
../../libi2pd_client/SOCKS.h \
../../daemon/Daemon.h \
../../daemon/HTTPServer.h \
../../daemon/I2PControl.h \
../../daemon/UPnP.h \
ClientTunnelPane.h \ ClientTunnelPane.h \
MainWindowItems.h \ MainWindowItems.h \
ServerTunnelPane.h \ ServerTunnelPane.h \
@ -180,7 +55,12 @@ HEADERS += DaemonQT.h mainwindow.h \
DelayedSaveManager.h \ DelayedSaveManager.h \
Saver.h \ Saver.h \
DelayedSaveManagerImpl.h \ DelayedSaveManagerImpl.h \
SaverImpl.h SaverImpl.h \
../../daemon/Daemon.h \
../../daemon/HTTPServer.h \
../../daemon/I2PControl.h \
../../daemon/UPnP.h
INCLUDEPATH += ../../libi2pd INCLUDEPATH += ../../libi2pd
INCLUDEPATH += ../../libi2pd_client INCLUDEPATH += ../../libi2pd_client
@ -193,7 +73,23 @@ FORMS += mainwindow.ui \
routercommandswidget.ui \ routercommandswidget.ui \
generalsettingswidget.ui generalsettingswidget.ui
LIBS += -lz LIBS += $$PWD/../../libi2pd.a $$PWD/../../libi2pdclient.a -lz
libi2pd.commands = @echo Building i2pd libraries
libi2pd.target = $$PWD/../../libi2pd.a
libi2pd.depends = i2pd FORCE
i2pd.commands = cd $$PWD/../../ && mkdir -p obj/libi2pd_client && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) USE_UPNP=yes $$I2PDMAKE api_client
i2pd.target += $$PWD/../../libi2pdclient.a
i2pd.depends = FORCE
cleani2pd.commands = cd $$PWD/../../ && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) clean
cleani2pd.depends = clean
PRE_TARGETDEPS += $$PWD/../../libi2pd.a $$PWD/../../libi2pdclient.a
QMAKE_EXTRA_TARGETS += cleani2pd i2pd libi2pd
CLEAN_DEPS += cleani2pd
macx { macx {
message("using mac os x target") message("using mac os x target")
@ -230,10 +126,12 @@ windows {
QMAKE_CXXFLAGS_RELEASE = -Os QMAKE_CXXFLAGS_RELEASE = -Os
QMAKE_LFLAGS = -Wl,-Bstatic -static-libgcc -static-libstdc++ -mwindows QMAKE_LFLAGS = -Wl,-Bstatic -static-libgcc -static-libstdc++ -mwindows
#linker's -s means "strip" # linker's -s means "strip"
QMAKE_LFLAGS_RELEASE += -s QMAKE_LFLAGS_RELEASE += -s
LIBS = -lminiupnpc \ LIBS = \
$$PWD/../../libi2pd.a $$PWD/../../libi2pdclient.a \
-lminiupnpc \
-lboost_system$$BOOST_SUFFIX \ -lboost_system$$BOOST_SUFFIX \
-lboost_date_time$$BOOST_SUFFIX \ -lboost_date_time$$BOOST_SUFFIX \
-lboost_filesystem$$BOOST_SUFFIX \ -lboost_filesystem$$BOOST_SUFFIX \