mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
196 Commits
2.41.0
...
webconsole
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31936f6025 | ||
|
|
f3dcc5364f | ||
|
|
fbe2e734c2 | ||
|
|
78193fc8f8 | ||
|
|
463d43b0bb | ||
|
|
7197fce349 | ||
|
|
5ba387ba2b | ||
|
|
a843be75f3 | ||
|
|
47460d86b2 | ||
|
|
3cd74f0d4f | ||
|
|
690c9f7c6f | ||
|
|
e2718e5a12 | ||
|
|
d9fefe757e | ||
|
|
55e4bf6b65 | ||
|
|
0176e5cf18 | ||
|
|
4670b12d49 | ||
|
|
321ec8ae4d | ||
|
|
1ccbb8d10b | ||
|
|
86c0accdce | ||
|
|
38d6c29ce9 | ||
|
|
0cf9478cd4 | ||
|
|
a04abd304a | ||
|
|
84aec9fe31 | ||
|
|
593b9bb6c5 | ||
|
|
d3a9cc8fde | ||
|
|
87a434c377 | ||
|
|
56022c9442 | ||
|
|
593d6bf466 | ||
|
|
29a4366dcf | ||
|
|
0a42f414bf | ||
|
|
9b2ac4349e | ||
|
|
2d4c7729ad | ||
|
|
6ecab66b0e | ||
|
|
1dded57a1c | ||
|
|
1d6104ecf3 | ||
|
|
14da941ff4 | ||
|
|
06b87311ea | ||
|
|
3b31773117 | ||
|
|
9c87fe79ea | ||
|
|
bd00112562 | ||
|
|
1c9160c37d | ||
|
|
e2ef88229f | ||
|
|
fd7b889a0f | ||
|
|
a7aa056ec1 | ||
|
|
4f74acb2d3 | ||
|
|
22ef1be82b | ||
|
|
9ddbf255ba | ||
|
|
dfb171d32a | ||
|
|
6b4ffcff5a | ||
|
|
d31cd2e5d6 | ||
|
|
396c74e6c6 | ||
|
|
609c658a9b | ||
|
|
ee6bb40736 | ||
|
|
f8c5ea2b42 | ||
|
|
923eb9fdb3 | ||
|
|
2cd3ebbdb3 | ||
|
|
5e25e30330 | ||
|
|
5aa2a8f60f | ||
|
|
0a1e302e8a | ||
|
|
bb705a77cf | ||
|
|
cb6155b946 | ||
|
|
714d1cc993 | ||
|
|
bc8e4494c4 | ||
|
|
c3a064f980 | ||
|
|
eb3feb7dbd | ||
|
|
da3f3ccac9 | ||
|
|
1a1871e8cd | ||
|
|
c22ab7e1fc | ||
|
|
436992b069 | ||
|
|
18cb3912e5 | ||
|
|
a818b0ba02 | ||
|
|
3716b6f988 | ||
|
|
c9e4e78f41 | ||
|
|
9b4e8bf64b | ||
|
|
5aebefe73f | ||
|
|
8f2124beab | ||
|
|
8b8b43df28 | ||
|
|
c42b991bc9 | ||
|
|
ec08333bf9 | ||
|
|
9e5b4e14c9 | ||
|
|
1f5ed89a88 | ||
|
|
2304a2bc2e | ||
|
|
dc82105226 | ||
|
|
5221f3ddc9 | ||
|
|
e970deb92b | ||
|
|
9db7ec6bb0 | ||
|
|
2e691b6655 | ||
|
|
f22e10537b | ||
|
|
6e532c494c | ||
|
|
f9ed0d4aa2 | ||
|
|
78b1afcc8c | ||
|
|
40340cf9c2 | ||
|
|
eb6437050f | ||
|
|
45ebfe378b | ||
|
|
1326597226 | ||
|
|
751da92c13 | ||
|
|
e10ca637da | ||
|
|
c5d9d71a8a | ||
|
|
3e0f5d231d | ||
|
|
6990f177ba | ||
|
|
98e713166b | ||
|
|
4c91ae0085 | ||
|
|
43f74d4d5a | ||
|
|
8c3e716c3f | ||
|
|
05946125b5 | ||
|
|
1e2a0a4549 | ||
|
|
f9f5084dd7 | ||
|
|
b7e7c6db7b | ||
|
|
f9d67b28ec | ||
|
|
46b77cc280 | ||
|
|
2f10decf56 | ||
|
|
678a1ae0fb | ||
|
|
51cbffd097 | ||
|
|
207b13dcab | ||
|
|
3052dbd1e8 | ||
|
|
5891b1ceb2 | ||
|
|
07e14ddda8 | ||
|
|
db5e90787c | ||
|
|
67e501f5c7 | ||
|
|
2160001167 | ||
|
|
f5f4150d17 | ||
|
|
887f292612 | ||
|
|
f5f282af97 | ||
|
|
82f9585b7a | ||
|
|
eb561bb0c2 | ||
|
|
81207999eb | ||
|
|
2fef595b83 | ||
|
|
2024e790ca | ||
|
|
f9925c7374 | ||
|
|
dd774b8dfd | ||
|
|
064b8042a5 | ||
|
|
7923ed9567 | ||
|
|
30b83414ef | ||
|
|
990906c57f | ||
|
|
4c323a666a | ||
|
|
a3f165d374 | ||
|
|
4977f9e6b4 | ||
|
|
7d5f51e357 | ||
|
|
371a339b18 | ||
|
|
7e7aee27b6 | ||
|
|
53148fe58f | ||
|
|
56b6de6962 | ||
|
|
44735681af | ||
|
|
ee1c4f4fdc | ||
|
|
fb6ecdde1e | ||
|
|
861166d8a9 | ||
|
|
70dca81c40 | ||
|
|
2774d72888 | ||
|
|
2440ffbfc9 | ||
|
|
77c5dde320 | ||
|
|
aa49cad279 | ||
|
|
f56ae240ab | ||
|
|
e871a30a78 | ||
|
|
30e6984889 | ||
|
|
324932c758 | ||
|
|
421800bc8f | ||
|
|
86fb47b2b4 | ||
|
|
715f83bf84 | ||
|
|
87bf5c2418 | ||
|
|
5c9af1c613 | ||
|
|
765e0e5c6b | ||
|
|
cc296e16dc | ||
|
|
ab9901525b | ||
|
|
3643a46a0c | ||
|
|
d467e6869d | ||
|
|
db36018849 | ||
|
|
3c5c375f71 | ||
|
|
7473d8c9aa | ||
|
|
33645d7f09 | ||
|
|
9f1106b14a | ||
|
|
3dd952b49b | ||
|
|
6b85bd2cb8 | ||
|
|
60b164c853 | ||
|
|
40c8a1bc1d | ||
|
|
22de695f12 | ||
|
|
e91f588cd7 | ||
|
|
7b72d91549 | ||
|
|
b3c2e86436 | ||
|
|
908bdc7624 | ||
|
|
21c1ec9c8c | ||
|
|
6d7d71bb16 | ||
|
|
6eba061c2a | ||
|
|
f184f550b9 | ||
|
|
bb7c0fef20 | ||
|
|
5c15a12116 | ||
|
|
68d015763e | ||
|
|
7faa732f38 | ||
|
|
11f9eeabf1 | ||
|
|
a152f36894 | ||
|
|
d4ede6ff01 | ||
|
|
35542d803c | ||
|
|
f6ba776c12 | ||
|
|
1511dcb309 | ||
|
|
35afa98112 | ||
|
|
df62b40ca7 | ||
|
|
9f1a125ed9 |
38
.github/workflows/build-windows.yml
vendored
38
.github/workflows/build-windows.yml
vendored
@@ -35,3 +35,41 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: i2pd-${{ matrix.arch_short }}.exe
|
name: i2pd-${{ matrix.arch_short }}.exe
|
||||||
path: i2pd.exe
|
path: i2pd.exe
|
||||||
|
build-xp:
|
||||||
|
name: Building for Windows XP
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Setup MSYS2
|
||||||
|
uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
msystem: MINGW32
|
||||||
|
install: base-devel git mingw-w64-i686-gcc mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-miniupnpc
|
||||||
|
update: true
|
||||||
|
- name: Build WinXP-capable CRT packages
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/msys2/MINGW-packages
|
||||||
|
pushd MINGW-packages
|
||||||
|
pushd mingw-w64-headers-git
|
||||||
|
sed -i 's/0x601/0x501/' PKGBUILD
|
||||||
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
|
||||||
|
pacman --noconfirm -U mingw-w64-i686-headers-git-*-any.pkg.tar.zst
|
||||||
|
popd
|
||||||
|
pushd mingw-w64-crt-git
|
||||||
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
|
||||||
|
pacman --noconfirm -U mingw-w64-i686-crt-git-*-any.pkg.tar.zst
|
||||||
|
popd
|
||||||
|
pushd mingw-w64-winpthreads-git
|
||||||
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm
|
||||||
|
pacman --noconfirm -U mingw-w64-i686-libwinpthread-git-*-any.pkg.tar.zst mingw-w64-i686-winpthreads-git-*-any.pkg.tar.zst
|
||||||
|
popd
|
||||||
|
popd
|
||||||
|
- name: Build application
|
||||||
|
run: |
|
||||||
|
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
|
||||||
|
make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes USE_WINXP_FLAGS=yes -j3
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: i2pd-xp.exe
|
||||||
|
path: i2pd.exe
|
||||||
|
|||||||
9
.github/workflows/docker.yml
vendored
9
.github/workflows/docker.yml
vendored
@@ -1,6 +1,11 @@
|
|||||||
name: Build containers
|
name: Build containers
|
||||||
|
|
||||||
on: [push]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- openssl
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
@@ -58,6 +63,8 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
purplei2p/i2pd:latest
|
purplei2p/i2pd:latest
|
||||||
|
purplei2p/i2pd:latest-release
|
||||||
purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||||
ghcr.io/purplei2p/i2pd:latest
|
ghcr.io/purplei2p/i2pd:latest
|
||||||
|
ghcr.io/purplei2p/i2pd:latest-release
|
||||||
ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -8,12 +8,15 @@ netDb
|
|||||||
/libi2pd.a
|
/libi2pd.a
|
||||||
/libi2pdclient.a
|
/libi2pdclient.a
|
||||||
/libi2pdlang.a
|
/libi2pdlang.a
|
||||||
|
/libi2pdwebconsole.a
|
||||||
/libi2pd.so
|
/libi2pd.so
|
||||||
/libi2pdclient.so
|
/libi2pdclient.so
|
||||||
/libi2pdlang.so
|
/libi2pdlang.so
|
||||||
|
/libi2pdwebconsole.so
|
||||||
/libi2pd.dll
|
/libi2pd.dll
|
||||||
/libi2pdclient.dll
|
/libi2pdclient.dll
|
||||||
/libi2pdlang.dll
|
/libi2pdlang.dll
|
||||||
|
/libi2pdwebconsole.dll
|
||||||
*.exe
|
*.exe
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
34
ChangeLog
34
ChangeLog
@@ -1,6 +1,40 @@
|
|||||||
# 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.42.1] - 2022-05-24
|
||||||
|
### Fixed
|
||||||
|
- Incorrect jump link in HTTP Proxy
|
||||||
|
|
||||||
|
## [2.42.0] - 2022-05-22
|
||||||
|
### Added
|
||||||
|
- Preliminary SSU2 implementation
|
||||||
|
- Tunnel length variance
|
||||||
|
- Localization to French
|
||||||
|
- Daily cleanup of obsolete peer profiles
|
||||||
|
- Ordered jump services list in HTTP proxy
|
||||||
|
- Win32 service
|
||||||
|
- Show port for local non-published SSU addresses in web console
|
||||||
|
### Changed
|
||||||
|
- Maximum RouterInfo length increased to 3K
|
||||||
|
- Skip unknown addresses in RouterInfo
|
||||||
|
- Don't pick own router for peer test
|
||||||
|
- Reseeds list
|
||||||
|
- Internal numeric id for families
|
||||||
|
- Use ipv6 preference only when netinet headers not used
|
||||||
|
- Close stream if delete requested
|
||||||
|
- Remove version from title in web console
|
||||||
|
- Drop MESHNET build option
|
||||||
|
- Set data path before initialization
|
||||||
|
- Don't show registration block in web console if token is not provided
|
||||||
|
### Fixed
|
||||||
|
- Encrypted LeaseSet for EdDSA signature
|
||||||
|
- Clients tunnels are not built if clock is not synced on start
|
||||||
|
- Incorrect processing of i2cp.dontPublishLeaseSet param
|
||||||
|
- UDP tunnels reload
|
||||||
|
- Build for LibreSSL 3.5.2
|
||||||
|
- Race condition in short tunnel build message
|
||||||
|
- Race condition in local RouterInfo buffer allocation
|
||||||
|
|
||||||
## [2.41.0] - 2022-02-20
|
## [2.41.0] - 2022-02-20
|
||||||
### Added
|
### Added
|
||||||
- Clock syncronization through SSU
|
- Clock syncronization through SSU
|
||||||
|
|||||||
59
Makefile
59
Makefile
@@ -4,7 +4,7 @@ SYS := $(shell $(CXX) -dumpmachine)
|
|||||||
|
|
||||||
ifneq (, $(findstring darwin, $(SYS)))
|
ifneq (, $(findstring darwin, $(SYS)))
|
||||||
SHARED_SUFFIX = dylib
|
SHARED_SUFFIX = dylib
|
||||||
else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS)))
|
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||||
SHARED_SUFFIX = dll
|
SHARED_SUFFIX = dll
|
||||||
else
|
else
|
||||||
SHARED_SUFFIX = so
|
SHARED_SUFFIX = so
|
||||||
@@ -12,26 +12,28 @@ endif
|
|||||||
|
|
||||||
SHLIB := libi2pd.$(SHARED_SUFFIX)
|
SHLIB := libi2pd.$(SHARED_SUFFIX)
|
||||||
ARLIB := libi2pd.a
|
ARLIB := libi2pd.a
|
||||||
SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
|
|
||||||
ARLIB_LANG := libi2pdlang.a
|
|
||||||
SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX)
|
SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX)
|
||||||
ARLIB_CLIENT := libi2pdclient.a
|
ARLIB_CLIENT := libi2pdclient.a
|
||||||
|
SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
|
||||||
|
ARLIB_LANG := libi2pdlang.a
|
||||||
|
SHLIB_WEBCONSOLE := libi2pdwebconsole.$(SHARED_SUFFIX)
|
||||||
|
ARLIB_WEBCONSOLE := libi2pdwebconsole.a
|
||||||
SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX)
|
SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX)
|
||||||
ARLIB_WRAP := libi2pdwrapper.a
|
ARLIB_WRAP := libi2pdwrapper.a
|
||||||
I2PD := i2pd
|
I2PD := i2pd
|
||||||
|
|
||||||
LIB_SRC_DIR := libi2pd
|
LIB_SRC_DIR := libi2pd
|
||||||
LIB_CLIENT_SRC_DIR := libi2pd_client
|
LIB_CLIENT_SRC_DIR := libi2pd_client
|
||||||
WRAP_SRC_DIR := libi2pd_wrapper
|
WEBCONSOLE_SRC_DIR := libi2pd_webconsole
|
||||||
LANG_SRC_DIR := i18n
|
LANG_SRC_DIR := i18n
|
||||||
DAEMON_SRC_DIR := daemon
|
DAEMON_SRC_DIR := daemon
|
||||||
|
WRAP_SRC_DIR := libi2pd_wrapper
|
||||||
|
|
||||||
# import source files lists
|
# import source files lists
|
||||||
include filelist.mk
|
include filelist.mk
|
||||||
|
|
||||||
USE_AESNI := $(or $(USE_AESNI),yes)
|
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||||
USE_STATIC := $(or $(USE_STATIC),no)
|
USE_STATIC := $(or $(USE_STATIC),no)
|
||||||
USE_MESHNET := $(or $(USE_MESHNET),no)
|
|
||||||
USE_UPNP := $(or $(USE_UPNP),no)
|
USE_UPNP := $(or $(USE_UPNP),no)
|
||||||
DEBUG := $(or $(DEBUG),yes)
|
DEBUG := $(or $(DEBUG),yes)
|
||||||
|
|
||||||
@@ -49,50 +51,48 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq (, $(findstring darwin, $(SYS)))
|
ifneq (, $(findstring darwin, $(SYS)))
|
||||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonUnix.cpp
|
||||||
ifeq ($(HOMEBREW),1)
|
ifeq ($(HOMEBREW),1)
|
||||||
include Makefile.homebrew
|
include Makefile.homebrew
|
||||||
else
|
else
|
||||||
include Makefile.osx
|
include Makefile.osx
|
||||||
endif
|
endif
|
||||||
else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS)))
|
else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS)))
|
||||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonUnix.cpp
|
||||||
include Makefile.linux
|
include Makefile.linux
|
||||||
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
||||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonUnix.cpp
|
||||||
include Makefile.bsd
|
include Makefile.bsd
|
||||||
else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS)))
|
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||||
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32NetState.cpp
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
|
||||||
include Makefile.mingw
|
include Makefile.mingw
|
||||||
else # not supported
|
else # not supported
|
||||||
$(error Not supported platform)
|
$(error Not supported platform)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(USE_MESHNET),yes)
|
|
||||||
NEEDED_CXXFLAGS += -DMESHNET
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(USE_GIT_VERSION),yes)
|
ifeq ($(USE_GIT_VERSION),yes)
|
||||||
GIT_VERSION := $(shell git describe --tags)
|
GIT_VERSION := $(shell git describe --tags)
|
||||||
NEEDED_CXXFLAGS += -DGITVER=\"$(GIT_VERSION)\"
|
NEEDED_CXXFLAGS += -DGITVER=\"$(GIT_VERSION)\"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR)
|
NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR) -I$(WEBCONSOLE_SRC_DIR) -DOPENSSL_SUPPRESS_DEPRECATED
|
||||||
|
|
||||||
LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||||
LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||||
LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC))
|
LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC))
|
||||||
|
WEBCONSOLE_OBJS += $(patsubst %.cpp,obj/%.o,$(WEBCONSOLE_SRC))
|
||||||
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
||||||
WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC))
|
WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC))
|
||||||
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d)
|
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(WEBCONSOLE_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d)
|
||||||
|
|
||||||
## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary
|
## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary
|
||||||
all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD)
|
all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(ARLIB_WEBCONSOLE) $(I2PD)
|
||||||
|
|
||||||
mk_obj_dir:
|
mk_obj_dir:
|
||||||
@mkdir -p obj/$(LIB_SRC_DIR)
|
@mkdir -p obj/$(LIB_SRC_DIR)
|
||||||
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
|
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
|
||||||
@mkdir -p obj/$(LANG_SRC_DIR)
|
@mkdir -p obj/$(LANG_SRC_DIR)
|
||||||
|
@mkdir -p obj/$(WEBCONSOLE_SRC_DIR)
|
||||||
@mkdir -p obj/$(DAEMON_SRC_DIR)
|
@mkdir -p obj/$(DAEMON_SRC_DIR)
|
||||||
@mkdir -p obj/$(WRAP_SRC_DIR)
|
@mkdir -p obj/$(WRAP_SRC_DIR)
|
||||||
@mkdir -p obj/Win32
|
@mkdir -p obj/Win32
|
||||||
@@ -100,7 +100,8 @@ mk_obj_dir:
|
|||||||
api: $(SHLIB) $(ARLIB)
|
api: $(SHLIB) $(ARLIB)
|
||||||
client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||||
lang: $(SHLIB_LANG) $(ARLIB_LANG)
|
lang: $(SHLIB_LANG) $(ARLIB_LANG)
|
||||||
api_client: api client lang
|
webconsole: $(SHLIB_WEBCONSOLE) $(ARLIB_WEBCONSOLE)
|
||||||
|
api_client: api client lang webconsole
|
||||||
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
|
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||||
|
|
||||||
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
|
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
|
||||||
@@ -116,7 +117,7 @@ obj/%.o: %.cpp | mk_obj_dir
|
|||||||
# '-' is 'ignore if missing' on first run
|
# '-' is 'ignore if missing' on first run
|
||||||
-include $(DEPS)
|
-include $(DEPS)
|
||||||
|
|
||||||
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
|
$(I2PD): $(DAEMON_OBJS) $(ARLIB_WEBCONSOLE) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
|
||||||
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
|
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
|
||||||
|
|
||||||
$(SHLIB): $(LIB_OBJS) $(SHLIB_LANG)
|
$(SHLIB): $(LIB_OBJS) $(SHLIB_LANG)
|
||||||
@@ -129,12 +130,17 @@ ifneq ($(USE_STATIC),yes)
|
|||||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG)
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(SHLIB_WRAP): $(WRAP_LIB_OBJS)
|
$(SHLIB_LANG): $(LANG_OBJS)
|
||||||
ifneq ($(USE_STATIC),yes)
|
ifneq ($(USE_STATIC),yes)
|
||||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(SHLIB_LANG): $(LANG_OBJS)
|
$(SHLIB_WEBCONSOLE): $(WEBCONSOLE_OBJS) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
|
||||||
|
ifneq ($(USE_STATIC),yes)
|
||||||
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(SHLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||||
ifneq ($(USE_STATIC),yes)
|
ifneq ($(USE_STATIC),yes)
|
||||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||||
endif
|
endif
|
||||||
@@ -145,18 +151,21 @@ $(ARLIB): $(LIB_OBJS)
|
|||||||
$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
|
$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
|
||||||
$(AR) -r $@ $^
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
|
$(ARLIB_LANG): $(LANG_OBJS)
|
||||||
$(AR) -r $@ $^
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
$(ARLIB_LANG): $(LANG_OBJS)
|
$(ARLIB_WEBCONSOLE): $(WEBCONSOLE_OBJS)
|
||||||
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
|
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||||
$(AR) -r $@ $^
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) -r obj
|
$(RM) -r obj
|
||||||
$(RM) -r docs/generated
|
$(RM) -r docs/generated
|
||||||
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WRAP) $(ARLIB_WRAP)
|
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WEBCONSOLE) $(ARLIB_WEBCONSOLE) $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||||
|
|
||||||
strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
|
strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG) $(SHLIB_WEBCONSOLE)
|
||||||
strip $^
|
strip $^
|
||||||
|
|
||||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
||||||
|
|||||||
@@ -39,13 +39,14 @@ ifeq ($(USE_AESNI),yes)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
install -d ${PREFIX}/bin ${PREFIX}/etc/i2pd ${PREFIX}/share/doc/i2pd ${PREFIX}/share/i2pd ${PREFIX}/share/man/man1 ${PREFIX}/var/lib/i2pd
|
install -d ${PREFIX}/bin ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/share/doc/i2pd ${PREFIX}/share/i2pd ${PREFIX}/share/man/man1 ${PREFIX}/var/lib/i2pd
|
||||||
install -m 755 ${I2PD} ${PREFIX}/bin/
|
install -m 755 ${I2PD} ${PREFIX}/bin/
|
||||||
install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd
|
install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd
|
||||||
@cp -R contrib/certificates ${PREFIX}/share/i2pd/
|
@cp -R contrib/certificates ${PREFIX}/share/i2pd/
|
||||||
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
|
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
|
||||||
@gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1
|
@gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1
|
||||||
@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/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d
|
||||||
@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
|
||||||
|
|||||||
@@ -62,3 +62,16 @@ ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64
|
|||||||
NEEDED_CXXFLAGS += -D__AES__ -maes
|
NEEDED_CXXFLAGS += -D__AES__ -maes
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
install: all
|
||||||
|
install -d ${PREFIX}/bin ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/usr ${PREFIX}/usr/share ${PREFIX}/usr/share/doc/i2pd ${PREFIX}/usr/share/i2pd ${PREFIX}/usr/share/man ${PREFIX}/usr/share/man/man1 ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd
|
||||||
|
install -m 755 ${I2PD} ${PREFIX}/bin/
|
||||||
|
install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd
|
||||||
|
@cp -R contrib/certificates ${PREFIX}/usr/share/i2pd/
|
||||||
|
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/usr/share/doc/i2pd
|
||||||
|
@gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/usr/share/man/man1
|
||||||
|
@ln -sf ${PREFIX}/usr/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d
|
||||||
|
@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/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf
|
||||||
|
|||||||
@@ -3,19 +3,11 @@ USE_WIN32_APP := yes
|
|||||||
|
|
||||||
WINDRES = windres
|
WINDRES = windres
|
||||||
|
|
||||||
CXXFLAGS := $(CXX_DEBUG) -DWIN32_LEAN_AND_MEAN -fPIC -msse
|
CXXFLAGS := $(CXX_DEBUG) -fPIC -msse
|
||||||
INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32
|
INCFLAGS = -IWin32
|
||||||
LDFLAGS := ${LD_DEBUG} -static
|
LDFLAGS := ${LD_DEBUG} -static
|
||||||
|
|
||||||
# detect proper flag for c++11 support by compilers
|
NEEDED_CXXFLAGS += -std=c++17 -DWIN32_LEAN_AND_MEAN
|
||||||
CXXVER := $(shell $(CXX) -dumpversion)
|
|
||||||
ifeq ($(shell expr match ${CXXVER} "[4]\.[7-9]\|4\.1[0-9]\|[5-6]"),4) # gcc 4.7 - 6
|
|
||||||
NEEDED_CXXFLAGS += -std=c++11
|
|
||||||
else ifeq ($(shell expr match ${CXXVER} "[1,7-9]"),1) # gcc >= 7
|
|
||||||
NEEDED_CXXFLAGS += -std=c++17
|
|
||||||
else # not supported
|
|
||||||
$(error Compiler too old)
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Boost libraries suffix
|
# Boost libraries suffix
|
||||||
BOOST_SUFFIX = -mt
|
BOOST_SUFFIX = -mt
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
#include "Tunnel.h"
|
#include "Tunnel.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "Daemon.h"
|
|
||||||
#include "Win32App.h"
|
#include "Win32App.h"
|
||||||
#include "Win32NetState.h"
|
#include "Win32NetState.h"
|
||||||
|
|
||||||
@@ -55,13 +55,15 @@ namespace win32
|
|||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
|
||||||
if(!i2p::context.AcceptsTunnels())
|
if(!i2p::context.AcceptsTunnels())
|
||||||
InsertMenu (hPopup, -1,
|
if(m_getIsGraceful)
|
||||||
i2p::util::DaemonWin32::Instance ().isGraceful ? MF_BYPOSITION | MF_STRING | MF_GRAYED : MF_BYPOSITION | MF_STRING,
|
if(m_getIsGraceful())
|
||||||
ID_ACCEPT_TRANSIT, "Accept &transit");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING | MF_GRAYED, ID_ACCEPT_TRANSIT, "Accept &transit");
|
||||||
|
else
|
||||||
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ACCEPT_TRANSIT, "Accept &transit");
|
||||||
else
|
else
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config");
|
||||||
if (!i2p::util::DaemonWin32::Instance ().isGraceful)
|
if (!m_getIsGraceful)
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
|
||||||
else
|
else
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown");
|
||||||
@@ -270,7 +272,7 @@ namespace win32
|
|||||||
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
|
||||||
g_GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
|
g_GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
|
||||||
i2p::util::DaemonWin32::Instance ().isGraceful = true;
|
if (m_setIsGraceful) m_setIsGraceful(true);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case ID_STOP_GRACEFUL_SHUTDOWN:
|
case ID_STOP_GRACEFUL_SHUTDOWN:
|
||||||
@@ -279,7 +281,7 @@ namespace win32
|
|||||||
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
|
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
|
||||||
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
|
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
|
||||||
g_GracefulShutdownEndtime = 0;
|
g_GracefulShutdownEndtime = 0;
|
||||||
i2p::util::DaemonWin32::Instance ().isGraceful = false;
|
if (m_setIsGraceful) m_setIsGraceful(false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case ID_RELOAD:
|
case ID_RELOAD:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -22,6 +22,15 @@ namespace win32
|
|||||||
int RunWin32App ();
|
int RunWin32App ();
|
||||||
bool GracefulShutdown ();
|
bool GracefulShutdown ();
|
||||||
bool StopGracefulShutdown ();
|
bool StopGracefulShutdown ();
|
||||||
|
|
||||||
|
inline typedef std::function<void (bool)> DaemonSetIsGraceful;
|
||||||
|
inline DaemonSetIsGraceful m_setIsGraceful;
|
||||||
|
inline void SetIsGraceful (const DaemonSetIsGraceful& f) { m_setIsGraceful = f; };
|
||||||
|
|
||||||
|
inline typedef std::function<bool ()> DaemonGetIsGraceful;
|
||||||
|
inline DaemonGetIsGraceful m_getIsGraceful;
|
||||||
|
inline void GetIsGraceful (const DaemonGetIsGraceful& f) { m_getIsGraceful = f; };
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // WIN32APP_H__
|
#endif // WIN32APP_H__
|
||||||
|
|||||||
292
Win32/Win32Service.cpp
Normal file
292
Win32/Win32Service.cpp
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||||
|
*
|
||||||
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
|
*
|
||||||
|
* See full license text in LICENSE file at top of project tree
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Win32Service.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
I2PService *I2PService::s_service = NULL;
|
||||||
|
|
||||||
|
BOOL I2PService::isService()
|
||||||
|
{
|
||||||
|
BOOL bIsService = FALSE;
|
||||||
|
HWINSTA hWinStation = GetProcessWindowStation();
|
||||||
|
if (hWinStation != NULL)
|
||||||
|
{
|
||||||
|
USEROBJECTFLAGS uof = { 0 };
|
||||||
|
if (GetUserObjectInformation(hWinStation, UOI_FLAGS, &uof, sizeof(USEROBJECTFLAGS), NULL) && ((uof.dwFlags & WSF_VISIBLE) == 0))
|
||||||
|
{
|
||||||
|
bIsService = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bIsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL I2PService::Run(I2PService &service)
|
||||||
|
{
|
||||||
|
s_service = &service;
|
||||||
|
SERVICE_TABLE_ENTRY serviceTable[] =
|
||||||
|
{
|
||||||
|
{ service.m_name, ServiceMain },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
return StartServiceCtrlDispatcher(serviceTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WINAPI I2PService::ServiceMain(DWORD dwArgc, PSTR *pszArgv)
|
||||||
|
{
|
||||||
|
assert(s_service != NULL);
|
||||||
|
s_service->m_statusHandle = RegisterServiceCtrlHandler(
|
||||||
|
s_service->m_name, ServiceCtrlHandler);
|
||||||
|
if (s_service->m_statusHandle == NULL)
|
||||||
|
{
|
||||||
|
throw GetLastError();
|
||||||
|
}
|
||||||
|
s_service->Start(dwArgc, pszArgv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WINAPI I2PService::ServiceCtrlHandler(DWORD dwCtrl)
|
||||||
|
{
|
||||||
|
switch (dwCtrl)
|
||||||
|
{
|
||||||
|
case SERVICE_CONTROL_STOP: s_service->Stop(); break;
|
||||||
|
case SERVICE_CONTROL_PAUSE: s_service->Pause(); break;
|
||||||
|
case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break;
|
||||||
|
case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break;
|
||||||
|
case SERVICE_CONTROL_INTERROGATE: break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
I2PService::I2PService(PSTR pszServiceName,
|
||||||
|
BOOL fCanStop,
|
||||||
|
BOOL fCanShutdown,
|
||||||
|
BOOL fCanPauseContinue)
|
||||||
|
{
|
||||||
|
m_name = (pszServiceName == NULL) ? (PSTR)"" : pszServiceName;
|
||||||
|
m_statusHandle = NULL;
|
||||||
|
m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||||
|
m_status.dwCurrentState = SERVICE_START_PENDING;
|
||||||
|
|
||||||
|
DWORD dwControlsAccepted = 0;
|
||||||
|
if (fCanStop)
|
||||||
|
dwControlsAccepted |= SERVICE_ACCEPT_STOP;
|
||||||
|
if (fCanShutdown)
|
||||||
|
dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;
|
||||||
|
if (fCanPauseContinue)
|
||||||
|
dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
|
||||||
|
|
||||||
|
m_status.dwControlsAccepted = dwControlsAccepted;
|
||||||
|
m_status.dwWin32ExitCode = NO_ERROR;
|
||||||
|
m_status.dwServiceSpecificExitCode = 0;
|
||||||
|
m_status.dwCheckPoint = 0;
|
||||||
|
m_status.dwWaitHint = 0;
|
||||||
|
m_fStopping = FALSE;
|
||||||
|
// Create a manual-reset event that is not signaled at first to indicate
|
||||||
|
// the stopped signal of the service.
|
||||||
|
m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
|
if (m_hStoppedEvent == NULL)
|
||||||
|
{
|
||||||
|
throw GetLastError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
I2PService::~I2PService(void)
|
||||||
|
{
|
||||||
|
if (m_hStoppedEvent)
|
||||||
|
{
|
||||||
|
CloseHandle(m_hStoppedEvent);
|
||||||
|
m_hStoppedEvent = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::Start(DWORD dwArgc, PSTR *pszArgv)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SetServiceStatus(SERVICE_START_PENDING);
|
||||||
|
OnStart(dwArgc, pszArgv);
|
||||||
|
SetServiceStatus(SERVICE_RUNNING);
|
||||||
|
}
|
||||||
|
catch (DWORD dwError)
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Win32Service: Start error: ", dwError);
|
||||||
|
SetServiceStatus(SERVICE_STOPPED, dwError);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Win32Service: failed to start: ", EVENTLOG_ERROR_TYPE);
|
||||||
|
SetServiceStatus(SERVICE_STOPPED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv)
|
||||||
|
{
|
||||||
|
LogPrint(eLogInfo, "Win32Service: in OnStart (", EVENTLOG_INFORMATION_TYPE, ")");
|
||||||
|
if(m_daemonStart)
|
||||||
|
m_daemonStart();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Win32Service: failed to start: Unable to call callback");
|
||||||
|
SetServiceStatus(SERVICE_STOPPED);
|
||||||
|
}
|
||||||
|
_worker = new std::thread(std::bind(&I2PService::WorkerThread, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::WorkerThread()
|
||||||
|
{
|
||||||
|
while (!m_fStopping)
|
||||||
|
{
|
||||||
|
::Sleep(1000); // Simulate some lengthy operations.
|
||||||
|
}
|
||||||
|
// Signal the stopped event.
|
||||||
|
SetEvent(m_hStoppedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::Stop()
|
||||||
|
{
|
||||||
|
DWORD dwOriginalState = m_status.dwCurrentState;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SetServiceStatus(SERVICE_STOP_PENDING);
|
||||||
|
OnStop();
|
||||||
|
SetServiceStatus(SERVICE_STOPPED);
|
||||||
|
}
|
||||||
|
catch (DWORD dwError)
|
||||||
|
{
|
||||||
|
LogPrint(eLogInfo, "Win32Service: Stop error: ", dwError);
|
||||||
|
SetServiceStatus(dwOriginalState);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Win32Service: Failed to stop: ", EVENTLOG_ERROR_TYPE);
|
||||||
|
SetServiceStatus(dwOriginalState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::OnStop()
|
||||||
|
{
|
||||||
|
// Log a service stop message to the Application log.
|
||||||
|
LogPrint(eLogInfo, "Win32Service: in OnStop (", EVENTLOG_INFORMATION_TYPE, ")");
|
||||||
|
if(m_daemonStop)
|
||||||
|
m_daemonStop();
|
||||||
|
else
|
||||||
|
LogPrint(eLogError, "Win32Service: failed to stop: Unable to call callback");
|
||||||
|
|
||||||
|
m_fStopping = TRUE;
|
||||||
|
if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
throw GetLastError();
|
||||||
|
}
|
||||||
|
_worker->join();
|
||||||
|
delete _worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::Pause()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SetServiceStatus(SERVICE_PAUSE_PENDING);
|
||||||
|
OnPause();
|
||||||
|
SetServiceStatus(SERVICE_PAUSED);
|
||||||
|
}
|
||||||
|
catch (DWORD dwError)
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Win32Service: Pause error: ", dwError);
|
||||||
|
SetServiceStatus(SERVICE_RUNNING);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Win32Service: Failed to pause: ", EVENTLOG_ERROR_TYPE);
|
||||||
|
SetServiceStatus(SERVICE_RUNNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::OnPause()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::Continue()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SetServiceStatus(SERVICE_CONTINUE_PENDING);
|
||||||
|
OnContinue();
|
||||||
|
SetServiceStatus(SERVICE_RUNNING);
|
||||||
|
}
|
||||||
|
catch (DWORD dwError)
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Win32Service: Continue error: ", dwError);
|
||||||
|
SetServiceStatus(SERVICE_PAUSED);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Win32Service: Failed to resume: ", EVENTLOG_ERROR_TYPE);
|
||||||
|
SetServiceStatus(SERVICE_PAUSED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::OnContinue()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::Shutdown()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OnShutdown();
|
||||||
|
SetServiceStatus(SERVICE_STOPPED);
|
||||||
|
}
|
||||||
|
catch (DWORD dwError)
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Win32Service: Shutdown error: ", dwError);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Win32Service: Failed to shut down: ", EVENTLOG_ERROR_TYPE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::OnShutdown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PService::SetServiceStatus(DWORD dwCurrentState,
|
||||||
|
DWORD dwWin32ExitCode,
|
||||||
|
DWORD dwWaitHint)
|
||||||
|
{
|
||||||
|
static DWORD dwCheckPoint = 1;
|
||||||
|
m_status.dwCurrentState = dwCurrentState;
|
||||||
|
m_status.dwWin32ExitCode = dwWin32ExitCode;
|
||||||
|
m_status.dwWaitHint = dwWaitHint;
|
||||||
|
m_status.dwCheckPoint =
|
||||||
|
((dwCurrentState == SERVICE_RUNNING) ||
|
||||||
|
(dwCurrentState == SERVICE_STOPPED)) ?
|
||||||
|
0 : dwCheckPoint++;
|
||||||
|
|
||||||
|
::SetServiceStatus(m_statusHandle, &m_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
//*****************************************************************************
|
||||||
|
|
||||||
|
void FreeHandles(SC_HANDLE schSCManager, SC_HANDLE schService)
|
||||||
|
{
|
||||||
|
if (schSCManager)
|
||||||
|
{
|
||||||
|
CloseServiceHandle(schSCManager);
|
||||||
|
schSCManager = NULL;
|
||||||
|
}
|
||||||
|
if (schService)
|
||||||
|
{
|
||||||
|
CloseServiceHandle(schService);
|
||||||
|
schService = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
76
Win32/Win32Service.h
Normal file
76
Win32/Win32Service.h
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||||
|
*
|
||||||
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
|
*
|
||||||
|
* See full license text in LICENSE file at top of project tree
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WIN32SERVICE_H__
|
||||||
|
#define WIN32SERVICE_H__
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#define SERVICE_NAME "i2pdService"
|
||||||
|
|
||||||
|
class I2PService
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
I2PService(PSTR pszServiceName,
|
||||||
|
BOOL fCanStop = TRUE,
|
||||||
|
BOOL fCanShutdown = TRUE,
|
||||||
|
BOOL fCanPauseContinue = FALSE);
|
||||||
|
|
||||||
|
virtual ~I2PService(void);
|
||||||
|
|
||||||
|
static BOOL isService();
|
||||||
|
static BOOL Run(I2PService &service);
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
typedef std::function<bool ()> DaemonStart;
|
||||||
|
void SetDaemonStart (const DaemonStart& f) { m_daemonStart = f; };
|
||||||
|
|
||||||
|
typedef std::function<bool ()> DaemonStop;
|
||||||
|
void SetDaemonStop (const DaemonStop& f) { m_daemonStop = f; };
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual void OnStart(DWORD dwArgc, PSTR *pszArgv);
|
||||||
|
virtual void OnStop();
|
||||||
|
virtual void OnPause();
|
||||||
|
virtual void OnContinue();
|
||||||
|
virtual void OnShutdown();
|
||||||
|
void SetServiceStatus(DWORD dwCurrentState,
|
||||||
|
DWORD dwWin32ExitCode = NO_ERROR,
|
||||||
|
DWORD dwWaitHint = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv);
|
||||||
|
static void WINAPI ServiceCtrlHandler(DWORD dwCtrl);
|
||||||
|
void WorkerThread();
|
||||||
|
void Start(DWORD dwArgc, PSTR *pszArgv);
|
||||||
|
void Pause();
|
||||||
|
void Continue();
|
||||||
|
void Shutdown();
|
||||||
|
static I2PService* s_service;
|
||||||
|
PSTR m_name;
|
||||||
|
SERVICE_STATUS m_status;
|
||||||
|
SERVICE_STATUS_HANDLE m_statusHandle;
|
||||||
|
|
||||||
|
BOOL m_fStopping;
|
||||||
|
HANDLE m_hStoppedEvent;
|
||||||
|
|
||||||
|
std::thread* _worker;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
DaemonStart m_daemonStart;
|
||||||
|
DaemonStop m_daemonStop;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WIN32SERVICE_H__
|
||||||
@@ -17,7 +17,6 @@ option(WITH_LIBRARY "Build library" ON)
|
|||||||
option(WITH_BINARY "Build binary" ON)
|
option(WITH_BINARY "Build binary" ON)
|
||||||
option(WITH_STATIC "Static build" OFF)
|
option(WITH_STATIC "Static build" OFF)
|
||||||
option(WITH_UPNP "Include support for UPnP client" OFF)
|
option(WITH_UPNP "Include support for UPnP client" OFF)
|
||||||
option(WITH_MESHNET "Build for cjdns test network" OFF)
|
|
||||||
option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF)
|
option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF)
|
||||||
option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF)
|
option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF)
|
||||||
|
|
||||||
@@ -34,11 +33,13 @@ target_architecture(ARCHITECTURE)
|
|||||||
|
|
||||||
set(LIBI2PD_SRC_DIR ../libi2pd)
|
set(LIBI2PD_SRC_DIR ../libi2pd)
|
||||||
set(LIBI2PD_CLIENT_SRC_DIR ../libi2pd_client)
|
set(LIBI2PD_CLIENT_SRC_DIR ../libi2pd_client)
|
||||||
|
set(WEBCONSOLE_SRC_DIR ../libi2pd_webconsole)
|
||||||
set(LANG_SRC_DIR ../i18n)
|
set(LANG_SRC_DIR ../i18n)
|
||||||
set(DAEMON_SRC_DIR ../daemon)
|
set(DAEMON_SRC_DIR ../daemon)
|
||||||
|
|
||||||
include_directories(${LIBI2PD_SRC_DIR})
|
include_directories(${LIBI2PD_SRC_DIR})
|
||||||
include_directories(${LIBI2PD_CLIENT_SRC_DIR})
|
include_directories(${LIBI2PD_CLIENT_SRC_DIR})
|
||||||
|
include_directories(${WEBCONSOLE_SRC_DIR})
|
||||||
include_directories(${LANG_SRC_DIR})
|
include_directories(${LANG_SRC_DIR})
|
||||||
include_directories(${DAEMON_SRC_DIR})
|
include_directories(${DAEMON_SRC_DIR})
|
||||||
|
|
||||||
@@ -69,6 +70,18 @@ if(WITH_LIBRARY)
|
|||||||
COMPONENT Libraries)
|
COMPONENT Libraries)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
FILE(GLOB WEBCONSOLE_SRC ${WEBCONSOLE_SRC_DIR}/*.cpp)
|
||||||
|
add_library(libi2pdwebconsole ${WEBCONSOLE_SRC})
|
||||||
|
set_target_properties(libi2pdwebconsole PROPERTIES PREFIX "")
|
||||||
|
|
||||||
|
if(WITH_LIBRARY)
|
||||||
|
install(TARGETS libi2pdwebconsole
|
||||||
|
EXPORT libi2pdwebconsole
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
COMPONENT Libraries)
|
||||||
|
endif()
|
||||||
|
|
||||||
FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp)
|
FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp)
|
||||||
add_library(libi2pdlang ${LANG_SRC})
|
add_library(libi2pdlang ${LANG_SRC})
|
||||||
set_target_properties(libi2pdlang PROPERTIES PREFIX "")
|
set_target_properties(libi2pdlang PROPERTIES PREFIX "")
|
||||||
@@ -83,16 +96,11 @@ endif()
|
|||||||
|
|
||||||
set(DAEMON_SRC
|
set(DAEMON_SRC
|
||||||
"${DAEMON_SRC_DIR}/Daemon.cpp"
|
"${DAEMON_SRC_DIR}/Daemon.cpp"
|
||||||
"${DAEMON_SRC_DIR}/HTTPServer.cpp"
|
|
||||||
"${DAEMON_SRC_DIR}/I2PControl.cpp"
|
"${DAEMON_SRC_DIR}/I2PControl.cpp"
|
||||||
"${DAEMON_SRC_DIR}/i2pd.cpp"
|
"${DAEMON_SRC_DIR}/i2pd.cpp"
|
||||||
"${DAEMON_SRC_DIR}/UPnP.cpp"
|
"${DAEMON_SRC_DIR}/UPnP.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WITH_MESHNET)
|
|
||||||
add_definitions(-DMESHNET)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WITH_UPNP)
|
if(WITH_UPNP)
|
||||||
add_definitions(-DUSE_UPNP)
|
add_definitions(-DUSE_UPNP)
|
||||||
endif()
|
endif()
|
||||||
@@ -140,7 +148,7 @@ endif()
|
|||||||
|
|
||||||
# compiler flags customization(by system)
|
# compiler flags customization(by system)
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
list(APPEND DAEMON_SRC "${DAEMON_SRC_DIR}/UnixDaemon.cpp")
|
list(APPEND DAEMON_SRC "${DAEMON_SRC_DIR}/DaemonUnix.cpp")
|
||||||
if(NOT(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR APPLE))
|
if(NOT(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR APPLE))
|
||||||
# "'sleep_for' is not a member of 'std::this_thread'" in gcc 4.7/4.8
|
# "'sleep_for' is not a member of 'std::this_thread'" in gcc 4.7/4.8
|
||||||
add_definitions("-D_GLIBCXX_USE_NANOSLEEP=1")
|
add_definitions("-D_GLIBCXX_USE_NANOSLEEP=1")
|
||||||
@@ -170,20 +178,8 @@ endif()
|
|||||||
|
|
||||||
|
|
||||||
# libraries
|
# libraries
|
||||||
# TODO: once CMake 3.1+ becomes mainstream, see e.g. http://stackoverflow.com/a/29871891/673826
|
|
||||||
# use imported Threads::Threads instead
|
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
if(IOS)
|
find_package(Threads REQUIRED)
|
||||||
set(CMAKE_THREAD_LIBS_INIT "-lpthread")
|
|
||||||
set(CMAKE_HAVE_THREADS_LIBRARY 1)
|
|
||||||
set(CMAKE_USE_WIN32_THREADS_INIT 0)
|
|
||||||
set(CMAKE_USE_PTHREADS_INIT 1)
|
|
||||||
else()
|
|
||||||
find_package(Threads REQUIRED)
|
|
||||||
endif()
|
|
||||||
if(THREADS_HAVE_PTHREAD_ARG) # compile time flag
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WITH_STATIC)
|
if(WITH_STATIC)
|
||||||
set(Boost_USE_STATIC_LIBS ON)
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
@@ -202,7 +198,7 @@ else()
|
|||||||
add_definitions(-DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_REGEX_DYN_LINK)
|
add_definitions(-DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_REGEX_DYN_LINK)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(libi2pdclient libi2pd libi2pdlang)
|
target_link_libraries(libi2pdwebconsole libi2pdclient libi2pd libi2pdlang)
|
||||||
|
|
||||||
find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED)
|
find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED)
|
||||||
if(NOT DEFINED Boost_INCLUDE_DIRS)
|
if(NOT DEFINED Boost_INCLUDE_DIRS)
|
||||||
@@ -215,7 +211,7 @@ if(NOT DEFINED OPENSSL_INCLUDE_DIR)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(OPENSSL_VERSION VERSION_GREATER_EQUAL "3.0.0")
|
if(OPENSSL_VERSION VERSION_GREATER_EQUAL "3.0.0")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
|
add_definitions(-DOPENSSL_SUPPRESS_DEPRECATED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_UPNP)
|
if(WITH_UPNP)
|
||||||
@@ -235,12 +231,6 @@ endif()
|
|||||||
# load includes
|
# load includes
|
||||||
include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
|
include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
|
||||||
|
|
||||||
# warn if for meshnet
|
|
||||||
if(WITH_MESHNET)
|
|
||||||
message(STATUS "Building for testnet")
|
|
||||||
message(WARNING "This build will NOT work on mainline i2p")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(CheckAtomic)
|
include(CheckAtomic)
|
||||||
|
|
||||||
# show summary
|
# show summary
|
||||||
@@ -258,15 +248,10 @@ message(STATUS " LIBRARY : ${WITH_LIBRARY}")
|
|||||||
message(STATUS " BINARY : ${WITH_BINARY}")
|
message(STATUS " BINARY : ${WITH_BINARY}")
|
||||||
message(STATUS " STATIC BUILD : ${WITH_STATIC}")
|
message(STATUS " STATIC BUILD : ${WITH_STATIC}")
|
||||||
message(STATUS " UPnP : ${WITH_UPNP}")
|
message(STATUS " UPnP : ${WITH_UPNP}")
|
||||||
message(STATUS " MESHNET : ${WITH_MESHNET}")
|
|
||||||
message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}")
|
message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}")
|
||||||
message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}")
|
message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}")
|
||||||
message(STATUS "---------------------------------------")
|
message(STATUS "---------------------------------------")
|
||||||
|
|
||||||
if(WITH_MESHNET)
|
|
||||||
message(STATUS "WARNING: Using the MESHNET option will make it impossible to use the application with the main network!!!")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WITH_BINARY)
|
if(WITH_BINARY)
|
||||||
add_executable("${PROJECT_NAME}" ${DAEMON_SRC})
|
add_executable("${PROJECT_NAME}" ${DAEMON_SRC})
|
||||||
|
|
||||||
@@ -294,7 +279,7 @@ if(WITH_BINARY)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY})
|
target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY})
|
||||||
target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
|
target_link_libraries("${PROJECT_NAME}" libi2pdwebconsole libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} Threads::Threads ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
|
||||||
|
|
||||||
install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime)
|
install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime)
|
||||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
|
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
|
||||||
|
|||||||
@@ -52,9 +52,8 @@ REM converting configuration files to DOS format (make usable in Windows Notepad
|
|||||||
%xSH% "unix2dos contrib/i2pd.conf contrib/tunnels.conf contrib/tunnels.d/* contrib/webconsole/style.css" >> build\build.log 2>&1
|
%xSH% "unix2dos contrib/i2pd.conf contrib/tunnels.conf contrib/tunnels.d/* contrib/webconsole/style.css" >> build\build.log 2>&1
|
||||||
|
|
||||||
REM Prepare binary signing command if signing key and password provided
|
REM Prepare binary signing command if signing key and password provided
|
||||||
if defined SIGNKEY (
|
if defined SIGN (
|
||||||
if defined SIGNPASS (
|
echo Signing enabled
|
||||||
echo Signing options found
|
|
||||||
|
|
||||||
for %%X in (signtool.exe) do (set xSIGNTOOL=%%~$PATH:X)
|
for %%X in (signtool.exe) do (set xSIGNTOOL=%%~$PATH:X)
|
||||||
if not defined xSIGNTOOL (
|
if not defined xSIGNTOOL (
|
||||||
@@ -66,8 +65,15 @@ if defined SIGNKEY (
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
set "xSIGNOPTS=sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 /f ^"%SIGNKEY%^" /p ^"%SIGNPASS%^""
|
if defined SIGNKEY (
|
||||||
|
set "xSIGNKEYOPTS=/f ^"%SIGNKEY%^""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if defined SIGNPASS (
|
||||||
|
set "xSIGNPASSOPTS=/p ^"%SIGNPASS%^""
|
||||||
|
)
|
||||||
|
|
||||||
|
set "xSIGNOPTS=sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 %xSIGNKEYOPTS% %xSIGNPASSOPTS%"
|
||||||
)
|
)
|
||||||
|
|
||||||
REM starting building
|
REM starting building
|
||||||
|
|||||||
18
contrib/android_binary_only/.gitignore
vendored
18
contrib/android_binary_only/.gitignore
vendored
@@ -1,18 +0,0 @@
|
|||||||
gen
|
|
||||||
tests
|
|
||||||
bin
|
|
||||||
libs
|
|
||||||
log*
|
|
||||||
obj
|
|
||||||
.gradle
|
|
||||||
.idea
|
|
||||||
.externalNativeBuild
|
|
||||||
ant.properties
|
|
||||||
local.properties
|
|
||||||
build.sh
|
|
||||||
android.iml
|
|
||||||
build
|
|
||||||
gradle
|
|
||||||
gradlew
|
|
||||||
gradlew.bat
|
|
||||||
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
LOCAL_PATH := $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := i2pd
|
|
||||||
LOCAL_CPP_FEATURES := rtti exceptions
|
|
||||||
LOCAL_C_INCLUDES += $(IFADDRS_PATH) $(LIB_SRC_PATH) $(LIB_CLIENT_SRC_PATH) $(DAEMON_SRC_PATH)
|
|
||||||
LOCAL_STATIC_LIBRARIES := \
|
|
||||||
boost_system \
|
|
||||||
boost_date_time \
|
|
||||||
boost_filesystem \
|
|
||||||
boost_program_options \
|
|
||||||
crypto ssl \
|
|
||||||
miniupnpc
|
|
||||||
LOCAL_LDLIBS := -lz
|
|
||||||
|
|
||||||
LOCAL_SRC_FILES := $(IFADDRS_PATH)/ifaddrs.c \
|
|
||||||
$(wildcard $(LIB_SRC_PATH)/*.cpp)\
|
|
||||||
$(wildcard $(LIB_CLIENT_SRC_PATH)/*.cpp)\
|
|
||||||
$(DAEMON_SRC_PATH)/UnixDaemon.cpp \
|
|
||||||
$(DAEMON_SRC_PATH)/Daemon.cpp \
|
|
||||||
$(DAEMON_SRC_PATH)/UPnP.cpp \
|
|
||||||
$(DAEMON_SRC_PATH)/HTTPServer.cpp \
|
|
||||||
$(DAEMON_SRC_PATH)/I2PControl.cpp \
|
|
||||||
$(DAEMON_SRC_PATH)/i2pd.cpp
|
|
||||||
include $(BUILD_EXECUTABLE)
|
|
||||||
|
|
||||||
LOCAL_PATH := $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := boost_system
|
|
||||||
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_system.a
|
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
|
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
|
||||||
|
|
||||||
LOCAL_PATH := $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := boost_date_time
|
|
||||||
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a
|
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
|
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
|
||||||
|
|
||||||
LOCAL_PATH := $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := boost_filesystem
|
|
||||||
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a
|
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
|
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
|
||||||
|
|
||||||
LOCAL_PATH := $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := boost_program_options
|
|
||||||
LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a
|
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include
|
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
|
||||||
|
|
||||||
LOCAL_PATH := $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := crypto
|
|
||||||
LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/$(TARGET_ARCH_ABI)/lib/libcrypto.a
|
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/include
|
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
|
||||||
|
|
||||||
LOCAL_PATH := $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := ssl
|
|
||||||
LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/$(TARGET_ARCH_ABI)/lib/libssl.a
|
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/include
|
|
||||||
LOCAL_STATIC_LIBRARIES := crypto
|
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
|
||||||
|
|
||||||
LOCAL_PATH := $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := miniupnpc
|
|
||||||
LOCAL_SRC_FILES := $(MINIUPNP_PATH)/miniupnpc-2.1/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a
|
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnpc-2.1/include
|
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
APP_ABI := all
|
|
||||||
#APP_ABI += x86
|
|
||||||
#APP_ABI += x86_64
|
|
||||||
#APP_ABI += armeabi-v7a
|
|
||||||
#APP_ABI += arm64-v8a
|
|
||||||
#can be android-3 but will fail for x86 since arch-x86 is not present at ndkroot/platforms/android-3/ . libz is taken from there.
|
|
||||||
APP_PLATFORM := android-14
|
|
||||||
|
|
||||||
NDK_TOOLCHAIN_VERSION := clang
|
|
||||||
APP_STL := c++_static
|
|
||||||
|
|
||||||
# Enable c++17 extensions in source code
|
|
||||||
APP_CPPFLAGS += -std=c++17 -fvisibility=default -fPIE
|
|
||||||
|
|
||||||
APP_CPPFLAGS += -DANDROID_BINARY -DANDROID -D__ANDROID__ -DUSE_UPNP
|
|
||||||
APP_LDFLAGS += -rdynamic -fPIE -pie
|
|
||||||
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
|
|
||||||
APP_CPPFLAGS += -DANDROID_ARM7A
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Forcing debug optimization. Use `ndk-build NDK_DEBUG=1` instead.
|
|
||||||
#APP_OPTIM := debug
|
|
||||||
|
|
||||||
# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -b boost-1_72_0
|
|
||||||
# git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git
|
|
||||||
# git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git
|
|
||||||
# git clone https://github.com/PurpleI2P/android-ifaddrs.git
|
|
||||||
# change to your own
|
|
||||||
I2PD_LIBS_PATH = /path/to/libraries
|
|
||||||
BOOST_PATH = $(I2PD_LIBS_PATH)/Boost-for-Android-Prebuilt
|
|
||||||
OPENSSL_PATH = $(I2PD_LIBS_PATH)/OpenSSL-for-Android-Prebuilt
|
|
||||||
MINIUPNP_PATH = $(I2PD_LIBS_PATH)/MiniUPnP-for-Android-Prebuilt
|
|
||||||
IFADDRS_PATH = $(I2PD_LIBS_PATH)/android-ifaddrs
|
|
||||||
|
|
||||||
# don't change me
|
|
||||||
I2PD_SRC_PATH = $(PWD)/../..
|
|
||||||
|
|
||||||
LIB_SRC_PATH = $(I2PD_SRC_PATH)/libi2pd
|
|
||||||
LIB_CLIENT_SRC_PATH = $(I2PD_SRC_PATH)/libi2pd_client
|
|
||||||
DAEMON_SRC_PATH = $(I2PD_SRC_PATH)/daemon
|
|
||||||
2
contrib/android_binary_pack/.gitignore
vendored
2
contrib/android_binary_pack/.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
archive
|
|
||||||
i2pd_*_android_binary.zip
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Copyright (c) 2013-2020, The PurpleI2P Project
|
|
||||||
#
|
|
||||||
# This file is part of Purple i2pd project and licensed under BSD3
|
|
||||||
#
|
|
||||||
# See full license text in LICENSE file at top of project tree
|
|
||||||
|
|
||||||
GITDESC=$(git describe --tags)
|
|
||||||
|
|
||||||
declare -A ABILIST=(
|
|
||||||
["armeabi-v7a"]="armv7l"
|
|
||||||
["arm64-v8a"]="aarch64"
|
|
||||||
["x86"]="x86"
|
|
||||||
["x86_64"]="x86_64"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Remove old files and archives
|
|
||||||
if [ -d archive ]; then
|
|
||||||
rm -r archive
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f ../i2pd_*_android_binary.zip ]; then
|
|
||||||
rm i2pd_*_android_binary.zip
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Prepare files for package
|
|
||||||
mkdir archive
|
|
||||||
|
|
||||||
for ABI in "${!ABILIST[@]}"; do
|
|
||||||
if [ -f ../android_binary_only/libs/${ABI}/i2pd ]; then
|
|
||||||
cp ../android_binary_only/libs/${ABI}/i2pd archive/i2pd-${ABILIST[$ABI]}
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
cp i2pd archive/i2pd
|
|
||||||
cp -rH ../android/assets/certificates archive/
|
|
||||||
cp -rH ../android/assets/tunnels.conf.d archive/
|
|
||||||
cp -H ../android/assets/i2pd.conf archive/
|
|
||||||
cp -H ../android/assets/tunnels.conf archive/
|
|
||||||
|
|
||||||
# Compress files
|
|
||||||
cd archive
|
|
||||||
zip -r6 ../i2pd_${GITDESC}_android_binary.zip .
|
|
||||||
|
|
||||||
# Remove temporary folder
|
|
||||||
cd ..
|
|
||||||
rm -r archive
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Copyright (c) 2013-2020, The PurpleI2P Project
|
|
||||||
#
|
|
||||||
# This file is part of Purple i2pd project and licensed under BSD3
|
|
||||||
#
|
|
||||||
# See full license text in LICENSE file at top of project tree
|
|
||||||
#
|
|
||||||
# That script written for use with Termux.
|
|
||||||
|
|
||||||
# https://stackoverflow.com/a/246128
|
|
||||||
SOURCE="${0}"
|
|
||||||
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
|
|
||||||
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
|
|
||||||
SOURCE="$(readlink "$SOURCE")"
|
|
||||||
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
|
||||||
done
|
|
||||||
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
|
|
||||||
|
|
||||||
arch=$(uname -m)
|
|
||||||
|
|
||||||
screenfind=$(which screen)
|
|
||||||
if [ -z $screenfind ]; then
|
|
||||||
echo "Can't find 'screen' installed. That script needs it!";
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z i2pd-$arch ]; then
|
|
||||||
echo "Can't find i2pd binary for your archtecture.";
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
|
|
||||||
screen -AmdS i2pd ./i2pd-$arch --datadir=$DIR
|
|
||||||
14
contrib/certificates/family/stormycloud.crt
Normal file
14
contrib/certificates/family/stormycloud.crt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICKDCCAc6gAwIBAgIUcPHZXtYSqGNRCD6z8gp79WUFtI0wCgYIKoZIzj0EAwIw
|
||||||
|
gZMxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEPMA0GA1UEBwwGQXVzdGlu
|
||||||
|
MRgwFgYDVQQKDA9TdG9ybXlDbG91ZCBJbmMxIzAhBgNVBAMMGnN0b3JteWNsb3Vk
|
||||||
|
LmZhbWlseS5pMnAubmV0MSQwIgYJKoZIhvcNAQkBFhVhZG1pbkBzdG9ybXljbG91
|
||||||
|
ZC5vcmcwHhcNMjIwMzE5MTU1MjU2WhcNMzIwMzE2MTU1MjU2WjCBkzELMAkGA1UE
|
||||||
|
BhMCVVMxDjAMBgNVBAgMBVRleGFzMQ8wDQYDVQQHDAZBdXN0aW4xGDAWBgNVBAoM
|
||||||
|
D1N0b3JteUNsb3VkIEluYzEjMCEGA1UEAwwac3Rvcm15Y2xvdWQuZmFtaWx5Lmky
|
||||||
|
cC5uZXQxJDAiBgkqhkiG9w0BCQEWFWFkbWluQHN0b3JteWNsb3VkLm9yZzBZMBMG
|
||||||
|
ByqGSM49AgEGCCqGSM49AwEHA0IABFUli0hvJEmowNjJVjbKEIWBJhqe973S4VdL
|
||||||
|
cJuA5yY3dC4Y998abWEox7/Y1BhnBbpJuiodA341bXKkLMXQy/kwCgYIKoZIzj0E
|
||||||
|
AwIDSAAwRQIgD12F/TfY3iV1/WDF7BSKgbD5g2MfELUIy1dtUlJQuJUCIQD69mZw
|
||||||
|
V1Z9j2x0ZsuirS3i6AMfVyTDj0RFS3U1jeHzIQ==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
34
contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt
Normal file
34
contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFzTCCA7WgAwIBAgIQeUqFi0fHNQopg6BZlBLhVzANBgkqhkiG9w0BAQsFADBy
|
||||||
|
MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK
|
||||||
|
ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEbMBkGA1UEAwwS
|
||||||
|
aTJwLXJlc2VlZEBtazE2LmRlMB4XDTIyMDIwNTE3MzkzM1oXDTMyMDIwNTE3Mzkz
|
||||||
|
M1owcjELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwG
|
||||||
|
A1UEChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGzAZBgNV
|
||||||
|
BAMMEmkycC1yZXNlZWRAbWsxNi5kZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
|
||||||
|
AgoCggIBAMYxs2D2xpN/8blGawvAlU9DemHIxApOEwaLNfh8aAvqEdB41NTqcx4U
|
||||||
|
H8VchSormCfkCvezuMHO+K2HX7ihEZ1v6tbr6aX6hY9UZUyDDYsKmJoB1oKEhddv
|
||||||
|
5UYfcWPE2eSykdFsWgTQD6Z+cRQWHEoCzb7qc+Jrw6KcnHMD0VrmBrEQPzTBxMHW
|
||||||
|
4HC97PVkSLJTDArnS6ZiX4IbWRPw/mbpJT6EoVZo8J/it0pdn/X4KodEXDcnEMSe
|
||||||
|
VRulfZH/nSmOOvKhoHPckmgz/u66BlnuSYXEIB0KfDIcAlSYiPDxGnAemTozJYXA
|
||||||
|
UVMeFMs+YE5wiPgzzu+vpC31xtZLq0gyaCfgEi1P9j2ES/8pH3Gw6W2OH4kBx+jO
|
||||||
|
TBsfI+ph6qFZ3WWT23MRVyl3ATuI/GHdczTxD9JaOn74lLI+Hnu8wXnyztVWkTMB
|
||||||
|
4sAnzjdeHkvNDyQ10vSaN0HnGfg6zuAuUSqFQujFF8Vg8ZCcsh8GouWfzYDvi9mj
|
||||||
|
9pfxx8v6UCC719I4J9CgFjWnn2Hqez3fO8fFulY61VPyCCZp4gKWbI2SIQP/n5gz
|
||||||
|
ecYJRrJoem+rYfEQ/fwxROsvm3fCO4D6dt7ILRuX286GDIw2qSvP1zZVAioMwSj3
|
||||||
|
9CAjKLwD/BhTRiMOlpaVv6IWqjtevbiaIKvbHTnoxvkGsDqe3gJhAgMBAAGjXzBd
|
||||||
|
MA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEw
|
||||||
|
DwYDVR0TAQH/BAUwAwEB/zAbBgNVHQ4EFAQSaTJwLXJlc2VlZEBtazE2LmRlMA0G
|
||||||
|
CSqGSIb3DQEBCwUAA4ICAQAb+x6XpJdjpVYw2bvWIUbatQJwq0YaEW5W61xGLgIG
|
||||||
|
a37oll3YZbSY9Vk+N1cE0f61L3ya4Ioz6zlH/MO2zUG/dEk8vqdgIPUYJvyF7wwF
|
||||||
|
w3/G4VMaDKOJx4bAZNmaiRFGYNhCOhCnZx6uZGrLNIJ2Dc+mflrGmGwYphtXVV3e
|
||||||
|
Iv+ki3gSRgfXuMfKi4B5bLPnz7XDe4TSmwZZSRac4ly4KqmZUyntqbilRxaGTej3
|
||||||
|
VYJ1tac8yppyk5N3VopMQNmBarNZG16wSOTD7CtKgn382jgRW8cR7BMeqhORivp0
|
||||||
|
ZnPJFhzh4uthdlPdXXo6lxfvZjfiwlDPytvEu2QBz3urTgopGqRLcTBnLucWg9li
|
||||||
|
OSy9z7hNEnIN3iIJJAwI1wBdDa7K0h3PFBbIUa7X2ybn81VeNSfO25Lo8YTZEKsc
|
||||||
|
wcThJrNV6qOQv8rM/7aXugi6+VzPlCR+18iKRbebCnlqGR2dT1zFtj3negtOkrjo
|
||||||
|
LH4H6VUr3q2Ie56IubS2hUKiUkDm0ckP3Vum35GGntyEAzl6uyog0hJFOJb3aq30
|
||||||
|
YQLzyVEOz8NnA+32oMRzJJdDxQ7pqG5fgq7EF4d++YSgEfdVXxvfgXQ6m3jAyC7Z
|
||||||
|
p/gX4rlxNsjeGU3Ds51wkmhH4IB1aSQr52PE6RaBhhh3SmADEv6S/3eGvE4F4MN5
|
||||||
|
2Q==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
@@ -110,8 +110,8 @@ port = 7070
|
|||||||
# user = i2pd
|
# user = i2pd
|
||||||
# pass = changeme
|
# pass = changeme
|
||||||
## Select webconsole language
|
## Select webconsole language
|
||||||
## Currently supported english (default), afrikaans, armenian, german, russian,
|
## Currently supported english (default), afrikaans, armenian, french, german,
|
||||||
## turkmen, ukrainian and uzbek languages
|
## russian, turkmen, ukrainian and uzbek languages
|
||||||
# lang = english
|
# lang = english
|
||||||
|
|
||||||
[httpproxy]
|
[httpproxy]
|
||||||
|
|||||||
@@ -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.41.0
|
Version: 2.42.1
|
||||||
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
|
||||||
@@ -57,8 +57,14 @@ cd build
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
|
||||||
%if 0%{?fedora} >= 35
|
%if 0%{?rhel} == 9
|
||||||
pushd redhat-linux-build
|
pushd redhat-linux-build
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%if 0%{?fedora} >= 35
|
||||||
|
%if 0%{?fedora} < 37
|
||||||
|
pushd redhat-linux-build
|
||||||
|
%endif
|
||||||
%else
|
%else
|
||||||
%if 0%{?fedora} >= 33
|
%if 0%{?fedora} >= 33
|
||||||
pushd %{_target_platform}
|
pushd %{_target_platform}
|
||||||
@@ -71,10 +77,16 @@ pushd build
|
|||||||
|
|
||||||
make %{?_smp_mflags}
|
make %{?_smp_mflags}
|
||||||
|
|
||||||
%if 0%{?fedora} >= 33
|
%if 0%{?rhel} == 9
|
||||||
popd
|
popd
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if 0%{?fedora} >= 33
|
||||||
|
%if 0%{?fedora} < 37
|
||||||
|
popd
|
||||||
|
%endif
|
||||||
|
%endif
|
||||||
|
|
||||||
%if 0%{?mageia} > 7
|
%if 0%{?mageia} > 7
|
||||||
popd
|
popd
|
||||||
%endif
|
%endif
|
||||||
@@ -82,8 +94,14 @@ popd
|
|||||||
%install
|
%install
|
||||||
pushd build
|
pushd build
|
||||||
|
|
||||||
%if 0%{?fedora} >= 35
|
%if 0%{?rhel} == 9
|
||||||
pushd redhat-linux-build
|
pushd redhat-linux-build
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%if 0%{?fedora} >= 35
|
||||||
|
%if 0%{?fedora} < 37
|
||||||
|
pushd redhat-linux-build
|
||||||
|
%endif
|
||||||
%else
|
%else
|
||||||
%if 0%{?fedora} >= 33
|
%if 0%{?fedora} >= 33
|
||||||
pushd %{_target_platform}
|
pushd %{_target_platform}
|
||||||
@@ -146,6 +164,12 @@ getent passwd i2pd >/dev/null || \
|
|||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1
|
||||||
|
- update to 2.42.1
|
||||||
|
|
||||||
|
* Sun May 22 2022 orignal <orignal@i2pmail.org> - 2.42.0
|
||||||
|
- update to 2.42.0
|
||||||
|
|
||||||
* Sun Feb 20 2022 r4sas <r4sas@i2pmail.org> - 2.41.0
|
* Sun Feb 20 2022 r4sas <r4sas@i2pmail.org> - 2.41.0
|
||||||
- update to 2.41.0
|
- update to 2.41.0
|
||||||
- fixed build on Fedora Copr over openssl trunk code
|
- fixed build on Fedora Copr over openssl trunk code
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Name: i2pd
|
Name: i2pd
|
||||||
Version: 2.41.0
|
Version: 2.42.1
|
||||||
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,8 +54,14 @@ cd build
|
|||||||
%endif
|
%endif
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
%if 0%{?fedora} >= 35
|
%if 0%{?rhel} == 9
|
||||||
pushd redhat-linux-build
|
pushd redhat-linux-build
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%if 0%{?fedora} >= 35
|
||||||
|
%if 0%{?fedora} < 37
|
||||||
|
pushd redhat-linux-build
|
||||||
|
%endif
|
||||||
%else
|
%else
|
||||||
%if 0%{?fedora} >= 33
|
%if 0%{?fedora} >= 33
|
||||||
pushd %{_target_platform}
|
pushd %{_target_platform}
|
||||||
@@ -68,10 +74,16 @@ pushd build
|
|||||||
|
|
||||||
make %{?_smp_mflags}
|
make %{?_smp_mflags}
|
||||||
|
|
||||||
%if 0%{?fedora} >= 33
|
%if 0%{?rhel} == 9
|
||||||
popd
|
popd
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if 0%{?fedora} >= 33
|
||||||
|
%if 0%{?fedora} < 37
|
||||||
|
popd
|
||||||
|
%endif
|
||||||
|
%endif
|
||||||
|
|
||||||
%if 0%{?mageia} > 7
|
%if 0%{?mageia} > 7
|
||||||
popd
|
popd
|
||||||
%endif
|
%endif
|
||||||
@@ -79,8 +91,14 @@ popd
|
|||||||
%install
|
%install
|
||||||
pushd build
|
pushd build
|
||||||
|
|
||||||
%if 0%{?fedora} >= 35
|
%if 0%{?rhel} == 9
|
||||||
pushd redhat-linux-build
|
pushd redhat-linux-build
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%if 0%{?fedora} >= 35
|
||||||
|
%if 0%{?fedora} < 37
|
||||||
|
pushd redhat-linux-build
|
||||||
|
%endif
|
||||||
%else
|
%else
|
||||||
%if 0%{?fedora} >= 33
|
%if 0%{?fedora} >= 33
|
||||||
pushd %{_target_platform}
|
pushd %{_target_platform}
|
||||||
@@ -143,6 +161,12 @@ getent passwd i2pd >/dev/null || \
|
|||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1
|
||||||
|
- update to 2.42.1
|
||||||
|
|
||||||
|
* Sun May 22 2022 orignal <orignal@i2pmail.org> - 2.42.0
|
||||||
|
- update to 2.42.0
|
||||||
|
|
||||||
* Sun Feb 20 2022 r4sas <r4sas@i2pmail.org> - 2.41.0
|
* Sun Feb 20 2022 r4sas <r4sas@i2pmail.org> - 2.41.0
|
||||||
- update to 2.41.0
|
- update to 2.41.0
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -58,12 +58,16 @@ namespace util
|
|||||||
bool Daemon_Singleton::IsService () const
|
bool Daemon_Singleton::IsService () const
|
||||||
{
|
{
|
||||||
bool service = false;
|
bool service = false;
|
||||||
#ifndef _WIN32
|
|
||||||
i2p::config::GetOption("service", service);
|
i2p::config::GetOption("service", service);
|
||||||
#endif
|
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Daemon_Singleton::setDataDir(std::string path)
|
||||||
|
{
|
||||||
|
if (path != "")
|
||||||
|
DaemonDataDir = path;
|
||||||
|
}
|
||||||
|
|
||||||
bool Daemon_Singleton::init(int argc, char* argv[]) {
|
bool Daemon_Singleton::init(int argc, char* argv[]) {
|
||||||
return init(argc, argv, nullptr);
|
return init(argc, argv, nullptr);
|
||||||
}
|
}
|
||||||
@@ -74,7 +78,13 @@ namespace util
|
|||||||
i2p::config::ParseCmdline(argc, argv);
|
i2p::config::ParseCmdline(argc, argv);
|
||||||
|
|
||||||
std::string config; i2p::config::GetOption("conf", config);
|
std::string config; i2p::config::GetOption("conf", config);
|
||||||
std::string datadir; i2p::config::GetOption("datadir", datadir);
|
std::string datadir;
|
||||||
|
if(DaemonDataDir != "") {
|
||||||
|
datadir = DaemonDataDir;
|
||||||
|
} else {
|
||||||
|
i2p::config::GetOption("datadir", datadir);
|
||||||
|
}
|
||||||
|
|
||||||
i2p::fs::DetectDataDir(datadir, IsService());
|
i2p::fs::DetectDataDir(datadir, IsService());
|
||||||
i2p::fs::Init();
|
i2p::fs::Init();
|
||||||
|
|
||||||
@@ -151,11 +161,7 @@ namespace util
|
|||||||
|
|
||||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||||
#ifdef MESHNET
|
|
||||||
// manual override for meshnet
|
|
||||||
ipv4 = false;
|
|
||||||
ipv6 = true;
|
|
||||||
#endif
|
|
||||||
// ifname -> address
|
// ifname -> address
|
||||||
std::string ifname; i2p::config::GetOption("ifname", ifname);
|
std::string ifname; i2p::config::GetOption("ifname", ifname);
|
||||||
if (ipv4 && i2p::config::IsDefault ("address4"))
|
if (ipv4 && i2p::config::IsDefault ("address4"))
|
||||||
@@ -244,6 +250,18 @@ namespace util
|
|||||||
if (!ipv4 && !ipv6)
|
if (!ipv4 && !ipv6)
|
||||||
i2p::context.SetStatus (eRouterStatusMesh);
|
i2p::context.SetStatus (eRouterStatusMesh);
|
||||||
}
|
}
|
||||||
|
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||||
|
if (ssu2)
|
||||||
|
{
|
||||||
|
bool published; i2p::config::GetOption("ssu2.published", published);
|
||||||
|
if (published)
|
||||||
|
{
|
||||||
|
uint16_t ssu2port; i2p::config::GetOption("ssu2.port", ssu2port);
|
||||||
|
i2p::context.PublishSSU2Address (ssu2port, true, ipv4, ipv6); // publish
|
||||||
|
}
|
||||||
|
else
|
||||||
|
i2p::context.PublishSSU2Address (0, false, ipv4, ipv6); // unpublish
|
||||||
|
}
|
||||||
|
|
||||||
bool transit; i2p::config::GetOption("notransit", transit);
|
bool transit; i2p::config::GetOption("notransit", transit);
|
||||||
i2p::context.SetAcceptsTunnels (!transit);
|
i2p::context.SetAcceptsTunnels (!transit);
|
||||||
@@ -377,6 +395,7 @@ namespace util
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||||
|
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||||
bool checkInReserved; i2p::config::GetOption("reservedrange", checkInReserved);
|
bool checkInReserved; i2p::config::GetOption("reservedrange", checkInReserved);
|
||||||
LogPrint(eLogInfo, "Daemon: Starting Transports");
|
LogPrint(eLogInfo, "Daemon: Starting Transports");
|
||||||
@@ -384,7 +403,7 @@ namespace util
|
|||||||
if(!ntcp2) LogPrint(eLogInfo, "Daemon: NTCP2 disabled");
|
if(!ntcp2) LogPrint(eLogInfo, "Daemon: NTCP2 disabled");
|
||||||
|
|
||||||
i2p::transport::transports.SetCheckReserved(checkInReserved);
|
i2p::transport::transports.SetCheckReserved(checkInReserved);
|
||||||
i2p::transport::transports.Start(ntcp2, ssu);
|
i2p::transport::transports.Start(ntcp2, ssu, ssu2);
|
||||||
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2())
|
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2())
|
||||||
LogPrint(eLogInfo, "Daemon: Transports started");
|
LogPrint(eLogInfo, "Daemon: Transports started");
|
||||||
else
|
else
|
||||||
@@ -404,6 +423,10 @@ namespace util
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
d.httpServer = std::unique_ptr<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
|
d.httpServer = std::unique_ptr<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
|
||||||
|
d.httpServer->SetDaemonStop (std::bind (Daemon_Singleton::stop, this));
|
||||||
|
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
||||||
|
d.httpServer->SetDaemonGracefulTimer (std::bind (gracefulShutdownInterval, this));
|
||||||
|
#endif
|
||||||
d.httpServer->Start();
|
d.httpServer->Start();
|
||||||
}
|
}
|
||||||
catch (std::exception& ex)
|
catch (std::exception& ex)
|
||||||
|
|||||||
@@ -22,25 +22,31 @@ namespace util
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual bool init(int argc, char* argv[], std::shared_ptr<std::ostream> logstream);
|
virtual bool init (int argc, char* argv[], std::shared_ptr<std::ostream> logstream);
|
||||||
virtual bool init(int argc, char* argv[]);
|
virtual bool init (int argc, char* argv[]);
|
||||||
virtual bool start();
|
virtual bool start ();
|
||||||
virtual bool stop();
|
virtual bool stop ();
|
||||||
virtual void run () {};
|
virtual void run () {};
|
||||||
|
|
||||||
|
virtual void setDataDir (std::string path);
|
||||||
|
|
||||||
bool isDaemon;
|
bool isDaemon;
|
||||||
bool running;
|
bool running;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
Daemon_Singleton();
|
Daemon_Singleton ();
|
||||||
virtual ~Daemon_Singleton();
|
virtual ~Daemon_Singleton ();
|
||||||
|
|
||||||
bool IsService () const;
|
bool IsService () const;
|
||||||
|
|
||||||
// d-pointer for httpServer, httpProxy, etc.
|
// d-pointer for httpServer, httpProxy, etc.
|
||||||
class Daemon_Singleton_Private;
|
class Daemon_Singleton_Private;
|
||||||
Daemon_Singleton_Private &d;
|
Daemon_Singleton_Private &d;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::string DaemonDataDir;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(QT_GUI_LIB) // check if QT
|
#if defined(QT_GUI_LIB) // check if QT
|
||||||
@@ -69,14 +75,17 @@ namespace util
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init(int argc, char* argv[]);
|
bool init (int argc, char* argv[]);
|
||||||
bool start();
|
bool start ();
|
||||||
bool stop();
|
bool stop ();
|
||||||
void run ();
|
void run ();
|
||||||
|
|
||||||
bool isGraceful;
|
void SetIsGraceful (bool state) { m_isGraceful = state; };
|
||||||
|
const bool GetIsGraceful () { return m_isGraceful; };
|
||||||
|
|
||||||
DaemonWin32 ():isGraceful(false) {}
|
private:
|
||||||
|
|
||||||
|
bool m_isGraceful;
|
||||||
};
|
};
|
||||||
#elif (defined(ANDROID) && !defined(ANDROID_BINARY))
|
#elif (defined(ANDROID) && !defined(ANDROID_BINARY))
|
||||||
#define Daemon i2p::util::DaemonAndroid::Instance()
|
#define Daemon i2p::util::DaemonAndroid::Instance()
|
||||||
@@ -103,8 +112,8 @@ namespace util
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool start();
|
bool start ();
|
||||||
bool stop();
|
bool stop ();
|
||||||
void run ();
|
void run ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <clocale>
|
#include <clocale>
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
@@ -13,7 +15,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#include "Win32Service.h"
|
||||||
#ifdef WIN32_APP
|
#ifdef WIN32_APP
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "Win32App.h"
|
#include "Win32App.h"
|
||||||
@@ -37,8 +39,26 @@ namespace util
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
i2p::win32::SetIsGraceful (std::bind (&DaemonWin32::SetIsGraceful, this, std::placeholders::_1));
|
||||||
|
i2p::win32::GetIsGraceful (std::bind (&DaemonWin32::GetIsGraceful, this));
|
||||||
|
|
||||||
if (!Daemon_Singleton::init(argc, argv))
|
if (!Daemon_Singleton::init(argc, argv))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (isDaemon)
|
||||||
|
{
|
||||||
|
LogPrint(eLogDebug, "Daemon: running as service");
|
||||||
|
I2PService service((PSTR)SERVICE_NAME);
|
||||||
|
service.SetDaemonStart (std::bind (&DaemonWin32::start, this));
|
||||||
|
service.SetDaemonStop (std::bind (&DaemonWin32::stop, this));
|
||||||
|
if (!I2PService::Run(service))
|
||||||
|
{
|
||||||
|
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,8 +87,7 @@ namespace client
|
|||||||
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
|
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
|
||||||
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
|
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
|
||||||
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler;
|
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler;
|
||||||
m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] =
|
m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = &I2PControlService::TunnelsSuccessRateHandler;
|
||||||
&I2PControlService::TunnelsSuccessRateHandler;
|
|
||||||
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
|
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
|
||||||
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
|
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
|
||||||
|
|
||||||
|
|||||||
13
debian/changelog
vendored
13
debian/changelog
vendored
@@ -1,3 +1,16 @@
|
|||||||
|
i2pd (2.42.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* updated to version 2.42.1/0.9.54
|
||||||
|
* remove -O3 optimization flag
|
||||||
|
|
||||||
|
-- r4sas <r4sas@i2pmail.org> Tue, 24 May 2022 12:00:00 +0000
|
||||||
|
|
||||||
|
i2pd (2.42.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* updated to version 2.42.0/0.9.54
|
||||||
|
|
||||||
|
-- orignal <orignal@i2pmail.org> Sun, 22 May 2022 16:00:00 +0000
|
||||||
|
|
||||||
i2pd (2.41.0-1) unstable; urgency=medium
|
i2pd (2.41.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
* updated to version 2.41.0/0.9.53
|
* updated to version 2.41.0/0.9.53
|
||||||
|
|||||||
6
debian/patches/02-upnp.patch
vendored
6
debian/patches/02-upnp.patch
vendored
@@ -2,14 +2,14 @@ Description: Enable UPnP usage in package
|
|||||||
Author: r4sas <r4sas@i2pmail.org>
|
Author: r4sas <r4sas@i2pmail.org>
|
||||||
|
|
||||||
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
Reviewed-By: r4sas <r4sas@i2pmail.org>
|
||||||
Last-Update: 2021-10-22
|
Last-Update: 2022-03-23
|
||||||
|
|
||||||
--- i2pd.orig/Makefile
|
--- i2pd.orig/Makefile
|
||||||
+++ i2pd/Makefile
|
+++ i2pd/Makefile
|
||||||
@@ -32,7 +32,7 @@ include filelist.mk
|
@@ -31,7 +31,7 @@ include filelist.mk
|
||||||
|
|
||||||
USE_AESNI := $(or $(USE_AESNI),yes)
|
USE_AESNI := $(or $(USE_AESNI),yes)
|
||||||
USE_STATIC := $(or $(USE_STATIC),no)
|
USE_STATIC := $(or $(USE_STATIC),no)
|
||||||
USE_MESHNET := $(or $(USE_MESHNET),no)
|
|
||||||
-USE_UPNP := $(or $(USE_UPNP),no)
|
-USE_UPNP := $(or $(USE_UPNP),no)
|
||||||
+USE_UPNP := $(or $(USE_UPNP),yes)
|
+USE_UPNP := $(or $(USE_UPNP),yes)
|
||||||
DEBUG := $(or $(DEBUG),yes)
|
DEBUG := $(or $(DEBUG),yes)
|
||||||
|
|||||||
9
debian/rules
vendored
9
debian/rules
vendored
@@ -1,16 +1,13 @@
|
|||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
#export DH_VERBOSE=1
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
|
|
||||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||||
|
|
||||||
|
|
||||||
include /usr/share/dpkg/architecture.mk
|
include /usr/share/dpkg/architecture.mk
|
||||||
|
|
||||||
export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic -O3
|
export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic
|
||||||
|
|
||||||
export DEB_LDFLAGS_MAINT_APPEND =
|
export DEB_LDFLAGS_MAINT_APPEND =
|
||||||
|
|
||||||
|
|
||||||
%:
|
%:
|
||||||
dh $@ --parallel
|
dh $@ --parallel
|
||||||
|
|
||||||
|
override_dh_auto_install:
|
||||||
|
|||||||
23
filelist.mk
23
filelist.mk
@@ -1,26 +1,7 @@
|
|||||||
#LIB_SRC = \
|
|
||||||
# BloomFilter.cpp Gzip.cpp Crypto.cpp Datagram.cpp Garlic.cpp I2NPProtocol.cpp LeaseSet.cpp \
|
|
||||||
# Log.cpp NTCPSession.cpp NetDb.cpp NetDbRequests.cpp Profiling.cpp \
|
|
||||||
# Reseed.cpp RouterContext.cpp RouterInfo.cpp Signature.cpp SSU.cpp \
|
|
||||||
# SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \
|
|
||||||
# Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp TunnelGateway.cpp \
|
|
||||||
# Destination.cpp Base.cpp I2PEndian.cpp FS.cpp Config.cpp Family.cpp \
|
|
||||||
# Config.cpp HTTP.cpp Timestamp.cpp util.cpp api.cpp Gost.cpp
|
|
||||||
|
|
||||||
LIB_SRC = $(wildcard $(LIB_SRC_DIR)/*.cpp)
|
LIB_SRC = $(wildcard $(LIB_SRC_DIR)/*.cpp)
|
||||||
|
|
||||||
#LIB_CLIENT_SRC = \
|
|
||||||
# AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp MatchedDestination.cpp \
|
|
||||||
# SAM.cpp SOCKS.cpp HTTPProxy.cpp I2CP.cpp
|
|
||||||
|
|
||||||
LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_SRC_DIR)/*.cpp)
|
LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_SRC_DIR)/*.cpp)
|
||||||
|
|
||||||
# also: Daemon{Linux,Win32}.cpp will be added later
|
|
||||||
#DAEMON_SRC = \
|
|
||||||
# HTTPServer.cpp I2PControl.cpp UPnP.cpp Daemon.cpp i2pd.cpp
|
|
||||||
|
|
||||||
LANG_SRC = $(wildcard $(LANG_SRC_DIR)/*.cpp)
|
LANG_SRC = $(wildcard $(LANG_SRC_DIR)/*.cpp)
|
||||||
|
WEBCONSOLE_SRC = $(wildcard $(WEBCONSOLE_SRC_DIR)/*.cpp)
|
||||||
|
|
||||||
WRAP_LIB_SRC = $(wildcard $(WRAP_SRC_DIR)/*.cpp)
|
WRAP_LIB_SRC = $(wildcard $(WRAP_SRC_DIR)/*.cpp)
|
||||||
|
DAEMON_SRC = $(DAEMON_SRC_DIR)/Daemon.cpp $(DAEMON_SRC_DIR)/I2PControl.cpp $(DAEMON_SRC_DIR)/i2pd.cpp $(DAEMON_SRC_DIR)/UPnP.cpp
|
||||||
DAEMON_SRC = $(wildcard $(DAEMON_SRC_DIR)/*.cpp)
|
|
||||||
|
|||||||
102
i18n/French.cpp
Normal file
102
i18n/French.cpp
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022, The PurpleI2P Project
|
||||||
|
*
|
||||||
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
|
*
|
||||||
|
* See full license text in LICENSE file at top of project tree
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include "I18N.h"
|
||||||
|
|
||||||
|
// French localization file
|
||||||
|
|
||||||
|
namespace i2p
|
||||||
|
{
|
||||||
|
namespace i18n
|
||||||
|
{
|
||||||
|
namespace french // language namespace
|
||||||
|
{
|
||||||
|
// language name in lowercase
|
||||||
|
static std::string language = "french";
|
||||||
|
|
||||||
|
// See for language plural forms here:
|
||||||
|
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
||||||
|
static int plural (int n) {
|
||||||
|
return n != 1 ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::map<std::string, std::string> strings
|
||||||
|
{
|
||||||
|
{"KiB", "Kio"},
|
||||||
|
{"MiB", "Mio"},
|
||||||
|
{"GiB", "Gio"},
|
||||||
|
{"building", "En construction"},
|
||||||
|
{"failed", "echoué"},
|
||||||
|
{"expiring", "expiré"},
|
||||||
|
{"established", "établi"},
|
||||||
|
{"unknown", "inconnu"},
|
||||||
|
{"exploratory", "exploratoire"},
|
||||||
|
{"<b>i2pd</b> webconsole", "Console web <b>i2pd</b>"},
|
||||||
|
{"Main page", "Page principale"},
|
||||||
|
{"Router commands", "Commandes du routeur"},
|
||||||
|
{"Local Destinations", "Destinations locales"},
|
||||||
|
{"Tunnels", "Tunnels"},
|
||||||
|
{"Transit Tunnels", "Tunnels transitoires"},
|
||||||
|
{"I2P tunnels", "Tunnels I2P"},
|
||||||
|
{"SAM sessions", "Sessions SAM"},
|
||||||
|
{"ERROR", "ERREUR"},
|
||||||
|
{"OK", "OK"},
|
||||||
|
{"Firewalled", "Derrière un pare-feu"},
|
||||||
|
{"Error", "Erreur"},
|
||||||
|
{"Offline", "Hors ligne"},
|
||||||
|
{"Uptime", "Temps de fonctionnement"},
|
||||||
|
{"Network status", "État du réseau"},
|
||||||
|
{"Network status v6", "État du réseau v6"},
|
||||||
|
{"Stopping in", "Arrêt dans"},
|
||||||
|
{"Family", "Famille"},
|
||||||
|
{"Tunnel creation success rate", "Taux de succès de création de tunnels"},
|
||||||
|
{"Received", "Reçu"},
|
||||||
|
{"KiB/s", "kio/s"},
|
||||||
|
{"Sent", "Envoyé"},
|
||||||
|
{"Transit", "Transit"},
|
||||||
|
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour regarder."},
|
||||||
|
{"Router Ident", "Identifiant du routeur"},
|
||||||
|
{"Router Family", "Famille du routeur"},
|
||||||
|
{"Version", "Version"},
|
||||||
|
{"Our external address", "Notre adresse externe"},
|
||||||
|
{"Client Tunnels", "Tunnels clients"},
|
||||||
|
{"Services", "Services"},
|
||||||
|
{"Enabled", "Activé"},
|
||||||
|
{"Disabled", "Désactivé"},
|
||||||
|
{"Encrypted B33 address", "Adresse B33 chiffrée"},
|
||||||
|
{"Domain", "Domaine"},
|
||||||
|
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Note:</b> La chaîne résultante peut seulement être utilisée pour enregistrer les domaines 2LD (exemple.i2p). Pour enregistrer des sous-domaines, veuillez utiliser i2pd-tools."},
|
||||||
|
{"Address", "Adresse"},
|
||||||
|
{"ms", "ms"},
|
||||||
|
{"Outbound tunnels", "Tunnels sortants"},
|
||||||
|
{"Destination", "Destination"},
|
||||||
|
{"Local Destination", "Destination locale"},
|
||||||
|
{"", ""},
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::map<std::string, std::vector<std::string>> plurals
|
||||||
|
{
|
||||||
|
{"days", {"jour", "jours"}},
|
||||||
|
{"hours", {"heure", "heures"}},
|
||||||
|
{"minutes", {"minute", "minutes"}},
|
||||||
|
{"seconds", {"seconde", "secondes"}},
|
||||||
|
{"", {"", ""}},
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
||||||
|
{
|
||||||
|
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // language
|
||||||
|
} // i18n
|
||||||
|
} // i2p
|
||||||
@@ -74,6 +74,7 @@ namespace i18n
|
|||||||
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
|
namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
@@ -88,6 +89,7 @@ namespace i18n
|
|||||||
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
|
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
|
||||||
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} },
|
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} },
|
||||||
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
|
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
|
||||||
|
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
|
||||||
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
|
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
|
||||||
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} },
|
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} },
|
||||||
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
|
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace data {
|
|||||||
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
|
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
|
* Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
|
||||||
*/
|
*/
|
||||||
size_t Base64EncodingBufferSize(const size_t input_size);
|
size_t Base64EncodingBufferSize(const size_t input_size);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -146,6 +146,9 @@ namespace data
|
|||||||
m_PublicKey.resize (len);
|
m_PublicKey.resize (len);
|
||||||
memcpy (m_PublicKey.data (), identity->GetSigningPublicKeyBuffer (), len);
|
memcpy (m_PublicKey.data (), identity->GetSigningPublicKeyBuffer (), len);
|
||||||
m_SigType = identity->GetSigningKeyType ();
|
m_SigType = identity->GetSigningKeyType ();
|
||||||
|
if (m_SigType == i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519)
|
||||||
|
m_BlindedSigType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519; // 7 -> 11
|
||||||
|
else
|
||||||
m_BlindedSigType = m_SigType;
|
m_BlindedSigType = m_SigType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,9 +78,9 @@ namespace config {
|
|||||||
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
|
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
|
||||||
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
|
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
|
||||||
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
|
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
|
||||||
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)")
|
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Ignored")
|
||||||
("limits.ntcphard", value<uint16_t>()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)")
|
("limits.ntcphard", value<uint16_t>()->default_value(0), "Ignored")
|
||||||
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)")
|
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Ignored")
|
||||||
;
|
;
|
||||||
|
|
||||||
options_description httpserver("HTTP Server options");
|
options_description httpserver("HTTP Server options");
|
||||||
@@ -109,6 +109,8 @@ namespace config {
|
|||||||
("httpproxy.outbound.length", value<std::string>()->default_value("3"), "HTTP proxy outbound tunnel length")
|
("httpproxy.outbound.length", value<std::string>()->default_value("3"), "HTTP proxy outbound tunnel length")
|
||||||
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity")
|
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity")
|
||||||
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity")
|
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity")
|
||||||
|
("httpproxy.inbound.lengthVariance", value<std::string>()->default_value("0"), "HTTP proxy inbound tunnels length variance")
|
||||||
|
("httpproxy.outbound.lengthVariance", value<std::string>()->default_value("0"), "HTTP proxy outbound tunnels length variance")
|
||||||
("httpproxy.latency.min", value<std::string>()->default_value("0"), "HTTP proxy min latency for tunnels")
|
("httpproxy.latency.min", value<std::string>()->default_value("0"), "HTTP proxy min latency for tunnels")
|
||||||
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
|
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
|
||||||
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
|
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
|
||||||
@@ -130,6 +132,8 @@ namespace config {
|
|||||||
("socksproxy.outbound.length", value<std::string>()->default_value("3"), "SOCKS proxy outbound tunnel length")
|
("socksproxy.outbound.length", value<std::string>()->default_value("3"), "SOCKS proxy outbound tunnel length")
|
||||||
("socksproxy.inbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy inbound tunnels quantity")
|
("socksproxy.inbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy inbound tunnels quantity")
|
||||||
("socksproxy.outbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy outbound tunnels quantity")
|
("socksproxy.outbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy outbound tunnels quantity")
|
||||||
|
("socksproxy.inbound.lengthVariance", value<std::string>()->default_value("0"), "SOCKS proxy inbound tunnels length variance")
|
||||||
|
("socksproxy.outbound.lengthVariance", value<std::string>()->default_value("0"), "SOCKS proxy outbound tunnels length variance")
|
||||||
("socksproxy.latency.min", value<std::string>()->default_value("0"), "SOCKS proxy min latency for tunnels")
|
("socksproxy.latency.min", value<std::string>()->default_value("0"), "SOCKS proxy min latency for tunnels")
|
||||||
("socksproxy.latency.max", value<std::string>()->default_value("0"), "SOCKS proxy max latency for tunnels")
|
("socksproxy.latency.max", value<std::string>()->default_value("0"), "SOCKS proxy max latency for tunnels")
|
||||||
("socksproxy.outproxy.enabled", value<bool>()->default_value(false), "Enable or disable SOCKS outproxy")
|
("socksproxy.outproxy.enabled", value<bool>()->default_value(false), "Enable or disable SOCKS outproxy")
|
||||||
@@ -211,18 +215,22 @@ namespace config {
|
|||||||
"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/,"
|
||||||
"https://banana.incognet.io/"
|
"https://banana.incognet.io/,"
|
||||||
|
"https://reseed-pl.i2pd.xyz/,"
|
||||||
|
"https://www2.mk16.de/"
|
||||||
), "Reseed URLs, separated by comma")
|
), "Reseed URLs, separated by comma")
|
||||||
("reseed.yggurls", value<std::string>()->default_value(
|
("reseed.yggurls", value<std::string>()->default_value(
|
||||||
"http://[324:71e:281a:9ed3::ace]:7070/,"
|
"http://[324:71e:281a:9ed3::ace]:7070/,"
|
||||||
"http://[301:65b9:c7cd:9a36::1]:18801/,"
|
"http://[301:65b9:c7cd:9a36::1]:18801/,"
|
||||||
"http://[320:8936:ec1a:31f1::216]/,"
|
"http://[320:8936:ec1a:31f1::216]/,"
|
||||||
"http://[306:3834:97b9:a00a::1]/"
|
"http://[306:3834:97b9:a00a::1]/,"
|
||||||
|
"http://[316:f9e0:f22e:a74f::216]/"
|
||||||
), "Reseed URLs through the Yggdrasil, separated by comma")
|
), "Reseed URLs through the Yggdrasil, separated by comma")
|
||||||
;
|
;
|
||||||
|
|
||||||
options_description addressbook("AddressBook options");
|
options_description addressbook("AddressBook options");
|
||||||
addressbook.add_options()
|
addressbook.add_options()
|
||||||
|
("addressbook.enabled", value<bool>()->default_value(true), "Enable address book lookups and subscritions (default: enabled)")
|
||||||
("addressbook.defaulturl", value<std::string>()->default_value(
|
("addressbook.defaulturl", value<std::string>()->default_value(
|
||||||
"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt"
|
"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt"
|
||||||
), "AddressBook subscription URL for initial setup")
|
), "AddressBook subscription URL for initial setup")
|
||||||
@@ -264,6 +272,13 @@ namespace config {
|
|||||||
("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport")
|
("ntcp2.proxy", value<std::string>()->default_value(""), "Proxy URL for NTCP2 transport")
|
||||||
;
|
;
|
||||||
|
|
||||||
|
options_description ssu2("SSU2 Options");
|
||||||
|
ssu2.add_options()
|
||||||
|
("ssu2.enabled", value<bool>()->default_value(false), "Enable SSU2 (default: disabled)")
|
||||||
|
("ssu2.published", value<bool>()->default_value(false), "Publish SSU2 (default: disabled)")
|
||||||
|
("ssu2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming SSU2 packets (default: auto)")
|
||||||
|
;
|
||||||
|
|
||||||
options_description nettime("Time sync options");
|
options_description nettime("Time sync options");
|
||||||
nettime.add_options()
|
nettime.add_options()
|
||||||
("nettime.enabled", value<bool>()->default_value(false), "Disable time sync (default: disabled)")
|
("nettime.enabled", value<bool>()->default_value(false), "Disable time sync (default: disabled)")
|
||||||
@@ -314,6 +329,7 @@ namespace config {
|
|||||||
.add(websocket) // deprecated
|
.add(websocket) // deprecated
|
||||||
.add(exploratory)
|
.add(exploratory)
|
||||||
.add(ntcp2)
|
.add(ntcp2)
|
||||||
|
.add(ssu2)
|
||||||
.add(nettime)
|
.add(nettime)
|
||||||
.add(persist)
|
.add(persist)
|
||||||
.add(cpuext)
|
.add(cpuext)
|
||||||
|
|||||||
@@ -1305,6 +1305,16 @@ namespace crypto
|
|||||||
SHA256_Final (m_H, &ctx);
|
SHA256_Final (m_H, &ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NoiseSymmetricState::MixHash (const std::vector<std::pair<uint8_t *, size_t> >& bufs)
|
||||||
|
{
|
||||||
|
SHA256_CTX ctx;
|
||||||
|
SHA256_Init (&ctx);
|
||||||
|
SHA256_Update (&ctx, m_H, 32);
|
||||||
|
for (const auto& it: bufs)
|
||||||
|
SHA256_Update (&ctx, it.first, it.second);
|
||||||
|
SHA256_Final (m_H, &ctx);
|
||||||
|
}
|
||||||
|
|
||||||
void NoiseSymmetricState::MixKey (const uint8_t * sharedSecret)
|
void NoiseSymmetricState::MixKey (const uint8_t * sharedSecret)
|
||||||
{
|
{
|
||||||
HKDF (m_CK, sharedSecret, 32, "", m_CK);
|
HKDF (m_CK, sharedSecret, 32, "", m_CK);
|
||||||
|
|||||||
@@ -29,7 +29,9 @@
|
|||||||
#include "CPU.h"
|
#include "CPU.h"
|
||||||
|
|
||||||
// recognize openssl version and features
|
// recognize openssl version and features
|
||||||
#if ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL
|
#if (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3050200fL)) // LibreSSL 3.5.2 and above
|
||||||
|
# define LEGACY_OPENSSL 0
|
||||||
|
#elif ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL
|
||||||
# define LEGACY_OPENSSL 1
|
# define LEGACY_OPENSSL 1
|
||||||
# define X509_getm_notBefore X509_get_notBefore
|
# define X509_getm_notBefore X509_get_notBefore
|
||||||
# define X509_getm_notAfter X509_get_notAfter
|
# define X509_getm_notAfter X509_get_notAfter
|
||||||
@@ -39,7 +41,7 @@
|
|||||||
# define OPENSSL_HKDF 1
|
# define OPENSSL_HKDF 1
|
||||||
# define OPENSSL_EDDSA 1
|
# define OPENSSL_EDDSA 1
|
||||||
# define OPENSSL_X25519 1
|
# define OPENSSL_X25519 1
|
||||||
# if (OPENSSL_VERSION_NUMBER < 0x030000000) // 3.0.0, regression in SipHash
|
# if (OPENSSL_VERSION_NUMBER != 0x030000000) // 3.0.0, regression in SipHash
|
||||||
# define OPENSSL_SIPHASH 1
|
# define OPENSSL_SIPHASH 1
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
@@ -317,6 +319,7 @@ namespace crypto
|
|||||||
uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/;
|
uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/;
|
||||||
|
|
||||||
void MixHash (const uint8_t * buf, size_t len);
|
void MixHash (const uint8_t * buf, size_t len);
|
||||||
|
void MixHash (const std::vector<std::pair<uint8_t *, size_t> >& bufs);
|
||||||
void MixKey (const uint8_t * sharedSecret);
|
void MixKey (const uint8_t * sharedSecret);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "Config.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "FS.h"
|
#include "FS.h"
|
||||||
#include "Timestamp.h"
|
#include "Timestamp.h"
|
||||||
@@ -35,6 +34,8 @@ namespace client
|
|||||||
int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY;
|
int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY;
|
||||||
int outLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
|
int outLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
|
||||||
int outQty = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
|
int outQty = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
|
||||||
|
int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE;
|
||||||
|
int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE;
|
||||||
int numTags = DEFAULT_TAGS_TO_SEND;
|
int numTags = DEFAULT_TAGS_TO_SEND;
|
||||||
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
|
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
|
||||||
try
|
try
|
||||||
@@ -53,6 +54,12 @@ namespace client
|
|||||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
|
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
|
||||||
if (it != params->end ())
|
if (it != params->end ())
|
||||||
outQty = std::stoi(it->second);
|
outQty = std::stoi(it->second);
|
||||||
|
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE);
|
||||||
|
if (it != params->end ())
|
||||||
|
inVar = std::stoi(it->second);
|
||||||
|
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE);
|
||||||
|
if (it != params->end ())
|
||||||
|
outVar = std::stoi(it->second);
|
||||||
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
|
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
|
||||||
if (it != params->end ())
|
if (it != params->end ())
|
||||||
numTags = std::stoi(it->second);
|
numTags = std::stoi(it->second);
|
||||||
@@ -86,9 +93,7 @@ namespace client
|
|||||||
if (it != params->end ())
|
if (it != params->end ())
|
||||||
{
|
{
|
||||||
// oveeride isPublic
|
// oveeride isPublic
|
||||||
bool dontpublish = false;
|
m_IsPublic = (it->second != "true");
|
||||||
i2p::config::GetOption (it->second, dontpublish);
|
|
||||||
m_IsPublic = !dontpublish;
|
|
||||||
}
|
}
|
||||||
it = params->find (I2CP_PARAM_LEASESET_TYPE);
|
it = params->find (I2CP_PARAM_LEASESET_TYPE);
|
||||||
if (it != params->end ())
|
if (it != params->end ())
|
||||||
@@ -123,7 +128,7 @@ namespace client
|
|||||||
LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what());
|
LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what());
|
||||||
}
|
}
|
||||||
SetNumTags (numTags);
|
SetNumTags (numTags);
|
||||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty);
|
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar);
|
||||||
if (explicitPeers)
|
if (explicitPeers)
|
||||||
m_Pool->SetExplicitPeers (explicitPeers);
|
m_Pool->SetExplicitPeers (explicitPeers);
|
||||||
if(params)
|
if(params)
|
||||||
@@ -331,6 +336,22 @@ namespace client
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LeaseSetDestination::SubmitECIESx25519Key (const uint8_t * key, uint64_t tag)
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t k[32];
|
||||||
|
uint64_t t;
|
||||||
|
} data;
|
||||||
|
memcpy (data.k, key, 32);
|
||||||
|
data.t = tag;
|
||||||
|
auto s = shared_from_this ();
|
||||||
|
m_Service.post ([s,data](void)
|
||||||
|
{
|
||||||
|
s->AddECIESx25519Key (data.k, data.t);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg));
|
m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg));
|
||||||
@@ -930,7 +951,7 @@ namespace client
|
|||||||
for (auto& it: encryptionKeyTypes)
|
for (auto& it: encryptionKeyTypes)
|
||||||
{
|
{
|
||||||
auto encryptionKey = new EncryptionKey (it);
|
auto encryptionKey = new EncryptionKey (it);
|
||||||
if (isPublic)
|
if (IsPublic ())
|
||||||
PersistTemporaryKeys (encryptionKey, isSingleKey);
|
PersistTemporaryKeys (encryptionKey, isSingleKey);
|
||||||
else
|
else
|
||||||
encryptionKey->GenerateKeys ();
|
encryptionKey->GenerateKeys ();
|
||||||
@@ -945,7 +966,7 @@ namespace client
|
|||||||
m_StandardEncryptionKey.reset (encryptionKey);
|
m_StandardEncryptionKey.reset (encryptionKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPublic)
|
if (IsPublic ())
|
||||||
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
|
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -958,7 +979,7 @@ namespace client
|
|||||||
m_StreamingAckDelay = std::stoi(it->second);
|
m_StreamingAckDelay = std::stoi(it->second);
|
||||||
it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS);
|
it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS);
|
||||||
if (it != params->end ())
|
if (it != params->end ())
|
||||||
i2p::config::GetOption (it->second, m_IsStreamingAnswerPings);
|
m_IsStreamingAnswerPings = (it->second == "true");
|
||||||
|
|
||||||
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -53,6 +53,10 @@ namespace client
|
|||||||
const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5;
|
const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5;
|
||||||
const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity";
|
const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity";
|
||||||
const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5;
|
const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5;
|
||||||
|
const char I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE[] = "inbound.lengthVariance";
|
||||||
|
const int DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE = 0;
|
||||||
|
const char I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE[] = "outbound.lengthVariance";
|
||||||
|
const int DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE = 0;
|
||||||
const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers";
|
const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers";
|
||||||
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
|
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
|
||||||
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
|
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
|
||||||
@@ -134,6 +138,7 @@ namespace client
|
|||||||
|
|
||||||
// override GarlicDestination
|
// override GarlicDestination
|
||||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
||||||
|
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
|
||||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
void SetLeaseSetUpdated ();
|
void SetLeaseSetUpdated ();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -60,10 +60,38 @@ namespace fs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DetectDataDir(const std::string & cmdline_param, bool isService) {
|
void DetectDataDir(const std::string & cmdline_param, bool isService) {
|
||||||
|
// with 'datadir' option
|
||||||
if (cmdline_param != "") {
|
if (cmdline_param != "") {
|
||||||
dataDir = cmdline_param;
|
dataDir = cmdline_param;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(MAC_OSX) && !defined(ANDROID)
|
||||||
|
// with 'service' option
|
||||||
|
if (isService) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
wchar_t commonAppData[MAX_PATH];
|
||||||
|
if(SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, commonAppData) != S_OK)
|
||||||
|
{
|
||||||
|
#ifdef WIN32_APP
|
||||||
|
MessageBox(NULL, TEXT("Unable to get common AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "Error: Unable to get common AppData path!");
|
||||||
|
#endif
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
dataDir = "/var/lib/" + appName;
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// detect directory as usual
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
wchar_t localAppData[MAX_PATH];
|
wchar_t localAppData[MAX_PATH];
|
||||||
|
|
||||||
@@ -117,12 +145,10 @@ namespace fs {
|
|||||||
dataDir = std::string (ext) + "/" + appName;
|
dataDir = std::string (ext) + "/" + appName;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif // ANDROID
|
||||||
// otherwise use /data/files
|
// use /home/user/.i2pd or /tmp/i2pd
|
||||||
char *home = getenv("HOME");
|
char *home = getenv("HOME");
|
||||||
if (isService) {
|
if (home != NULL && strlen(home) > 0) {
|
||||||
dataDir = "/var/lib/" + appName;
|
|
||||||
} else if (home != NULL && strlen(home) > 0) {
|
|
||||||
dataDir = std::string(home) + "/." + appName;
|
dataDir = std::string(home) + "/." + appName;
|
||||||
} else {
|
} else {
|
||||||
dataDir = "/tmp/" + appName;
|
dataDir = "/tmp/" + appName;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -88,7 +88,7 @@ namespace data
|
|||||||
}
|
}
|
||||||
EVP_PKEY_free (pkey);
|
EVP_PKEY_free (pkey);
|
||||||
if (verifier && cn)
|
if (verifier && cn)
|
||||||
m_SigningKeys[cn] = verifier;
|
m_SigningKeys.emplace (cn, std::make_pair(verifier, m_SigningKeys.size () + 1));
|
||||||
}
|
}
|
||||||
SSL_free (ssl);
|
SSL_free (ssl);
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,7 @@ namespace data
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Families::VerifyFamily (const std::string& family, const IdentHash& ident,
|
bool Families::VerifyFamily (const std::string& family, const IdentHash& ident,
|
||||||
const char * signature, const char * key)
|
const char * signature, const char * key) const
|
||||||
{
|
{
|
||||||
uint8_t buf[100], signatureBuf[64];
|
uint8_t buf[100], signatureBuf[64];
|
||||||
size_t len = family.length (), signatureLen = strlen (signature);
|
size_t len = family.length (), signatureLen = strlen (signature);
|
||||||
@@ -137,11 +137,19 @@ namespace data
|
|||||||
Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
|
Base64ToByteStream (signature, signatureLen, signatureBuf, 64);
|
||||||
auto it = m_SigningKeys.find (family);
|
auto it = m_SigningKeys.find (family);
|
||||||
if (it != m_SigningKeys.end ())
|
if (it != m_SigningKeys.end ())
|
||||||
return it->second->Verify (buf, len, signatureBuf);
|
return it->second.first->Verify (buf, len, signatureBuf);
|
||||||
// TODO: process key
|
// TODO: process key
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FamilyID Families::GetFamilyID (const std::string& family) const
|
||||||
|
{
|
||||||
|
auto it = m_SigningKeys.find (family);
|
||||||
|
if (it != m_SigningKeys.end ())
|
||||||
|
return it->second.second;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident)
|
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident)
|
||||||
{
|
{
|
||||||
auto filename = i2p::fs::DataDirPath("family", (family + ".key"));
|
auto filename = i2p::fs::DataDirPath("family", (family + ".key"));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -19,6 +19,7 @@ namespace i2p
|
|||||||
{
|
{
|
||||||
namespace data
|
namespace data
|
||||||
{
|
{
|
||||||
|
typedef int FamilyID;
|
||||||
class Families
|
class Families
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -27,7 +28,8 @@ namespace data
|
|||||||
~Families ();
|
~Families ();
|
||||||
void LoadCertificates ();
|
void LoadCertificates ();
|
||||||
bool VerifyFamily (const std::string& family, const IdentHash& ident,
|
bool VerifyFamily (const std::string& family, const IdentHash& ident,
|
||||||
const char * signature, const char * key = nullptr);
|
const char * signature, const char * key = nullptr) const;
|
||||||
|
FamilyID GetFamilyID (const std::string& family) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -35,7 +37,7 @@ namespace data
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::map<std::string, std::shared_ptr<i2p::crypto::Verifier> > m_SigningKeys;
|
std::map<std::string, std::pair<std::shared_ptr<i2p::crypto::Verifier>, FamilyID> > m_SigningKeys; // family -> (verifier, id)
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident);
|
std::string CreateFamilySignature (const std::string& family, const IdentHash& ident);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -484,6 +484,11 @@ namespace garlic
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GarlicDestination::SubmitECIESx25519Key (const uint8_t * key, uint64_t tag)
|
||||||
|
{
|
||||||
|
AddECIESx25519Key (key, tag);
|
||||||
|
}
|
||||||
|
|
||||||
void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
uint8_t * buf = msg->GetPayload ();
|
uint8_t * buf = msg->GetPayload ();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -245,6 +245,7 @@ namespace garlic
|
|||||||
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, uint64_t tag); // one tag
|
void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag
|
||||||
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
|
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
|
||||||
|
virtual void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag); // from different thread
|
||||||
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
|
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
|
||||||
uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset);
|
uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset);
|
||||||
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
|
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -802,13 +802,8 @@ namespace i2p
|
|||||||
break;
|
break;
|
||||||
case eI2NPGarlic:
|
case eI2NPGarlic:
|
||||||
{
|
{
|
||||||
if (msg->from)
|
if (msg->from && msg->from->GetTunnelPool ())
|
||||||
{
|
|
||||||
if (msg->from->GetTunnelPool ())
|
|
||||||
msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
|
msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
|
||||||
else
|
|
||||||
LogPrint (eLogInfo, "I2NP: Local destination for garlic doesn't exist anymore");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
i2p::context.ProcessGarlicMessage (msg);
|
i2p::context.ProcessGarlicMessage (msg);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -128,8 +128,8 @@ namespace data
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
validate lease set buffer signature and extract expiration timestamp
|
* validate lease set buffer signature and extract expiration timestamp
|
||||||
@returns true if the leaseset is well formed and signature is valid
|
* @returns true if the leaseset is well formed and signature is valid
|
||||||
*/
|
*/
|
||||||
bool LeaseSetBufferValidate(const uint8_t * ptr, size_t sz, uint64_t & expires);
|
bool LeaseSetBufferValidate(const uint8_t * ptr, size_t sz, uint64_t & expires);
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
#include "HTTP.h"
|
#include "HTTP.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#ifdef __linux__
|
#if defined(__linux__) && !defined(_NETINET_IN_H)
|
||||||
#include <linux/in6.h>
|
#include <linux/in6.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ namespace transport
|
|||||||
|
|
||||||
void NTCP2Establisher::KDF1Bob ()
|
void NTCP2Establisher::KDF1Bob ()
|
||||||
{
|
{
|
||||||
KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetStaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ());
|
KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub)
|
void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub)
|
||||||
@@ -91,7 +91,7 @@ namespace transport
|
|||||||
void NTCP2Establisher::KDF3Alice ()
|
void NTCP2Establisher::KDF3Alice ()
|
||||||
{
|
{
|
||||||
uint8_t inputKeyMaterial[32];
|
uint8_t inputKeyMaterial[32];
|
||||||
i2p::context.GetStaticKeys ().Agree (GetRemotePub (), inputKeyMaterial);
|
i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial);
|
||||||
MixKey (inputKeyMaterial);
|
MixKey (inputKeyMaterial);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1243,7 +1243,7 @@ namespace transport
|
|||||||
m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6());
|
m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6());
|
||||||
m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true));
|
m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true));
|
||||||
m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true));
|
m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true));
|
||||||
#ifdef __linux__
|
#if defined(__linux__) && !defined(_NETINET_IN_H)
|
||||||
if (!m_Address6 && !m_YggdrasilAddress) // only if not binded to address
|
if (!m_Address6 && !m_YggdrasilAddress) // only if not binded to address
|
||||||
{
|
{
|
||||||
// Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others
|
// Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others
|
||||||
|
|||||||
@@ -107,7 +107,10 @@ namespace data
|
|||||||
{
|
{
|
||||||
i2p::util::SetThreadName("NetDB");
|
i2p::util::SetThreadName("NetDB");
|
||||||
|
|
||||||
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0;
|
uint64_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0;
|
||||||
|
uint64_t lastProfilesCleanup = i2p::util::GetSecondsSinceEpoch ();
|
||||||
|
int16_t profilesCleanupVariance = 0;
|
||||||
|
|
||||||
while (m_IsRunning)
|
while (m_IsRunning)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -155,6 +158,7 @@ namespace data
|
|||||||
m_Requests.ManageRequests ();
|
m_Requests.ManageRequests ();
|
||||||
lastManageRequest = ts;
|
lastManageRequest = ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute
|
if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute
|
||||||
{
|
{
|
||||||
if (lastSave)
|
if (lastSave)
|
||||||
@@ -164,12 +168,20 @@ namespace data
|
|||||||
}
|
}
|
||||||
lastSave = ts;
|
lastSave = ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
||||||
{
|
{
|
||||||
i2p::context.CleanupDestination ();
|
i2p::context.CleanupDestination ();
|
||||||
lastDestinationCleanup = ts;
|
lastDestinationCleanup = ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ts - lastProfilesCleanup >= (uint64_t)(i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT + profilesCleanupVariance))
|
||||||
|
{
|
||||||
|
DeleteObsoleteProfiles ();
|
||||||
|
lastProfilesCleanup = ts;
|
||||||
|
profilesCleanupVariance = (rand () % (2 * i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE) - i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE);
|
||||||
|
}
|
||||||
|
|
||||||
// publish
|
// publish
|
||||||
if (!m_HiddenMode && i2p::transport::transports.IsOnline ())
|
if (!m_HiddenMode && i2p::transport::transports.IsOnline ())
|
||||||
{
|
{
|
||||||
@@ -195,6 +207,7 @@ namespace data
|
|||||||
lastPublish = ts;
|
lastPublish = ts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ts - lastExploratory >= 30) // exploratory every 30 seconds
|
if (ts - lastExploratory >= 30) // exploratory every 30 seconds
|
||||||
{
|
{
|
||||||
auto numRouters = m_RouterInfos.size ();
|
auto numRouters = m_RouterInfos.size ();
|
||||||
@@ -1190,6 +1203,16 @@ namespace data
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const
|
||||||
|
{
|
||||||
|
return GetRandomRouter (
|
||||||
|
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
|
||||||
|
{
|
||||||
|
return !router->IsHidden () && router->IsECIES () &&
|
||||||
|
router->IsSSU2PeerTesting (v4) && !excluded.count (router->GetIdentHash ());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSUV6Router () const
|
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSUV6Router () const
|
||||||
{
|
{
|
||||||
return GetRandomRouter (
|
return GetRandomRouter (
|
||||||
@@ -1364,7 +1387,8 @@ namespace data
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouterInFamily(const std::string & fam) const {
|
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouterInFamily (FamilyID fam) const
|
||||||
|
{
|
||||||
return GetRandomRouter(
|
return GetRandomRouter(
|
||||||
[fam](std::shared_ptr<const RouterInfo> router)->bool
|
[fam](std::shared_ptr<const RouterInfo> router)->bool
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -90,13 +90,14 @@ namespace data
|
|||||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
||||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
||||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
|
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
|
||||||
|
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
|
||||||
std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later
|
std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later
|
||||||
std::shared_ptr<const RouterInfo> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const;
|
std::shared_ptr<const RouterInfo> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const;
|
||||||
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
||||||
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
|
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
|
||||||
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
||||||
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
|
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
|
||||||
std::shared_ptr<const RouterInfo> GetRandomRouterInFamily(const std::string & fam) const;
|
std::shared_ptr<const RouterInfo> GetRandomRouterInFamily (FamilyID fam) const;
|
||||||
void SetUnreachable (const IdentHash& ident, bool unreachable);
|
void SetUnreachable (const IdentHash& ident, bool unreachable);
|
||||||
|
|
||||||
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
|
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#include "Poly1305.h"
|
|
||||||
/**
|
/**
|
||||||
This code is licensed under the MCGSI Public License
|
* This code is licensed under the MCGSI Public License
|
||||||
Copyright 2018 Jeff Becker
|
* Copyright 2018 Jeff Becker
|
||||||
|
*
|
||||||
Kovri go write your own code
|
*Kovri go write your own code
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "Poly1305.h"
|
||||||
|
|
||||||
#if !OPENSSL_AEAD_CHACHA20_POLY1305
|
#if !OPENSSL_AEAD_CHACHA20_POLY1305
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
* Kovri go write your own code
|
* Kovri go write your own code
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LIBI2PD_POLY1305_H
|
#ifndef LIBI2PD_POLY1305_H
|
||||||
#define LIBI2PD_POLY1305_H
|
#define LIBI2PD_POLY1305_H
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -29,6 +29,8 @@ namespace data
|
|||||||
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
|
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
|
||||||
|
|
||||||
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
|
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
|
||||||
|
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day)
|
||||||
|
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours)
|
||||||
|
|
||||||
class RouterProfile
|
class RouterProfile
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,11 +69,14 @@ namespace i2p
|
|||||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||||
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
|
||||||
|
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||||
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
|
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
|
||||||
bool nat; i2p::config::GetOption("nat", nat);
|
bool nat; i2p::config::GetOption("nat", nat);
|
||||||
|
|
||||||
if ((ntcp2 || ygg) && !m_NTCP2Keys)
|
if ((ntcp2 || ygg) && !m_NTCP2Keys)
|
||||||
NewNTCP2Keys ();
|
NewNTCP2Keys ();
|
||||||
|
if (ssu2 && !m_SSU2Keys)
|
||||||
|
NewSSU2Keys ();
|
||||||
bool ntcp2Published = false;
|
bool ntcp2Published = false;
|
||||||
if (ntcp2)
|
if (ntcp2)
|
||||||
{
|
{
|
||||||
@@ -84,6 +87,9 @@ namespace i2p
|
|||||||
if (!ntcp2proxy.empty ()) ntcp2Published = false;
|
if (!ntcp2proxy.empty ()) ntcp2Published = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bool ssu2Published = false;
|
||||||
|
if (ssu2)
|
||||||
|
i2p::config::GetOption("ssu2.published", ssu2Published);
|
||||||
uint8_t caps = 0, addressCaps = 0;
|
uint8_t caps = 0, addressCaps = 0;
|
||||||
if (ipv4)
|
if (ipv4)
|
||||||
{
|
{
|
||||||
@@ -112,6 +118,16 @@ namespace i2p
|
|||||||
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
||||||
caps |= i2p::data::RouterInfo::eReachable; // R
|
caps |= i2p::data::RouterInfo::eReachable; // R
|
||||||
}
|
}
|
||||||
|
if (ssu2)
|
||||||
|
{
|
||||||
|
if (ssu2Published)
|
||||||
|
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), port);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4;
|
||||||
|
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ipv6)
|
if (ipv6)
|
||||||
{
|
{
|
||||||
@@ -147,6 +163,17 @@ namespace i2p
|
|||||||
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
routerInfo.AddSSUAddress (host.c_str(), port, nullptr);
|
||||||
caps |= i2p::data::RouterInfo::eReachable; // R
|
caps |= i2p::data::RouterInfo::eReachable; // R
|
||||||
}
|
}
|
||||||
|
if (ssu2)
|
||||||
|
{
|
||||||
|
if (ssu2Published)
|
||||||
|
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), port);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!ipv4) // no other ssu2 addresses yet
|
||||||
|
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro);
|
||||||
|
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ygg)
|
if (ygg)
|
||||||
{
|
{
|
||||||
@@ -174,17 +201,30 @@ namespace i2p
|
|||||||
|
|
||||||
void RouterContext::NewNTCP2Keys ()
|
void RouterContext::NewNTCP2Keys ()
|
||||||
{
|
{
|
||||||
m_StaticKeys.reset (new i2p::crypto::X25519Keys ());
|
m_NTCP2StaticKeys.reset (new i2p::crypto::X25519Keys ());
|
||||||
m_StaticKeys->GenerateKeys ();
|
m_NTCP2StaticKeys->GenerateKeys ();
|
||||||
m_NTCP2Keys.reset (new NTCP2PrivateKeys ());
|
m_NTCP2Keys.reset (new NTCP2PrivateKeys ());
|
||||||
m_StaticKeys->GetPrivateKey (m_NTCP2Keys->staticPrivateKey);
|
m_NTCP2StaticKeys->GetPrivateKey (m_NTCP2Keys->staticPrivateKey);
|
||||||
memcpy (m_NTCP2Keys->staticPublicKey, m_StaticKeys->GetPublicKey (), 32);
|
memcpy (m_NTCP2Keys->staticPublicKey, m_NTCP2StaticKeys->GetPublicKey (), 32);
|
||||||
RAND_bytes (m_NTCP2Keys->iv, 16);
|
RAND_bytes (m_NTCP2Keys->iv, 16);
|
||||||
// save
|
// save
|
||||||
std::ofstream fk (i2p::fs::DataDirPath (NTCP2_KEYS), std::ofstream::binary | std::ofstream::out);
|
std::ofstream fk (i2p::fs::DataDirPath (NTCP2_KEYS), std::ofstream::binary | std::ofstream::out);
|
||||||
fk.write ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys));
|
fk.write ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RouterContext::NewSSU2Keys ()
|
||||||
|
{
|
||||||
|
m_SSU2StaticKeys.reset (new i2p::crypto::X25519Keys ());
|
||||||
|
m_SSU2StaticKeys->GenerateKeys ();
|
||||||
|
m_SSU2Keys.reset (new SSU2PrivateKeys ());
|
||||||
|
m_SSU2StaticKeys->GetPrivateKey (m_SSU2Keys->staticPrivateKey);
|
||||||
|
memcpy (m_SSU2Keys->staticPublicKey, m_SSU2StaticKeys->GetPublicKey (), 32);
|
||||||
|
RAND_bytes (m_SSU2Keys->intro, 32);
|
||||||
|
// save
|
||||||
|
std::ofstream fk (i2p::fs::DataDirPath (SSU2_KEYS), std::ofstream::binary | std::ofstream::out);
|
||||||
|
fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys));
|
||||||
|
}
|
||||||
|
|
||||||
void RouterContext::SetStatus (RouterStatus status)
|
void RouterContext::SetStatus (RouterStatus status)
|
||||||
{
|
{
|
||||||
if (status != m_Status)
|
if (status != m_Status)
|
||||||
@@ -229,7 +269,7 @@ namespace i2p
|
|||||||
bool updated = false;
|
bool updated = false;
|
||||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||||
{
|
{
|
||||||
if (!address->IsNTCP2 () && address->port != port)
|
if (!address->IsNTCP2 () && !address->IsSSU2 () && address->port != port)
|
||||||
{
|
{
|
||||||
address->port = port;
|
address->port = port;
|
||||||
updated = true;
|
updated = true;
|
||||||
@@ -300,6 +340,59 @@ namespace i2p
|
|||||||
UpdateRouterInfo ();
|
UpdateRouterInfo ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RouterContext::PublishSSU2Address (int port, bool publish, bool v4, bool v6)
|
||||||
|
{
|
||||||
|
if (!m_SSU2Keys || (publish && !port)) return;
|
||||||
|
bool updated = false;
|
||||||
|
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||||
|
{
|
||||||
|
if (address->IsSSU2 () && (address->port != port || address->published != publish) &&
|
||||||
|
((v4 && address->IsV4 ()) || (v6 && address->IsV6 ())))
|
||||||
|
{
|
||||||
|
address->port = port;
|
||||||
|
address->published = publish;
|
||||||
|
if (publish)
|
||||||
|
address->caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
||||||
|
else
|
||||||
|
address->caps &= ~i2p::data::RouterInfo::eSSUIntroducer;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (updated)
|
||||||
|
UpdateRouterInfo ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RouterContext::UpdateSSU2Address (bool enable)
|
||||||
|
{
|
||||||
|
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||||
|
bool found = false, updated = false;
|
||||||
|
for (auto it = addresses.begin (); it != addresses.end (); ++it)
|
||||||
|
{
|
||||||
|
if ((*it)->IsSSU2 ())
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
if (!enable)
|
||||||
|
{
|
||||||
|
addresses.erase (it);
|
||||||
|
updated= true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (enable && !found)
|
||||||
|
{
|
||||||
|
uint8_t addressCaps = 0;
|
||||||
|
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||||
|
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||||
|
if (ipv4) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4;
|
||||||
|
if (ipv6) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
|
||||||
|
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addressCaps);
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
if (updated)
|
||||||
|
UpdateRouterInfo ();
|
||||||
|
}
|
||||||
|
|
||||||
void RouterContext::UpdateAddress (const boost::asio::ip::address& host)
|
void RouterContext::UpdateAddress (const boost::asio::ip::address& host)
|
||||||
{
|
{
|
||||||
bool updated = false;
|
bool updated = false;
|
||||||
@@ -475,7 +568,7 @@ namespace i2p
|
|||||||
// delete previous introducers
|
// delete previous introducers
|
||||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||||
for (auto& addr : addresses)
|
for (auto& addr : addresses)
|
||||||
if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||||
{
|
{
|
||||||
addr->published = false;
|
addr->published = false;
|
||||||
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
|
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
|
||||||
@@ -507,7 +600,7 @@ namespace i2p
|
|||||||
// delete previous introducers
|
// delete previous introducers
|
||||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||||
for (auto& addr : addresses)
|
for (auto& addr : addresses)
|
||||||
if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||||
{
|
{
|
||||||
addr->published = true;
|
addr->published = true;
|
||||||
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
||||||
@@ -536,17 +629,26 @@ namespace i2p
|
|||||||
if (supportsV6)
|
if (supportsV6)
|
||||||
{
|
{
|
||||||
// insert v6 addresses if necessary
|
// insert v6 addresses if necessary
|
||||||
bool foundSSU = false, foundNTCP2 = false;
|
bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false;
|
||||||
uint16_t port = 0;
|
uint16_t port = 0;
|
||||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||||
for (auto& addr: addresses)
|
for (auto& addr: addresses)
|
||||||
{
|
{
|
||||||
if (addr->IsV6 () && !i2p::util::net::IsYggdrasilAddress (addr->host))
|
if (addr->IsV6 () && !i2p::util::net::IsYggdrasilAddress (addr->host))
|
||||||
{
|
{
|
||||||
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU)
|
switch (addr->transportStyle)
|
||||||
|
{
|
||||||
|
case i2p::data::RouterInfo::eTransportSSU:
|
||||||
foundSSU = true;
|
foundSSU = true;
|
||||||
else if (addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
|
break;
|
||||||
|
case i2p::data::RouterInfo::eTransportNTCP:
|
||||||
foundNTCP2 = true;
|
foundNTCP2 = true;
|
||||||
|
break;
|
||||||
|
case i2p::data::RouterInfo::eTransportSSU2:
|
||||||
|
foundSSU2 = true;
|
||||||
|
break;
|
||||||
|
default: ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
port = addr->port;
|
port = addr->port;
|
||||||
}
|
}
|
||||||
@@ -583,6 +685,22 @@ namespace i2p
|
|||||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV6);
|
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// SSU2
|
||||||
|
if (!foundSSU2)
|
||||||
|
{
|
||||||
|
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||||
|
if (ssu2)
|
||||||
|
{
|
||||||
|
bool ssu2Published; i2p::config::GetOption("ssu2.published", ssu2Published);
|
||||||
|
if (ssu2Published)
|
||||||
|
{
|
||||||
|
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||||
|
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("::1"), ssu2Port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV6);
|
||||||
|
}
|
||||||
|
}
|
||||||
m_RouterInfo.EnableV6 ();
|
m_RouterInfo.EnableV6 ();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -598,7 +716,7 @@ namespace i2p
|
|||||||
// update
|
// update
|
||||||
if (supportsV4)
|
if (supportsV4)
|
||||||
{
|
{
|
||||||
bool foundSSU = false, foundNTCP2 = false;
|
bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false;
|
||||||
std::string host = "127.0.0.1";
|
std::string host = "127.0.0.1";
|
||||||
uint16_t port = 0;
|
uint16_t port = 0;
|
||||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||||
@@ -606,10 +724,19 @@ namespace i2p
|
|||||||
{
|
{
|
||||||
if (addr->IsV4 ())
|
if (addr->IsV4 ())
|
||||||
{
|
{
|
||||||
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU)
|
switch (addr->transportStyle)
|
||||||
|
{
|
||||||
|
case i2p::data::RouterInfo::eTransportSSU:
|
||||||
foundSSU = true;
|
foundSSU = true;
|
||||||
else if (addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
|
break;
|
||||||
|
case i2p::data::RouterInfo::eTransportNTCP:
|
||||||
foundNTCP2 = true;
|
foundNTCP2 = true;
|
||||||
|
break;
|
||||||
|
case i2p::data::RouterInfo::eTransportSSU2:
|
||||||
|
foundSSU2 = true;
|
||||||
|
break;
|
||||||
|
default: ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (addr->port) port = addr->port;
|
if (addr->port) port = addr->port;
|
||||||
}
|
}
|
||||||
@@ -638,6 +765,22 @@ namespace i2p
|
|||||||
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV4);
|
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// SSU2
|
||||||
|
if (!foundSSU2)
|
||||||
|
{
|
||||||
|
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||||
|
if (ssu2)
|
||||||
|
{
|
||||||
|
bool ssu2Published; i2p::config::GetOption("ssu2.published", ssu2Published);
|
||||||
|
if (ssu2Published)
|
||||||
|
{
|
||||||
|
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||||
|
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("127.0.0.1"), ssu2Port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV6);
|
||||||
|
}
|
||||||
|
}
|
||||||
m_RouterInfo.EnableV4 ();
|
m_RouterInfo.EnableV4 ();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -794,6 +937,30 @@ namespace i2p
|
|||||||
else
|
else
|
||||||
UpdateNTCP2Address (false); // disable NTCP2
|
UpdateNTCP2Address (false); // disable NTCP2
|
||||||
|
|
||||||
|
// read SSU2
|
||||||
|
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
|
||||||
|
if (ssu2)
|
||||||
|
{
|
||||||
|
// read SSU2 keys if available
|
||||||
|
std::ifstream s2k (i2p::fs::DataDirPath (SSU2_KEYS), std::ifstream::in | std::ifstream::binary);
|
||||||
|
if (s2k)
|
||||||
|
{
|
||||||
|
s2k.seekg (0, std::ios::end);
|
||||||
|
size_t len = s2k.tellg();
|
||||||
|
s2k.seekg (0, std::ios::beg);
|
||||||
|
if (len == sizeof (SSU2PrivateKeys))
|
||||||
|
{
|
||||||
|
m_SSU2Keys.reset (new SSU2PrivateKeys ());
|
||||||
|
s2k.read ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys));
|
||||||
|
}
|
||||||
|
s2k.close ();
|
||||||
|
}
|
||||||
|
if (!m_SSU2Keys) NewSSU2Keys ();
|
||||||
|
UpdateSSU2Address (true); // enable SSU2
|
||||||
|
}
|
||||||
|
else
|
||||||
|
UpdateSSU2Address (false); // disable SSU2
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -910,17 +1077,31 @@ namespace i2p
|
|||||||
return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE);
|
return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()
|
i2p::crypto::X25519Keys& RouterContext::GetNTCP2StaticKeys ()
|
||||||
{
|
{
|
||||||
if (!m_StaticKeys)
|
if (!m_NTCP2StaticKeys)
|
||||||
{
|
{
|
||||||
if (!m_NTCP2Keys) NewNTCP2Keys ();
|
if (!m_NTCP2Keys) NewNTCP2Keys ();
|
||||||
auto x = new i2p::crypto::X25519Keys (m_NTCP2Keys->staticPrivateKey, m_NTCP2Keys->staticPublicKey);
|
auto x = new i2p::crypto::X25519Keys (m_NTCP2Keys->staticPrivateKey, m_NTCP2Keys->staticPublicKey);
|
||||||
if (!m_StaticKeys)
|
if (!m_NTCP2StaticKeys)
|
||||||
m_StaticKeys.reset (x);
|
m_NTCP2StaticKeys.reset (x);
|
||||||
else
|
else
|
||||||
delete x;
|
delete x;
|
||||||
}
|
}
|
||||||
return *m_StaticKeys;
|
return *m_NTCP2StaticKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2p::crypto::X25519Keys& RouterContext::GetSSU2StaticKeys ()
|
||||||
|
{
|
||||||
|
if (!m_SSU2StaticKeys)
|
||||||
|
{
|
||||||
|
if (!m_SSU2Keys) NewSSU2Keys ();
|
||||||
|
auto x = new i2p::crypto::X25519Keys (m_SSU2Keys->staticPrivateKey, m_SSU2Keys->staticPublicKey);
|
||||||
|
if (!m_SSU2StaticKeys)
|
||||||
|
m_SSU2StaticKeys.reset (x);
|
||||||
|
else
|
||||||
|
delete x;
|
||||||
|
}
|
||||||
|
return *m_SSU2StaticKeys;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace garlic
|
|||||||
const char ROUTER_INFO[] = "router.info";
|
const char ROUTER_INFO[] = "router.info";
|
||||||
const char ROUTER_KEYS[] = "router.keys";
|
const char ROUTER_KEYS[] = "router.keys";
|
||||||
const char NTCP2_KEYS[] = "ntcp2.keys";
|
const char NTCP2_KEYS[] = "ntcp2.keys";
|
||||||
|
const char SSU2_KEYS[] = "ssu2.keys";
|
||||||
const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes
|
const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes
|
||||||
|
|
||||||
enum RouterStatus
|
enum RouterStatus
|
||||||
@@ -61,6 +62,13 @@ namespace garlic
|
|||||||
uint8_t iv[16];
|
uint8_t iv[16];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SSU2PrivateKeys
|
||||||
|
{
|
||||||
|
uint8_t staticPublicKey[32];
|
||||||
|
uint8_t staticPrivateKey[32];
|
||||||
|
uint8_t intro[32];
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
RouterContext ();
|
RouterContext ();
|
||||||
@@ -78,10 +86,16 @@ namespace garlic
|
|||||||
return std::shared_ptr<i2p::garlic::GarlicDestination> (this,
|
return std::shared_ptr<i2p::garlic::GarlicDestination> (this,
|
||||||
[](i2p::garlic::GarlicDestination *) {});
|
[](i2p::garlic::GarlicDestination *) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; };
|
const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; };
|
||||||
const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; };
|
const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; };
|
||||||
const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
|
const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
|
||||||
i2p::crypto::X25519Keys& GetStaticKeys ();
|
i2p::crypto::X25519Keys& GetNTCP2StaticKeys ();
|
||||||
|
|
||||||
|
const uint8_t * GetSSU2StaticPublicKey () const { return m_SSU2Keys ? m_SSU2Keys->staticPublicKey : nullptr; };
|
||||||
|
const uint8_t * GetSSU2StaticPrivateKey () const { return m_SSU2Keys ? m_SSU2Keys->staticPrivateKey : nullptr; };
|
||||||
|
const uint8_t * GetSSU2IntroKey () const { return m_SSU2Keys ? m_SSU2Keys->intro : nullptr; };
|
||||||
|
i2p::crypto::X25519Keys& GetSSU2StaticKeys ();
|
||||||
|
|
||||||
uint32_t GetUptime () const; // in seconds
|
uint32_t GetUptime () const; // in seconds
|
||||||
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
|
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
|
||||||
@@ -102,6 +116,8 @@ namespace garlic
|
|||||||
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
|
||||||
void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg);
|
void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg);
|
||||||
void UpdateNTCP2Address (bool enable);
|
void UpdateNTCP2Address (bool enable);
|
||||||
|
void PublishSSU2Address (int port, bool publish, bool v4, bool v6);
|
||||||
|
void UpdateSSU2Address (bool enable);
|
||||||
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
|
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
|
||||||
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
|
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
|
||||||
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||||
@@ -156,6 +172,7 @@ namespace garlic
|
|||||||
void NewRouterInfo ();
|
void NewRouterInfo ();
|
||||||
void UpdateRouterInfo ();
|
void UpdateRouterInfo ();
|
||||||
void NewNTCP2Keys ();
|
void NewNTCP2Keys ();
|
||||||
|
void NewSSU2Keys ();
|
||||||
bool Load ();
|
bool Load ();
|
||||||
void SaveKeys ();
|
void SaveKeys ();
|
||||||
|
|
||||||
@@ -177,7 +194,8 @@ namespace garlic
|
|||||||
int m_NetID;
|
int m_NetID;
|
||||||
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<SSU2PrivateKeys> m_SSU2Keys;
|
||||||
|
std::unique_ptr<i2p::crypto::X25519Keys> m_NTCP2StaticKeys, m_SSU2StaticKeys;
|
||||||
// for ECIESx25519
|
// for ECIESx25519
|
||||||
i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState;
|
i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,18 +41,19 @@ namespace data
|
|||||||
}
|
}
|
||||||
|
|
||||||
RouterInfo::RouterInfo (const std::string& fullPath):
|
RouterInfo::RouterInfo (const std::string& fullPath):
|
||||||
m_IsUpdated (false), m_IsUnreachable (false),
|
m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false),
|
||||||
m_SupportedTransports (0),m_ReachableTransports (0),
|
m_SupportedTransports (0),m_ReachableTransports (0),
|
||||||
m_Caps (0), m_Version (0)
|
m_Caps (0), m_Version (0)
|
||||||
{
|
{
|
||||||
m_Addresses = boost::make_shared<Addresses>(); // create empty list
|
m_Addresses = boost::make_shared<Addresses>(); // create empty list
|
||||||
m_Buffer = netdb.NewRouterInfoBuffer ();
|
m_Buffer = NewBuffer (); // always RouterInfo's
|
||||||
ReadFromFile (fullPath);
|
ReadFromFile (fullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
RouterInfo::RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len):
|
RouterInfo::RouterInfo (std::shared_ptr<Buffer>&& buf, size_t len):
|
||||||
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0),
|
m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false),
|
||||||
m_ReachableTransports (0), m_Caps (0), m_Version (0)
|
m_SupportedTransports (0), m_ReachableTransports (0),
|
||||||
|
m_Caps (0), m_Version (0)
|
||||||
{
|
{
|
||||||
if (len <= MAX_RI_BUFFER_SIZE)
|
if (len <= MAX_RI_BUFFER_SIZE)
|
||||||
{
|
{
|
||||||
@@ -134,7 +135,7 @@ namespace data
|
|||||||
}
|
}
|
||||||
s.seekg(0, std::ios::beg);
|
s.seekg(0, std::ios::beg);
|
||||||
if (!m_Buffer)
|
if (!m_Buffer)
|
||||||
m_Buffer = netdb.NewRouterInfoBuffer ();
|
m_Buffer = NewBuffer ();
|
||||||
s.read((char *)m_Buffer->data (), m_BufferLen);
|
s.read((char *)m_Buffer->data (), m_BufferLen);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -216,14 +217,15 @@ namespace data
|
|||||||
uint8_t cost; // ignore
|
uint8_t cost; // ignore
|
||||||
s.read ((char *)&cost, sizeof (cost));
|
s.read ((char *)&cost, sizeof (cost));
|
||||||
s.read ((char *)&address->date, sizeof (address->date));
|
s.read ((char *)&address->date, sizeof (address->date));
|
||||||
bool isHost = false, isIntroKey = false, isStaticKey = false;
|
bool isHost = false, isIntroKey = false, isStaticKey = false, isV2 = false;
|
||||||
|
Tag<32> iV2; // for 'i' field in SSU, TODO: remove later
|
||||||
char transportStyle[6];
|
char transportStyle[6];
|
||||||
ReadString (transportStyle, 6, s);
|
ReadString (transportStyle, 6, s);
|
||||||
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
|
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
|
||||||
address->transportStyle = eTransportNTCP;
|
address->transportStyle = eTransportNTCP;
|
||||||
else if (!strcmp (transportStyle, "SSU"))
|
else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2
|
||||||
{
|
{
|
||||||
address->transportStyle = eTransportSSU;
|
address->transportStyle = (transportStyle[3] == '2') ? eTransportSSU2 : eTransportSSU;
|
||||||
address->ssu.reset (new SSUExt ());
|
address->ssu.reset (new SSUExt ());
|
||||||
address->ssu->mtu = 0;
|
address->ssu->mtu = 0;
|
||||||
}
|
}
|
||||||
@@ -234,6 +236,12 @@ namespace data
|
|||||||
uint16_t size, r = 0;
|
uint16_t size, r = 0;
|
||||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||||
size = be16toh (size);
|
size = be16toh (size);
|
||||||
|
if (address->transportStyle == eTransportUnknown)
|
||||||
|
{
|
||||||
|
// skip unknown address
|
||||||
|
s.seekg (size, std::ios_base::cur);
|
||||||
|
if (s) continue; else return;
|
||||||
|
}
|
||||||
while (r < size)
|
while (r < size)
|
||||||
{
|
{
|
||||||
char key[255], value[255];
|
char key[255], value[255];
|
||||||
@@ -266,15 +274,29 @@ namespace data
|
|||||||
}
|
}
|
||||||
else if (!strcmp (key, "caps"))
|
else if (!strcmp (key, "caps"))
|
||||||
address->caps = ExtractAddressCaps (value);
|
address->caps = ExtractAddressCaps (value);
|
||||||
else if (!strcmp (key, "s")) // ntcp2 static key
|
else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key
|
||||||
{
|
{
|
||||||
Base64ToByteStream (value, strlen (value), address->s, 32);
|
Base64ToByteStream (value, strlen (value), address->s, 32);
|
||||||
isStaticKey = true;
|
isStaticKey = true;
|
||||||
}
|
}
|
||||||
else if (!strcmp (key, "i")) // ntcp2 iv
|
else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro
|
||||||
|
{
|
||||||
|
if (address->IsNTCP2 ())
|
||||||
{
|
{
|
||||||
Base64ToByteStream (value, strlen (value), address->i, 16);
|
Base64ToByteStream (value, strlen (value), address->i, 16);
|
||||||
address->published = true; // presence if "i" means "published"
|
address->published = true; // presence of "i" means "published" NTCP2
|
||||||
|
}
|
||||||
|
else if (address->IsSSU2 ())
|
||||||
|
Base64ToByteStream (value, strlen (value), address->i, 32);
|
||||||
|
else
|
||||||
|
Base64ToByteStream (value, strlen (value), iV2, 32);
|
||||||
|
}
|
||||||
|
else if (!strcmp (key, "v"))
|
||||||
|
{
|
||||||
|
if (!strcmp (value, "2"))
|
||||||
|
isV2 = true;
|
||||||
|
else
|
||||||
|
LogPrint (eLogWarning, "RouterInfo: Unexpected value ", value, " for v");
|
||||||
}
|
}
|
||||||
else if (key[0] == 'i')
|
else if (key[0] == 'i')
|
||||||
{
|
{
|
||||||
@@ -308,7 +330,7 @@ namespace data
|
|||||||
introducer.iPort = boost::lexical_cast<int>(value);
|
introducer.iPort = boost::lexical_cast<int>(value);
|
||||||
else if (!strcmp (key, "itag"))
|
else if (!strcmp (key, "itag"))
|
||||||
introducer.iTag = boost::lexical_cast<uint32_t>(value);
|
introducer.iTag = boost::lexical_cast<uint32_t>(value);
|
||||||
else if (!strcmp (key, "ikey"))
|
else if (!strcmp (key, "ikey") || !strcmp (key, "ih"))
|
||||||
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
|
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
|
||||||
else if (!strcmp (key, "iexp"))
|
else if (!strcmp (key, "iexp"))
|
||||||
introducer.iExp = boost::lexical_cast<uint32_t>(value);
|
introducer.iExp = boost::lexical_cast<uint32_t>(value);
|
||||||
@@ -378,10 +400,37 @@ namespace data
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (address->transportStyle == eTransportSSU2 || (isV2 && address->transportStyle == eTransportSSU))
|
||||||
|
{
|
||||||
|
if (address->IsV4 ()) supportedTransports |= eSSU2V4;
|
||||||
|
if (address->IsV6 ()) supportedTransports |= eSSU2V6;
|
||||||
|
if (address->port)
|
||||||
|
{
|
||||||
|
if (address->host.is_v4 ()) m_ReachableTransports |= eSSU2V4;
|
||||||
|
if (address->host.is_v6 ()) m_ReachableTransports |= eSSU2V6;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (supportedTransports)
|
if (supportedTransports)
|
||||||
{
|
{
|
||||||
if (!(m_SupportedTransports & supportedTransports)) // avoid duplicates
|
if (!(m_SupportedTransports & supportedTransports)) // avoid duplicates
|
||||||
|
{
|
||||||
addresses->push_back(address);
|
addresses->push_back(address);
|
||||||
|
if (address->transportStyle == eTransportSSU && isV2)
|
||||||
|
{
|
||||||
|
// create additional SSU2 address. TODO: remove later
|
||||||
|
auto ssu2addr = std::make_shared<Address> ();
|
||||||
|
ssu2addr->transportStyle = eTransportSSU2;
|
||||||
|
ssu2addr->host = address->host; ssu2addr->port = address->port;
|
||||||
|
ssu2addr->s = address->s; ssu2addr->i = iV2;
|
||||||
|
ssu2addr->date = address->date; ssu2addr->caps = address->caps;
|
||||||
|
ssu2addr->published = address->published;
|
||||||
|
ssu2addr->ssu.reset (new SSUExt ()); ssu2addr->ssu->mtu = address->ssu->mtu;
|
||||||
|
for (const auto& introducer: address->ssu->introducers)
|
||||||
|
if (!introducer.iPort) // SSU2
|
||||||
|
ssu2addr->ssu->introducers.push_back (introducer);
|
||||||
|
addresses->push_back(ssu2addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
m_SupportedTransports |= supportedTransports;
|
m_SupportedTransports |= supportedTransports;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -397,6 +446,7 @@ namespace data
|
|||||||
// read properties
|
// read properties
|
||||||
m_Version = 0;
|
m_Version = 0;
|
||||||
bool isNetId = false;
|
bool isNetId = false;
|
||||||
|
std::string family;
|
||||||
uint16_t size, r = 0;
|
uint16_t size, r = 0;
|
||||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||||
size = be16toh (size);
|
size = be16toh (size);
|
||||||
@@ -441,16 +491,15 @@ namespace data
|
|||||||
// family
|
// family
|
||||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY))
|
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY))
|
||||||
{
|
{
|
||||||
m_Family = value;
|
family = value;
|
||||||
boost::to_lower (m_Family);
|
boost::to_lower (family);
|
||||||
}
|
}
|
||||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG))
|
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG))
|
||||||
{
|
{
|
||||||
if (!netdb.GetFamilies ().VerifyFamily (m_Family, GetIdentHash (), value))
|
if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value))
|
||||||
{
|
m_FamilyID = netdb.GetFamilies ().GetFamilyID (family);
|
||||||
LogPrint (eLogWarning, "RouterInfo: Family signature verification failed");
|
else
|
||||||
m_Family.clear ();
|
LogPrint (eLogWarning, "RouterInfo: Family ", family, " signature verification failed");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s) return;
|
if (!s) return;
|
||||||
@@ -460,9 +509,9 @@ namespace data
|
|||||||
SetUnreachable (true);
|
SetUnreachable (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RouterInfo::IsFamily(const std::string & fam) const
|
bool RouterInfo::IsFamily (FamilyID famid) const
|
||||||
{
|
{
|
||||||
return m_Family == fam;
|
return m_FamilyID == famid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RouterInfo::ExtractCaps (const char * value)
|
void RouterInfo::ExtractCaps (const char * value)
|
||||||
@@ -627,6 +676,48 @@ namespace data
|
|||||||
m_Addresses->push_back(std::move(addr));
|
m_Addresses->push_back(std::move(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps)
|
||||||
|
{
|
||||||
|
auto addr = std::make_shared<Address>();
|
||||||
|
addr->transportStyle = eTransportSSU2;
|
||||||
|
addr->caps = caps;
|
||||||
|
addr->date = 0;
|
||||||
|
addr->ssu.reset (new SSUExt ());
|
||||||
|
addr->ssu->mtu = 0;
|
||||||
|
memcpy (addr->s, staticKey, 32);
|
||||||
|
memcpy (addr->i, introKey, 32);
|
||||||
|
if (addr->IsV4 ()) m_SupportedTransports |= eSSU2V4;
|
||||||
|
if (addr->IsV6 ()) m_SupportedTransports |= eSSU2V6;
|
||||||
|
m_Addresses->push_back(std::move(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey,
|
||||||
|
const boost::asio::ip::address& host, int port)
|
||||||
|
{
|
||||||
|
auto addr = std::make_shared<Address>();
|
||||||
|
addr->transportStyle = eTransportSSU2;
|
||||||
|
addr->host = host;
|
||||||
|
addr->port = port;
|
||||||
|
addr->published = true;
|
||||||
|
addr->caps = 0;
|
||||||
|
addr->date = 0;
|
||||||
|
addr->ssu.reset (new SSUExt ());
|
||||||
|
addr->ssu->mtu = 0;
|
||||||
|
memcpy (addr->s, staticKey, 32);
|
||||||
|
memcpy (addr->i, introKey, 32);
|
||||||
|
if (addr->IsV4 ())
|
||||||
|
{
|
||||||
|
m_SupportedTransports |= eSSU2V4;
|
||||||
|
m_ReachableTransports |= eSSU2V4;
|
||||||
|
}
|
||||||
|
if (addr->IsV6 ())
|
||||||
|
{
|
||||||
|
m_SupportedTransports |= eSSU2V6;
|
||||||
|
m_ReachableTransports |= eSSU2V6;
|
||||||
|
}
|
||||||
|
m_Addresses->push_back(std::move(addr));
|
||||||
|
}
|
||||||
|
|
||||||
bool RouterInfo::AddIntroducer (const Introducer& introducer)
|
bool RouterInfo::AddIntroducer (const Introducer& introducer)
|
||||||
{
|
{
|
||||||
for (auto& addr : *m_Addresses)
|
for (auto& addr : *m_Addresses)
|
||||||
@@ -672,11 +763,6 @@ namespace data
|
|||||||
return m_SupportedTransports & (eSSUV4 | eSSUV6);
|
return m_SupportedTransports & (eSSUV4 | eSSUV6);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RouterInfo::IsSSUV6 () const
|
|
||||||
{
|
|
||||||
return m_SupportedTransports & eSSUV6;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::IsNTCP2 (bool v4only) const
|
bool RouterInfo::IsNTCP2 (bool v4only) const
|
||||||
{
|
{
|
||||||
if (v4only)
|
if (v4only)
|
||||||
@@ -685,25 +771,6 @@ namespace data
|
|||||||
return m_SupportedTransports & (eNTCP2V4 | eNTCP2V6);
|
return m_SupportedTransports & (eNTCP2V4 | eNTCP2V6);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RouterInfo::IsNTCP2V6 () const
|
|
||||||
{
|
|
||||||
return m_SupportedTransports & eNTCP2V6;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::IsV6 () const
|
|
||||||
{
|
|
||||||
return m_SupportedTransports & (eSSUV6 | eNTCP2V6);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::IsV4 () const
|
|
||||||
{
|
|
||||||
return m_SupportedTransports & (eSSUV4 | eNTCP2V4);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::IsMesh () const
|
|
||||||
{
|
|
||||||
return m_SupportedTransports & eNTCP2V6Mesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::EnableV6 ()
|
void RouterInfo::EnableV6 ()
|
||||||
{
|
{
|
||||||
@@ -820,6 +887,24 @@ namespace data
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2V4Address () const
|
||||||
|
{
|
||||||
|
return GetAddress (
|
||||||
|
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||||
|
{
|
||||||
|
return (address->transportStyle == eTransportSSU2) && address->IsV4();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2V6Address () const
|
||||||
|
{
|
||||||
|
return GetAddress (
|
||||||
|
[](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||||
|
{
|
||||||
|
return (address->transportStyle == eTransportSSU2) && address->IsV6();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Filter>
|
template<typename Filter>
|
||||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (Filter filter) const
|
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (Filter filter) const
|
||||||
{
|
{
|
||||||
@@ -845,6 +930,16 @@ namespace data
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const
|
||||||
|
{
|
||||||
|
if (!key) return nullptr;
|
||||||
|
return GetAddress (
|
||||||
|
[key, isV6](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||||
|
{
|
||||||
|
return address->IsSSU2 () && !memcmp (address->s, key, 32) && address->IsV6 () == isV6;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetPublishedNTCP2V4Address () const
|
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetPublishedNTCP2V4Address () const
|
||||||
{
|
{
|
||||||
return GetAddress (
|
return GetAddress (
|
||||||
@@ -905,6 +1000,17 @@ namespace data
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RouterInfo::IsSSU2PeerTesting (bool v4) const
|
||||||
|
{
|
||||||
|
if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false;
|
||||||
|
return (bool)GetAddress (
|
||||||
|
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
|
||||||
|
{
|
||||||
|
return (address->IsSSU2 ()) && address->IsPeerTesting () &&
|
||||||
|
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU ();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
bool RouterInfo::IsIntroducer (bool v4) const
|
bool RouterInfo::IsIntroducer (bool v4) const
|
||||||
{
|
{
|
||||||
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
||||||
@@ -921,7 +1027,7 @@ namespace data
|
|||||||
for (auto& addr: *m_Addresses)
|
for (auto& addr: *m_Addresses)
|
||||||
{
|
{
|
||||||
// TODO: implement SSU
|
// TODO: implement SSU
|
||||||
if (addr->transportStyle == eTransportNTCP && !addr->IsPublishedNTCP2 ())
|
if (!addr->published && (addr->transportStyle == eTransportNTCP || addr->transportStyle == eTransportSSU2))
|
||||||
{
|
{
|
||||||
addr->caps &= ~(eV4 | eV6);
|
addr->caps &= ~(eV4 | eV6);
|
||||||
addr->caps |= transports;
|
addr->caps |= transports;
|
||||||
@@ -958,12 +1064,17 @@ namespace data
|
|||||||
void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len)
|
void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
if (!m_Buffer)
|
if (!m_Buffer)
|
||||||
m_Buffer = netdb.NewRouterInfoBuffer ();
|
m_Buffer = NewBuffer ();
|
||||||
if (len > m_Buffer->size ()) len = m_Buffer->size ();
|
if (len > m_Buffer->size ()) len = m_Buffer->size ();
|
||||||
memcpy (m_Buffer->data (), buf, len);
|
memcpy (m_Buffer->data (), buf, len);
|
||||||
m_BufferLen = len;
|
m_BufferLen = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<RouterInfo::Buffer> RouterInfo::NewBuffer () const
|
||||||
|
{
|
||||||
|
return netdb.NewRouterInfoBuffer ();
|
||||||
|
}
|
||||||
|
|
||||||
void RouterInfo::RefreshTimestamp ()
|
void RouterInfo::RefreshTimestamp ()
|
||||||
{
|
{
|
||||||
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
|
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
@@ -1041,6 +1152,8 @@ namespace data
|
|||||||
cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED;
|
cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED;
|
||||||
else if (address.transportStyle == eTransportSSU)
|
else if (address.transportStyle == eTransportSSU)
|
||||||
cost = address.published ? COST_SSU_DIRECT : COST_SSU_THROUGH_INTRODUCERS;
|
cost = address.published ? COST_SSU_DIRECT : COST_SSU_THROUGH_INTRODUCERS;
|
||||||
|
else if (address.transportStyle == eTransportSSU2)
|
||||||
|
cost = address.published ? COST_SSU2_DIRECT : COST_SSU2_NON_PUBLISHED;
|
||||||
s.write ((const char *)&cost, sizeof (cost));
|
s.write ((const char *)&cost, sizeof (cost));
|
||||||
s.write ((const char *)&address.date, sizeof (address.date));
|
s.write ((const char *)&address.date, sizeof (address.date));
|
||||||
std::stringstream properties;
|
std::stringstream properties;
|
||||||
@@ -1104,6 +1217,30 @@ namespace data
|
|||||||
WriteString (caps, properties);
|
WriteString (caps, properties);
|
||||||
properties << ';';
|
properties << ';';
|
||||||
}
|
}
|
||||||
|
else if (address.transportStyle == eTransportSSU2)
|
||||||
|
{
|
||||||
|
WriteString ("SSU2", s);
|
||||||
|
// caps
|
||||||
|
std::string caps;
|
||||||
|
if (address.published)
|
||||||
|
{
|
||||||
|
isPublished = true;
|
||||||
|
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (address.IsV4 ()) caps += CAPS_FLAG_V4;
|
||||||
|
if (address.IsV6 ()) caps += CAPS_FLAG_V6;
|
||||||
|
if (caps.empty ()) caps += CAPS_FLAG_V4;
|
||||||
|
}
|
||||||
|
if (!caps.empty ())
|
||||||
|
{
|
||||||
|
WriteString ("caps", properties);
|
||||||
|
properties << '=';
|
||||||
|
WriteString (caps, properties);
|
||||||
|
properties << ';';
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
WriteString ("", s);
|
WriteString ("", s);
|
||||||
|
|
||||||
@@ -1114,10 +1251,17 @@ namespace data
|
|||||||
WriteString (address.host.to_string (), properties);
|
WriteString (address.host.to_string (), properties);
|
||||||
properties << ';';
|
properties << ';';
|
||||||
}
|
}
|
||||||
if (address.transportStyle == eTransportSSU)
|
if ((address.IsNTCP2 () && isPublished) || address.IsSSU2 ())
|
||||||
|
{
|
||||||
|
// publish i for NTCP2 or SSU2
|
||||||
|
WriteString ("i", properties); properties << '=';
|
||||||
|
size_t len = address.IsSSU2 () ? 32 : 16;
|
||||||
|
WriteString (address.i.ToBase64 (len), properties); properties << ';';
|
||||||
|
}
|
||||||
|
if (address.transportStyle == eTransportSSU || address.IsSSU2 ())
|
||||||
{
|
{
|
||||||
// write introducers if any
|
// write introducers if any
|
||||||
if (!address.ssu->introducers.empty())
|
if (address.ssu && !address.ssu->introducers.empty())
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const auto& introducer: address.ssu->introducers)
|
for (const auto& introducer: address.ssu->introducers)
|
||||||
@@ -1131,6 +1275,8 @@ namespace data
|
|||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
if (address.transportStyle == eTransportSSU)
|
||||||
|
{
|
||||||
i = 0;
|
i = 0;
|
||||||
for (const auto& introducer: address.ssu->introducers)
|
for (const auto& introducer: address.ssu->introducers)
|
||||||
{
|
{
|
||||||
@@ -1140,9 +1286,13 @@ namespace data
|
|||||||
properties << ';';
|
properties << ';';
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
i = 0;
|
i = 0;
|
||||||
for (const auto& introducer: address.ssu->introducers)
|
for (const auto& introducer: address.ssu->introducers)
|
||||||
{
|
{
|
||||||
|
if (address.IsSSU2 ())
|
||||||
|
WriteString ("ih" + boost::lexical_cast<std::string>(i), properties);
|
||||||
|
else
|
||||||
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
|
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
|
||||||
properties << '=';
|
properties << '=';
|
||||||
char value[64];
|
char value[64];
|
||||||
@@ -1152,6 +1302,8 @@ namespace data
|
|||||||
properties << ';';
|
properties << ';';
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
if (address.transportStyle == eTransportSSU)
|
||||||
|
{
|
||||||
i = 0;
|
i = 0;
|
||||||
for (const auto& introducer: address.ssu->introducers)
|
for (const auto& introducer: address.ssu->introducers)
|
||||||
{
|
{
|
||||||
@@ -1161,6 +1313,7 @@ namespace data
|
|||||||
properties << ';';
|
properties << ';';
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
i = 0;
|
i = 0;
|
||||||
for (const auto& introducer: address.ssu->introducers)
|
for (const auto& introducer: address.ssu->introducers)
|
||||||
{
|
{
|
||||||
@@ -1171,6 +1324,9 @@ namespace data
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (address.transportStyle == eTransportSSU)
|
||||||
|
{
|
||||||
// write intro key
|
// write intro key
|
||||||
WriteString ("key", properties);
|
WriteString ("key", properties);
|
||||||
properties << '=';
|
properties << '=';
|
||||||
@@ -1179,8 +1335,11 @@ namespace data
|
|||||||
value[l] = 0;
|
value[l] = 0;
|
||||||
WriteString (value, properties);
|
WriteString (value, properties);
|
||||||
properties << ';';
|
properties << ';';
|
||||||
|
}
|
||||||
|
if (address.transportStyle == eTransportSSU || address.IsSSU2 ())
|
||||||
|
{
|
||||||
// write mtu
|
// write mtu
|
||||||
if (address.ssu->mtu)
|
if (address.ssu && address.ssu->mtu)
|
||||||
{
|
{
|
||||||
WriteString ("mtu", properties);
|
WriteString ("mtu", properties);
|
||||||
properties << '=';
|
properties << '=';
|
||||||
@@ -1188,24 +1347,16 @@ namespace data
|
|||||||
properties << ';';
|
properties << ';';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isPublished || (address.ssu && !address.IsSSU2 ()))
|
||||||
if (address.IsNTCP2 () && isPublished)
|
|
||||||
{
|
|
||||||
// publish i for NTCP2
|
|
||||||
WriteString ("i", properties); properties << '=';
|
|
||||||
WriteString (address.i.ToBase64 (16), properties); properties << ';';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPublished || address.ssu)
|
|
||||||
{
|
{
|
||||||
WriteString ("port", properties);
|
WriteString ("port", properties);
|
||||||
properties << '=';
|
properties << '=';
|
||||||
WriteString (boost::lexical_cast<std::string>(address.port), properties);
|
WriteString (boost::lexical_cast<std::string>(address.port), properties);
|
||||||
properties << ';';
|
properties << ';';
|
||||||
}
|
}
|
||||||
if (address.IsNTCP2 ())
|
if (address.IsNTCP2 () || address.IsSSU2 ())
|
||||||
{
|
{
|
||||||
// publish s and v for NTCP2
|
// publish s and v for NTCP2 or SSU2
|
||||||
WriteString ("s", properties); properties << '=';
|
WriteString ("s", properties); properties << '=';
|
||||||
WriteString (address.s.ToBase64 (), properties); properties << ';';
|
WriteString (address.s.ToBase64 (), properties); properties << ';';
|
||||||
WriteString ("v", properties); properties << '=';
|
WriteString ("v", properties); properties << '=';
|
||||||
@@ -1259,5 +1410,10 @@ namespace data
|
|||||||
s.write ((char *)&len, 1);
|
s.write ((char *)&len, 1);
|
||||||
s.write (str.c_str (), len);
|
s.write (str.c_str (), len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<RouterInfo::Buffer> LocalRouterInfo::NewBuffer () const
|
||||||
|
{
|
||||||
|
return std::make_shared<Buffer> ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include "Identity.h"
|
#include "Identity.h"
|
||||||
#include "Profiling.h"
|
#include "Profiling.h"
|
||||||
|
#include "Family.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
@@ -51,10 +52,12 @@ namespace data
|
|||||||
|
|
||||||
const uint8_t COST_NTCP2_PUBLISHED = 3;
|
const uint8_t COST_NTCP2_PUBLISHED = 3;
|
||||||
const uint8_t COST_NTCP2_NON_PUBLISHED = 14;
|
const uint8_t COST_NTCP2_NON_PUBLISHED = 14;
|
||||||
|
const uint8_t COST_SSU2_DIRECT = 8;
|
||||||
const uint8_t COST_SSU_DIRECT = 9;
|
const uint8_t COST_SSU_DIRECT = 9;
|
||||||
const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11;
|
const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11;
|
||||||
|
const uint8_t COST_SSU2_NON_PUBLISHED = 15;
|
||||||
|
|
||||||
const size_t MAX_RI_BUFFER_SIZE = 2048; // if RouterInfo exceeds 2048 we consider it as malformed, might be changed later
|
const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later
|
||||||
class RouterInfo: public RoutingDestination
|
class RouterInfo: public RoutingDestination
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -66,6 +69,8 @@ namespace data
|
|||||||
eSSUV4 = 0x04,
|
eSSUV4 = 0x04,
|
||||||
eSSUV6 = 0x08,
|
eSSUV6 = 0x08,
|
||||||
eNTCP2V6Mesh = 0x10,
|
eNTCP2V6Mesh = 0x10,
|
||||||
|
eSSU2V4 = 0x20,
|
||||||
|
eSSU2V6 = 0x40,
|
||||||
eAllTransports = 0xFF
|
eAllTransports = 0xFF
|
||||||
};
|
};
|
||||||
typedef uint8_t CompatibleTransports;
|
typedef uint8_t CompatibleTransports;
|
||||||
@@ -92,7 +97,8 @@ namespace data
|
|||||||
{
|
{
|
||||||
eTransportUnknown = 0,
|
eTransportUnknown = 0,
|
||||||
eTransportNTCP,
|
eTransportNTCP,
|
||||||
eTransportSSU
|
eTransportSSU,
|
||||||
|
eTransportSSU2
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
|
typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey
|
||||||
@@ -101,7 +107,7 @@ namespace data
|
|||||||
Introducer (): iPort (0), iExp (0) {};
|
Introducer (): iPort (0), iExp (0) {};
|
||||||
boost::asio::ip::address iHost;
|
boost::asio::ip::address iHost;
|
||||||
int iPort;
|
int iPort;
|
||||||
IntroKey iKey;
|
IntroKey iKey; // or ih for SSU2
|
||||||
uint32_t iTag;
|
uint32_t iTag;
|
||||||
uint32_t iExp;
|
uint32_t iExp;
|
||||||
};
|
};
|
||||||
@@ -131,7 +137,7 @@ namespace data
|
|||||||
|
|
||||||
bool operator==(const Address& other) const
|
bool operator==(const Address& other) const
|
||||||
{
|
{
|
||||||
return transportStyle == other.transportStyle && IsNTCP2 () == other.IsNTCP2 () &&
|
return transportStyle == other.transportStyle &&
|
||||||
host == other.host && port == other.port;
|
host == other.host && port == other.port;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,8 +147,9 @@ namespace data
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IsNTCP2 () const { return transportStyle == eTransportNTCP; };
|
bool IsNTCP2 () const { return transportStyle == eTransportNTCP; };
|
||||||
|
bool IsSSU2 () const { return transportStyle == eTransportSSU2; };
|
||||||
bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; };
|
bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; };
|
||||||
bool IsReachableSSU () const { return (bool)ssu && (published || !ssu->introducers.empty ()); };
|
bool IsReachableSSU () const { return (bool)ssu && (published || UsesIntroducer ()); };
|
||||||
bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); };
|
bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); };
|
||||||
|
|
||||||
bool IsIntroducer () const { return caps & eSSUIntroducer; };
|
bool IsIntroducer () const { return caps & eSSUIntroducer; };
|
||||||
@@ -178,15 +185,21 @@ namespace data
|
|||||||
virtual void ClearProperties () {};
|
virtual void ClearProperties () {};
|
||||||
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
|
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
|
||||||
std::shared_ptr<const Address> GetNTCP2AddressWithStaticKey (const uint8_t * key) const;
|
std::shared_ptr<const Address> GetNTCP2AddressWithStaticKey (const uint8_t * key) const;
|
||||||
|
std::shared_ptr<const Address> GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const;
|
||||||
std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const;
|
std::shared_ptr<const Address> GetPublishedNTCP2V4Address () const;
|
||||||
std::shared_ptr<const Address> GetPublishedNTCP2V6Address () const;
|
std::shared_ptr<const Address> GetPublishedNTCP2V6Address () const;
|
||||||
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
|
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
|
||||||
std::shared_ptr<const Address> GetSSUV6Address () const;
|
std::shared_ptr<const Address> GetSSUV6Address () const;
|
||||||
std::shared_ptr<const Address> GetYggdrasilAddress () const;
|
std::shared_ptr<const Address> GetYggdrasilAddress () const;
|
||||||
|
std::shared_ptr<const Address> GetSSU2V4Address () const;
|
||||||
|
std::shared_ptr<const Address> GetSSU2V6Address () const;
|
||||||
|
|
||||||
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
|
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
|
||||||
void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
|
void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
|
||||||
const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0, uint8_t caps = 0);
|
const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0, uint8_t caps = 0);
|
||||||
|
void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps = 0); // non published
|
||||||
|
void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey,
|
||||||
|
const boost::asio::ip::address& host, int port); // published
|
||||||
bool AddIntroducer (const Introducer& introducer);
|
bool AddIntroducer (const Introducer& introducer);
|
||||||
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||||
void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
|
void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps
|
||||||
@@ -195,12 +208,14 @@ namespace data
|
|||||||
bool IsReachable () const { return m_Caps & Caps::eReachable; };
|
bool IsReachable () const { return m_Caps & Caps::eReachable; };
|
||||||
bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
|
bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; };
|
||||||
bool IsSSU (bool v4only = true) const;
|
bool IsSSU (bool v4only = true) const;
|
||||||
bool IsSSUV6 () const;
|
bool IsSSUV6 () const { return m_SupportedTransports & eSSUV6; };
|
||||||
bool IsNTCP2 (bool v4only = true) const;
|
bool IsNTCP2 (bool v4only = true) const;
|
||||||
bool IsNTCP2V6 () const;
|
bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; };
|
||||||
bool IsV6 () const;
|
bool IsSSU2V4 () const { return m_SupportedTransports & eSSU2V4; };
|
||||||
bool IsV4 () const;
|
bool IsSSU2V6 () const { return m_SupportedTransports & eSSU2V6; };
|
||||||
bool IsMesh () const;
|
bool IsV6 () const { return m_SupportedTransports & (eSSUV6 | eNTCP2V6 | eSSU2V6); };
|
||||||
|
bool IsV4 () const { return m_SupportedTransports & (eSSUV4 | eNTCP2V4 | eSSU2V4); };
|
||||||
|
bool IsMesh () const { return m_SupportedTransports & eNTCP2V6Mesh; };
|
||||||
void EnableV6 ();
|
void EnableV6 ();
|
||||||
void DisableV6 ();
|
void DisableV6 ();
|
||||||
void EnableV4 ();
|
void EnableV4 ();
|
||||||
@@ -217,6 +232,7 @@ namespace data
|
|||||||
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
|
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
|
||||||
bool IsEligibleFloodfill () const;
|
bool IsEligibleFloodfill () const;
|
||||||
bool IsPeerTesting (bool v4) const;
|
bool IsPeerTesting (bool v4) const;
|
||||||
|
bool IsSSU2PeerTesting (bool v4) const;
|
||||||
bool IsIntroducer (bool v4) const;
|
bool IsIntroducer (bool v4) const;
|
||||||
|
|
||||||
uint8_t GetCaps () const { return m_Caps; };
|
uint8_t GetCaps () const { return m_Caps; };
|
||||||
@@ -241,7 +257,7 @@ namespace data
|
|||||||
bool IsNewer (const uint8_t * buf, size_t len) const;
|
bool IsNewer (const uint8_t * buf, size_t len) const;
|
||||||
|
|
||||||
/** return true if we are in a router family and the signature is valid */
|
/** return true if we are in a router family and the signature is valid */
|
||||||
bool IsFamily(const std::string & fam) const;
|
bool IsFamily (FamilyID famid) const;
|
||||||
|
|
||||||
// implements RoutingDestination
|
// implements RoutingDestination
|
||||||
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_RouterIdentity; };
|
std::shared_ptr<const IdentityEx> GetIdentity () const { return m_RouterIdentity; };
|
||||||
@@ -269,10 +285,11 @@ namespace data
|
|||||||
uint8_t ExtractAddressCaps (const char * value) const;
|
uint8_t ExtractAddressCaps (const char * value) const;
|
||||||
template<typename Filter>
|
template<typename Filter>
|
||||||
std::shared_ptr<const Address> GetAddress (Filter filter) const;
|
std::shared_ptr<const Address> GetAddress (Filter filter) const;
|
||||||
|
virtual std::shared_ptr<Buffer> NewBuffer () const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::string m_Family;
|
FamilyID m_FamilyID;
|
||||||
std::shared_ptr<const IdentityEx> m_RouterIdentity;
|
std::shared_ptr<const IdentityEx> m_RouterIdentity;
|
||||||
std::shared_ptr<Buffer> m_Buffer;
|
std::shared_ptr<Buffer> m_Buffer;
|
||||||
size_t m_BufferLen;
|
size_t m_BufferLen;
|
||||||
@@ -303,6 +320,7 @@ namespace data
|
|||||||
void WriteToStream (std::ostream& s) const;
|
void WriteToStream (std::ostream& s) const;
|
||||||
void UpdateCapsProperty ();
|
void UpdateCapsProperty ();
|
||||||
void WriteString (const std::string& str, std::ostream& s) const;
|
void WriteString (const std::string& str, std::ostream& s) const;
|
||||||
|
std::shared_ptr<Buffer> NewBuffer () const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "SSU.h"
|
#include "SSU.h"
|
||||||
|
|
||||||
#ifdef __linux__
|
#if defined(__linux__) && !defined(_NETINET_IN_H)
|
||||||
#include <linux/in6.h>
|
#include <linux/in6.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ namespace transport
|
|||||||
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
|
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
|
||||||
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
|
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
|
||||||
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
|
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
|
||||||
#ifdef __linux__
|
#if defined(__linux__) && !defined(_NETINET_IN_H)
|
||||||
if (m_EndpointV6.address() == boost::asio::ip::address().from_string("::")) // only if not binded to address
|
if (m_EndpointV6.address() == boost::asio::ip::address().from_string("::")) // only if not binded to address
|
||||||
{
|
{
|
||||||
// Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others
|
// Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others
|
||||||
|
|||||||
591
libi2pd/SSU2.cpp
591
libi2pd/SSU2.cpp
@@ -6,75 +6,562 @@
|
|||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include "Log.h"
|
||||||
#include <openssl/rand.h>
|
#include "RouterContext.h"
|
||||||
#include "Transports.h"
|
#include "Transports.h"
|
||||||
|
#include "NetDb.hpp"
|
||||||
|
#include "Config.h"
|
||||||
#include "SSU2.h"
|
#include "SSU2.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
namespace transport
|
namespace transport
|
||||||
{
|
{
|
||||||
SSU2Session::SSU2Session (std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter,
|
SSU2Server::SSU2Server ():
|
||||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr, bool peerTest):
|
RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"),
|
||||||
TransportSession (in_RemoteRouter, SSU2_TERMINATION_TIMEOUT),
|
m_SocketV4 (m_ReceiveService.GetService ()), m_SocketV6 (m_ReceiveService.GetService ()),
|
||||||
m_Address (addr)
|
m_TerminationTimer (GetService ()), m_ResendTimer (GetService ())
|
||||||
{
|
|
||||||
m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState);
|
|
||||||
if (in_RemoteRouter && addr)
|
|
||||||
{
|
|
||||||
// outgoing
|
|
||||||
InitNoiseXKState1 (*m_NoiseState, addr->s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SSU2Session::~SSU2Session ()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSU2Session::SendSessionRequest ()
|
void SSU2Server::Start ()
|
||||||
{
|
{
|
||||||
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
|
if (!IsRunning ())
|
||||||
m_NoiseState->MixHash (m_EphemeralKeys->GetPublicKey (), 32);
|
{
|
||||||
uint8_t sharedSecret[32];
|
StartIOService ();
|
||||||
m_EphemeralKeys->Agree (m_Address->s, sharedSecret);
|
bool found = false;
|
||||||
m_NoiseState->MixKey (sharedSecret);
|
auto& addresses = i2p::context.GetRouterInfo ().GetAddresses ();
|
||||||
|
for (const auto& address: addresses)
|
||||||
Header header;
|
{
|
||||||
uint8_t headerX[48], payload[1200]; // TODO: correct payload size
|
if (!address) continue;
|
||||||
size_t payloadSize = 8;
|
if (address->transportStyle == i2p::data::RouterInfo::eTransportSSU2)
|
||||||
// fill packet
|
{
|
||||||
RAND_bytes (header.h.connID, 8);
|
auto port = address->port;
|
||||||
memset (header.h.packetNum, 0, 4);
|
if (!port)
|
||||||
header.h.type = eSSU2SessionRequest;
|
{
|
||||||
header.h.flags[0] = 2; // ver
|
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||||
header.h.flags[1] = 2; // netID TODO:
|
if (ssu2Port) port = ssu2Port;
|
||||||
header.h.flags[2] = 0; // flag
|
else
|
||||||
RAND_bytes (headerX, 8); // source id
|
{
|
||||||
memset (headerX + 8, 0, 8); // token
|
uint16_t p; i2p::config::GetOption ("port", p);
|
||||||
memcpy (headerX + 16, m_EphemeralKeys->GetPublicKey (), 32); // X
|
if (p) port = p;
|
||||||
// encrypt
|
}
|
||||||
const uint8_t nonce[12] = {0};
|
}
|
||||||
i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true);
|
if (port)
|
||||||
payloadSize += 16;
|
{
|
||||||
CreateHeaderMask (m_Address->i, payload + (payloadSize - 24), m_Address->i, payload + (payloadSize - 12));
|
if (address->IsV4 ())
|
||||||
EncryptHeader (header);
|
{
|
||||||
i2p::crypto::ChaCha20 (headerX, 48, m_Address->i, nonce, headerX);
|
found = true;
|
||||||
|
OpenSocket (boost::asio::ip::udp::endpoint (boost::asio::ip::udp::v4(), port));
|
||||||
|
m_ReceiveService.GetService ().post(
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
Receive (m_SocketV4);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (address->IsV6 ())
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
OpenSocket (boost::asio::ip::udp::endpoint (boost::asio::ip::udp::v6(), port));
|
||||||
|
m_ReceiveService.GetService ().post(
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
Receive (m_SocketV6);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LogPrint (eLogError, "SSU2: Can't start server because port not specified");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found)
|
||||||
|
m_ReceiveService.Start ();
|
||||||
|
ScheduleTermination ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSU2Session::EncryptHeader (Header& h)
|
void SSU2Server::Stop ()
|
||||||
{
|
{
|
||||||
h.ll[0] ^= m_HeaderMask.ll[0];
|
if (context.SupportsV4 () || context.SupportsV6 ())
|
||||||
h.ll[1] ^= m_HeaderMask.ll[1];
|
m_ReceiveService.Stop ();
|
||||||
|
|
||||||
|
if (IsRunning ())
|
||||||
|
m_TerminationTimer.cancel ();
|
||||||
|
|
||||||
|
StopIOService ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSU2Session::CreateHeaderMask (const uint8_t * kh1, const uint8_t * nonce1, const uint8_t * kh2, const uint8_t * nonce2)
|
boost::asio::ip::udp::socket& SSU2Server::OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint)
|
||||||
{
|
{
|
||||||
// Header Encryption KDF
|
boost::asio::ip::udp::socket& socket = localEndpoint.address ().is_v6 () ? m_SocketV6 : m_SocketV4;
|
||||||
uint8_t data[8] = {0};
|
try
|
||||||
i2p::crypto::ChaCha20 (data, 8, kh1, nonce1, m_HeaderMask.buf);
|
{
|
||||||
i2p::crypto::ChaCha20 (data, 8, kh2, nonce2, m_HeaderMask.buf + 8);
|
socket.open (localEndpoint.protocol ());
|
||||||
|
if (localEndpoint.address ().is_v6 ())
|
||||||
|
socket.set_option (boost::asio::ip::v6_only (true));
|
||||||
|
socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU2_SOCKET_RECEIVE_BUFFER_SIZE));
|
||||||
|
socket.set_option (boost::asio::socket_base::send_buffer_size (SSU2_SOCKET_SEND_BUFFER_SIZE));
|
||||||
|
socket.bind (localEndpoint);
|
||||||
|
LogPrint (eLogInfo, "SSU2: Start listening on ", localEndpoint);
|
||||||
|
}
|
||||||
|
catch (std::exception& ex )
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "SSU2: Failed to bind to ", localEndpoint, ": ", ex.what());
|
||||||
|
ThrowFatal ("Unable to start SSU2 transport on ", localEndpoint, ": ", ex.what ());
|
||||||
|
}
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::Receive (boost::asio::ip::udp::socket& socket)
|
||||||
|
{
|
||||||
|
Packet * packet = m_PacketsPool.AcquireMt ();
|
||||||
|
socket.async_receive_from (boost::asio::buffer (packet->buf, SSU2_MTU), packet->from,
|
||||||
|
std::bind (&SSU2Server::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet, std::ref (socket)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred,
|
||||||
|
Packet * packet, boost::asio::ip::udp::socket& socket)
|
||||||
|
{
|
||||||
|
if (!ecode)
|
||||||
|
{
|
||||||
|
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred);
|
||||||
|
packet->len = bytes_transferred;
|
||||||
|
|
||||||
|
boost::system::error_code ec;
|
||||||
|
size_t moreBytes = socket.available (ec);
|
||||||
|
if (!ec && moreBytes)
|
||||||
|
{
|
||||||
|
std::vector<Packet *> packets;
|
||||||
|
packets.push_back (packet);
|
||||||
|
while (moreBytes && packets.size () < 32)
|
||||||
|
{
|
||||||
|
packet = m_PacketsPool.AcquireMt ();
|
||||||
|
packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MTU), packet->from, 0, ec);
|
||||||
|
if (!ec)
|
||||||
|
{
|
||||||
|
i2p::transport::transports.UpdateReceivedBytes (packet->len);
|
||||||
|
packets.push_back (packet);
|
||||||
|
moreBytes = socket.available(ec);
|
||||||
|
if (ec) break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "SSU2: receive_from error: code ", ec.value(), ": ", ec.message ());
|
||||||
|
m_PacketsPool.ReleaseMt (packet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GetService ().post (std::bind (&SSU2Server::HandleReceivedPackets, this, packets));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
GetService ().post (std::bind (&SSU2Server::HandleReceivedPacket, this, packet));
|
||||||
|
Receive (socket);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_PacketsPool.ReleaseMt (packet);
|
||||||
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "SSU2: Receive error: code ", ecode.value(), ": ", ecode.message ());
|
||||||
|
auto ep = socket.local_endpoint ();
|
||||||
|
socket.close ();
|
||||||
|
OpenSocket (ep);
|
||||||
|
Receive (socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::HandleReceivedPacket (Packet * packet)
|
||||||
|
{
|
||||||
|
if (packet)
|
||||||
|
{
|
||||||
|
ProcessNextPacket (packet->buf, packet->len, packet->from);
|
||||||
|
m_PacketsPool.ReleaseMt (packet);
|
||||||
|
if (m_LastSession) m_LastSession->FlushData ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::HandleReceivedPackets (std::vector<Packet *> packets)
|
||||||
|
{
|
||||||
|
for (auto& packet: packets)
|
||||||
|
ProcessNextPacket (packet->buf, packet->len, packet->from);
|
||||||
|
m_PacketsPool.ReleaseMt (packets);
|
||||||
|
if (m_LastSession) m_LastSession->FlushData ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::AddSession (std::shared_ptr<SSU2Session> session)
|
||||||
|
{
|
||||||
|
if (session)
|
||||||
|
{
|
||||||
|
m_Sessions.emplace (session->GetConnID (), session);
|
||||||
|
AddSessionByRouterHash (session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::RemoveSession (uint64_t connID)
|
||||||
|
{
|
||||||
|
auto it = m_Sessions.find (connID);
|
||||||
|
if (it != m_Sessions.end ())
|
||||||
|
{
|
||||||
|
auto ident = it->second->GetRemoteIdentity ();
|
||||||
|
if (ident)
|
||||||
|
m_SessionsByRouterHash.erase (ident->GetIdentHash ());
|
||||||
|
m_Sessions.erase (it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::AddSessionByRouterHash (std::shared_ptr<SSU2Session> session)
|
||||||
|
{
|
||||||
|
if (session)
|
||||||
|
{
|
||||||
|
auto ident = session->GetRemoteIdentity ();
|
||||||
|
if (ident)
|
||||||
|
{
|
||||||
|
auto ret = m_SessionsByRouterHash.emplace (ident->GetIdentHash (), session);
|
||||||
|
if (!ret.second)
|
||||||
|
{
|
||||||
|
// session already exists
|
||||||
|
LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " aready exists");
|
||||||
|
// terminate existing
|
||||||
|
GetService ().post (std::bind (&SSU2Session::Terminate, ret.first->second));
|
||||||
|
// update session
|
||||||
|
ret.first->second = session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session)
|
||||||
|
{
|
||||||
|
if (session)
|
||||||
|
m_PendingOutgoingSessions.emplace (session->GetRemoteEndpoint (), session);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<SSU2Session> SSU2Server::FindSession (const i2p::data::IdentHash& ident) const
|
||||||
|
{
|
||||||
|
auto it = m_SessionsByRouterHash.find (ident);
|
||||||
|
if (it != m_SessionsByRouterHash.end ())
|
||||||
|
return it->second;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay)
|
||||||
|
{
|
||||||
|
m_Relays.emplace (tag, relay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::RemoveRelay (uint32_t tag)
|
||||||
|
{
|
||||||
|
m_Relays.erase (tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<SSU2Session> SSU2Server::FindRelaySession (uint32_t tag)
|
||||||
|
{
|
||||||
|
auto it = m_Relays.find (tag);
|
||||||
|
if (it != m_Relays.end ())
|
||||||
|
{
|
||||||
|
if (it->second->IsEstablished ())
|
||||||
|
return it->second;
|
||||||
|
else
|
||||||
|
m_Relays.erase (it);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint)
|
||||||
|
{
|
||||||
|
if (len < 24) return;
|
||||||
|
uint64_t connID;
|
||||||
|
memcpy (&connID, buf, 8);
|
||||||
|
connID ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24));
|
||||||
|
if (!m_LastSession || m_LastSession->GetConnID () != connID)
|
||||||
|
{
|
||||||
|
if (m_LastSession) m_LastSession->FlushData ();
|
||||||
|
auto it = m_Sessions.find (connID);
|
||||||
|
if (it != m_Sessions.end ())
|
||||||
|
m_LastSession = it->second;
|
||||||
|
else
|
||||||
|
m_LastSession = nullptr;
|
||||||
|
}
|
||||||
|
if (m_LastSession)
|
||||||
|
{
|
||||||
|
switch (m_LastSession->GetState ())
|
||||||
|
{
|
||||||
|
case eSSU2SessionStateEstablished:
|
||||||
|
m_LastSession->ProcessData (buf, len);
|
||||||
|
break;
|
||||||
|
case eSSU2SessionStateUnknown:
|
||||||
|
m_LastSession->ProcessSessionConfirmed (buf, len);
|
||||||
|
break;
|
||||||
|
case eSSU2SessionStateIntroduced:
|
||||||
|
m_LastSession->SetRemoteEndpoint (senderEndpoint);
|
||||||
|
m_LastSession->ProcessHolePunch (buf, len);
|
||||||
|
break;
|
||||||
|
case eSSU2SessionStatePeerTest:
|
||||||
|
m_LastSession->SetRemoteEndpoint (senderEndpoint);
|
||||||
|
m_LastSession->ProcessPeerTest (buf, len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LogPrint (eLogWarning, "SSU2: Invalid session state ", (int)m_LastSession->GetState ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// check pending sessions if it's SessionCreated or Retry
|
||||||
|
auto it1 = m_PendingOutgoingSessions.find (senderEndpoint);
|
||||||
|
if (it1 != m_PendingOutgoingSessions.end ())
|
||||||
|
{
|
||||||
|
if (it1->second->ProcessSessionCreated (buf, len))
|
||||||
|
m_PendingOutgoingSessions.erase (it1); // we are done with that endpoint
|
||||||
|
else
|
||||||
|
it1->second->ProcessRetry (buf, len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// assume new incoming session
|
||||||
|
auto session = std::make_shared<SSU2Session> (*this);
|
||||||
|
session->SetRemoteEndpoint (senderEndpoint);
|
||||||
|
session->ProcessFirstIncomingMessage (connID, buf, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen,
|
||||||
|
const boost::asio::ip::udp::endpoint& to)
|
||||||
|
{
|
||||||
|
std::vector<boost::asio::const_buffer> bufs
|
||||||
|
{
|
||||||
|
boost::asio::buffer (header, headerLen),
|
||||||
|
boost::asio::buffer (payload, payloadLen)
|
||||||
|
};
|
||||||
|
boost::system::error_code ec;
|
||||||
|
if (to.address ().is_v6 ())
|
||||||
|
m_SocketV6.send_to (bufs, to, 0, ec);
|
||||||
|
else
|
||||||
|
m_SocketV4.send_to (bufs, to, 0, ec);
|
||||||
|
if (!ec)
|
||||||
|
i2p::transport::transports.UpdateSentBytes (headerLen + payloadLen);
|
||||||
|
else
|
||||||
|
LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen,
|
||||||
|
const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to)
|
||||||
|
{
|
||||||
|
std::vector<boost::asio::const_buffer> bufs
|
||||||
|
{
|
||||||
|
boost::asio::buffer (header, headerLen),
|
||||||
|
boost::asio::buffer (headerX, headerXLen),
|
||||||
|
boost::asio::buffer (payload, payloadLen)
|
||||||
|
};
|
||||||
|
boost::system::error_code ec;
|
||||||
|
if (to.address ().is_v6 ())
|
||||||
|
m_SocketV6.send_to (bufs, to, 0, ec);
|
||||||
|
else
|
||||||
|
m_SocketV4.send_to (bufs, to, 0, ec);
|
||||||
|
|
||||||
|
if (!ec)
|
||||||
|
i2p::transport::transports.UpdateSentBytes (headerLen + headerXLen + payloadLen);
|
||||||
|
else
|
||||||
|
LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSU2Server::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo::Address> address)
|
||||||
|
{
|
||||||
|
if (router && address)
|
||||||
|
{
|
||||||
|
if (address->UsesIntroducer ())
|
||||||
|
GetService ().post (std::bind (&SSU2Server::ConnectThroughIntroducer, this, router, address));
|
||||||
|
else
|
||||||
|
GetService ().post (
|
||||||
|
[this, router, address]()
|
||||||
|
{
|
||||||
|
auto session = std::make_shared<SSU2Session> (*this, router, address);
|
||||||
|
session->Connect ();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::ConnectThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo::Address> address)
|
||||||
|
{
|
||||||
|
auto session = std::make_shared<SSU2Session> (*this, router, address);
|
||||||
|
session->SetState (eSSU2SessionStateIntroduced);
|
||||||
|
// try to find existing session first
|
||||||
|
for (auto& it: address->ssu->introducers)
|
||||||
|
{
|
||||||
|
auto it1 = m_SessionsByRouterHash.find (it.iKey);
|
||||||
|
if (it1 != m_SessionsByRouterHash.end ())
|
||||||
|
{
|
||||||
|
it1->second->Introduce (session, it.iTag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we have to start a new session to an introducer
|
||||||
|
std::shared_ptr<i2p::data::RouterInfo> r;
|
||||||
|
uint32_t relayTag = 0;
|
||||||
|
for (auto& it: address->ssu->introducers)
|
||||||
|
{
|
||||||
|
r = i2p::data::netdb.FindRouter (it.iKey);
|
||||||
|
if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ()))
|
||||||
|
{
|
||||||
|
relayTag = it.iTag;
|
||||||
|
if (relayTag) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
if (relayTag)
|
||||||
|
{
|
||||||
|
// introducer and tag found connect to it through SSU2
|
||||||
|
auto addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address ();
|
||||||
|
if (addr)
|
||||||
|
{
|
||||||
|
auto s = std::make_shared<SSU2Session> (*this, r, addr);
|
||||||
|
s->SetOnEstablished (
|
||||||
|
[session, s, relayTag]()
|
||||||
|
{
|
||||||
|
s->Introduce (session, relayTag);
|
||||||
|
});
|
||||||
|
s->Connect ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// introducers not found, try to request them
|
||||||
|
for (auto& it: address->ssu->introducers)
|
||||||
|
i2p::data::netdb.RequestDestination (it.iKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSU2Server::StartPeerTest (std::shared_ptr<const i2p::data::RouterInfo> router, bool v4)
|
||||||
|
{
|
||||||
|
if (!router) return false;
|
||||||
|
auto addr = v4 ? router->GetSSU2V4Address () : router->GetSSU2V6Address ();
|
||||||
|
if (!addr) return false;
|
||||||
|
auto it = m_SessionsByRouterHash.find (router->GetIdentHash ());
|
||||||
|
if (it != m_SessionsByRouterHash.end ())
|
||||||
|
{
|
||||||
|
if (it->second->IsEstablished ())
|
||||||
|
it->second->SendPeerTest ();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto s = it->second;
|
||||||
|
s->SetOnEstablished ([s]() { s->SendPeerTest (); });
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto s = std::make_shared<SSU2Session> (*this, router, addr);
|
||||||
|
s->SetOnEstablished ([s]() {s->SendPeerTest (); });
|
||||||
|
s->Connect ();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::ScheduleTermination ()
|
||||||
|
{
|
||||||
|
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(SSU2_TERMINATION_CHECK_TIMEOUT));
|
||||||
|
m_TerminationTimer.async_wait (std::bind (&SSU2Server::HandleTerminationTimer,
|
||||||
|
this, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::HandleTerminationTimer (const boost::system::error_code& ecode)
|
||||||
|
{
|
||||||
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
|
{
|
||||||
|
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||||
|
for (auto it = m_PendingOutgoingSessions.begin (); it != m_PendingOutgoingSessions.end ();)
|
||||||
|
{
|
||||||
|
if (it->second->IsTerminationTimeoutExpired (ts))
|
||||||
|
{
|
||||||
|
//it->second->Terminate ();
|
||||||
|
it = m_PendingOutgoingSessions.erase (it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
|
||||||
|
{
|
||||||
|
if (it->second->IsTerminationTimeoutExpired (ts))
|
||||||
|
{
|
||||||
|
if (it->second->IsEstablished ())
|
||||||
|
it->second->TerminateByTimeout ();
|
||||||
|
if (it->second == m_LastSession)
|
||||||
|
m_LastSession = nullptr;
|
||||||
|
it = m_Sessions.erase (it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it->second->CleanUp (ts);
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = m_IncomingTokens.begin (); it != m_IncomingTokens.end (); )
|
||||||
|
{
|
||||||
|
if (ts > it->second.second)
|
||||||
|
it = m_IncomingTokens.erase (it);
|
||||||
|
else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = m_OutgoingTokens.begin (); it != m_OutgoingTokens.end (); )
|
||||||
|
{
|
||||||
|
if (ts > it->second.second)
|
||||||
|
it = m_OutgoingTokens.erase (it);
|
||||||
|
else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScheduleTermination ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::ScheduleResend ()
|
||||||
|
{
|
||||||
|
m_ResendTimer.expires_from_now (boost::posix_time::seconds(SSU2_RESEND_INTERVAL));
|
||||||
|
m_ResendTimer.async_wait (std::bind (&SSU2Server::HandleResendTimer,
|
||||||
|
this, std::placeholders::_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::HandleResendTimer (const boost::system::error_code& ecode)
|
||||||
|
{
|
||||||
|
if (ecode != boost::asio::error::operation_aborted)
|
||||||
|
{
|
||||||
|
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||||
|
for (auto it: m_Sessions)
|
||||||
|
it.second->Resend (ts);
|
||||||
|
ScheduleResend ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSU2Server::UpdateOutgoingToken (const boost::asio::ip::udp::endpoint& ep, uint64_t token, uint32_t exp)
|
||||||
|
{
|
||||||
|
m_OutgoingTokens[ep] = {token, exp};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t SSU2Server::FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) const
|
||||||
|
{
|
||||||
|
auto it = m_OutgoingTokens.find (ep);
|
||||||
|
if (it != m_OutgoingTokens.end ())
|
||||||
|
return it->second.first;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t SSU2Server::GetIncomingToken (const boost::asio::ip::udp::endpoint& ep)
|
||||||
|
{
|
||||||
|
auto it = m_IncomingTokens.find (ep);
|
||||||
|
if (it != m_IncomingTokens.end ())
|
||||||
|
return it->second.first;
|
||||||
|
uint64_t token;
|
||||||
|
RAND_bytes ((uint8_t *)&token, 8);
|
||||||
|
m_IncomingTokens.emplace (ep, std::make_pair (token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT));
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
111
libi2pd/SSU2.h
111
libi2pd/SSU2.h
@@ -9,60 +9,105 @@
|
|||||||
#ifndef SSU2_H__
|
#ifndef SSU2_H__
|
||||||
#define SSU2_H__
|
#define SSU2_H__
|
||||||
|
|
||||||
#include <memory>
|
#include <unordered_map>
|
||||||
#include "Crypto.h"
|
#include "util.h"
|
||||||
#include "RouterInfo.h"
|
#include "SSU2Session.h"
|
||||||
#include "TransportSession.h"
|
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
namespace transport
|
namespace transport
|
||||||
{
|
{
|
||||||
const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
|
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
||||||
|
const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
|
||||||
|
const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
|
||||||
|
|
||||||
enum SSU2MessageType
|
class SSU2Server: private i2p::util::RunnableServiceWithWork
|
||||||
{
|
{
|
||||||
eSSU2SessionRequest = 0
|
struct Packet
|
||||||
|
{
|
||||||
|
uint8_t buf[SSU2_MTU];
|
||||||
|
size_t len;
|
||||||
|
boost::asio::ip::udp::endpoint from;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session>
|
class ReceiveService: public i2p::util::RunnableService
|
||||||
{
|
{
|
||||||
union Header
|
public:
|
||||||
{
|
|
||||||
uint64_t ll[2];
|
ReceiveService (const std::string& name): RunnableService (name) {};
|
||||||
uint8_t buf[16];
|
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||||
struct
|
void Start () { StartIOService (); };
|
||||||
{
|
void Stop () { StopIOService (); };
|
||||||
uint8_t connID[8];
|
|
||||||
uint8_t packetNum[4];
|
|
||||||
uint8_t type;
|
|
||||||
uint8_t flags[3];
|
|
||||||
} h;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
SSU2Session (std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
|
SSU2Server ();
|
||||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr, bool peerTest = false);
|
~SSU2Server () {};
|
||||||
~SSU2Session ();
|
|
||||||
|
void Start ();
|
||||||
|
void Stop ();
|
||||||
|
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||||
|
|
||||||
|
void AddSession (std::shared_ptr<SSU2Session> session);
|
||||||
|
void RemoveSession (uint64_t connID);
|
||||||
|
void AddSessionByRouterHash (std::shared_ptr<SSU2Session> session);
|
||||||
|
void AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session);
|
||||||
|
std::shared_ptr<SSU2Session> FindSession (const i2p::data::IdentHash& ident) const;
|
||||||
|
|
||||||
|
void AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay);
|
||||||
|
void RemoveRelay (uint32_t tag);
|
||||||
|
std::shared_ptr<SSU2Session> FindRelaySession (uint32_t tag);
|
||||||
|
|
||||||
|
void Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen,
|
||||||
|
const boost::asio::ip::udp::endpoint& to);
|
||||||
|
void Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen,
|
||||||
|
const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to);
|
||||||
|
|
||||||
|
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo::Address> address);
|
||||||
|
bool StartPeerTest (std::shared_ptr<const i2p::data::RouterInfo> router, bool v4);
|
||||||
|
|
||||||
|
void UpdateOutgoingToken (const boost::asio::ip::udp::endpoint& ep, uint64_t token, uint32_t exp);
|
||||||
|
uint64_t FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) const;
|
||||||
|
uint64_t GetIncomingToken (const boost::asio::ip::udp::endpoint& ep);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void SendSessionRequest ();
|
boost::asio::ip::udp::socket& OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint);
|
||||||
void EncryptHeader (Header& h);
|
void Receive (boost::asio::ip::udp::socket& socket);
|
||||||
void CreateHeaderMask (const uint8_t * kh1, const uint8_t * nonce1, const uint8_t * kh2, const uint8_t * nonce2);
|
void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred,
|
||||||
|
Packet * packet, boost::asio::ip::udp::socket& socket);
|
||||||
|
void HandleReceivedPacket (Packet * packet);
|
||||||
|
void HandleReceivedPackets (std::vector<Packet *> packets);
|
||||||
|
void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||||
|
|
||||||
|
void ScheduleTermination ();
|
||||||
|
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||||
|
|
||||||
|
void ScheduleResend ();
|
||||||
|
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||||
|
|
||||||
|
void ConnectThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo::Address> address);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
ReceiveService m_ReceiveService;
|
||||||
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
|
boost::asio::ip::udp::socket m_SocketV4, m_SocketV6;
|
||||||
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
|
std::unordered_map<uint64_t, std::shared_ptr<SSU2Session> > m_Sessions;
|
||||||
|
std::map<i2p::data::IdentHash, std::shared_ptr<SSU2Session> > m_SessionsByRouterHash;
|
||||||
|
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSU2Session> > m_PendingOutgoingSessions;
|
||||||
|
std::map<boost::asio::ip::udp::endpoint, std::pair<uint64_t, uint32_t> > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds)
|
||||||
|
std::map<uint32_t, std::shared_ptr<SSU2Session> > m_Relays; // we are introducer, relay tag -> session
|
||||||
|
i2p::util::MemoryPoolMt<Packet> m_PacketsPool;
|
||||||
|
boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer;
|
||||||
|
std::shared_ptr<SSU2Session> m_LastSession;
|
||||||
|
|
||||||
union
|
public:
|
||||||
{
|
|
||||||
uint64_t ll[2];
|
// for HTTP/I2PControl
|
||||||
uint8_t buf[16];
|
const decltype(m_Sessions)& GetSSU2Sessions () const { return m_Sessions; };
|
||||||
} m_HeaderMask;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1843
libi2pd/SSU2Session.cpp
Normal file
1843
libi2pd/SSU2Session.cpp
Normal file
File diff suppressed because it is too large
Load Diff
273
libi2pd/SSU2Session.h
Normal file
273
libi2pd/SSU2Session.h
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022, The PurpleI2P Project
|
||||||
|
*
|
||||||
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
|
*
|
||||||
|
* See full license text in LICENSE file at top of project tree
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SSU2_SESSION_H__
|
||||||
|
#define SSU2_SESSION_H__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <list>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include "Crypto.h"
|
||||||
|
#include "RouterInfo.h"
|
||||||
|
#include "TransportSession.h"
|
||||||
|
|
||||||
|
namespace i2p
|
||||||
|
{
|
||||||
|
namespace transport
|
||||||
|
{
|
||||||
|
const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||||
|
const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
|
||||||
|
const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // in seconds
|
||||||
|
const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds
|
||||||
|
const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds
|
||||||
|
const size_t SSU2_MTU = 1488;
|
||||||
|
const size_t SSU2_MAX_PAYLOAD_SIZE = SSU2_MTU - 32;
|
||||||
|
const int SSU2_RESEND_INTERVAL = 3; // in seconds
|
||||||
|
const int SSU2_MAX_NUM_RESENDS = 5;
|
||||||
|
const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
|
||||||
|
const size_t SSU2_MAX_WINDOW_SIZE = 128; // in packets
|
||||||
|
|
||||||
|
enum SSU2MessageType
|
||||||
|
{
|
||||||
|
eSSU2SessionRequest = 0,
|
||||||
|
eSSU2SessionCreated = 1,
|
||||||
|
eSSU2SessionConfirmed = 2,
|
||||||
|
eSSU2Data = 6,
|
||||||
|
eSSU2PeerTest = 7,
|
||||||
|
eSSU2Retry = 9,
|
||||||
|
eSSU2TokenRequest = 10,
|
||||||
|
eSSU2HolePunch = 11
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SSU2BlockType
|
||||||
|
{
|
||||||
|
eSSU2BlkDateTime = 0,
|
||||||
|
eSSU2BlkOptions, // 1
|
||||||
|
eSSU2BlkRouterInfo, // 2
|
||||||
|
eSSU2BlkI2NPMessage, // 3
|
||||||
|
eSSU2BlkFirstFragment, // 4
|
||||||
|
eSSU2BlkFollowOnFragment, // 5
|
||||||
|
eSSU2BlkTermination, // 6
|
||||||
|
eSSU2BlkRelayRequest, // 7
|
||||||
|
eSSU2BlkRelayResponse, // 8
|
||||||
|
eSSU2BlkRelayIntro, // 9
|
||||||
|
eSSU2BlkPeerTest, // 10
|
||||||
|
eSSU2BlkNextNonce, // 11
|
||||||
|
eSSU2BlkAck, // 12
|
||||||
|
eSSU2BlkAddress, // 13
|
||||||
|
eSSU2BlkIntroKey, // 14
|
||||||
|
eSSU2BlkRelayTagRequest, // 15
|
||||||
|
eSSU2BlkRelayTag, // 16
|
||||||
|
eSSU2BlkNewToken, // 17
|
||||||
|
eSSU2BlkPathChallenge, // 18
|
||||||
|
eSSU2BlkPathResponse, // 19
|
||||||
|
eSSU2BlkFirstPacketNumber, // 20
|
||||||
|
eSSU2BlkPadding = 254
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SSU2SessionState
|
||||||
|
{
|
||||||
|
eSSU2SessionStateUnknown,
|
||||||
|
eSSU2SessionStateIntroduced,
|
||||||
|
eSSU2SessionStatePeerTest,
|
||||||
|
eSSU2SessionStateEstablished,
|
||||||
|
eSSU2SessionStateTerminated,
|
||||||
|
eSSU2SessionStateFailed
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SSU2PeerTestCode
|
||||||
|
{
|
||||||
|
eSSU2PeerTestCodeAccept = 0,
|
||||||
|
eSSU2PeerTestCodeBobReasonUnspecified = 1,
|
||||||
|
eSSU2PeerTestCodeBobNoCharlieAvailable = 2,
|
||||||
|
eSSU2PeerTestCodeBobLimitExceeded = 3,
|
||||||
|
eSSU2PeerTestCodeBobSignatureFailure = 4,
|
||||||
|
eSSU2PeerTestCodeCharlieReasonUnspecified = 64,
|
||||||
|
eSSU2PeerTestCodeCharlieUnsupportedAddress = 65,
|
||||||
|
eSSU2PeerTestCodeCharlieLimitExceeded = 66,
|
||||||
|
eSSU2PeerTestCodeCharlieSignatureFailure = 67,
|
||||||
|
eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected = 68,
|
||||||
|
eSSU2PeerTestCodeCharlieAliceIsBanned = 69,
|
||||||
|
eSSU2PeerTestCodeCharlieAliceIsUnknown = 70,
|
||||||
|
eSSU2PeerTestCodeUnspecified = 128
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SSU2IncompleteMessage
|
||||||
|
{
|
||||||
|
struct Fragment
|
||||||
|
{
|
||||||
|
uint8_t buf[SSU2_MTU];
|
||||||
|
size_t len;
|
||||||
|
bool isLast;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<I2NPMessage> msg;
|
||||||
|
int nextFragmentNum;
|
||||||
|
uint32_t lastFragmentInsertTime; // in seconds
|
||||||
|
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments;
|
||||||
|
};
|
||||||
|
|
||||||
|
// RouterInfo flags
|
||||||
|
const uint8_t SSU2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
|
||||||
|
const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02;
|
||||||
|
|
||||||
|
class SSU2Server;
|
||||||
|
class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session>
|
||||||
|
{
|
||||||
|
union Header
|
||||||
|
{
|
||||||
|
uint64_t ll[2];
|
||||||
|
uint8_t buf[16];
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint64_t connID;
|
||||||
|
uint32_t packetNum;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t flags[3];
|
||||||
|
} h;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SentPacket
|
||||||
|
{
|
||||||
|
uint8_t payload[SSU2_MAX_PAYLOAD_SIZE];
|
||||||
|
size_t payloadSize = 0;
|
||||||
|
uint32_t nextResendTime; // in seconds
|
||||||
|
int numResends = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SessionConfirmedFragment
|
||||||
|
{
|
||||||
|
Header header;
|
||||||
|
uint8_t payload[SSU2_MAX_PAYLOAD_SIZE];
|
||||||
|
size_t payloadSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::function<void ()> OnEstablished;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr);
|
||||||
|
~SSU2Session ();
|
||||||
|
|
||||||
|
void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; };
|
||||||
|
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; };
|
||||||
|
void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; };
|
||||||
|
|
||||||
|
void Connect ();
|
||||||
|
bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag);
|
||||||
|
void SendPeerTest (); // Alice, Data message
|
||||||
|
void Terminate ();
|
||||||
|
void TerminateByTimeout ();
|
||||||
|
void CleanUp (uint64_t ts);
|
||||||
|
void FlushData ();
|
||||||
|
void Done () override;
|
||||||
|
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
|
||||||
|
void Resend (uint64_t ts);
|
||||||
|
bool IsEstablished () const { return m_State == eSSU2SessionStateEstablished; };
|
||||||
|
uint64_t GetConnID () const { return m_SourceConnID; };
|
||||||
|
SSU2SessionState GetState () const { return m_State; };
|
||||||
|
void SetState (SSU2SessionState state) { m_State = state; };
|
||||||
|
|
||||||
|
bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len);
|
||||||
|
bool ProcessSessionCreated (uint8_t * buf, size_t len);
|
||||||
|
bool ProcessSessionConfirmed (uint8_t * buf, size_t len);
|
||||||
|
bool ProcessRetry (uint8_t * buf, size_t len);
|
||||||
|
bool ProcessHolePunch (uint8_t * buf, size_t len);
|
||||||
|
bool ProcessPeerTest (uint8_t * buf, size_t len);
|
||||||
|
void ProcessData (uint8_t * buf, size_t len);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void Established ();
|
||||||
|
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
||||||
|
bool SendQueue ();
|
||||||
|
void SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
|
|
||||||
|
void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len);
|
||||||
|
void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len);
|
||||||
|
|
||||||
|
void SendSessionRequest (uint64_t token = 0);
|
||||||
|
void SendSessionCreated (const uint8_t * X);
|
||||||
|
void SendSessionConfirmed (const uint8_t * Y);
|
||||||
|
void KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba);
|
||||||
|
void SendTokenRequest ();
|
||||||
|
void SendRetry ();
|
||||||
|
uint32_t SendData (const uint8_t * buf, size_t len); // returns packet num
|
||||||
|
void SendQuickAck ();
|
||||||
|
void SendTermination ();
|
||||||
|
void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey);
|
||||||
|
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message
|
||||||
|
|
||||||
|
void HandlePayload (const uint8_t * buf, size_t len);
|
||||||
|
void HandleAck (const uint8_t * buf, size_t len);
|
||||||
|
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum);
|
||||||
|
bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep);
|
||||||
|
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const;
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size);
|
||||||
|
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
||||||
|
bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate
|
||||||
|
void HandleFirstFragment (const uint8_t * buf, size_t len);
|
||||||
|
void HandleFollowOnFragment (const uint8_t * buf, size_t len);
|
||||||
|
bool ConcatOutOfSequenceFragments (std::shared_ptr<SSU2IncompleteMessage> m); // true if message complete
|
||||||
|
void HandleRelayRequest (const uint8_t * buf, size_t len);
|
||||||
|
void HandleRelayIntro (const uint8_t * buf, size_t len);
|
||||||
|
void HandleRelayResponse (const uint8_t * buf, size_t len);
|
||||||
|
void HandlePeerTest (const uint8_t * buf, size_t len);
|
||||||
|
|
||||||
|
size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
|
||||||
|
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r);
|
||||||
|
size_t CreateAckBlock (uint8_t * buf, size_t len);
|
||||||
|
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0);
|
||||||
|
size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage>&& msg);
|
||||||
|
size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg);
|
||||||
|
size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID);
|
||||||
|
size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen);
|
||||||
|
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, uint32_t nonce); // Charlie
|
||||||
|
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen);
|
||||||
|
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
SSU2Server& m_Server;
|
||||||
|
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
||||||
|
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
|
||||||
|
std::unique_ptr<SessionConfirmedFragment> m_SessionConfirmedFragment1; // for Bob if applicable
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
|
||||||
|
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
||||||
|
uint64_t m_DestConnID, m_SourceConnID;
|
||||||
|
SSU2SessionState m_State;
|
||||||
|
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
|
||||||
|
uint32_t m_SendPacketNum, m_ReceivePacketNum;
|
||||||
|
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
|
||||||
|
std::map<uint32_t, std::shared_ptr<SentPacket> > m_SentPackets; // packetNum -> packet
|
||||||
|
std::map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // I2NP
|
||||||
|
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
|
||||||
|
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions
|
||||||
|
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||||
|
i2p::I2NPMessagesHandler m_Handler;
|
||||||
|
bool m_IsDataReceived;
|
||||||
|
size_t m_WindowSize;
|
||||||
|
uint32_t m_RelayTag; // between Bob and Charlie
|
||||||
|
OnEstablished m_OnEstablished; // callback from Established
|
||||||
|
};
|
||||||
|
|
||||||
|
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)
|
||||||
|
{
|
||||||
|
uint64_t data = 0;
|
||||||
|
i2p::crypto::ChaCha20 ((uint8_t *)&data, 8, kh, nonce, (uint8_t *)&data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -26,11 +26,7 @@ namespace transport
|
|||||||
{
|
{
|
||||||
|
|
||||||
const size_t SSU_MTU_V4 = 1484;
|
const size_t SSU_MTU_V4 = 1484;
|
||||||
#ifdef MESHNET
|
|
||||||
const size_t SSU_MTU_V6 = 1286;
|
|
||||||
#else
|
|
||||||
const size_t SSU_MTU_V6 = 1488;
|
const size_t SSU_MTU_V6 = 1488;
|
||||||
#endif
|
|
||||||
const size_t IPV4_HEADER_SIZE = 20;
|
const size_t IPV4_HEADER_SIZE = 20;
|
||||||
const size_t IPV6_HEADER_SIZE = 40;
|
const size_t IPV6_HEADER_SIZE = 40;
|
||||||
const size_t UDP_HEADER_SIZE = 8;
|
const size_t UDP_HEADER_SIZE = 8;
|
||||||
|
|||||||
@@ -1285,7 +1285,13 @@ namespace stream
|
|||||||
auto it = m_Streams.find (recvStreamID);
|
auto it = m_Streams.find (recvStreamID);
|
||||||
if (it == m_Streams.end ())
|
if (it == m_Streams.end ())
|
||||||
return false;
|
return false;
|
||||||
DeleteStream (it->second);
|
auto s = it->second;
|
||||||
|
m_Owner->GetService ().post ([this, s] ()
|
||||||
|
{
|
||||||
|
s->Close (); // try to send FIN
|
||||||
|
s->Terminate (false);
|
||||||
|
DeleteStream (s);
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ namespace transport
|
|||||||
Transports::Transports ():
|
Transports::Transports ():
|
||||||
m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_CheckReserved(true), m_Thread (nullptr),
|
m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_CheckReserved(true), m_Thread (nullptr),
|
||||||
m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr),
|
m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr),
|
||||||
m_SSUServer (nullptr), m_NTCP2Server (nullptr),
|
m_SSUServer (nullptr), m_SSU2Server (nullptr), m_NTCP2Server (nullptr),
|
||||||
m_X25519KeysPairSupplier (15), // 15 pre-generated keys
|
m_X25519KeysPairSupplier (15), // 15 pre-generated keys
|
||||||
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0),
|
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0),
|
||||||
m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0),
|
m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0),
|
||||||
@@ -157,7 +157,7 @@ namespace transport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transports::Start (bool enableNTCP2, bool enableSSU)
|
void Transports::Start (bool enableNTCP2, bool enableSSU, bool enableSSU2)
|
||||||
{
|
{
|
||||||
if (!m_Service)
|
if (!m_Service)
|
||||||
{
|
{
|
||||||
@@ -217,6 +217,8 @@ namespace transport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// create SSU2 server
|
||||||
|
if (enableSSU2) m_SSU2Server = new SSU2Server ();
|
||||||
|
|
||||||
// bind to interfaces
|
// bind to interfaces
|
||||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||||
@@ -266,6 +268,7 @@ namespace transport
|
|||||||
|
|
||||||
// start servers
|
// start servers
|
||||||
if (m_NTCP2Server) m_NTCP2Server->Start ();
|
if (m_NTCP2Server) m_NTCP2Server->Start ();
|
||||||
|
if (m_SSU2Server) m_SSU2Server->Start ();
|
||||||
if (m_SSUServer)
|
if (m_SSUServer)
|
||||||
{
|
{
|
||||||
LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort);
|
LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort);
|
||||||
@@ -305,6 +308,13 @@ namespace transport
|
|||||||
m_SSUServer = nullptr;
|
m_SSUServer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_SSU2Server)
|
||||||
|
{
|
||||||
|
m_SSU2Server->Stop ();
|
||||||
|
delete m_SSU2Server;
|
||||||
|
m_SSU2Server = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_NTCP2Server)
|
if (m_NTCP2Server)
|
||||||
{
|
{
|
||||||
m_NTCP2Server->Stop ();
|
m_NTCP2Server->Stop ();
|
||||||
@@ -526,6 +536,40 @@ namespace transport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (peer.numAttempts == 5 || peer.numAttempts == 6) // SSU2
|
||||||
|
{
|
||||||
|
if (m_SSU2Server)
|
||||||
|
{
|
||||||
|
std::shared_ptr<const RouterInfo::Address> address;
|
||||||
|
if (peer.numAttempts == 5) // SSU2 ipv6
|
||||||
|
{
|
||||||
|
if (context.GetRouterInfo ().IsSSU2V6 () && peer.router->IsReachableBy (RouterInfo::eSSU2V6))
|
||||||
|
{
|
||||||
|
address = peer.router->GetSSU2V6Address ();
|
||||||
|
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
|
||||||
|
address = nullptr;
|
||||||
|
}
|
||||||
|
peer.numAttempts++;
|
||||||
|
}
|
||||||
|
if (!address && peer.numAttempts == 6) // SSU2 ipv4
|
||||||
|
{
|
||||||
|
if (context.GetRouterInfo ().IsSSU2V4 () && peer.router->IsReachableBy (RouterInfo::eSSU2V4))
|
||||||
|
{
|
||||||
|
address = peer.router->GetSSU2V4Address ();
|
||||||
|
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
|
||||||
|
address = nullptr;
|
||||||
|
}
|
||||||
|
peer.numAttempts++;
|
||||||
|
}
|
||||||
|
if (address && address->IsReachableSSU ())
|
||||||
|
{
|
||||||
|
if (m_SSU2Server->CreateSession (peer.router, address))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
peer.numAttempts += 2;
|
||||||
|
}
|
||||||
LogPrint (eLogInfo, "Transports: No compatble NTCP2 or SSU addresses available");
|
LogPrint (eLogInfo, "Transports: No compatble NTCP2 or SSU addresses available");
|
||||||
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed
|
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed
|
||||||
peer.Done ();
|
peer.Done ();
|
||||||
@@ -610,6 +654,16 @@ namespace transport
|
|||||||
}
|
}
|
||||||
if (!statusChanged)
|
if (!statusChanged)
|
||||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
|
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
|
||||||
|
|
||||||
|
// SSU2
|
||||||
|
if (m_SSU2Server)
|
||||||
|
{
|
||||||
|
excluded.clear ();
|
||||||
|
excluded.insert (i2p::context.GetIdentHash ());
|
||||||
|
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4
|
||||||
|
if (router)
|
||||||
|
m_SSU2Server->StartPeerTest (router, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ipv6 && i2p::context.SupportsV6 ())
|
if (ipv6 && i2p::context.SupportsV6 ())
|
||||||
{
|
{
|
||||||
@@ -637,6 +691,16 @@ namespace transport
|
|||||||
}
|
}
|
||||||
if (!statusChanged)
|
if (!statusChanged)
|
||||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
|
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
|
||||||
|
|
||||||
|
// SSU2
|
||||||
|
if (m_SSU2Server)
|
||||||
|
{
|
||||||
|
excluded.clear ();
|
||||||
|
excluded.insert (i2p::context.GetIdentHash ());
|
||||||
|
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6
|
||||||
|
if (router)
|
||||||
|
m_SSU2Server->StartPeerTest (router, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -785,12 +849,18 @@ namespace transport
|
|||||||
}
|
}
|
||||||
return i2p::data::netdb.FindRouter (ident);
|
return i2p::data::netdb.FindRouter (ident);
|
||||||
}
|
}
|
||||||
void Transports::RestrictRoutesToFamilies(std::set<std::string> families)
|
|
||||||
|
void Transports::RestrictRoutesToFamilies(const std::set<std::string>& families)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
||||||
m_TrustedFamilies.clear();
|
m_TrustedFamilies.clear();
|
||||||
for ( const auto& fam : families )
|
for (auto fam : families)
|
||||||
m_TrustedFamilies.push_back(fam);
|
{
|
||||||
|
boost::to_lower (fam);
|
||||||
|
auto id = i2p::data::netdb.GetFamilies ().GetFamilyID (fam);
|
||||||
|
if (id)
|
||||||
|
m_TrustedFamilies.push_back (id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transports::RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers)
|
void Transports::RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers)
|
||||||
@@ -812,20 +882,19 @@ namespace transport
|
|||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(m_FamilyMutex);
|
std::lock_guard<std::mutex> l(m_FamilyMutex);
|
||||||
std::string fam;
|
i2p::data::FamilyID fam = 0;
|
||||||
auto sz = m_TrustedFamilies.size();
|
auto sz = m_TrustedFamilies.size();
|
||||||
if(sz > 1)
|
if(sz > 1)
|
||||||
{
|
{
|
||||||
auto it = m_TrustedFamilies.begin ();
|
auto it = m_TrustedFamilies.begin ();
|
||||||
std::advance(it, rand() % sz);
|
std::advance(it, rand() % sz);
|
||||||
fam = *it;
|
fam = *it;
|
||||||
boost::to_lower(fam);
|
|
||||||
}
|
}
|
||||||
else if (sz == 1)
|
else if (sz == 1)
|
||||||
{
|
{
|
||||||
fam = m_TrustedFamilies[0];
|
fam = m_TrustedFamilies[0];
|
||||||
}
|
}
|
||||||
if (fam.size())
|
if (fam)
|
||||||
return i2p::data::netdb.GetRandomRouterInFamily(fam);
|
return i2p::data::netdb.GetRandomRouterInFamily(fam);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include "TransportSession.h"
|
#include "TransportSession.h"
|
||||||
#include "SSU.h"
|
#include "SSU.h"
|
||||||
|
#include "SSU2.h"
|
||||||
#include "NTCP2.h"
|
#include "NTCP2.h"
|
||||||
#include "RouterInfo.h"
|
#include "RouterInfo.h"
|
||||||
#include "I2NPProtocol.h"
|
#include "I2NPProtocol.h"
|
||||||
@@ -86,7 +87,7 @@ namespace transport
|
|||||||
Transports ();
|
Transports ();
|
||||||
~Transports ();
|
~Transports ();
|
||||||
|
|
||||||
void Start (bool enableNTCP2=true, bool enableSSU=true);
|
void Start (bool enableNTCP2=true, bool enableSSU=true, bool enableSSU2=false);
|
||||||
void Stop ();
|
void Stop ();
|
||||||
|
|
||||||
bool IsBoundSSU() const { return m_SSUServer != nullptr; }
|
bool IsBoundSSU() const { return m_SSUServer != nullptr; }
|
||||||
@@ -125,7 +126,7 @@ namespace transport
|
|||||||
/** do we want to use restricted routes? */
|
/** do we want to use restricted routes? */
|
||||||
bool RoutesRestricted() const;
|
bool RoutesRestricted() const;
|
||||||
/** restrict routes to use only these router families for first hops */
|
/** restrict routes to use only these router families for first hops */
|
||||||
void RestrictRoutesToFamilies(std::set<std::string> families);
|
void RestrictRoutesToFamilies(const std::set<std::string>& families);
|
||||||
/** restrict routes to use only these routers for first hops */
|
/** restrict routes to use only these routers for first hops */
|
||||||
void RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers);
|
void RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers);
|
||||||
|
|
||||||
@@ -159,6 +160,7 @@ namespace transport
|
|||||||
boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer;
|
boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer;
|
||||||
|
|
||||||
SSUServer * m_SSUServer;
|
SSUServer * m_SSUServer;
|
||||||
|
SSU2Server * m_SSU2Server;
|
||||||
NTCP2Server * m_NTCP2Server;
|
NTCP2Server * m_NTCP2Server;
|
||||||
mutable std::mutex m_PeersMutex;
|
mutable std::mutex m_PeersMutex;
|
||||||
std::unordered_map<i2p::data::IdentHash, Peer> m_Peers;
|
std::unordered_map<i2p::data::IdentHash, Peer> m_Peers;
|
||||||
@@ -171,7 +173,7 @@ namespace transport
|
|||||||
uint64_t m_LastBandwidthUpdateTime;
|
uint64_t m_LastBandwidthUpdateTime;
|
||||||
|
|
||||||
/** which router families to trust for first hops */
|
/** which router families to trust for first hops */
|
||||||
std::vector<std::string> m_TrustedFamilies;
|
std::vector<i2p::data::FamilyID> m_TrustedFamilies;
|
||||||
mutable std::mutex m_FamilyMutex;
|
mutable std::mutex m_FamilyMutex;
|
||||||
|
|
||||||
/** which routers for first hop to trust */
|
/** which routers for first hop to trust */
|
||||||
@@ -185,6 +187,7 @@ namespace transport
|
|||||||
// for HTTP only
|
// for HTTP only
|
||||||
const SSUServer * GetSSUServer () const { return m_SSUServer; };
|
const SSUServer * GetSSUServer () const { return m_SSUServer; };
|
||||||
const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; };
|
const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; };
|
||||||
|
const SSU2Server * GetSSU2Server () const { return m_SSU2Server; };
|
||||||
const decltype(m_Peers)& GetPeers () const { return m_Peers; };
|
const decltype(m_Peers)& GetPeers () const { return m_Peers; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -114,7 +114,7 @@ namespace tunnel
|
|||||||
uint8_t key[32];
|
uint8_t key[32];
|
||||||
uint64_t tag = m_Config->GetLastHop ()->GetGarlicKey (key);
|
uint64_t tag = m_Config->GetLastHop ()->GetGarlicKey (key);
|
||||||
if (m_Pool && m_Pool->GetLocalDestination ())
|
if (m_Pool && m_Pool->GetLocalDestination ())
|
||||||
m_Pool->GetLocalDestination ()->AddECIESx25519Key (key, tag);
|
m_Pool->GetLocalDestination ()->SubmitECIESx25519Key (key, tag);
|
||||||
else
|
else
|
||||||
i2p::context.AddECIESx25519Key (key, tag);
|
i2p::context.AddECIESx25519Key (key, tag);
|
||||||
}
|
}
|
||||||
@@ -235,15 +235,11 @@ namespace tunnel
|
|||||||
m_State = state;
|
m_State = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Tunnel::VisitTunnelHops(TunnelHopVisitor v)
|
||||||
void Tunnel::PrintHops (std::stringstream& s) const
|
|
||||||
{
|
{
|
||||||
// hops are in inverted order, we must print in direct order
|
// hops are in inverted order, we must return in direct order
|
||||||
for (auto it = m_Hops.rbegin (); it != m_Hops.rend (); it++)
|
for (auto it = m_Hops.rbegin (); it != m_Hops.rend (); it++)
|
||||||
{
|
v((*it).ident);
|
||||||
s << " ⇒ ";
|
|
||||||
s << i2p::data::GetIdentHashAbbreviation ((*it).ident->GetIdentHash ());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<I2NPMessage>&& msg)
|
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<I2NPMessage>&& msg)
|
||||||
@@ -254,12 +250,6 @@ namespace tunnel
|
|||||||
m_Endpoint.HandleDecryptedTunnelDataMsg (msg);
|
m_Endpoint.HandleDecryptedTunnelDataMsg (msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InboundTunnel::Print (std::stringstream& s) const
|
|
||||||
{
|
|
||||||
PrintHops (s);
|
|
||||||
s << " ⇒ " << GetTunnelID () << ":me";
|
|
||||||
}
|
|
||||||
|
|
||||||
ZeroHopsInboundTunnel::ZeroHopsInboundTunnel ():
|
ZeroHopsInboundTunnel::ZeroHopsInboundTunnel ():
|
||||||
InboundTunnel (std::make_shared<ZeroHopsTunnelConfig> ()),
|
InboundTunnel (std::make_shared<ZeroHopsTunnelConfig> ()),
|
||||||
m_NumReceivedBytes (0)
|
m_NumReceivedBytes (0)
|
||||||
@@ -276,11 +266,6 @@ namespace tunnel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZeroHopsInboundTunnel::Print (std::stringstream& s) const
|
|
||||||
{
|
|
||||||
s << " ⇒ " << GetTunnelID () << ":me";
|
|
||||||
}
|
|
||||||
|
|
||||||
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg)
|
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg)
|
||||||
{
|
{
|
||||||
TunnelMessageBlock block;
|
TunnelMessageBlock block;
|
||||||
@@ -315,13 +300,6 @@ namespace tunnel
|
|||||||
LogPrint (eLogError, "Tunnel: Incoming message for outbound tunnel ", GetTunnelID ());
|
LogPrint (eLogError, "Tunnel: Incoming message for outbound tunnel ", GetTunnelID ());
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutboundTunnel::Print (std::stringstream& s) const
|
|
||||||
{
|
|
||||||
s << GetTunnelID () << ":me";
|
|
||||||
PrintHops (s);
|
|
||||||
s << " ⇒ ";
|
|
||||||
}
|
|
||||||
|
|
||||||
ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel ():
|
ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel ():
|
||||||
OutboundTunnel (std::make_shared<ZeroHopsTunnelConfig> ()),
|
OutboundTunnel (std::make_shared<ZeroHopsTunnelConfig> ()),
|
||||||
m_NumSentBytes (0)
|
m_NumSentBytes (0)
|
||||||
@@ -351,11 +329,6 @@ namespace tunnel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZeroHopsOutboundTunnel::Print (std::stringstream& s) const
|
|
||||||
{
|
|
||||||
s << GetTunnelID () << ":me ⇒ ";
|
|
||||||
}
|
|
||||||
|
|
||||||
Tunnels tunnels;
|
Tunnels tunnels;
|
||||||
|
|
||||||
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr),
|
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr),
|
||||||
@@ -430,10 +403,10 @@ namespace tunnel
|
|||||||
return tunnel;
|
return tunnel;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (int numInboundHops,
|
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (int numInboundHops, int numOutboundHops,
|
||||||
int numOutboundHops, int numInboundTunnels, int numOutboundTunnels)
|
int numInboundTunnels, int numOutboundTunnels, int inboundVariance, int outboundVariance)
|
||||||
{
|
{
|
||||||
auto pool = std::make_shared<TunnelPool> (numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels);
|
auto pool = std::make_shared<TunnelPool> (numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels, inboundVariance, outboundVariance);
|
||||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
||||||
m_Pools.push_back (pool);
|
m_Pools.push_back (pool);
|
||||||
return pool;
|
return pool;
|
||||||
@@ -783,7 +756,7 @@ namespace tunnel
|
|||||||
int obLen; i2p::config::GetOption("exploratory.outbound.length", obLen);
|
int obLen; i2p::config::GetOption("exploratory.outbound.length", obLen);
|
||||||
int ibNum; i2p::config::GetOption("exploratory.inbound.quantity", ibNum);
|
int ibNum; i2p::config::GetOption("exploratory.inbound.quantity", ibNum);
|
||||||
int obNum; i2p::config::GetOption("exploratory.outbound.quantity", obNum);
|
int obNum; i2p::config::GetOption("exploratory.outbound.quantity", obNum);
|
||||||
m_ExploratoryPool = CreateTunnelPool (ibLen, obLen, ibNum, obNum);
|
m_ExploratoryPool = CreateTunnelPool (ibLen, obLen, ibNum, obNum, 0, 0);
|
||||||
m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ());
|
m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -67,6 +67,9 @@ namespace tunnel
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/** function for visiting a hops stored in a tunnel */
|
||||||
|
typedef std::function<void(std::shared_ptr<const i2p::data::IdentityEx>)> TunnelHopVisitor;
|
||||||
|
|
||||||
Tunnel (std::shared_ptr<const TunnelConfig> config);
|
Tunnel (std::shared_ptr<const TunnelConfig> config);
|
||||||
~Tunnel ();
|
~Tunnel ();
|
||||||
|
|
||||||
@@ -91,8 +94,6 @@ namespace tunnel
|
|||||||
|
|
||||||
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
|
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
|
||||||
|
|
||||||
virtual void Print (std::stringstream&) const {};
|
|
||||||
|
|
||||||
// implements TunnelBase
|
// implements TunnelBase
|
||||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
||||||
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
|
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
|
||||||
@@ -107,9 +108,8 @@ namespace tunnel
|
|||||||
bool LatencyIsKnown() const { return m_Latency > 0; }
|
bool LatencyIsKnown() const { return m_Latency > 0; }
|
||||||
bool IsSlow () const { return LatencyIsKnown() && (int)m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); }
|
bool IsSlow () const { return LatencyIsKnown() && (int)m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); }
|
||||||
|
|
||||||
protected:
|
/** visit all hops we currently store */
|
||||||
|
void VisitTunnelHops(TunnelHopVisitor v);
|
||||||
void PrintHops (std::stringstream& s) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -134,7 +134,6 @@ namespace tunnel
|
|||||||
virtual void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
|
virtual void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
|
||||||
const i2p::data::IdentHash& GetEndpointIdentHash () const { return m_EndpointIdentHash; };
|
const i2p::data::IdentHash& GetEndpointIdentHash () const { return m_EndpointIdentHash; };
|
||||||
virtual size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
|
virtual size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
|
||||||
void Print (std::stringstream& s) const;
|
|
||||||
|
|
||||||
// implements TunnelBase
|
// implements TunnelBase
|
||||||
void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg);
|
void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg);
|
||||||
@@ -155,7 +154,6 @@ namespace tunnel
|
|||||||
InboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Endpoint (true) {};
|
InboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Endpoint (true) {};
|
||||||
void HandleTunnelDataMsg (std::shared_ptr<I2NPMessage>&& msg);
|
void HandleTunnelDataMsg (std::shared_ptr<I2NPMessage>&& msg);
|
||||||
virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
|
virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
|
||||||
void Print (std::stringstream& s) const;
|
|
||||||
bool IsInbound() const { return true; }
|
bool IsInbound() const { return true; }
|
||||||
|
|
||||||
// override TunnelBase
|
// override TunnelBase
|
||||||
@@ -172,7 +170,6 @@ namespace tunnel
|
|||||||
|
|
||||||
ZeroHopsInboundTunnel ();
|
ZeroHopsInboundTunnel ();
|
||||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
||||||
void Print (std::stringstream& s) const;
|
|
||||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -186,7 +183,6 @@ namespace tunnel
|
|||||||
|
|
||||||
ZeroHopsOutboundTunnel ();
|
ZeroHopsOutboundTunnel ();
|
||||||
void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs);
|
void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs);
|
||||||
void Print (std::stringstream& s) const;
|
|
||||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -219,8 +215,8 @@ namespace tunnel
|
|||||||
void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||||
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
|
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
|
||||||
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel);
|
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel);
|
||||||
std::shared_ptr<TunnelPool> CreateTunnelPool (int numInboundHops,
|
std::shared_ptr<TunnelPool> CreateTunnelPool (int numInboundHops, int numOuboundHops,
|
||||||
int numOuboundHops, int numInboundTunnels, int numOutboundTunnels);
|
int numInboundTunnels, int numOutboundTunnels, int inboundVariance, int outboundVariance);
|
||||||
void DeleteTunnelPool (std::shared_ptr<TunnelPool> pool);
|
void DeleteTunnelPool (std::shared_ptr<TunnelPool> pool);
|
||||||
void StopTunnelPool (std::shared_ptr<TunnelPool> pool);
|
void StopTunnelPool (std::shared_ptr<TunnelPool> pool);
|
||||||
|
|
||||||
|
|||||||
@@ -40,15 +40,25 @@ namespace tunnel
|
|||||||
std::reverse (peers.begin (), peers.end ());
|
std::reverse (peers.begin (), peers.end ());
|
||||||
}
|
}
|
||||||
|
|
||||||
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
|
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels,
|
||||||
|
int numOutboundTunnels, int inboundVariance, int outboundVariance):
|
||||||
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
|
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
|
||||||
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
|
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels),
|
||||||
|
m_InboundVariance (inboundVariance), m_OutboundVariance (outboundVariance),
|
||||||
m_IsActive (true), m_CustomPeerSelector(nullptr)
|
m_IsActive (true), m_CustomPeerSelector(nullptr)
|
||||||
{
|
{
|
||||||
if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY)
|
if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY)
|
||||||
m_NumInboundTunnels = TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY;
|
m_NumInboundTunnels = TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY;
|
||||||
if (m_NumOutboundTunnels > TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY)
|
if (m_NumOutboundTunnels > TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY)
|
||||||
m_NumOutboundTunnels = TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY;
|
m_NumOutboundTunnels = TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY;
|
||||||
|
if (m_InboundVariance < 0 && m_NumInboundHops + m_InboundVariance <= 0)
|
||||||
|
m_InboundVariance = m_NumInboundHops ? -m_NumInboundHops + 1 : 0;
|
||||||
|
if (m_OutboundVariance < 0 && m_NumOutboundHops + m_OutboundVariance <= 0)
|
||||||
|
m_OutboundVariance = m_NumOutboundHops ? -m_NumOutboundHops + 1 : 0;
|
||||||
|
if (m_InboundVariance > 0 && m_NumInboundHops + m_InboundVariance > STANDARD_NUM_RECORDS)
|
||||||
|
m_InboundVariance = (m_NumInboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumInboundHops : 0;
|
||||||
|
if (m_OutboundVariance > 0 && m_NumOutboundHops + m_OutboundVariance > STANDARD_NUM_RECORDS)
|
||||||
|
m_OutboundVariance = (m_NumOutboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumOutboundHops : 0;
|
||||||
m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + rand () % TUNNEL_POOL_MANAGE_INTERVAL;
|
m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + rand () % TUNNEL_POOL_MANAGE_INTERVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +382,7 @@ namespace tunnel
|
|||||||
|
|
||||||
void TunnelPool::ManageTunnels (uint64_t ts)
|
void TunnelPool::ManageTunnels (uint64_t ts)
|
||||||
{
|
{
|
||||||
if (ts > m_NextManageTime)
|
if (ts > m_NextManageTime || ts + 2*TUNNEL_POOL_MANAGE_INTERVAL < m_NextManageTime) // in case if clock was adjusted
|
||||||
{
|
{
|
||||||
CreateTunnels ();
|
CreateTunnels ();
|
||||||
TestTunnels ();
|
TestTunnels ();
|
||||||
@@ -411,13 +421,18 @@ namespace tunnel
|
|||||||
{
|
{
|
||||||
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");
|
||||||
uint64_t latency = dlt / 2;
|
int numHops = 0;
|
||||||
|
if (test.first) numHops += test.first->GetNumHops ();
|
||||||
|
if (test.second) numHops += test.second->GetNumHops ();
|
||||||
// restore from test failed state if any
|
// restore from test failed state if any
|
||||||
if (test.first)
|
if (test.first)
|
||||||
{
|
{
|
||||||
if (test.first->GetState () == eTunnelStateTestFailed)
|
if (test.first->GetState () == eTunnelStateTestFailed)
|
||||||
test.first->SetState (eTunnelStateEstablished);
|
test.first->SetState (eTunnelStateEstablished);
|
||||||
// update latency
|
// update latency
|
||||||
|
uint64_t latency = 0;
|
||||||
|
if (numHops) latency = dlt*test.first->GetNumHops ()/numHops;
|
||||||
|
if (!latency) latency = dlt/2;
|
||||||
test.first->AddLatencySample(latency);
|
test.first->AddLatencySample(latency);
|
||||||
}
|
}
|
||||||
if (test.second)
|
if (test.second)
|
||||||
@@ -425,6 +440,9 @@ namespace tunnel
|
|||||||
if (test.second->GetState () == eTunnelStateTestFailed)
|
if (test.second->GetState () == eTunnelStateTestFailed)
|
||||||
test.second->SetState (eTunnelStateEstablished);
|
test.second->SetState (eTunnelStateEstablished);
|
||||||
// update latency
|
// update latency
|
||||||
|
uint64_t latency = 0;
|
||||||
|
if (numHops) latency = dlt*test.second->GetNumHops ()/numHops;
|
||||||
|
if (!latency) latency = dlt/2;
|
||||||
test.second->AddLatencySample(latency);
|
test.second->AddLatencySample(latency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -507,7 +525,30 @@ namespace tunnel
|
|||||||
|
|
||||||
bool TunnelPool::SelectPeers (Path& path, bool isInbound)
|
bool TunnelPool::SelectPeers (Path& path, bool isInbound)
|
||||||
{
|
{
|
||||||
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
|
// explicit peers in use
|
||||||
|
if (m_ExplicitPeers) return SelectExplicitPeers (path, isInbound);
|
||||||
|
// calculate num hops
|
||||||
|
int numHops;
|
||||||
|
if (isInbound)
|
||||||
|
{
|
||||||
|
numHops = m_NumInboundHops;
|
||||||
|
if (m_InboundVariance)
|
||||||
|
{
|
||||||
|
int offset = rand () % (std::abs (m_InboundVariance) + 1);
|
||||||
|
if (m_InboundVariance < 0) offset = -offset;
|
||||||
|
numHops += offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
numHops = m_NumOutboundHops;
|
||||||
|
if (m_OutboundVariance)
|
||||||
|
{
|
||||||
|
int offset = rand () % (std::abs (m_OutboundVariance) + 1);
|
||||||
|
if (m_OutboundVariance < 0) offset = -offset;
|
||||||
|
numHops += offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
// peers is empty
|
// peers is empty
|
||||||
if (numHops <= 0) return true;
|
if (numHops <= 0) return true;
|
||||||
// custom peer selector in use ?
|
// custom peer selector in use ?
|
||||||
@@ -516,8 +557,6 @@ namespace tunnel
|
|||||||
if (m_CustomPeerSelector)
|
if (m_CustomPeerSelector)
|
||||||
return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound);
|
return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound);
|
||||||
}
|
}
|
||||||
// explicit peers in use
|
|
||||||
if (m_ExplicitPeers) return SelectExplicitPeers (path, isInbound);
|
|
||||||
return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2));
|
return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ namespace tunnel
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels);
|
TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels,
|
||||||
|
int numOutboundTunnels, int inboundVariance, int outboundVariance);
|
||||||
~TunnelPool ();
|
~TunnelPool ();
|
||||||
|
|
||||||
std::shared_ptr<i2p::garlic::GarlicDestination> GetLocalDestination () const { return m_LocalDestination; };
|
std::shared_ptr<i2p::garlic::GarlicDestination> GetLocalDestination () const { return m_LocalDestination; };
|
||||||
@@ -130,7 +131,8 @@ namespace tunnel
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
std::shared_ptr<i2p::garlic::GarlicDestination> m_LocalDestination;
|
std::shared_ptr<i2p::garlic::GarlicDestination> m_LocalDestination;
|
||||||
int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels;
|
int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels,
|
||||||
|
m_InboundVariance, m_OutboundVariance;
|
||||||
std::shared_ptr<std::vector<i2p::data::IdentHash> > m_ExplicitPeers;
|
std::shared_ptr<std::vector<i2p::data::IdentHash> > m_ExplicitPeers;
|
||||||
mutable std::mutex m_InboundTunnelsMutex;
|
mutable std::mutex m_InboundTunnelsMutex;
|
||||||
std::set<std::shared_ptr<InboundTunnel>, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first
|
std::set<std::shared_ptr<InboundTunnel>, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first
|
||||||
|
|||||||
@@ -32,10 +32,6 @@
|
|||||||
#include <iphlpapi.h>
|
#include <iphlpapi.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma comment(lib, "IPHLPAPI.lib")
|
|
||||||
#endif // _MSC_VER
|
|
||||||
|
|
||||||
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
|
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
|
||||||
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
|
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
#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 41
|
#define I2PD_VERSION_MINOR 42
|
||||||
#define I2PD_VERSION_MICRO 0
|
#define I2PD_VERSION_MICRO 1
|
||||||
#define I2PD_VERSION_PATCH 0
|
#define I2PD_VERSION_PATCH 0
|
||||||
#ifdef GITVER
|
#ifdef GITVER
|
||||||
#define I2PD_VERSION GITVER
|
#define I2PD_VERSION GITVER
|
||||||
@@ -27,15 +27,11 @@
|
|||||||
|
|
||||||
#define VERSION I2PD_VERSION
|
#define VERSION I2PD_VERSION
|
||||||
|
|
||||||
#ifdef MESHNET
|
|
||||||
#define I2PD_NET_ID 3
|
|
||||||
#else
|
|
||||||
#define I2PD_NET_ID 2
|
#define I2PD_NET_ID 2
|
||||||
#endif
|
|
||||||
|
|
||||||
#define I2P_VERSION_MAJOR 0
|
#define I2P_VERSION_MAJOR 0
|
||||||
#define I2P_VERSION_MINOR 9
|
#define I2P_VERSION_MINOR 9
|
||||||
#define I2P_VERSION_MICRO 53
|
#define I2P_VERSION_MICRO 54
|
||||||
#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)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -299,7 +299,8 @@ namespace client
|
|||||||
}
|
}
|
||||||
|
|
||||||
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false),
|
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false),
|
||||||
m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
|
m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr),
|
||||||
|
m_IsEnabled (true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,6 +310,9 @@ namespace client
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AddressBook::Start ()
|
void AddressBook::Start ()
|
||||||
|
{
|
||||||
|
i2p::config::GetOption("addressbook.enabled", m_IsEnabled);
|
||||||
|
if (m_IsEnabled)
|
||||||
{
|
{
|
||||||
if (!m_Storage)
|
if (!m_Storage)
|
||||||
m_Storage = new AddressBookFilesystemStorage;
|
m_Storage = new AddressBookFilesystemStorage;
|
||||||
@@ -317,6 +321,7 @@ namespace client
|
|||||||
StartSubscriptions ();
|
StartSubscriptions ();
|
||||||
StartLookups ();
|
StartLookups ();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AddressBook::StartResolvers ()
|
void AddressBook::StartResolvers ()
|
||||||
{
|
{
|
||||||
@@ -370,6 +375,7 @@ namespace client
|
|||||||
pos = address.find (".i2p");
|
pos = address.find (".i2p");
|
||||||
if (pos != std::string::npos)
|
if (pos != std::string::npos)
|
||||||
{
|
{
|
||||||
|
if (!m_IsEnabled) return nullptr;
|
||||||
auto addr = FindAddress (address);
|
auto addr = FindAddress (address);
|
||||||
if (!addr)
|
if (!addr)
|
||||||
LookupAddress (address); // TODO:
|
LookupAddress (address); // TODO:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -126,6 +126,7 @@ namespace client
|
|||||||
std::vector<std::shared_ptr<AddressBookSubscription> > m_Subscriptions;
|
std::vector<std::shared_ptr<AddressBookSubscription> > m_Subscriptions;
|
||||||
std::shared_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
|
std::shared_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
|
||||||
boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
|
boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
|
||||||
|
bool m_IsEnabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddressBookSubscription
|
class AddressBookSubscription
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -704,7 +704,7 @@ namespace client
|
|||||||
msg += operand;
|
msg += operand;
|
||||||
*(const_cast<char *>(value)) = '=';
|
*(const_cast<char *>(value)) = '=';
|
||||||
msg += " set to ";
|
msg += " set to ";
|
||||||
msg += value;
|
msg += value + 1;
|
||||||
SendReplyOK (msg.c_str ());
|
SendReplyOK (msg.c_str ());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -455,6 +455,8 @@ namespace client
|
|||||||
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH);
|
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH);
|
||||||
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY);
|
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY);
|
||||||
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY);
|
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY);
|
||||||
|
options[I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE);
|
||||||
|
options[I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE);
|
||||||
options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND);
|
options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND);
|
||||||
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY);
|
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY);
|
||||||
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY);
|
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY);
|
||||||
@@ -487,10 +489,14 @@ namespace client
|
|||||||
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value;
|
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value;
|
||||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value))
|
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value))
|
||||||
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value;
|
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value;
|
||||||
|
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, value))
|
||||||
|
options[I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE] = value;
|
||||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value))
|
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value))
|
||||||
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value;
|
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value;
|
||||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value))
|
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value))
|
||||||
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value;
|
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value;
|
||||||
|
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, value))
|
||||||
|
options[I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE] = value;
|
||||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value))
|
if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value))
|
||||||
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value;
|
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value;
|
||||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value))
|
if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value))
|
||||||
@@ -602,21 +608,29 @@ namespace client
|
|||||||
if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
|
if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
|
||||||
// udp client
|
// udp client
|
||||||
// TODO: hostnames
|
// TODO: hostnames
|
||||||
boost::asio::ip::udp::endpoint end(boost::asio::ip::address::from_string(address), port);
|
boost::asio::ip::udp::endpoint end (boost::asio::ip::address::from_string(address), port);
|
||||||
if (!localDestination)
|
if (!localDestination)
|
||||||
localDestination = m_SharedLocalDestination;
|
localDestination = m_SharedLocalDestination;
|
||||||
|
|
||||||
bool gzip = section.second.get (I2P_CLIENT_TUNNEL_GZIP, true);
|
bool gzip = section.second.get (I2P_CLIENT_TUNNEL_GZIP, true);
|
||||||
auto clientTunnel = std::make_shared<I2PUDPClientTunnel>(name, dest, end, localDestination, destinationPort, gzip);
|
auto clientTunnel = std::make_shared<I2PUDPClientTunnel> (name, dest, end, localDestination, destinationPort, gzip);
|
||||||
|
|
||||||
auto ins = m_ClientForwards.insert(std::make_pair(end, clientTunnel));
|
auto ins = m_ClientForwards.insert (std::make_pair (end, clientTunnel));
|
||||||
if (ins.second)
|
if (ins.second)
|
||||||
{
|
{
|
||||||
clientTunnel->Start();
|
clientTunnel->Start ();
|
||||||
numClientTunnels++;
|
numClientTunnels++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// TODO: update
|
||||||
|
if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ())
|
||||||
|
{
|
||||||
|
LogPrint (eLogInfo, "Clients: I2P UDP client tunnel destination updated");
|
||||||
|
ins.first->second->Stop ();
|
||||||
|
ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ());
|
||||||
|
ins.first->second->Start ();
|
||||||
|
}
|
||||||
ins.first->second->isUpdated = true;
|
ins.first->second->isUpdated = true;
|
||||||
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
|
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
|
||||||
}
|
}
|
||||||
@@ -850,6 +864,8 @@ namespace client
|
|||||||
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
|
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
|
||||||
std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL);
|
std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL);
|
||||||
bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper);
|
bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper);
|
||||||
|
if (httpAddresshelper)
|
||||||
|
i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book
|
||||||
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
|
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
|
||||||
LogPrint(eLogInfo, "Clients: Starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
|
LogPrint(eLogInfo, "Clients: Starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
|
||||||
if (httpProxyKeys.length () > 0)
|
if (httpProxyKeys.length () > 0)
|
||||||
@@ -970,11 +986,11 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* // TODO: Write correct UDP tunnels stop
|
// TODO: Write correct UDP tunnels stop
|
||||||
for (auto it = m_ClientForwards.begin (); it != m_ClientForwards.end ();)
|
for (auto it = m_ClientForwards.begin (); it != m_ClientForwards.end ();)
|
||||||
{
|
{
|
||||||
if(clean && !it->second->isUpdated) {
|
if(clean && !it->second->isUpdated) {
|
||||||
it->second = nullptr;
|
it->second->Stop ();
|
||||||
it = m_ClientForwards.erase(it);
|
it = m_ClientForwards.erase(it);
|
||||||
} else {
|
} else {
|
||||||
it->second->isUpdated = false;
|
it->second->isUpdated = false;
|
||||||
@@ -985,13 +1001,13 @@ namespace client
|
|||||||
for (auto it = m_ServerForwards.begin (); it != m_ServerForwards.end ();)
|
for (auto it = m_ServerForwards.begin (); it != m_ServerForwards.end ();)
|
||||||
{
|
{
|
||||||
if(clean && !it->second->isUpdated) {
|
if(clean && !it->second->isUpdated) {
|
||||||
it->second = nullptr;
|
it->second->Stop ();
|
||||||
it = m_ServerForwards.erase(it);
|
it = m_ServerForwards.erase(it);
|
||||||
} else {
|
} else {
|
||||||
it->second->isUpdated = false;
|
it->second->isUpdated = false;
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
} */
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,8 +142,8 @@ namespace client
|
|||||||
|
|
||||||
i2p::proxy::HTTPProxy * m_HttpProxy;
|
i2p::proxy::HTTPProxy * m_HttpProxy;
|
||||||
i2p::proxy::SOCKSProxy * m_SocksProxy;
|
i2p::proxy::SOCKSProxy * m_SocksProxy;
|
||||||
std::map<boost::asio::ip::tcp::endpoint, std::shared_ptr<I2PService> > m_ClientTunnels; // local endpoint->tunnel
|
std::map<boost::asio::ip::tcp::endpoint, std::shared_ptr<I2PService> > m_ClientTunnels; // local endpoint -> tunnel
|
||||||
std::map<std::pair<i2p::data::IdentHash, int>, std::shared_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->tunnel
|
std::map<std::pair<i2p::data::IdentHash, int>, std::shared_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port> -> tunnel
|
||||||
|
|
||||||
std::mutex m_ForwardsMutex;
|
std::mutex m_ForwardsMutex;
|
||||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<I2PUDPClientTunnel> > m_ClientForwards; // local endpoint -> udp tunnel
|
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<I2PUDPClientTunnel> > m_ClientForwards; // local endpoint -> udp tunnel
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -32,7 +32,13 @@
|
|||||||
|
|
||||||
namespace i2p {
|
namespace i2p {
|
||||||
namespace proxy {
|
namespace proxy {
|
||||||
std::map<std::string, std::string> jumpservices = {
|
static const std::vector<std::string> jumporder = {
|
||||||
|
"reg.i2p",
|
||||||
|
"stats.i2p",
|
||||||
|
"identiguy.i2p",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<std::string, std::string> jumpservices = {
|
||||||
{ "reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/" },
|
{ "reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/" },
|
||||||
{ "identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname=" },
|
{ "identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname=" },
|
||||||
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" },
|
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" },
|
||||||
@@ -174,8 +180,11 @@ namespace proxy {
|
|||||||
<< "<p>" << tr("Remote host not found in router's addressbook") << "</p>\r\n"
|
<< "<p>" << tr("Remote host not found in router's addressbook") << "</p>\r\n"
|
||||||
<< "<p>" << tr("You may try to find this host on jump services below") << ":</p>\r\n"
|
<< "<p>" << tr("You may try to find this host on jump services below") << ":</p>\r\n"
|
||||||
<< "<ul>\r\n";
|
<< "<ul>\r\n";
|
||||||
for (const auto& js : jumpservices) {
|
for (const auto& jump : jumporder)
|
||||||
ss << " <li><a href=\"" << js.second << host << "\">" << js.first << "</a></li>\r\n";
|
{
|
||||||
|
auto js = jumpservices.find (jump);
|
||||||
|
if (js != jumpservices.end())
|
||||||
|
ss << " <li><a href=\"" << js->second << host << "\">" << js->first << "</a></li>\r\n";
|
||||||
}
|
}
|
||||||
ss << "</ul>\r\n";
|
ss << "</ul>\r\n";
|
||||||
std::string content = ss.str();
|
std::string content = ss.str();
|
||||||
@@ -237,14 +246,14 @@ namespace proxy {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* according to i2p ticket #1862:
|
* according to i2p ticket #1862:
|
||||||
* leave Referrer if requested URL with same schema, host and port,
|
* leave Referer if requested URL with same schema, host and port,
|
||||||
* otherwise, drop it.
|
* otherwise, drop it.
|
||||||
*/
|
*/
|
||||||
if(req.GetHeader("Referrer") != "") {
|
if(req.GetHeader("Referer") != "") {
|
||||||
i2p::http::URL reqURL; reqURL.parse(req.uri);
|
i2p::http::URL reqURL; reqURL.parse(req.uri);
|
||||||
i2p::http::URL refURL; refURL.parse(req.GetHeader("Referrer"));
|
i2p::http::URL refURL; refURL.parse(req.GetHeader("Referer"));
|
||||||
if(!boost::iequals(reqURL.schema, refURL.schema) || !boost::iequals(reqURL.host, refURL.host) || reqURL.port != refURL.port)
|
if(!boost::iequals(reqURL.schema, refURL.schema) || !boost::iequals(reqURL.host, refURL.host) || reqURL.port != refURL.port)
|
||||||
req.RemoveHeader("Referrer");
|
req.RemoveHeader("Referer");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add headers */
|
/* add headers */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
#include "Base.h"
|
#include "Base.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Destination.h"
|
#include "Destination.h"
|
||||||
@@ -340,7 +341,7 @@ namespace client
|
|||||||
m_InHeader.clear ();
|
m_InHeader.clear ();
|
||||||
m_InHeader.write ((const char *)buf, len);
|
m_InHeader.write ((const char *)buf, len);
|
||||||
std::string line;
|
std::string line;
|
||||||
bool endOfHeader = false;
|
bool endOfHeader = false, connection = false;
|
||||||
while (!endOfHeader)
|
while (!endOfHeader)
|
||||||
{
|
{
|
||||||
std::getline(m_InHeader, line);
|
std::getline(m_InHeader, line);
|
||||||
@@ -349,9 +350,33 @@ namespace client
|
|||||||
if (line == "\r") endOfHeader = true;
|
if (line == "\r") endOfHeader = true;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_Host.length () > 0 && !line.compare(0, 5, "Host:"))
|
// strip up some headers
|
||||||
|
static const std::vector<std::string> excluded // list of excluded headers
|
||||||
|
{
|
||||||
|
"Keep-Alive:", "X-I2P"
|
||||||
|
};
|
||||||
|
bool matched = false;
|
||||||
|
for (const auto& it: excluded)
|
||||||
|
if (boost::iequals (line.substr (0, it.length ()), it))
|
||||||
|
{
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (matched) break;
|
||||||
|
|
||||||
|
// replace some headers
|
||||||
|
if (!m_Host.empty () && boost::iequals (line.substr (0, 5), "Host:"))
|
||||||
m_OutHeader << "Host: " << m_Host << "\r\n"; // override host
|
m_OutHeader << "Host: " << m_Host << "\r\n"; // override host
|
||||||
|
else if (boost::iequals (line.substr (0, 11), "Connection:"))
|
||||||
|
{
|
||||||
|
auto x = line.find("pgrade");
|
||||||
|
if (x != std::string::npos && x && std::tolower(line[x - 1]) != 'u') // upgrade or Upgrade
|
||||||
|
m_OutHeader << line << "\n";
|
||||||
else
|
else
|
||||||
|
m_OutHeader << "Connection: close\r\n";
|
||||||
|
connection = true;
|
||||||
|
}
|
||||||
|
else // forward as is
|
||||||
m_OutHeader << line << "\n";
|
m_OutHeader << line << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,6 +386,9 @@ namespace client
|
|||||||
|
|
||||||
if (endOfHeader)
|
if (endOfHeader)
|
||||||
{
|
{
|
||||||
|
// add Connection if not presented
|
||||||
|
if (!connection)
|
||||||
|
m_OutHeader << "Connection: close\r\n";
|
||||||
// add X-I2P fields
|
// add X-I2P fields
|
||||||
if (m_From)
|
if (m_From)
|
||||||
{
|
{
|
||||||
@@ -583,7 +611,7 @@ namespace client
|
|||||||
{
|
{
|
||||||
if (m_KeepAliveTimer)
|
if (m_KeepAliveTimer)
|
||||||
{
|
{
|
||||||
m_KeepAliveTimer->expires_from_now (boost::posix_time::seconds(m_KeepAliveInterval));
|
m_KeepAliveTimer->expires_from_now (boost::posix_time::seconds (m_KeepAliveInterval));
|
||||||
m_KeepAliveTimer->async_wait (std::bind (&I2PClientTunnel::HandleKeepAliveTimer,
|
m_KeepAliveTimer->async_wait (std::bind (&I2PClientTunnel::HandleKeepAliveTimer,
|
||||||
this, std::placeholders::_1));
|
this, std::placeholders::_1));
|
||||||
}
|
}
|
||||||
@@ -796,7 +824,8 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) {
|
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta)
|
||||||
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||||
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
||||||
auto itr = m_Sessions.begin();
|
auto itr = m_Sessions.begin();
|
||||||
@@ -808,7 +837,8 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) {
|
void I2PUDPClientTunnel::ExpireStale(const uint64_t delta)
|
||||||
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||||
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
||||||
std::vector<uint16_t> removePorts;
|
std::vector<uint16_t> removePorts;
|
||||||
@@ -864,7 +894,8 @@ namespace client
|
|||||||
Receive();
|
Receive();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDPSession::Receive() {
|
void UDPSession::Receive()
|
||||||
|
{
|
||||||
LogPrint(eLogDebug, "UDPSession: Receive");
|
LogPrint(eLogDebug, "UDPSession: Receive");
|
||||||
IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU),
|
IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU),
|
||||||
FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2));
|
FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
@@ -901,227 +932,241 @@ namespace client
|
|||||||
LogPrint(eLogError, "UDPSession: ", ecode.message());
|
LogPrint(eLogError, "UDPSession: ", ecode.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
I2PUDPServerTunnel::I2PUDPServerTunnel (const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||||
boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip) :
|
boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip) :
|
||||||
m_IsUniqueLocal(true),
|
m_IsUniqueLocal (true), m_Name (name), m_LocalAddress (localAddress),
|
||||||
m_Name(name),
|
m_RemoteEndpoint (forwardTo), m_LocalDest (localDestination), m_Gzip (gzip)
|
||||||
m_LocalAddress(localAddress),
|
|
||||||
m_RemoteEndpoint(forwardTo)
|
|
||||||
{
|
{
|
||||||
m_LocalDest = localDestination;
|
|
||||||
m_LocalDest->Start();
|
|
||||||
auto dgram = m_LocalDest->CreateDatagramDestination(gzip);
|
|
||||||
dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
|
||||||
dgram->SetRawReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
I2PUDPServerTunnel::~I2PUDPServerTunnel()
|
I2PUDPServerTunnel::~I2PUDPServerTunnel ()
|
||||||
{
|
{
|
||||||
auto dgram = m_LocalDest->GetDatagramDestination();
|
Stop ();
|
||||||
if (dgram) dgram->ResetReceiver();
|
|
||||||
|
|
||||||
LogPrint(eLogInfo, "UDPServer: Done");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPServerTunnel::Start()
|
void I2PUDPServerTunnel::Start ()
|
||||||
{
|
{
|
||||||
m_LocalDest->Start();
|
m_LocalDest->Start ();
|
||||||
|
|
||||||
|
auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip);
|
||||||
|
dgram->SetReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
||||||
|
dgram->SetRawReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPServerTunnel::GetSessions()
|
void I2PUDPServerTunnel::Stop ()
|
||||||
|
{
|
||||||
|
auto dgram = m_LocalDest->GetDatagramDestination ();
|
||||||
|
if (dgram) dgram->ResetReceiver ();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPServerTunnel::GetSessions ()
|
||||||
{
|
{
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
|
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
|
||||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
std::lock_guard<std::mutex> lock (m_SessionsMutex);
|
||||||
|
|
||||||
for ( UDPSessionPtr s : m_Sessions )
|
for (UDPSessionPtr s: m_Sessions)
|
||||||
{
|
{
|
||||||
if (!s->m_Destination) continue;
|
if (!s->m_Destination) continue;
|
||||||
auto info = s->m_Destination->GetInfoForRemote(s->Identity);
|
auto info = s->m_Destination->GetInfoForRemote (s->Identity);
|
||||||
if(!info) continue;
|
if (!info) continue;
|
||||||
|
|
||||||
auto sinfo = std::make_shared<DatagramSessionInfo>();
|
auto sinfo = std::make_shared<DatagramSessionInfo> ();
|
||||||
sinfo->Name = m_Name;
|
sinfo->Name = m_Name;
|
||||||
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash>(m_LocalDest->GetIdentHash().data());
|
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash> (m_LocalDest->GetIdentHash ().data ());
|
||||||
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash>(s->Identity.data());
|
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash> (s->Identity.data ());
|
||||||
sinfo->CurrentIBGW = info->IBGW;
|
sinfo->CurrentIBGW = info->IBGW;
|
||||||
sinfo->CurrentOBEP = info->OBEP;
|
sinfo->CurrentOBEP = info->OBEP;
|
||||||
sessions.push_back(sinfo);
|
sessions.push_back (sinfo);
|
||||||
}
|
}
|
||||||
return sessions;
|
return sessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
I2PUDPClientTunnel::I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
|
I2PUDPClientTunnel::I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest,
|
||||||
boost::asio::ip::udp::endpoint localEndpoint,
|
boost::asio::ip::udp::endpoint localEndpoint,
|
||||||
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||||
uint16_t remotePort, bool gzip) :
|
uint16_t remotePort, bool gzip) :
|
||||||
m_Name(name),
|
m_Name (name), m_RemoteDest (remoteDest), m_LocalDest (localDestination), m_LocalEndpoint (localEndpoint),
|
||||||
m_RemoteDest(remoteDest),
|
m_RemoteIdent (nullptr), m_ResolveThread (nullptr), m_LocalSocket (nullptr), RemotePort (remotePort),
|
||||||
m_LocalDest(localDestination),
|
m_LastPort (0), m_cancel_resolve (false), m_Gzip (gzip)
|
||||||
m_LocalEndpoint(localEndpoint),
|
|
||||||
m_RemoteIdent(nullptr),
|
|
||||||
m_ResolveThread(nullptr),
|
|
||||||
m_LocalSocket(localDestination->GetService(), localEndpoint),
|
|
||||||
RemotePort(remotePort), m_LastPort (0),
|
|
||||||
m_cancel_resolve(false)
|
|
||||||
{
|
{
|
||||||
m_LocalSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU));
|
}
|
||||||
m_LocalSocket.set_option (boost::asio::socket_base::reuse_address (true));
|
|
||||||
|
|
||||||
auto dgram = m_LocalDest->CreateDatagramDestination(gzip);
|
I2PUDPClientTunnel::~I2PUDPClientTunnel ()
|
||||||
dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this,
|
{
|
||||||
|
Stop ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2PUDPClientTunnel::Start ()
|
||||||
|
{
|
||||||
|
// Reset flag in case of tunnel reload
|
||||||
|
if (m_cancel_resolve) m_cancel_resolve = false;
|
||||||
|
|
||||||
|
m_LocalSocket.reset (new boost::asio::ip::udp::socket (m_LocalDest->GetService (), m_LocalEndpoint));
|
||||||
|
m_LocalSocket->set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU));
|
||||||
|
m_LocalSocket->set_option (boost::asio::socket_base::reuse_address (true));
|
||||||
|
|
||||||
|
auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip);
|
||||||
|
dgram->SetReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2P, this,
|
||||||
std::placeholders::_1, std::placeholders::_2,
|
std::placeholders::_1, std::placeholders::_2,
|
||||||
std::placeholders::_3, std::placeholders::_4,
|
std::placeholders::_3, std::placeholders::_4,
|
||||||
std::placeholders::_5));
|
std::placeholders::_5));
|
||||||
dgram->SetRawReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this,
|
dgram->SetRawReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this,
|
||||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||||
}
|
|
||||||
|
|
||||||
void I2PUDPClientTunnel::Start() {
|
m_LocalDest->Start ();
|
||||||
m_LocalDest->Start();
|
|
||||||
if (m_ResolveThread == nullptr)
|
if (m_ResolveThread == nullptr)
|
||||||
m_ResolveThread = new std::thread(std::bind(&I2PUDPClientTunnel::TryResolving, this));
|
m_ResolveThread = new std::thread (std::bind (&I2PUDPClientTunnel::TryResolving, this));
|
||||||
RecvFromLocal();
|
RecvFromLocal ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::RecvFromLocal()
|
void I2PUDPClientTunnel::Stop ()
|
||||||
{
|
{
|
||||||
m_LocalSocket.async_receive_from(boost::asio::buffer(m_RecvBuff, I2P_UDP_MAX_MTU),
|
auto dgram = m_LocalDest->GetDatagramDestination ();
|
||||||
m_RecvEndpoint, std::bind(&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2));
|
if (dgram) dgram->ResetReceiver ();
|
||||||
|
m_cancel_resolve = true;
|
||||||
|
|
||||||
|
m_Sessions.clear();
|
||||||
|
|
||||||
|
if(m_LocalSocket && m_LocalSocket->is_open ())
|
||||||
|
m_LocalSocket->close ();
|
||||||
|
|
||||||
|
if(m_ResolveThread)
|
||||||
|
{
|
||||||
|
m_ResolveThread->join ();
|
||||||
|
delete m_ResolveThread;
|
||||||
|
m_ResolveThread = nullptr;
|
||||||
|
}
|
||||||
|
if (m_RemoteIdent)
|
||||||
|
{
|
||||||
|
delete m_RemoteIdent;
|
||||||
|
m_RemoteIdent = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred)
|
void I2PUDPClientTunnel::RecvFromLocal ()
|
||||||
{
|
{
|
||||||
if(ec) {
|
m_LocalSocket->async_receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU),
|
||||||
LogPrint(eLogError, "UDP Client: Reading from socket error: ", ec.message(), ". Restarting listener...");
|
m_RecvEndpoint, std::bind (&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
RecvFromLocal(); // Restart listener and continue work
|
}
|
||||||
|
|
||||||
|
void I2PUDPClientTunnel::HandleRecvFromLocal (const boost::system::error_code & ec, std::size_t transferred)
|
||||||
|
{
|
||||||
|
if (m_cancel_resolve) {
|
||||||
|
LogPrint (eLogDebug, "UDP Client: Ignoring incomming data: stopping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!m_RemoteIdent) {
|
if (ec) {
|
||||||
LogPrint(eLogWarning, "UDP Client: Remote endpoint not resolved yet");
|
LogPrint (eLogError, "UDP Client: Reading from socket error: ", ec.message (), ". Restarting listener...");
|
||||||
RecvFromLocal();
|
RecvFromLocal (); // Restart listener and continue work
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_RemoteIdent) {
|
||||||
|
LogPrint (eLogWarning, "UDP Client: Remote endpoint not resolved yet");
|
||||||
|
RecvFromLocal ();
|
||||||
return; // drop, remote not resolved
|
return; // drop, remote not resolved
|
||||||
}
|
}
|
||||||
auto remotePort = m_RecvEndpoint.port();
|
auto remotePort = m_RecvEndpoint.port ();
|
||||||
if (!m_LastPort || m_LastPort != remotePort)
|
if (!m_LastPort || m_LastPort != remotePort)
|
||||||
{
|
{
|
||||||
auto itr = m_Sessions.find(remotePort);
|
auto itr = m_Sessions.find (remotePort);
|
||||||
if (itr != m_Sessions.end())
|
if (itr != m_Sessions.end ())
|
||||||
m_LastSession = itr->second;
|
m_LastSession = itr->second;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_LastSession = std::make_shared<UDPConvo>(boost::asio::ip::udp::endpoint(m_RecvEndpoint), 0);
|
m_LastSession = std::make_shared<UDPConvo> (boost::asio::ip::udp::endpoint (m_RecvEndpoint), 0);
|
||||||
m_Sessions.emplace (remotePort, m_LastSession);
|
m_Sessions.emplace (remotePort, m_LastSession);
|
||||||
}
|
}
|
||||||
m_LastPort = remotePort;
|
m_LastPort = remotePort;
|
||||||
}
|
}
|
||||||
// send off to remote i2p destination
|
// send off to remote i2p destination
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch();
|
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
LogPrint(eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort);
|
LogPrint (eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteIdent->ToBase32 (), ":", RemotePort);
|
||||||
auto session = m_LocalDest->GetDatagramDestination()->GetSession (*m_RemoteIdent);
|
auto session = m_LocalDest->GetDatagramDestination ()->GetSession (*m_RemoteIdent);
|
||||||
if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
|
if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
|
||||||
m_LocalDest->GetDatagramDestination()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
m_LocalDest->GetDatagramDestination ()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
||||||
else
|
else
|
||||||
m_LocalDest->GetDatagramDestination()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
||||||
size_t numPackets = 0;
|
size_t numPackets = 0;
|
||||||
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
|
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
|
||||||
{
|
{
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
size_t moreBytes = m_LocalSocket.available(ec);
|
size_t moreBytes = m_LocalSocket->available (ec);
|
||||||
if (ec || !moreBytes) break;
|
if (ec || !moreBytes) break;
|
||||||
transferred = m_LocalSocket.receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), m_RecvEndpoint, 0, ec);
|
transferred = m_LocalSocket->receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), m_RecvEndpoint, 0, ec);
|
||||||
remotePort = m_RecvEndpoint.port();
|
remotePort = m_RecvEndpoint.port ();
|
||||||
// TODO: check remotePort
|
// TODO: check remotePort
|
||||||
m_LocalDest->GetDatagramDestination()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
||||||
numPackets++;
|
numPackets++;
|
||||||
}
|
}
|
||||||
if (numPackets)
|
if (numPackets)
|
||||||
LogPrint(eLogDebug, "UDP Client: Sent ", numPackets, " more packets to ", m_RemoteIdent->ToBase32());
|
LogPrint (eLogDebug, "UDP Client: Sent ", numPackets, " more packets to ", m_RemoteIdent->ToBase32 ());
|
||||||
m_LocalDest->GetDatagramDestination()->FlushSendQueue (session);
|
m_LocalDest->GetDatagramDestination ()->FlushSendQueue (session);
|
||||||
|
|
||||||
// mark convo as active
|
// mark convo as active
|
||||||
if (m_LastSession)
|
if (m_LastSession)
|
||||||
m_LastSession->second = ts;
|
m_LastSession->second = ts;
|
||||||
RecvFromLocal();
|
RecvFromLocal ();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions()
|
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions ()
|
||||||
{
|
{
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
|
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::TryResolving() {
|
void I2PUDPClientTunnel::TryResolving ()
|
||||||
i2p::util::SetThreadName("UDP Resolver");
|
{
|
||||||
LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
|
i2p::util::SetThreadName ("UDP Resolver");
|
||||||
|
LogPrint (eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
|
||||||
|
|
||||||
std::shared_ptr<const Address> addr;
|
std::shared_ptr<const Address> addr;
|
||||||
while(!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve)
|
while (!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve)
|
||||||
{
|
{
|
||||||
LogPrint(eLogWarning, "UDP Tunnel: Failed to lookup ", m_RemoteDest);
|
LogPrint (eLogWarning, "UDP Tunnel: Failed to lookup ", m_RemoteDest);
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for (std::chrono::seconds (1));
|
||||||
}
|
}
|
||||||
if(m_cancel_resolve)
|
if (m_cancel_resolve)
|
||||||
{
|
{
|
||||||
LogPrint(eLogError, "UDP Tunnel: Lookup of ", m_RemoteDest, " was cancelled");
|
LogPrint(eLogError, "UDP Tunnel: Lookup of ", m_RemoteDest, " was cancelled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!addr || !addr->IsIdentHash ())
|
if (!addr || !addr->IsIdentHash ())
|
||||||
{
|
{
|
||||||
LogPrint(eLogError, "UDP Tunnel: ", m_RemoteDest, " not found");
|
LogPrint (eLogError, "UDP Tunnel: ", m_RemoteDest, " not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_RemoteIdent = new i2p::data::IdentHash;
|
m_RemoteIdent = new i2p::data::IdentHash;
|
||||||
*m_RemoteIdent = addr->identHash;
|
*m_RemoteIdent = addr->identHash;
|
||||||
LogPrint(eLogInfo, "UDP Tunnel: Resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32());
|
LogPrint(eLogInfo, "UDP Tunnel: Resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32 ());
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
void I2PUDPClientTunnel::HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
|
if (m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
|
||||||
HandleRecvFromI2PRaw (fromPort, toPort, buf, len);
|
HandleRecvFromI2PRaw (fromPort, toPort, buf, len);
|
||||||
else
|
else
|
||||||
LogPrint(eLogWarning, "UDP Client: Unwarranted traffic from ", from.GetIdentHash().ToBase32());
|
LogPrint(eLogWarning, "UDP Client: Unwarranted traffic from ", from.GetIdentHash().ToBase32 ());
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::HandleRecvFromI2PRaw(uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
void I2PUDPClientTunnel::HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
auto itr = m_Sessions.find(toPort);
|
auto itr = m_Sessions.find (toPort);
|
||||||
// found convo ?
|
// found convo ?
|
||||||
if(itr != m_Sessions.end())
|
if (itr != m_Sessions.end ())
|
||||||
{
|
{
|
||||||
// found convo
|
// found convo
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
{
|
{
|
||||||
LogPrint(eLogDebug, "UDP Client: Got ", len, "B from ", m_RemoteIdent ? m_RemoteIdent->ToBase32() : "");
|
LogPrint (eLogDebug, "UDP Client: Got ", len, "B from ", m_RemoteIdent ? m_RemoteIdent->ToBase32 () : "");
|
||||||
m_LocalSocket.send_to(boost::asio::buffer(buf, len), itr->second->first);
|
m_LocalSocket->send_to (boost::asio::buffer (buf, len), itr->second->first);
|
||||||
// mark convo as active
|
// mark convo as active
|
||||||
itr->second->second = i2p::util::GetMillisecondsSinceEpoch();
|
itr->second->second = i2p::util::GetMillisecondsSinceEpoch ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
LogPrint(eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort);
|
LogPrint (eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort);
|
||||||
}
|
|
||||||
|
|
||||||
I2PUDPClientTunnel::~I2PUDPClientTunnel()
|
|
||||||
{
|
|
||||||
auto dgram = m_LocalDest->GetDatagramDestination();
|
|
||||||
if (dgram) dgram->ResetReceiver();
|
|
||||||
|
|
||||||
m_Sessions.clear();
|
|
||||||
|
|
||||||
if(m_LocalSocket.is_open())
|
|
||||||
m_LocalSocket.close();
|
|
||||||
|
|
||||||
m_cancel_resolve = true;
|
|
||||||
|
|
||||||
if(m_ResolveThread)
|
|
||||||
{
|
|
||||||
m_ResolveThread->join();
|
|
||||||
delete m_ResolveThread;
|
|
||||||
m_ResolveThread = nullptr;
|
|
||||||
}
|
|
||||||
if (m_RemoteIdent) delete m_RemoteIdent;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, 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
|
||||||
*
|
*
|
||||||
@@ -230,25 +230,27 @@ namespace client
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
I2PUDPServerTunnel(const std::string & name,
|
I2PUDPServerTunnel (const std::string & name,
|
||||||
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||||
boost::asio::ip::address localAddress,
|
boost::asio::ip::address localAddress,
|
||||||
boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip);
|
boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip);
|
||||||
~I2PUDPServerTunnel();
|
~I2PUDPServerTunnel ();
|
||||||
|
|
||||||
/** expire stale udp conversations */
|
/** expire stale udp conversations */
|
||||||
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
|
void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
|
||||||
void Start();
|
void Start ();
|
||||||
const char * GetName() const { return m_Name.c_str(); }
|
void Stop ();
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
|
const char * GetName () const { return m_Name.c_str(); }
|
||||||
|
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions ();
|
||||||
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
||||||
|
|
||||||
void SetUniqueLocal(bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; }
|
void SetUniqueLocal (bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
void HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||||
void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||||
UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
|
UDPSessionPtr ObtainUDPSession (const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -260,6 +262,7 @@ namespace client
|
|||||||
std::vector<UDPSessionPtr> m_Sessions;
|
std::vector<UDPSessionPtr> m_Sessions;
|
||||||
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
|
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
|
||||||
UDPSessionPtr m_LastSession;
|
UDPSessionPtr m_LastSession;
|
||||||
|
bool m_Gzip;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -270,27 +273,36 @@ namespace client
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
|
I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest,
|
||||||
boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||||
uint16_t remotePort, bool gzip);
|
uint16_t remotePort, bool gzip);
|
||||||
~I2PUDPClientTunnel();
|
~I2PUDPClientTunnel ();
|
||||||
void Start();
|
|
||||||
const char * GetName() const { return m_Name.c_str(); }
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
|
|
||||||
|
|
||||||
bool IsLocalDestination(const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
|
void Start ();
|
||||||
|
void Stop ();
|
||||||
|
const char * GetName () const { return m_Name.c_str(); }
|
||||||
|
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions ();
|
||||||
|
|
||||||
|
bool IsLocalDestination (const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
||||||
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
|
inline void SetLocalDestination (std::shared_ptr<ClientDestination> dest)
|
||||||
|
{
|
||||||
|
if (m_LocalDest) m_LocalDest->Release ();
|
||||||
|
if (dest) dest->Acquire ();
|
||||||
|
m_LocalDest = dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
typedef std::pair<boost::asio::ip::udp::endpoint, uint64_t> UDPConvo;
|
typedef std::pair<boost::asio::ip::udp::endpoint, uint64_t> UDPConvo;
|
||||||
void RecvFromLocal();
|
void RecvFromLocal ();
|
||||||
void HandleRecvFromLocal(const boost::system::error_code & e, std::size_t transferred);
|
void HandleRecvFromLocal (const boost::system::error_code & e, std::size_t transferred);
|
||||||
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
void HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||||
void HandleRecvFromI2PRaw(uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||||
void TryResolving();
|
void TryResolving ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -302,11 +314,12 @@ namespace client
|
|||||||
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
|
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
|
||||||
i2p::data::IdentHash * m_RemoteIdent;
|
i2p::data::IdentHash * m_RemoteIdent;
|
||||||
std::thread * m_ResolveThread;
|
std::thread * m_ResolveThread;
|
||||||
boost::asio::ip::udp::socket m_LocalSocket;
|
std::unique_ptr<boost::asio::ip::udp::socket> m_LocalSocket;
|
||||||
boost::asio::ip::udp::endpoint m_RecvEndpoint;
|
boost::asio::ip::udp::endpoint m_RecvEndpoint;
|
||||||
uint8_t m_RecvBuff[I2P_UDP_MAX_MTU];
|
uint8_t m_RecvBuff[I2P_UDP_MAX_MTU];
|
||||||
uint16_t RemotePort, m_LastPort;
|
uint16_t RemotePort, m_LastPort;
|
||||||
bool m_cancel_resolve;
|
bool m_cancel_resolve;
|
||||||
|
bool m_Gzip;
|
||||||
std::shared_ptr<UDPConvo> m_LastSession;
|
std::shared_ptr<UDPConvo> m_LastSession;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -27,14 +27,16 @@
|
|||||||
#include "RouterContext.h"
|
#include "RouterContext.h"
|
||||||
#include "ClientContext.h"
|
#include "ClientContext.h"
|
||||||
#include "HTTPServer.h"
|
#include "HTTPServer.h"
|
||||||
#include "Daemon.h"
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "ECIESX25519AEADRatchetSession.h"
|
#include "ECIESX25519AEADRatchetSession.h"
|
||||||
#include "I18N.h"
|
#include "I18N.h"
|
||||||
|
|
||||||
#ifdef WIN32_APP
|
//#ifdef WIN32_APP
|
||||||
#include "Win32App.h"
|
//#include "Win32App.h"
|
||||||
#endif
|
//#endif
|
||||||
|
|
||||||
|
// Inja template engine
|
||||||
|
#include "inja/inja.hpp"
|
||||||
|
|
||||||
// For image, style and info
|
// For image, style and info
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@@ -58,7 +60,7 @@ namespace http {
|
|||||||
static void GetStyles (std::stringstream& s)
|
static void GetStyles (std::stringstream& s)
|
||||||
{
|
{
|
||||||
if (externalCSS.length() != 0)
|
if (externalCSS.length() != 0)
|
||||||
s << "<style>\r\n" << externalCSS << "</style>\r\n";
|
s << externalCSS;
|
||||||
else
|
else
|
||||||
s << internalCSS;
|
s << internalCSS;
|
||||||
}
|
}
|
||||||
@@ -182,9 +184,11 @@ namespace http {
|
|||||||
" <meta charset=\"UTF-8\">\r\n"
|
" <meta charset=\"UTF-8\">\r\n"
|
||||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n"
|
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n"
|
||||||
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
|
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
|
||||||
" <title>Purple I2P " VERSION " Webconsole</title>\r\n";
|
" <title>Purple I2P Webconsole</title>\r\n"
|
||||||
|
" <style>";
|
||||||
GetStyles(s);
|
GetStyles(s);
|
||||||
s <<
|
s <<
|
||||||
|
"\r\n</style>\r\n"
|
||||||
"</head>\r\n"
|
"</head>\r\n"
|
||||||
"<body>\r\n"
|
"<body>\r\n"
|
||||||
"<div class=\"header\">" << tr("<b>i2pd</b> webconsole") << "</div>\r\n"
|
"<div class=\"header\">" << tr("<b>i2pd</b> webconsole") << "</div>\r\n"
|
||||||
@@ -196,8 +200,10 @@ namespace http {
|
|||||||
if (i2p::context.IsFloodfill ())
|
if (i2p::context.IsFloodfill ())
|
||||||
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">" << tr("LeaseSets") << "</a><br>\r\n";
|
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">" << tr("LeaseSets") << "</a><br>\r\n";
|
||||||
s <<
|
s <<
|
||||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">" << tr("Tunnels") << "</a><br>\r\n"
|
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">" << tr("Tunnels") << "</a><br>\r\n";
|
||||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit Tunnels") << "</a><br>\r\n"
|
if (i2p::context.AcceptsTunnels () || i2p::tunnel::tunnels.CountTransitTunnels())
|
||||||
|
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit Tunnels") << "</a><br>\r\n";
|
||||||
|
s <<
|
||||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a><br>\r\n"
|
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a><br>\r\n"
|
||||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n";
|
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n";
|
||||||
if (i2p::client::context.GetSAMBridge ())
|
if (i2p::client::context.GetSAMBridge ())
|
||||||
@@ -266,23 +272,22 @@ namespace http {
|
|||||||
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
|
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
|
||||||
s << "<br>\r\n";
|
s << "<br>\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: rewrite timer access
|
||||||
#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 = m_DaemonGracefulTimer) {
|
||||||
s << "<b>" << tr("Stopping in") << ":</b> ";
|
s << "<b>" << tr("Stopping in") << ":</b> ";
|
||||||
ShowUptime(s, remains);
|
ShowUptime(s, remains);
|
||||||
s << "<br>\r\n";
|
s << "<br>\r\n";
|
||||||
}
|
}
|
||||||
#elif defined(WIN32_APP)
|
/*#elif defined(WIN32_APP)
|
||||||
if (i2p::win32::g_GracefulShutdownEndtime != 0) {
|
if (i2p::win32::g_GracefulShutdownEndtime != 0) {
|
||||||
uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000;
|
uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000;
|
||||||
s << "<b>" << tr("Stopping in") << ":</b> ";
|
s << "<b>" << tr("Stopping in") << ":</b> ";
|
||||||
ShowUptime(s, remains);
|
ShowUptime(s, remains);
|
||||||
s << "<br>\r\n";
|
s << "<br>\r\n";
|
||||||
}
|
}*/
|
||||||
#endif
|
#endif
|
||||||
auto family = i2p::context.GetFamily ();
|
|
||||||
if (family.length () > 0)
|
|
||||||
s << "<b>"<< tr("Family") << ":</b> " << family << "<br>\r\n";
|
|
||||||
s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
|
s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
|
||||||
s << "<b>" << tr("Received") << ":</b> ";
|
s << "<b>" << tr("Received") << ":</b> ";
|
||||||
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
|
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
|
||||||
@@ -295,10 +300,10 @@ namespace http {
|
|||||||
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
|
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
|
||||||
s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n";
|
s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n";
|
||||||
s << "<div class='slide'>";
|
s << "<div class='slide'>";
|
||||||
if((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) {
|
if ((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) {
|
||||||
s << "<label for=\"slide-info\">" << tr("Hidden content. Press on text to see.") << "</label>\r\n<input type=\"checkbox\" id=\"slide-info\" />\r\n<div class=\"slidecontent\">\r\n";
|
s << "<label for=\"slide-info\">" << tr("Hidden content. Press on text to see.") << "</label>\r\n<input type=\"checkbox\" id=\"slide-info\" />\r\n<div class=\"slidecontent\">\r\n";
|
||||||
}
|
}
|
||||||
if(includeHiddenContent) {
|
if (includeHiddenContent) {
|
||||||
s << "<b>" << tr("Router Ident") << ":</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
|
s << "<b>" << tr("Router Ident") << ":</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
|
||||||
if (!i2p::context.GetRouterInfo().GetProperty("family").empty())
|
if (!i2p::context.GetRouterInfo().GetProperty("family").empty())
|
||||||
s << "<b>" << tr("Router Family") << ":</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
|
s << "<b>" << tr("Router Family") << ":</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
|
||||||
@@ -307,41 +312,42 @@ namespace http {
|
|||||||
s << "<b>"<< tr("Our external address") << ":</b>" << "<br>\r\n<table class=\"extaddr\"><tbody>\r\n";
|
s << "<b>"<< tr("Our external address") << ":</b>" << "<br>\r\n<table class=\"extaddr\"><tbody>\r\n";
|
||||||
for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
|
for (const auto& address : i2p::context.GetRouterInfo().GetAddresses())
|
||||||
{
|
{
|
||||||
s << "<tr>\r\n";
|
s << "<tr>\r\n<td>";
|
||||||
if (address->IsNTCP2 () && !address->IsPublishedNTCP2 ())
|
|
||||||
{
|
|
||||||
s << "<td>NTCP2";
|
|
||||||
if (address->host.is_v6 ()) s << "v6";
|
|
||||||
s << "</td><td>" << tr("supported") << "</td>\r\n</tr>\r\n";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch (address->transportStyle)
|
switch (address->transportStyle)
|
||||||
{
|
{
|
||||||
case i2p::data::RouterInfo::eTransportNTCP:
|
case i2p::data::RouterInfo::eTransportNTCP:
|
||||||
{
|
s << "NTCP2";
|
||||||
s << "<td>NTCP";
|
|
||||||
if (address->IsPublishedNTCP2 ()) s << "2";
|
|
||||||
if (address->host.is_v6 ()) s << "v6";
|
|
||||||
s << "</td>\r\n";
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case i2p::data::RouterInfo::eTransportSSU:
|
case i2p::data::RouterInfo::eTransportSSU:
|
||||||
{
|
s << "SSU";
|
||||||
s << "<td>SSU";
|
break;
|
||||||
if (address->host.is_v6 ())
|
case i2p::data::RouterInfo::eTransportSSU2:
|
||||||
s << "v6";
|
s << "SSU2";
|
||||||
s << "</td>\r\n";
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
s << "<td>" << tr("Unknown") << "</td>\r\n";
|
s << tr("Unknown");
|
||||||
}
|
}
|
||||||
s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n</tr>\r\n";
|
if (address->IsV6 ())
|
||||||
|
{
|
||||||
|
if (address->IsV4 ()) s << "v4";
|
||||||
|
s << "v6";
|
||||||
|
}
|
||||||
|
s << "</td>\r\n";
|
||||||
|
if (address->published)
|
||||||
|
s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s << "<td>" << tr("supported");
|
||||||
|
if (address->port)
|
||||||
|
s << " :" << address->port;
|
||||||
|
s << "</td>\r\n";
|
||||||
|
}
|
||||||
|
s << "</tr>\r\n";
|
||||||
}
|
}
|
||||||
s << "</tbody></table>\r\n";
|
s << "</tbody></table>\r\n";
|
||||||
}
|
}
|
||||||
s << "</div>\r\n</div>\r\n";
|
s << "</div>\r\n</div>\r\n";
|
||||||
if(outputFormat == OutputFormatEnum::forQtUi) {
|
if (outputFormat == OutputFormatEnum::forQtUi) {
|
||||||
s << "<br>";
|
s << "<br>";
|
||||||
}
|
}
|
||||||
s << "<b>" << tr("Routers") << ":</b> " << i2p::data::netdb.GetNumRouters () << " ";
|
s << "<b>" << tr("Routers") << ":</b> " << i2p::data::netdb.GetNumRouters () << " ";
|
||||||
@@ -355,7 +361,7 @@ namespace http {
|
|||||||
s << "<b>" << tr("Client Tunnels") << ":</b> " << std::to_string(clientTunnelCount) << " ";
|
s << "<b>" << tr("Client Tunnels") << ":</b> " << std::to_string(clientTunnelCount) << " ";
|
||||||
s << "<b>" << tr("Transit Tunnels") << ":</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n";
|
s << "<b>" << tr("Transit Tunnels") << ":</b> " << std::to_string(transitTunnelCount) << "<br>\r\n<br>\r\n";
|
||||||
|
|
||||||
if(outputFormat==OutputFormatEnum::forWebConsole) {
|
if (outputFormat==OutputFormatEnum::forWebConsole) {
|
||||||
bool httpproxy = i2p::client::context.GetHttpProxy () ? true : false;
|
bool httpproxy = i2p::client::context.GetHttpProxy () ? true : false;
|
||||||
bool socksproxy = i2p::client::context.GetSocksProxy () ? true : false;
|
bool socksproxy = i2p::client::context.GetSocksProxy () ? true : false;
|
||||||
bool bob = i2p::client::context.GetBOBCommandChannel () ? true : false;
|
bool bob = i2p::client::context.GetBOBCommandChannel () ? true : false;
|
||||||
@@ -416,7 +422,7 @@ namespace http {
|
|||||||
s << "</div>\r\n</div>\r\n";
|
s << "</div>\r\n</div>\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(dest->IsPublic())
|
if (dest->IsPublic() && token && !dest->IsEncryptedLeaseSet ())
|
||||||
{
|
{
|
||||||
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
|
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
|
||||||
auto base32 = dest->GetIdentHash ().ToBase32 ();
|
auto base32 = dest->GetIdentHash ().ToBase32 ();
|
||||||
@@ -430,7 +436,7 @@ namespace http {
|
|||||||
"</form>\r\n<small>" << tr("<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.") << "</small>\r\n</div>\r\n</div>\r\n<br>\r\n";
|
"</form>\r\n<small>" << tr("<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.") << "</small>\r\n</div>\r\n</div>\r\n<br>\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(dest->GetNumRemoteLeaseSets())
|
if (dest->GetNumRemoteLeaseSets())
|
||||||
{
|
{
|
||||||
s << "<div class='slide'><label for='slide-lease'><b>" << tr("LeaseSets") << ":</b> <i>" << dest->GetNumRemoteLeaseSets ()
|
s << "<div class='slide'><label for='slide-lease'><b>" << tr("LeaseSets") << ":</b> <i>" << dest->GetNumRemoteLeaseSets ()
|
||||||
<< "</i></label>\r\n<input type=\"checkbox\" id=\"slide-lease\" />\r\n<div class=\"slidecontent\">\r\n<table><thead><th>"<< tr("Address") << "</th><th>" << tr("Type") << "</th><th>" << tr("EncType") << "</th></thead><tbody class=\"tableitem\">";
|
<< "</i></label>\r\n<input type=\"checkbox\" id=\"slide-lease\" />\r\n<div class=\"slidecontent\">\r\n<table><thead><th>"<< tr("Address") << "</th><th>" << tr("Type") << "</th><th>" << tr("EncType") << "</th></thead><tbody class=\"tableitem\">";
|
||||||
@@ -446,8 +452,18 @@ namespace http {
|
|||||||
s << "<b>" << tr("Inbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
s << "<b>" << tr("Inbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
||||||
for (auto & it : pool->GetInboundTunnels ()) {
|
for (auto & it : pool->GetInboundTunnels ()) {
|
||||||
s << "<div class=\"listitem\">";
|
s << "<div class=\"listitem\">";
|
||||||
it->Print(s);
|
// for each tunnel hop if not zero-hop
|
||||||
if(it->LatencyIsKnown())
|
if (it->GetNumHops ())
|
||||||
|
{
|
||||||
|
it->VisitTunnelHops(
|
||||||
|
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
|
||||||
|
{
|
||||||
|
s << "⇒ " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " ";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
s << "⇒ " << it->GetTunnelID () << ":me";
|
||||||
|
if (it->LatencyIsKnown())
|
||||||
s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )";
|
s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )";
|
||||||
ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
|
ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
|
||||||
s << "</div>\r\n";
|
s << "</div>\r\n";
|
||||||
@@ -456,8 +472,18 @@ namespace http {
|
|||||||
s << "<b>" << tr("Outbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
s << "<b>" << tr("Outbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
||||||
for (auto & it : pool->GetOutboundTunnels ()) {
|
for (auto & it : pool->GetOutboundTunnels ()) {
|
||||||
s << "<div class=\"listitem\">";
|
s << "<div class=\"listitem\">";
|
||||||
it->Print(s);
|
s << it->GetTunnelID () << ":me ⇒";
|
||||||
if(it->LatencyIsKnown())
|
// for each tunnel hop if not zero-hop
|
||||||
|
if (it->GetNumHops ())
|
||||||
|
{
|
||||||
|
it->VisitTunnelHops(
|
||||||
|
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
|
||||||
|
{
|
||||||
|
s << " " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " ⇒";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (it->LatencyIsKnown())
|
||||||
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
||||||
ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ());
|
ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ());
|
||||||
s << "</div>\r\n";
|
s << "</div>\r\n";
|
||||||
@@ -630,8 +656,17 @@ namespace http {
|
|||||||
s << "<b>" << tr("Inbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
s << "<b>" << tr("Inbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
||||||
for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
|
for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
|
||||||
s << "<div class=\"listitem\">";
|
s << "<div class=\"listitem\">";
|
||||||
it->Print(s);
|
if (it->GetNumHops ())
|
||||||
if(it->LatencyIsKnown())
|
{
|
||||||
|
it->VisitTunnelHops(
|
||||||
|
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
|
||||||
|
{
|
||||||
|
s << "⇒ " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " ";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
s << "⇒ " << it->GetTunnelID () << ":me";
|
||||||
|
if (it->LatencyIsKnown())
|
||||||
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
||||||
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ());
|
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ());
|
||||||
s << "</div>\r\n";
|
s << "</div>\r\n";
|
||||||
@@ -640,8 +675,18 @@ namespace http {
|
|||||||
s << "<b>" << tr("Outbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
s << "<b>" << tr("Outbound tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
||||||
for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
|
for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
|
||||||
s << "<div class=\"listitem\">";
|
s << "<div class=\"listitem\">";
|
||||||
it->Print(s);
|
s << it->GetTunnelID () << ":me ⇒";
|
||||||
if(it->LatencyIsKnown())
|
// for each tunnel hop if not zero-hop
|
||||||
|
if (it->GetNumHops ())
|
||||||
|
{
|
||||||
|
it->VisitTunnelHops(
|
||||||
|
[&s](std::shared_ptr<const i2p::data::IdentityEx> hopIdent)
|
||||||
|
{
|
||||||
|
s << " " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " ⇒";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (it->LatencyIsKnown())
|
||||||
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
|
||||||
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ());
|
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ());
|
||||||
s << "</div>\r\n";
|
s << "</div>\r\n";
|
||||||
@@ -668,11 +713,12 @@ namespace http {
|
|||||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
|
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
|
||||||
else
|
else
|
||||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
|
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
|
||||||
#elif defined(WIN32_APP)
|
/*#elif defined(WIN32_APP)
|
||||||
if (i2p::util::DaemonWin32::Instance().isGraceful)
|
if (i2p::util::DaemonWin32::Instance().isGraceful)
|
||||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
|
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
|
||||||
else
|
else
|
||||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
|
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
|
||||||
|
*/
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">" << tr("Force shutdown") << "</a><br><br>\r\n";
|
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">" << tr("Force shutdown") << "</a><br><br>\r\n";
|
||||||
@@ -713,7 +759,7 @@ namespace http {
|
|||||||
|
|
||||||
void ShowTransitTunnels (std::stringstream& s)
|
void ShowTransitTunnels (std::stringstream& s)
|
||||||
{
|
{
|
||||||
if(i2p::tunnel::tunnels.CountTransitTunnels())
|
if (i2p::tunnel::tunnels.CountTransitTunnels())
|
||||||
{
|
{
|
||||||
s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
||||||
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
|
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
|
||||||
@@ -736,7 +782,7 @@ namespace http {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename Sessions>
|
template<typename Sessions>
|
||||||
static void ShowNTCPTransports (std::stringstream& s, const Sessions& sessions, const std::string name)
|
static void ShowTransportSessions (std::stringstream& s, const Sessions& sessions, const std::string name)
|
||||||
{
|
{
|
||||||
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
|
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
|
||||||
for (const auto& it: sessions )
|
for (const auto& it: sessions )
|
||||||
@@ -786,7 +832,7 @@ namespace http {
|
|||||||
{
|
{
|
||||||
auto sessions = ntcp2Server->GetNTCP2Sessions ();
|
auto sessions = ntcp2Server->GetNTCP2Sessions ();
|
||||||
if (!sessions.empty ())
|
if (!sessions.empty ())
|
||||||
ShowNTCPTransports (s, sessions, "NTCP2");
|
ShowTransportSessions (s, sessions, "NTCP2");
|
||||||
}
|
}
|
||||||
auto ssuServer = i2p::transport::transports.GetSSUServer ();
|
auto ssuServer = i2p::transport::transports.GetSSUServer ();
|
||||||
if (ssuServer)
|
if (ssuServer)
|
||||||
@@ -828,6 +874,13 @@ namespace http {
|
|||||||
s << "</div>\r\n</div>\r\n";
|
s << "</div>\r\n</div>\r\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto ssu2Server = i2p::transport::transports.GetSSU2Server ();
|
||||||
|
if (ssu2Server)
|
||||||
|
{
|
||||||
|
auto sessions = ssu2Server->GetSSU2Sessions ();
|
||||||
|
if (!sessions.empty ())
|
||||||
|
ShowTransportSessions (s, sessions, "SSU2");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowSAMSessions (std::stringstream& s)
|
void ShowSAMSessions (std::stringstream& s)
|
||||||
@@ -840,7 +893,7 @@ namespace http {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sam->GetSessions ().size ())
|
if (sam->GetSessions ().size ())
|
||||||
{
|
{
|
||||||
s << "<b>" << tr("SAM sessions") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
s << "<b>" << tr("SAM sessions") << ":</b><br>\r\n<div class=\"list\">\r\n";
|
||||||
for (auto& it: sam->GetSessions ())
|
for (auto& it: sam->GetSessions ())
|
||||||
@@ -970,8 +1023,8 @@ namespace http {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
|
HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket, HTTPServer& server):
|
||||||
m_Socket (socket), m_BufferLen (0), expected_host(hostname)
|
m_Socket (socket), m_Server (server), m_BufferLen (0), expected_host(hostname)
|
||||||
{
|
{
|
||||||
/* cache options */
|
/* cache options */
|
||||||
i2p::config::GetOption("http.auth", needAuth);
|
i2p::config::GetOption("http.auth", needAuth);
|
||||||
@@ -1057,6 +1110,7 @@ namespace http {
|
|||||||
SendReply(res, content);
|
SendReply(res, content);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool strictheaders;
|
bool strictheaders;
|
||||||
i2p::config::GetOption("http.strictheaders", strictheaders);
|
i2p::config::GetOption("http.strictheaders", strictheaders);
|
||||||
if (strictheaders)
|
if (strictheaders)
|
||||||
@@ -1079,6 +1133,7 @@ namespace http {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTML head start
|
// HTML head start
|
||||||
ShowPageHead (s);
|
ShowPageHead (s);
|
||||||
if (req.uri.find("page=") != std::string::npos) {
|
if (req.uri.find("page=") != std::string::npos) {
|
||||||
@@ -1190,28 +1245,33 @@ namespace http {
|
|||||||
else if (cmd == HTTP_COMMAND_SHUTDOWN_START)
|
else if (cmd == HTTP_COMMAND_SHUTDOWN_START)
|
||||||
{
|
{
|
||||||
i2p::context.SetAcceptsTunnels (false);
|
i2p::context.SetAcceptsTunnels (false);
|
||||||
|
// TODO: rewrite timer access
|
||||||
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
||||||
Daemon.gracefulShutdownInterval = 10*60;
|
if (m_DaemonGracefulTimer) m_DaemonGracefulTimer = 10 * 60;
|
||||||
#elif defined(WIN32_APP)
|
/*#elif defined(WIN32_APP)
|
||||||
i2p::win32::GracefulShutdown ();
|
i2p::win32::GracefulShutdown ();
|
||||||
|
*/
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL)
|
else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL)
|
||||||
{
|
{
|
||||||
i2p::context.SetAcceptsTunnels (true);
|
i2p::context.SetAcceptsTunnels (true);
|
||||||
|
// TODO: rewrite timer access
|
||||||
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
||||||
Daemon.gracefulShutdownInterval = 0;
|
if (m_DaemonGracefulTimer) m_DaemonGracefulTimer = 0;
|
||||||
#elif defined(WIN32_APP)
|
/*#elif defined(WIN32_APP)
|
||||||
i2p::win32::StopGracefulShutdown ();
|
i2p::win32::StopGracefulShutdown ();
|
||||||
|
*/
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW)
|
else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW)
|
||||||
{
|
{
|
||||||
#ifndef WIN32_APP
|
// TODO: rewrite stop command access
|
||||||
Daemon.running = false;
|
//#ifndef WIN32_APP
|
||||||
#else
|
m_Server.GetDaemonStop();
|
||||||
i2p::win32::StopWin32App ();
|
//#else
|
||||||
#endif
|
// i2p::win32::StopWin32App ();
|
||||||
|
//#endif
|
||||||
}
|
}
|
||||||
else if (cmd == HTTP_COMMAND_LOGLEVEL)
|
else if (cmd == HTTP_COMMAND_LOGLEVEL)
|
||||||
{
|
{
|
||||||
@@ -1231,7 +1291,7 @@ namespace http {
|
|||||||
{
|
{
|
||||||
if (dest)
|
if (dest)
|
||||||
{
|
{
|
||||||
if(dest->DeleteStream (streamID))
|
if (dest->DeleteStream (streamID))
|
||||||
s << "<b>" << tr("SUCCESS") << "</b>: " << tr("Stream closed") << "<br>\r\n<br>\r\n";
|
s << "<b>" << tr("SUCCESS") << "</b>: " << tr("Stream closed") << "<br>\r\n<br>\r\n";
|
||||||
else
|
else
|
||||||
s << "<b>" << tr("ERROR") << "</b>: " << tr("Stream not found or already was closed") << "<br>\r\n<br>\r\n";
|
s << "<b>" << tr("ERROR") << "</b>: " << tr("Stream not found or already was closed") << "<br>\r\n<br>\r\n";
|
||||||
@@ -1437,7 +1497,7 @@ namespace http {
|
|||||||
CreateConnection(newSocket);
|
CreateConnection(newSocket);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(newSocket) newSocket->close();
|
if (newSocket) newSocket->close();
|
||||||
LogPrint(eLogError, "HTTP Server: Error handling accept: ", ecode.message());
|
LogPrint(eLogError, "HTTP Server: Error handling accept: ", ecode.message());
|
||||||
}
|
}
|
||||||
Accept ();
|
Accept ();
|
||||||
@@ -1445,7 +1505,7 @@ namespace http {
|
|||||||
|
|
||||||
void HTTPServer::CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
|
void HTTPServer::CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
|
||||||
{
|
{
|
||||||
auto conn = std::make_shared<HTTPConnection> (m_Hostname, newSocket);
|
auto conn = std::make_shared<HTTPConnection> (m_Hostname, newSocket, *this);
|
||||||
conn->Receive ();
|
conn->Receive ();
|
||||||
}
|
}
|
||||||
} // http
|
} // http
|
||||||
@@ -25,11 +25,13 @@ namespace http
|
|||||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||||
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
|
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
|
||||||
|
|
||||||
|
class HTTPServer;
|
||||||
|
|
||||||
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
|
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HTTPConnection (std::string serverhost, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
HTTPConnection (std::string serverhost, std::shared_ptr<boost::asio::ip::tcp::socket> socket, HTTPServer& server);
|
||||||
void Receive ();
|
void Receive ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -48,6 +50,7 @@ namespace http
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
||||||
|
HTTPServer& m_Server;
|
||||||
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
||||||
size_t m_BufferLen;
|
size_t m_BufferLen;
|
||||||
std::string m_SendBuffer;
|
std::string m_SendBuffer;
|
||||||
@@ -69,6 +72,13 @@ namespace http
|
|||||||
void Start ();
|
void Start ();
|
||||||
void Stop ();
|
void Stop ();
|
||||||
|
|
||||||
|
typedef std::function<void ()> DaemonStop;
|
||||||
|
void SetDaemonStop (const DaemonStop& f) { m_DaemonStop = f; };
|
||||||
|
DaemonStop GetDaemonStop () { return m_DaemonStop; };
|
||||||
|
|
||||||
|
void SetDaemonGracefulTimer (const int& f) { m_DaemonGracefulTimer = f; };
|
||||||
|
int GetDaemonGracefulTimer () { return m_DaemonGracefulTimer; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void Run ();
|
void Run ();
|
||||||
@@ -85,6 +95,11 @@ namespace http
|
|||||||
boost::asio::io_service::work m_Work;
|
boost::asio::io_service::work m_Work;
|
||||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||||
std::string m_Hostname;
|
std::string m_Hostname;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
DaemonStop m_DaemonStop;
|
||||||
|
int m_DaemonGracefulTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
//all the below functions are also used by Qt GUI, see mainwindow.cpp -> getStatusPageHtml
|
//all the below functions are also used by Qt GUI, see mainwindow.cpp -> getStatusPageHtml
|
||||||
@@ -33,7 +33,6 @@ namespace http
|
|||||||
|
|
||||||
// bundled style sheet
|
// bundled style sheet
|
||||||
const std::string internalCSS =
|
const std::string internalCSS =
|
||||||
"<style>\r\n"
|
|
||||||
":root { --main-bg-color: #fafafa; --main-text-color: #103456; --main-link-color: #894c84; --main-link-hover-color: #fafafa; }\r\n"
|
":root { --main-bg-color: #fafafa; --main-text-color: #103456; --main-link-color: #894c84; --main-link-hover-color: #fafafa; }\r\n"
|
||||||
"@media (prefers-color-scheme: dark) { :root { --main-bg-color: #242424; --main-text-color: #17ab5c; --main-link-color: #bf64b7; --main-link-hover-color: #000000; } }\r\n"
|
"@media (prefers-color-scheme: dark) { :root { --main-bg-color: #242424; --main-text-color: #17ab5c; --main-link-color: #bf64b7; --main-link-hover-color: #000000; } }\r\n"
|
||||||
"body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: var(--main-bg-color); color: var(--main-text-color); }\r\n"
|
"body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: var(--main-bg-color); color: var(--main-text-color); }\r\n"
|
||||||
@@ -85,12 +84,127 @@ namespace http
|
|||||||
" border-radius: 5px; font-size: 12px; }\r\n"
|
" border-radius: 5px; font-size: 12px; }\r\n"
|
||||||
" button[type=submit] { padding: 5px 15px; background: transparent; border: 2px solid var(--main-link-color); cursor: pointer;\r\n"
|
" button[type=submit] { padding: 5px 15px; background: transparent; border: 2px solid var(--main-link-color); cursor: pointer;\r\n"
|
||||||
" border-radius: 5px; position: relative; height: 36px; display: -webkit-inline-box; margin-top: 10px; }\r\n"
|
" border-radius: 5px; position: relative; height: 36px; display: -webkit-inline-box; margin-top: 10px; }\r\n"
|
||||||
"}\r\n"
|
"}\r\n";
|
||||||
"</style>\r\n";
|
|
||||||
|
|
||||||
// for external style sheet
|
// for external style sheet
|
||||||
std::string externalCSS;
|
std::string externalCSS;
|
||||||
|
|
||||||
|
const std::string pageBase =
|
||||||
|
"<!DOCTYPE html> \
|
||||||
|
<html lang=\"{{ langCode }}\"> \
|
||||||
|
<head> \
|
||||||
|
{% block head %} \
|
||||||
|
<meta charset=\"UTF-8\"> \
|
||||||
|
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> \
|
||||||
|
<link rel=\"shortcut icon\" href=\"{% getFavicon() %}\"> \
|
||||||
|
<title>{% block title %}{% endblock %} - Purple I2P Webconsole</title> \
|
||||||
|
<style>{% getStyles() %}</style> \
|
||||||
|
{% endblock %} \
|
||||||
|
</head> \
|
||||||
|
<body> \
|
||||||
|
<div class=\"header\">{% tr(\"<b>i2pd</b> webconsole\") %}</div> \
|
||||||
|
<div class=\"wrapper\"> \
|
||||||
|
<div class=\"menu\"> \
|
||||||
|
<a href=\"{{ webroot }}\">{% tr(\"Main page\") %}</a><br><br> \
|
||||||
|
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_COMMANDS\") %}\">{% tr(\"Router commands\") %}</a><br> \
|
||||||
|
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_LOCAL_DESTINATIONS\") %}\">{% tr(\"Local Destinations\") %}</a><br> \
|
||||||
|
{% if isFloodfill %} \
|
||||||
|
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_LEASESETS\") %}\">{% tr(\"LeaseSets\") %}</a><br> \
|
||||||
|
{% endif %} \
|
||||||
|
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_TUNNELS\") %}\">{% tr(\"Tunnels\") %}</a><br> \
|
||||||
|
{% if (acceptingTunnels || transitTunnels) %} \
|
||||||
|
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_TRANSIT_TUNNELS\") %}\">{% tr(\"Transit Tunnels\") %}</a><br> \
|
||||||
|
{% endif %} \
|
||||||
|
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_TRANSPORTS\") %}\">{% tr(\"Transports\") %}</a><br> \
|
||||||
|
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_I2P_TUNNELS\") %}\">{% tr(\"I2P tunnels\") %}</a><br> \
|
||||||
|
{% if samEnabled %} \
|
||||||
|
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_SAM_SESSIONS\") %}\">{% tr(\"SAM sessions\") %}</a><br> \
|
||||||
|
{% endif %} \
|
||||||
|
</div> \
|
||||||
|
<div class=\"content\">{% block content %}{% endblock %}</div> \
|
||||||
|
</div> \
|
||||||
|
</body> \
|
||||||
|
</html>";
|
||||||
|
|
||||||
|
const std::string pageMain =
|
||||||
|
"{% extends \"base.html\" %} \
|
||||||
|
{% block title %}Main{% endblock %} \
|
||||||
|
{% block content %} \
|
||||||
|
<b>{% tr(\"Uptime\") %}:</b> {% getUptime() %}<br> \
|
||||||
|
<b>{% tr(\"Network status\") %}:</b> {% getNetworkStatus(false) %}<br> \
|
||||||
|
{% if supportsV6 %} \
|
||||||
|
<b>{% tr(\"Network status v6\") %}:</b> {% getNetworkStatus(true) %}<br> \
|
||||||
|
{% endif %} \
|
||||||
|
{% if gracefulShutdown %} \
|
||||||
|
<b>{% tr(\"Stopping in\") %}:</b> {% getShutdownTimer() %}<br> \
|
||||||
|
{% endif %} \
|
||||||
|
<b>{% tr(\"Tunnel creation success rate\") %}:</b> {% getSuccessRate() %}%<br> \
|
||||||
|
<b>{% tr(\"Received\") %}:</b> {% getInBytes() %} ({% getInBW() %} {% tr(/* tr: Kibibit/s */ \"KiB/s\") %})<br> \
|
||||||
|
<b>{% tr(\"Sent\") %}:</b> {% getOutBytes() %} ({% getOutBW() %} {% tr(/* tr: Kibibit/s */ \"KiB/s\") %})<br> \
|
||||||
|
<b>{% tr(\"Transit\") %}:</b> {% getTransitBytes() %} ({% getTransitBW() %} {% tr(/* tr: Kibibit/s */ \"KiB/s\") %})<br> \
|
||||||
|
<b>{% tr(\"Data path\") %}:</b> {% getDataPath() %}<br> \
|
||||||
|
{% if notQt || withHiddenContent %} \
|
||||||
|
<div class=\"slide\"> \
|
||||||
|
<label for=\"slide-info\">{% tr(\"Hidden content. Press on text to see.\") %}</label> \
|
||||||
|
<input type=\"checkbox\" id=\"slide-info\" /> \
|
||||||
|
<div class=\"slidecontent\"> \
|
||||||
|
<b>{% tr(\"Router Ident\") %}:</b> {% getRI() %}<br> \
|
||||||
|
{% if lenght(family) %} \
|
||||||
|
<b>{% tr(\"Family\") %}:</b> {{ family }}<br> \
|
||||||
|
{% endif %} \
|
||||||
|
<b>{% tr(\"Router Caps\") %}:</b> {% getCaps() %}<br> \
|
||||||
|
<b>{% tr(\"Version\") %}:</b> {{ version }}<br> \
|
||||||
|
<b>{% tr(\"Our external address\") %}:</b><br> \
|
||||||
|
<table class=\"extaddr\"> \
|
||||||
|
<tbody> \
|
||||||
|
{% for type, address in addresses %} \
|
||||||
|
<tr><td>{{ type }}</td><td>{{ address }}</td></tr> \
|
||||||
|
{% endfor %} \
|
||||||
|
</tbody> \
|
||||||
|
</table> \
|
||||||
|
</div> \
|
||||||
|
</div> \
|
||||||
|
{% else %} \
|
||||||
|
<br> \
|
||||||
|
{% endif %} \
|
||||||
|
<b>{% tr(\"Routers\") %}:</b> {% getNumRouter() %} \
|
||||||
|
<b>{% tr(\"Floodfills\") %}:</b> {% getNumFloodfills() %} \
|
||||||
|
<b>{% tr(\"LeaseSets\") %}:</b> {% getNumLeaseSets() %}<br> \
|
||||||
|
<b>{% tr(\"Client Tunnels\") %}:</b> {% getClientTunnelsCount() %} \
|
||||||
|
<b>{% tr(\"Transit Tunnels\") %}:</b> {% getTransitTunnelsCount() %}<br> \
|
||||||
|
<br> \
|
||||||
|
{% if notQt == false %} \
|
||||||
|
<table class=\"services\"> \
|
||||||
|
<caption>{% tr(\"Services\") %}</caption> \
|
||||||
|
<tbody> \
|
||||||
|
<tr> \
|
||||||
|
<td>HTTP {% tr(\"Proxy\") %}</td> \
|
||||||
|
<td class=\"{% if httpproxy %}enabled{% else %}disabled{% endif %}\">{% if httpproxy %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
||||||
|
</tr> \
|
||||||
|
<tr> \
|
||||||
|
<td>SOCKS {% tr(\"Proxy\") %}</td> \
|
||||||
|
<td class=\"{% if socksproxy %}enabled{% else %}disabled{% endif %}\">{% if socksproxy %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
||||||
|
</tr> \
|
||||||
|
<tr> \
|
||||||
|
<td>BOB</td> \
|
||||||
|
<td class=\"{% if bob %}enabled{% else %}disabled{% endif %}\">{% if bob %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
||||||
|
</tr> \
|
||||||
|
<tr> \
|
||||||
|
<td>SAM</td> \
|
||||||
|
<td class=\"{% if sam %}enabled{% else %}disabled{% endif %}\">{% if sam %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
||||||
|
</tr> \
|
||||||
|
<tr> \
|
||||||
|
<td>I2CP</td> \
|
||||||
|
<td class=\"{% if i2cp %}enabled{% else %}disabled{% endif %}\">{% if i2cp %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
||||||
|
</tr> \
|
||||||
|
<tr> \
|
||||||
|
<td>I2PControl</td> \
|
||||||
|
<td class=\"{% if i2pcontrol %}enabled{% else %}disabled{% endif %}\">{% if i2pcontrol %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
||||||
|
</tr> \
|
||||||
|
</tbody> \
|
||||||
|
</table> \
|
||||||
|
{% endblock %}";
|
||||||
|
|
||||||
} // http
|
} // http
|
||||||
} // i2p
|
} // i2p
|
||||||
|
|
||||||
81
libi2pd_webconsole/inja/config.hpp
Normal file
81
libi2pd_webconsole/inja/config.hpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#ifndef INCLUDE_INJA_CONFIG_HPP_
|
||||||
|
#define INCLUDE_INJA_CONFIG_HPP_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "template.hpp"
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Class for lexer configuration.
|
||||||
|
*/
|
||||||
|
struct LexerConfig {
|
||||||
|
std::string statement_open {"{%"};
|
||||||
|
std::string statement_open_no_lstrip {"{%+"};
|
||||||
|
std::string statement_open_force_lstrip {"{%-"};
|
||||||
|
std::string statement_close {"%}"};
|
||||||
|
std::string statement_close_force_rstrip {"-%}"};
|
||||||
|
std::string line_statement {"##"};
|
||||||
|
std::string expression_open {"{{"};
|
||||||
|
std::string expression_open_force_lstrip {"{{-"};
|
||||||
|
std::string expression_close {"}}"};
|
||||||
|
std::string expression_close_force_rstrip {"-}}"};
|
||||||
|
std::string comment_open {"{#"};
|
||||||
|
std::string comment_open_force_lstrip {"{#-"};
|
||||||
|
std::string comment_close {"#}"};
|
||||||
|
std::string comment_close_force_rstrip {"-#}"};
|
||||||
|
std::string open_chars {"#{"};
|
||||||
|
|
||||||
|
bool trim_blocks {false};
|
||||||
|
bool lstrip_blocks {false};
|
||||||
|
|
||||||
|
void update_open_chars() {
|
||||||
|
open_chars = "";
|
||||||
|
if (open_chars.find(line_statement[0]) == std::string::npos) {
|
||||||
|
open_chars += line_statement[0];
|
||||||
|
}
|
||||||
|
if (open_chars.find(statement_open[0]) == std::string::npos) {
|
||||||
|
open_chars += statement_open[0];
|
||||||
|
}
|
||||||
|
if (open_chars.find(statement_open_no_lstrip[0]) == std::string::npos) {
|
||||||
|
open_chars += statement_open_no_lstrip[0];
|
||||||
|
}
|
||||||
|
if (open_chars.find(statement_open_force_lstrip[0]) == std::string::npos) {
|
||||||
|
open_chars += statement_open_force_lstrip[0];
|
||||||
|
}
|
||||||
|
if (open_chars.find(expression_open[0]) == std::string::npos) {
|
||||||
|
open_chars += expression_open[0];
|
||||||
|
}
|
||||||
|
if (open_chars.find(expression_open_force_lstrip[0]) == std::string::npos) {
|
||||||
|
open_chars += expression_open_force_lstrip[0];
|
||||||
|
}
|
||||||
|
if (open_chars.find(comment_open[0]) == std::string::npos) {
|
||||||
|
open_chars += comment_open[0];
|
||||||
|
}
|
||||||
|
if (open_chars.find(comment_open_force_lstrip[0]) == std::string::npos) {
|
||||||
|
open_chars += comment_open_force_lstrip[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Class for parser configuration.
|
||||||
|
*/
|
||||||
|
struct ParserConfig {
|
||||||
|
bool search_included_templates_in_files {true};
|
||||||
|
|
||||||
|
std::function<Template(const std::string&, const std::string&)> include_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Class for render configuration.
|
||||||
|
*/
|
||||||
|
struct RenderConfig {
|
||||||
|
bool throw_at_missing_includes {true};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_CONFIG_HPP_
|
||||||
237
libi2pd_webconsole/inja/environment.hpp
Normal file
237
libi2pd_webconsole/inja/environment.hpp
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_
|
||||||
|
#define INCLUDE_INJA_ENVIRONMENT_HPP_
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "function_storage.hpp"
|
||||||
|
#include "parser.hpp"
|
||||||
|
#include "renderer.hpp"
|
||||||
|
#include "template.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Class for changing the configuration.
|
||||||
|
*/
|
||||||
|
class Environment {
|
||||||
|
LexerConfig lexer_config;
|
||||||
|
ParserConfig parser_config;
|
||||||
|
RenderConfig render_config;
|
||||||
|
|
||||||
|
FunctionStorage function_storage;
|
||||||
|
TemplateStorage template_storage;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string input_path;
|
||||||
|
std::string output_path;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Environment(): Environment("") {}
|
||||||
|
|
||||||
|
explicit Environment(const std::string& global_path): input_path(global_path), output_path(global_path) {}
|
||||||
|
|
||||||
|
Environment(const std::string& input_path, const std::string& output_path): input_path(input_path), output_path(output_path) {}
|
||||||
|
|
||||||
|
/// Sets the opener and closer for template statements
|
||||||
|
void set_statement(const std::string& open, const std::string& close) {
|
||||||
|
lexer_config.statement_open = open;
|
||||||
|
lexer_config.statement_open_no_lstrip = open + "+";
|
||||||
|
lexer_config.statement_open_force_lstrip = open + "-";
|
||||||
|
lexer_config.statement_close = close;
|
||||||
|
lexer_config.statement_close_force_rstrip = "-" + close;
|
||||||
|
lexer_config.update_open_chars();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the opener for template line statements
|
||||||
|
void set_line_statement(const std::string& open) {
|
||||||
|
lexer_config.line_statement = open;
|
||||||
|
lexer_config.update_open_chars();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the opener and closer for template expressions
|
||||||
|
void set_expression(const std::string& open, const std::string& close) {
|
||||||
|
lexer_config.expression_open = open;
|
||||||
|
lexer_config.expression_open_force_lstrip = open + "-";
|
||||||
|
lexer_config.expression_close = close;
|
||||||
|
lexer_config.expression_close_force_rstrip = "-" + close;
|
||||||
|
lexer_config.update_open_chars();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the opener and closer for template comments
|
||||||
|
void set_comment(const std::string& open, const std::string& close) {
|
||||||
|
lexer_config.comment_open = open;
|
||||||
|
lexer_config.comment_open_force_lstrip = open + "-";
|
||||||
|
lexer_config.comment_close = close;
|
||||||
|
lexer_config.comment_close_force_rstrip = "-" + close;
|
||||||
|
lexer_config.update_open_chars();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets whether to remove the first newline after a block
|
||||||
|
void set_trim_blocks(bool trim_blocks) {
|
||||||
|
lexer_config.trim_blocks = trim_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets whether to strip the spaces and tabs from the start of a line to a block
|
||||||
|
void set_lstrip_blocks(bool lstrip_blocks) {
|
||||||
|
lexer_config.lstrip_blocks = lstrip_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the element notation syntax
|
||||||
|
void set_search_included_templates_in_files(bool search_in_files) {
|
||||||
|
parser_config.search_included_templates_in_files = search_in_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets whether a missing include will throw an error
|
||||||
|
void set_throw_at_missing_includes(bool will_throw) {
|
||||||
|
render_config.throw_at_missing_includes = will_throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
Template parse(std::string_view input) {
|
||||||
|
Parser parser(parser_config, lexer_config, template_storage, function_storage);
|
||||||
|
return parser.parse(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
Template parse_template(const std::string& filename) {
|
||||||
|
Parser parser(parser_config, lexer_config, template_storage, function_storage);
|
||||||
|
auto result = Template(parser.load_file(input_path + static_cast<std::string>(filename)));
|
||||||
|
parser.parse_into_template(result, input_path + static_cast<std::string>(filename));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Template parse_file(const std::string& filename) {
|
||||||
|
return parse_template(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string render(std::string_view input, const json& data) {
|
||||||
|
return render(parse(input), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string render(const Template& tmpl, const json& data) {
|
||||||
|
std::stringstream os;
|
||||||
|
render_to(os, tmpl, data);
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string render_file(const std::string& filename, const json& data) {
|
||||||
|
return render(parse_template(filename), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) {
|
||||||
|
const json data = load_json(filename_data);
|
||||||
|
return render_file(filename, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const std::string& filename, const json& data, const std::string& filename_out) {
|
||||||
|
std::ofstream file(output_path + filename_out);
|
||||||
|
file << render_file(filename, data);
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const Template& temp, const json& data, const std::string& filename_out) {
|
||||||
|
std::ofstream file(output_path + filename_out);
|
||||||
|
file << render(temp, data);
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) {
|
||||||
|
const json data = load_json(filename_data);
|
||||||
|
write(filename, data, filename_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) {
|
||||||
|
const json data = load_json(filename_data);
|
||||||
|
write(temp, data, filename_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) {
|
||||||
|
Renderer(render_config, template_storage, function_storage).render_to(os, tmpl, data);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string load_file(const std::string& filename) {
|
||||||
|
Parser parser(parser_config, lexer_config, template_storage, function_storage);
|
||||||
|
return parser.load_file(input_path + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
json load_json(const std::string& filename) {
|
||||||
|
std::ifstream file;
|
||||||
|
file.open(input_path + filename);
|
||||||
|
if (file.fail()) {
|
||||||
|
INJA_THROW(FileError("failed accessing file at '" + input_path + filename + "'"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return json::parse(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Adds a variadic callback
|
||||||
|
*/
|
||||||
|
void add_callback(const std::string& name, const CallbackFunction& callback) {
|
||||||
|
add_callback(name, -1, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Adds a variadic void callback
|
||||||
|
*/
|
||||||
|
void add_void_callback(const std::string& name, const VoidCallbackFunction& callback) {
|
||||||
|
add_void_callback(name, -1, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Adds a callback with given number or arguments
|
||||||
|
*/
|
||||||
|
void add_callback(const std::string& name, int num_args, const CallbackFunction& callback) {
|
||||||
|
function_storage.add_callback(name, num_args, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Adds a void callback with given number or arguments
|
||||||
|
*/
|
||||||
|
void add_void_callback(const std::string& name, int num_args, const VoidCallbackFunction& callback) {
|
||||||
|
function_storage.add_callback(name, num_args, [callback](Arguments& args) {
|
||||||
|
callback(args);
|
||||||
|
return json();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Includes a template with a given name into the environment.
|
||||||
|
* Then, a template can be rendered in another template using the
|
||||||
|
* include "<name>" syntax.
|
||||||
|
*/
|
||||||
|
void include_template(const std::string& name, const Template& tmpl) {
|
||||||
|
template_storage[name] = tmpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Sets a function that is called when an included file is not found
|
||||||
|
*/
|
||||||
|
void set_include_callback(const std::function<Template(const std::string&, const std::string&)>& callback) {
|
||||||
|
parser_config.include_callback = callback;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief render with default settings to a string
|
||||||
|
*/
|
||||||
|
inline std::string render(std::string_view input, const json& data) {
|
||||||
|
return Environment().render(input, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief render with default settings to the given output stream
|
||||||
|
*/
|
||||||
|
inline void render_to(std::ostream& os, std::string_view input, const json& data) {
|
||||||
|
Environment env;
|
||||||
|
env.render_to(os, env.parse(input), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_ENVIRONMENT_HPP_
|
||||||
47
libi2pd_webconsole/inja/exceptions.hpp
Normal file
47
libi2pd_webconsole/inja/exceptions.hpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#ifndef INCLUDE_INJA_EXCEPTIONS_HPP_
|
||||||
|
#define INCLUDE_INJA_EXCEPTIONS_HPP_
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
struct SourceLocation {
|
||||||
|
size_t line;
|
||||||
|
size_t column;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InjaError : public std::runtime_error {
|
||||||
|
const std::string type;
|
||||||
|
const std::string message;
|
||||||
|
|
||||||
|
const SourceLocation location;
|
||||||
|
|
||||||
|
explicit InjaError(const std::string& type, const std::string& message)
|
||||||
|
: std::runtime_error("[inja.exception." + type + "] " + message), type(type), message(message), location({0, 0}) {}
|
||||||
|
|
||||||
|
explicit InjaError(const std::string& type, const std::string& message, SourceLocation location)
|
||||||
|
: std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" + std::to_string(location.column) + ") " + message),
|
||||||
|
type(type), message(message), location(location) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParserError : public InjaError {
|
||||||
|
explicit ParserError(const std::string& message, SourceLocation location): InjaError("parser_error", message, location) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderError : public InjaError {
|
||||||
|
explicit RenderError(const std::string& message, SourceLocation location): InjaError("render_error", message, location) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FileError : public InjaError {
|
||||||
|
explicit FileError(const std::string& message): InjaError("file_error", message) {}
|
||||||
|
explicit FileError(const std::string& message, SourceLocation location): InjaError("file_error", message, location) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DataError : public InjaError {
|
||||||
|
explicit DataError(const std::string& message, SourceLocation location): InjaError("data_error", message, location) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_EXCEPTIONS_HPP_
|
||||||
139
libi2pd_webconsole/inja/function_storage.hpp
Normal file
139
libi2pd_webconsole/inja/function_storage.hpp
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_
|
||||||
|
#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
using Arguments = std::vector<const json*>;
|
||||||
|
using CallbackFunction = std::function<json(Arguments& args)>;
|
||||||
|
using VoidCallbackFunction = std::function<void(Arguments& args)>;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Class for builtin functions and user-defined callbacks.
|
||||||
|
*/
|
||||||
|
class FunctionStorage {
|
||||||
|
public:
|
||||||
|
enum class Operation {
|
||||||
|
Not,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
In,
|
||||||
|
Equal,
|
||||||
|
NotEqual,
|
||||||
|
Greater,
|
||||||
|
GreaterEqual,
|
||||||
|
Less,
|
||||||
|
LessEqual,
|
||||||
|
Add,
|
||||||
|
Subtract,
|
||||||
|
Multiplication,
|
||||||
|
Division,
|
||||||
|
Power,
|
||||||
|
Modulo,
|
||||||
|
AtId,
|
||||||
|
At,
|
||||||
|
Default,
|
||||||
|
DivisibleBy,
|
||||||
|
Even,
|
||||||
|
Exists,
|
||||||
|
ExistsInObject,
|
||||||
|
First,
|
||||||
|
Float,
|
||||||
|
Int,
|
||||||
|
IsArray,
|
||||||
|
IsBoolean,
|
||||||
|
IsFloat,
|
||||||
|
IsInteger,
|
||||||
|
IsNumber,
|
||||||
|
IsObject,
|
||||||
|
IsString,
|
||||||
|
Last,
|
||||||
|
Length,
|
||||||
|
Lower,
|
||||||
|
Max,
|
||||||
|
Min,
|
||||||
|
Odd,
|
||||||
|
Range,
|
||||||
|
Round,
|
||||||
|
Sort,
|
||||||
|
Upper,
|
||||||
|
Super,
|
||||||
|
Join,
|
||||||
|
Callback,
|
||||||
|
ParenLeft,
|
||||||
|
ParenRight,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FunctionData {
|
||||||
|
explicit FunctionData(const Operation& op, const CallbackFunction& cb = CallbackFunction {}): operation(op), callback(cb) {}
|
||||||
|
const Operation operation;
|
||||||
|
const CallbackFunction callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int VARIADIC {-1};
|
||||||
|
|
||||||
|
std::map<std::pair<std::string, int>, FunctionData> function_storage = {
|
||||||
|
{std::make_pair("at", 2), FunctionData {Operation::At}},
|
||||||
|
{std::make_pair("default", 2), FunctionData {Operation::Default}},
|
||||||
|
{std::make_pair("divisibleBy", 2), FunctionData {Operation::DivisibleBy}},
|
||||||
|
{std::make_pair("even", 1), FunctionData {Operation::Even}},
|
||||||
|
{std::make_pair("exists", 1), FunctionData {Operation::Exists}},
|
||||||
|
{std::make_pair("existsIn", 2), FunctionData {Operation::ExistsInObject}},
|
||||||
|
{std::make_pair("first", 1), FunctionData {Operation::First}},
|
||||||
|
{std::make_pair("float", 1), FunctionData {Operation::Float}},
|
||||||
|
{std::make_pair("int", 1), FunctionData {Operation::Int}},
|
||||||
|
{std::make_pair("isArray", 1), FunctionData {Operation::IsArray}},
|
||||||
|
{std::make_pair("isBoolean", 1), FunctionData {Operation::IsBoolean}},
|
||||||
|
{std::make_pair("isFloat", 1), FunctionData {Operation::IsFloat}},
|
||||||
|
{std::make_pair("isInteger", 1), FunctionData {Operation::IsInteger}},
|
||||||
|
{std::make_pair("isNumber", 1), FunctionData {Operation::IsNumber}},
|
||||||
|
{std::make_pair("isObject", 1), FunctionData {Operation::IsObject}},
|
||||||
|
{std::make_pair("isString", 1), FunctionData {Operation::IsString}},
|
||||||
|
{std::make_pair("last", 1), FunctionData {Operation::Last}},
|
||||||
|
{std::make_pair("length", 1), FunctionData {Operation::Length}},
|
||||||
|
{std::make_pair("lower", 1), FunctionData {Operation::Lower}},
|
||||||
|
{std::make_pair("max", 1), FunctionData {Operation::Max}},
|
||||||
|
{std::make_pair("min", 1), FunctionData {Operation::Min}},
|
||||||
|
{std::make_pair("odd", 1), FunctionData {Operation::Odd}},
|
||||||
|
{std::make_pair("range", 1), FunctionData {Operation::Range}},
|
||||||
|
{std::make_pair("round", 2), FunctionData {Operation::Round}},
|
||||||
|
{std::make_pair("sort", 1), FunctionData {Operation::Sort}},
|
||||||
|
{std::make_pair("upper", 1), FunctionData {Operation::Upper}},
|
||||||
|
{std::make_pair("super", 0), FunctionData {Operation::Super}},
|
||||||
|
{std::make_pair("super", 1), FunctionData {Operation::Super}},
|
||||||
|
{std::make_pair("join", 2), FunctionData {Operation::Join}},
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
void add_builtin(std::string_view name, int num_args, Operation op) {
|
||||||
|
function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData {op});
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_callback(std::string_view name, int num_args, const CallbackFunction& callback) {
|
||||||
|
function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData {Operation::Callback, callback});
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionData find_function(std::string_view name, int num_args) const {
|
||||||
|
auto it = function_storage.find(std::make_pair(static_cast<std::string>(name), num_args));
|
||||||
|
if (it != function_storage.end()) {
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
// Find variadic function
|
||||||
|
} else if (num_args > 0) {
|
||||||
|
it = function_storage.find(std::make_pair(static_cast<std::string>(name), VARIADIC));
|
||||||
|
if (it != function_storage.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FunctionData {Operation::None};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_
|
||||||
61
libi2pd_webconsole/inja/inja.hpp
Normal file
61
libi2pd_webconsole/inja/inja.hpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
___ _ Version 3.3
|
||||||
|
|_ _|_ __ (_) __ _ https://github.com/pantor/inja
|
||||||
|
| || '_ \ | |/ _` | Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
| || | | || | (_| |
|
||||||
|
|___|_| |_|/ |\__,_| Copyright (c) 2018-2021 Lars Berscheid
|
||||||
|
|__/
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDE_INJA_INJA_HPP_
|
||||||
|
#define INCLUDE_INJA_INJA_HPP_
|
||||||
|
|
||||||
|
//#include <nlohmann/json.hpp>
|
||||||
|
#include "../nlohmann/json.hpp"
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
#ifndef INJA_DATA_TYPE
|
||||||
|
using json = nlohmann::json;
|
||||||
|
#else
|
||||||
|
using json = INJA_DATA_TYPE;
|
||||||
|
#endif
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(INJA_NOEXCEPTION)
|
||||||
|
#ifndef INJA_THROW
|
||||||
|
#define INJA_THROW(exception) throw exception
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#include <cstdlib>
|
||||||
|
#ifndef INJA_THROW
|
||||||
|
#define INJA_THROW(exception) \
|
||||||
|
std::abort(); \
|
||||||
|
std::ignore = exception
|
||||||
|
#endif
|
||||||
|
#ifndef INJA_NOEXCEPTION
|
||||||
|
#define INJA_NOEXCEPTION
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "environment.hpp"
|
||||||
|
#include "exceptions.hpp"
|
||||||
|
#include "parser.hpp"
|
||||||
|
#include "renderer.hpp"
|
||||||
|
#include "template.hpp"
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_INJA_HPP_
|
||||||
435
libi2pd_webconsole/inja/lexer.hpp
Normal file
435
libi2pd_webconsole/inja/lexer.hpp
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
#ifndef INCLUDE_INJA_LEXER_HPP_
|
||||||
|
#define INCLUDE_INJA_LEXER_HPP_
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "token.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Class for lexing an inja Template.
|
||||||
|
*/
|
||||||
|
class Lexer {
|
||||||
|
enum class State {
|
||||||
|
Text,
|
||||||
|
ExpressionStart,
|
||||||
|
ExpressionStartForceLstrip,
|
||||||
|
ExpressionBody,
|
||||||
|
LineStart,
|
||||||
|
LineBody,
|
||||||
|
StatementStart,
|
||||||
|
StatementStartNoLstrip,
|
||||||
|
StatementStartForceLstrip,
|
||||||
|
StatementBody,
|
||||||
|
CommentStart,
|
||||||
|
CommentStartForceLstrip,
|
||||||
|
CommentBody,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MinusState {
|
||||||
|
Operator,
|
||||||
|
Number,
|
||||||
|
};
|
||||||
|
|
||||||
|
const LexerConfig& config;
|
||||||
|
|
||||||
|
State state;
|
||||||
|
MinusState minus_state;
|
||||||
|
std::string_view m_in;
|
||||||
|
size_t tok_start;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
Token scan_body(std::string_view close, Token::Kind closeKind, std::string_view close_trim = std::string_view(), bool trim = false) {
|
||||||
|
again:
|
||||||
|
// skip whitespace (except for \n as it might be a close)
|
||||||
|
if (tok_start >= m_in.size()) {
|
||||||
|
return make_token(Token::Kind::Eof);
|
||||||
|
}
|
||||||
|
const char ch = m_in[tok_start];
|
||||||
|
if (ch == ' ' || ch == '\t' || ch == '\r') {
|
||||||
|
tok_start += 1;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for close
|
||||||
|
if (!close_trim.empty() && inja::string_view::starts_with(m_in.substr(tok_start), close_trim)) {
|
||||||
|
state = State::Text;
|
||||||
|
pos = tok_start + close_trim.size();
|
||||||
|
const Token tok = make_token(closeKind);
|
||||||
|
skip_whitespaces_and_newlines();
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inja::string_view::starts_with(m_in.substr(tok_start), close)) {
|
||||||
|
state = State::Text;
|
||||||
|
pos = tok_start + close.size();
|
||||||
|
const Token tok = make_token(closeKind);
|
||||||
|
if (trim) {
|
||||||
|
skip_whitespaces_and_first_newline();
|
||||||
|
}
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip \n
|
||||||
|
if (ch == '\n') {
|
||||||
|
tok_start += 1;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = tok_start + 1;
|
||||||
|
if (std::isalpha(ch)) {
|
||||||
|
minus_state = MinusState::Operator;
|
||||||
|
return scan_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
const MinusState current_minus_state = minus_state;
|
||||||
|
if (minus_state == MinusState::Operator) {
|
||||||
|
minus_state = MinusState::Number;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
case '+':
|
||||||
|
return make_token(Token::Kind::Plus);
|
||||||
|
case '-':
|
||||||
|
if (current_minus_state == MinusState::Operator) {
|
||||||
|
return make_token(Token::Kind::Minus);
|
||||||
|
}
|
||||||
|
return scan_number();
|
||||||
|
case '*':
|
||||||
|
return make_token(Token::Kind::Times);
|
||||||
|
case '/':
|
||||||
|
return make_token(Token::Kind::Slash);
|
||||||
|
case '^':
|
||||||
|
return make_token(Token::Kind::Power);
|
||||||
|
case '%':
|
||||||
|
return make_token(Token::Kind::Percent);
|
||||||
|
case '.':
|
||||||
|
return make_token(Token::Kind::Dot);
|
||||||
|
case ',':
|
||||||
|
return make_token(Token::Kind::Comma);
|
||||||
|
case ':':
|
||||||
|
return make_token(Token::Kind::Colon);
|
||||||
|
case '(':
|
||||||
|
return make_token(Token::Kind::LeftParen);
|
||||||
|
case ')':
|
||||||
|
minus_state = MinusState::Operator;
|
||||||
|
return make_token(Token::Kind::RightParen);
|
||||||
|
case '[':
|
||||||
|
return make_token(Token::Kind::LeftBracket);
|
||||||
|
case ']':
|
||||||
|
minus_state = MinusState::Operator;
|
||||||
|
return make_token(Token::Kind::RightBracket);
|
||||||
|
case '{':
|
||||||
|
return make_token(Token::Kind::LeftBrace);
|
||||||
|
case '}':
|
||||||
|
minus_state = MinusState::Operator;
|
||||||
|
return make_token(Token::Kind::RightBrace);
|
||||||
|
case '>':
|
||||||
|
if (pos < m_in.size() && m_in[pos] == '=') {
|
||||||
|
pos += 1;
|
||||||
|
return make_token(Token::Kind::GreaterEqual);
|
||||||
|
}
|
||||||
|
return make_token(Token::Kind::GreaterThan);
|
||||||
|
case '<':
|
||||||
|
if (pos < m_in.size() && m_in[pos] == '=') {
|
||||||
|
pos += 1;
|
||||||
|
return make_token(Token::Kind::LessEqual);
|
||||||
|
}
|
||||||
|
return make_token(Token::Kind::LessThan);
|
||||||
|
case '=':
|
||||||
|
if (pos < m_in.size() && m_in[pos] == '=') {
|
||||||
|
pos += 1;
|
||||||
|
return make_token(Token::Kind::Equal);
|
||||||
|
}
|
||||||
|
return make_token(Token::Kind::Unknown);
|
||||||
|
case '!':
|
||||||
|
if (pos < m_in.size() && m_in[pos] == '=') {
|
||||||
|
pos += 1;
|
||||||
|
return make_token(Token::Kind::NotEqual);
|
||||||
|
}
|
||||||
|
return make_token(Token::Kind::Unknown);
|
||||||
|
case '\"':
|
||||||
|
return scan_string();
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
minus_state = MinusState::Operator;
|
||||||
|
return scan_number();
|
||||||
|
case '_':
|
||||||
|
case '@':
|
||||||
|
case '$':
|
||||||
|
minus_state = MinusState::Operator;
|
||||||
|
return scan_id();
|
||||||
|
default:
|
||||||
|
return make_token(Token::Kind::Unknown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Token scan_id() {
|
||||||
|
for (;;) {
|
||||||
|
if (pos >= m_in.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const char ch = m_in[pos];
|
||||||
|
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
return make_token(Token::Kind::Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token scan_number() {
|
||||||
|
for (;;) {
|
||||||
|
if (pos >= m_in.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const char ch = m_in[pos];
|
||||||
|
// be very permissive in lexer (we'll catch errors when conversion happens)
|
||||||
|
if (!(std::isdigit(ch) || ch == '.' || ch == 'e' || ch == 'E' || (ch == '+' && (pos == 0 || m_in[pos-1] == 'e' || m_in[pos-1] == 'E')) || (ch == '-' && (pos == 0 || m_in[pos-1] == 'e' || m_in[pos-1] == 'E')))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
return make_token(Token::Kind::Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token scan_string() {
|
||||||
|
bool escape {false};
|
||||||
|
for (;;) {
|
||||||
|
if (pos >= m_in.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const char ch = m_in[pos++];
|
||||||
|
if (ch == '\\') {
|
||||||
|
escape = true;
|
||||||
|
} else if (!escape && ch == m_in[tok_start]) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
escape = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return make_token(Token::Kind::String);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token make_token(Token::Kind kind) const {
|
||||||
|
return Token(kind, string_view::slice(m_in, tok_start, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
void skip_whitespaces_and_newlines() {
|
||||||
|
if (pos < m_in.size()) {
|
||||||
|
while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t' || m_in[pos] == '\n' || m_in[pos] == '\r')) {
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void skip_whitespaces_and_first_newline() {
|
||||||
|
if (pos < m_in.size()) {
|
||||||
|
while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t')) {
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos < m_in.size()) {
|
||||||
|
const char ch = m_in[pos];
|
||||||
|
if (ch == '\n') {
|
||||||
|
pos += 1;
|
||||||
|
} else if (ch == '\r') {
|
||||||
|
pos += 1;
|
||||||
|
if (pos < m_in.size() && m_in[pos] == '\n') {
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string_view clear_final_line_if_whitespace(std::string_view text) {
|
||||||
|
std::string_view result = text;
|
||||||
|
while (!result.empty()) {
|
||||||
|
const char ch = result.back();
|
||||||
|
if (ch == ' ' || ch == '\t') {
|
||||||
|
result.remove_suffix(1);
|
||||||
|
} else if (ch == '\n' || ch == '\r') {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Lexer(const LexerConfig& config): config(config), state(State::Text), minus_state(MinusState::Number) {}
|
||||||
|
|
||||||
|
SourceLocation current_position() const {
|
||||||
|
return get_source_location(m_in, tok_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
void start(std::string_view input) {
|
||||||
|
m_in = input;
|
||||||
|
tok_start = 0;
|
||||||
|
pos = 0;
|
||||||
|
state = State::Text;
|
||||||
|
minus_state = MinusState::Number;
|
||||||
|
|
||||||
|
// Consume byte order mark (BOM) for UTF-8
|
||||||
|
if (inja::string_view::starts_with(m_in, "\xEF\xBB\xBF")) {
|
||||||
|
m_in = m_in.substr(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Token scan() {
|
||||||
|
tok_start = pos;
|
||||||
|
|
||||||
|
again:
|
||||||
|
if (tok_start >= m_in.size()) {
|
||||||
|
return make_token(Token::Kind::Eof);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
default:
|
||||||
|
case State::Text: {
|
||||||
|
// fast-scan to first open character
|
||||||
|
const size_t open_start = m_in.substr(pos).find_first_of(config.open_chars);
|
||||||
|
if (open_start == std::string_view::npos) {
|
||||||
|
// didn't find open, return remaining text as text token
|
||||||
|
pos = m_in.size();
|
||||||
|
return make_token(Token::Kind::Text);
|
||||||
|
}
|
||||||
|
pos += open_start;
|
||||||
|
|
||||||
|
// try to match one of the opening sequences, and get the close
|
||||||
|
std::string_view open_str = m_in.substr(pos);
|
||||||
|
bool must_lstrip = false;
|
||||||
|
if (inja::string_view::starts_with(open_str, config.expression_open)) {
|
||||||
|
if (inja::string_view::starts_with(open_str, config.expression_open_force_lstrip)) {
|
||||||
|
state = State::ExpressionStartForceLstrip;
|
||||||
|
must_lstrip = true;
|
||||||
|
} else {
|
||||||
|
state = State::ExpressionStart;
|
||||||
|
}
|
||||||
|
} else if (inja::string_view::starts_with(open_str, config.statement_open)) {
|
||||||
|
if (inja::string_view::starts_with(open_str, config.statement_open_no_lstrip)) {
|
||||||
|
state = State::StatementStartNoLstrip;
|
||||||
|
} else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip)) {
|
||||||
|
state = State::StatementStartForceLstrip;
|
||||||
|
must_lstrip = true;
|
||||||
|
} else {
|
||||||
|
state = State::StatementStart;
|
||||||
|
must_lstrip = config.lstrip_blocks;
|
||||||
|
}
|
||||||
|
} else if (inja::string_view::starts_with(open_str, config.comment_open)) {
|
||||||
|
if (inja::string_view::starts_with(open_str, config.comment_open_force_lstrip)) {
|
||||||
|
state = State::CommentStartForceLstrip;
|
||||||
|
must_lstrip = true;
|
||||||
|
} else {
|
||||||
|
state = State::CommentStart;
|
||||||
|
must_lstrip = config.lstrip_blocks;
|
||||||
|
}
|
||||||
|
} else if ((pos == 0 || m_in[pos - 1] == '\n') && inja::string_view::starts_with(open_str, config.line_statement)) {
|
||||||
|
state = State::LineStart;
|
||||||
|
} else {
|
||||||
|
pos += 1; // wasn't actually an opening sequence
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view text = string_view::slice(m_in, tok_start, pos);
|
||||||
|
if (must_lstrip) {
|
||||||
|
text = clear_final_line_if_whitespace(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.empty()) {
|
||||||
|
goto again; // don't generate empty token
|
||||||
|
}
|
||||||
|
return Token(Token::Kind::Text, text);
|
||||||
|
}
|
||||||
|
case State::ExpressionStart: {
|
||||||
|
state = State::ExpressionBody;
|
||||||
|
pos += config.expression_open.size();
|
||||||
|
return make_token(Token::Kind::ExpressionOpen);
|
||||||
|
}
|
||||||
|
case State::ExpressionStartForceLstrip: {
|
||||||
|
state = State::ExpressionBody;
|
||||||
|
pos += config.expression_open_force_lstrip.size();
|
||||||
|
return make_token(Token::Kind::ExpressionOpen);
|
||||||
|
}
|
||||||
|
case State::LineStart: {
|
||||||
|
state = State::LineBody;
|
||||||
|
pos += config.line_statement.size();
|
||||||
|
return make_token(Token::Kind::LineStatementOpen);
|
||||||
|
}
|
||||||
|
case State::StatementStart: {
|
||||||
|
state = State::StatementBody;
|
||||||
|
pos += config.statement_open.size();
|
||||||
|
return make_token(Token::Kind::StatementOpen);
|
||||||
|
}
|
||||||
|
case State::StatementStartNoLstrip: {
|
||||||
|
state = State::StatementBody;
|
||||||
|
pos += config.statement_open_no_lstrip.size();
|
||||||
|
return make_token(Token::Kind::StatementOpen);
|
||||||
|
}
|
||||||
|
case State::StatementStartForceLstrip: {
|
||||||
|
state = State::StatementBody;
|
||||||
|
pos += config.statement_open_force_lstrip.size();
|
||||||
|
return make_token(Token::Kind::StatementOpen);
|
||||||
|
}
|
||||||
|
case State::CommentStart: {
|
||||||
|
state = State::CommentBody;
|
||||||
|
pos += config.comment_open.size();
|
||||||
|
return make_token(Token::Kind::CommentOpen);
|
||||||
|
}
|
||||||
|
case State::CommentStartForceLstrip: {
|
||||||
|
state = State::CommentBody;
|
||||||
|
pos += config.comment_open_force_lstrip.size();
|
||||||
|
return make_token(Token::Kind::CommentOpen);
|
||||||
|
}
|
||||||
|
case State::ExpressionBody:
|
||||||
|
return scan_body(config.expression_close, Token::Kind::ExpressionClose, config.expression_close_force_rstrip);
|
||||||
|
case State::LineBody:
|
||||||
|
return scan_body("\n", Token::Kind::LineStatementClose);
|
||||||
|
case State::StatementBody:
|
||||||
|
return scan_body(config.statement_close, Token::Kind::StatementClose, config.statement_close_force_rstrip, config.trim_blocks);
|
||||||
|
case State::CommentBody: {
|
||||||
|
// fast-scan to comment close
|
||||||
|
const size_t end = m_in.substr(pos).find(config.comment_close);
|
||||||
|
if (end == std::string_view::npos) {
|
||||||
|
pos = m_in.size();
|
||||||
|
return make_token(Token::Kind::Eof);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for trim pattern
|
||||||
|
const bool must_rstrip = inja::string_view::starts_with(m_in.substr(pos + end - 1), config.comment_close_force_rstrip);
|
||||||
|
|
||||||
|
// return the entire comment in the close token
|
||||||
|
state = State::Text;
|
||||||
|
pos += end + config.comment_close.size();
|
||||||
|
Token tok = make_token(Token::Kind::CommentClose);
|
||||||
|
|
||||||
|
if (must_rstrip || config.trim_blocks) {
|
||||||
|
skip_whitespaces_and_first_newline();
|
||||||
|
}
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LexerConfig& get_config() const {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_LEXER_HPP_
|
||||||
372
libi2pd_webconsole/inja/node.hpp
Normal file
372
libi2pd_webconsole/inja/node.hpp
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
#ifndef INCLUDE_INJA_NODE_HPP_
|
||||||
|
#define INCLUDE_INJA_NODE_HPP_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "function_storage.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
class NodeVisitor;
|
||||||
|
class BlockNode;
|
||||||
|
class TextNode;
|
||||||
|
class ExpressionNode;
|
||||||
|
class LiteralNode;
|
||||||
|
class DataNode;
|
||||||
|
class FunctionNode;
|
||||||
|
class ExpressionListNode;
|
||||||
|
class StatementNode;
|
||||||
|
class ForStatementNode;
|
||||||
|
class ForArrayStatementNode;
|
||||||
|
class ForObjectStatementNode;
|
||||||
|
class IfStatementNode;
|
||||||
|
class IncludeStatementNode;
|
||||||
|
class ExtendsStatementNode;
|
||||||
|
class BlockStatementNode;
|
||||||
|
class SetStatementNode;
|
||||||
|
|
||||||
|
class NodeVisitor {
|
||||||
|
public:
|
||||||
|
virtual ~NodeVisitor() = default;
|
||||||
|
|
||||||
|
virtual void visit(const BlockNode& node) = 0;
|
||||||
|
virtual void visit(const TextNode& node) = 0;
|
||||||
|
virtual void visit(const ExpressionNode& node) = 0;
|
||||||
|
virtual void visit(const LiteralNode& node) = 0;
|
||||||
|
virtual void visit(const DataNode& node) = 0;
|
||||||
|
virtual void visit(const FunctionNode& node) = 0;
|
||||||
|
virtual void visit(const ExpressionListNode& node) = 0;
|
||||||
|
virtual void visit(const StatementNode& node) = 0;
|
||||||
|
virtual void visit(const ForStatementNode& node) = 0;
|
||||||
|
virtual void visit(const ForArrayStatementNode& node) = 0;
|
||||||
|
virtual void visit(const ForObjectStatementNode& node) = 0;
|
||||||
|
virtual void visit(const IfStatementNode& node) = 0;
|
||||||
|
virtual void visit(const IncludeStatementNode& node) = 0;
|
||||||
|
virtual void visit(const ExtendsStatementNode& node) = 0;
|
||||||
|
virtual void visit(const BlockStatementNode& node) = 0;
|
||||||
|
virtual void visit(const SetStatementNode& node) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Base node class for the abstract syntax tree (AST).
|
||||||
|
*/
|
||||||
|
class AstNode {
|
||||||
|
public:
|
||||||
|
virtual void accept(NodeVisitor& v) const = 0;
|
||||||
|
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
AstNode(size_t pos): pos(pos) {}
|
||||||
|
virtual ~AstNode() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BlockNode : public AstNode {
|
||||||
|
public:
|
||||||
|
std::vector<std::shared_ptr<AstNode>> nodes;
|
||||||
|
|
||||||
|
explicit BlockNode(): AstNode(0) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TextNode : public AstNode {
|
||||||
|
public:
|
||||||
|
const size_t length;
|
||||||
|
|
||||||
|
explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExpressionNode : public AstNode {
|
||||||
|
public:
|
||||||
|
explicit ExpressionNode(size_t pos): AstNode(pos) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LiteralNode : public ExpressionNode {
|
||||||
|
public:
|
||||||
|
const json value;
|
||||||
|
|
||||||
|
explicit LiteralNode(std::string_view data_text, size_t pos): ExpressionNode(pos), value(json::parse(data_text)) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DataNode : public ExpressionNode {
|
||||||
|
public:
|
||||||
|
const std::string name;
|
||||||
|
const json::json_pointer ptr;
|
||||||
|
|
||||||
|
static std::string convert_dot_to_ptr(std::string_view ptr_name) {
|
||||||
|
std::string result;
|
||||||
|
do {
|
||||||
|
std::string_view part;
|
||||||
|
std::tie(part, ptr_name) = string_view::split(ptr_name, '.');
|
||||||
|
result.push_back('/');
|
||||||
|
result.append(part.begin(), part.end());
|
||||||
|
} while (!ptr_name.empty());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit DataNode(std::string_view ptr_name, size_t pos): ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_ptr(ptr_name))) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FunctionNode : public ExpressionNode {
|
||||||
|
using Op = FunctionStorage::Operation;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class Associativity {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int precedence;
|
||||||
|
Associativity associativity;
|
||||||
|
|
||||||
|
Op operation;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
int number_args; // Should also be negative -> -1 for unknown number
|
||||||
|
std::vector<std::shared_ptr<ExpressionNode>> arguments;
|
||||||
|
CallbackFunction callback;
|
||||||
|
|
||||||
|
explicit FunctionNode(std::string_view name, size_t pos)
|
||||||
|
: ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) {}
|
||||||
|
explicit FunctionNode(Op operation, size_t pos): ExpressionNode(pos), operation(operation), number_args(1) {
|
||||||
|
switch (operation) {
|
||||||
|
case Op::Not: {
|
||||||
|
number_args = 1;
|
||||||
|
precedence = 4;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::And: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 1;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::Or: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 1;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::In: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 2;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::Equal: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 2;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::NotEqual: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 2;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::Greater: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 2;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::GreaterEqual: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 2;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::Less: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 2;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::LessEqual: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 2;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::Add: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 3;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::Subtract: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 3;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::Multiplication: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 4;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::Division: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 4;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::Power: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 5;
|
||||||
|
associativity = Associativity::Right;
|
||||||
|
} break;
|
||||||
|
case Op::Modulo: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 4;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
case Op::AtId: {
|
||||||
|
number_args = 2;
|
||||||
|
precedence = 8;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
precedence = 1;
|
||||||
|
associativity = Associativity::Left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExpressionListNode : public AstNode {
|
||||||
|
public:
|
||||||
|
std::shared_ptr<ExpressionNode> root;
|
||||||
|
|
||||||
|
explicit ExpressionListNode(): AstNode(0) {}
|
||||||
|
explicit ExpressionListNode(size_t pos): AstNode(pos) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class StatementNode : public AstNode {
|
||||||
|
public:
|
||||||
|
StatementNode(size_t pos): AstNode(pos) {}
|
||||||
|
|
||||||
|
virtual void accept(NodeVisitor& v) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ForStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
ExpressionListNode condition;
|
||||||
|
BlockNode body;
|
||||||
|
BlockNode* const parent;
|
||||||
|
|
||||||
|
ForStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent) {}
|
||||||
|
|
||||||
|
virtual void accept(NodeVisitor& v) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ForArrayStatementNode : public ForStatementNode {
|
||||||
|
public:
|
||||||
|
const std::string value;
|
||||||
|
|
||||||
|
explicit ForArrayStatementNode(const std::string& value, BlockNode* const parent, size_t pos): ForStatementNode(parent, pos), value(value) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ForObjectStatementNode : public ForStatementNode {
|
||||||
|
public:
|
||||||
|
const std::string key;
|
||||||
|
const std::string value;
|
||||||
|
|
||||||
|
explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode* const parent, size_t pos)
|
||||||
|
: ForStatementNode(parent, pos), key(key), value(value) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class IfStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
ExpressionListNode condition;
|
||||||
|
BlockNode true_statement;
|
||||||
|
BlockNode false_statement;
|
||||||
|
BlockNode* const parent;
|
||||||
|
|
||||||
|
const bool is_nested;
|
||||||
|
bool has_false_statement {false};
|
||||||
|
|
||||||
|
explicit IfStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(false) {}
|
||||||
|
explicit IfStatementNode(bool is_nested, BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(is_nested) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class IncludeStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
const std::string file;
|
||||||
|
|
||||||
|
explicit IncludeStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExtendsStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
const std::string file;
|
||||||
|
|
||||||
|
explicit ExtendsStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class BlockStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
const std::string name;
|
||||||
|
BlockNode block;
|
||||||
|
BlockNode* const parent;
|
||||||
|
|
||||||
|
explicit BlockStatementNode(BlockNode* const parent, const std::string& name, size_t pos): StatementNode(pos), name(name), parent(parent) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class SetStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
const std::string key;
|
||||||
|
ExpressionListNode expression;
|
||||||
|
|
||||||
|
explicit SetStatementNode(const std::string& key, size_t pos): StatementNode(pos), key(key) {}
|
||||||
|
|
||||||
|
void accept(NodeVisitor& v) const {
|
||||||
|
v.visit(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_NODE_HPP_
|
||||||
659
libi2pd_webconsole/inja/parser.hpp
Normal file
659
libi2pd_webconsole/inja/parser.hpp
Normal file
@@ -0,0 +1,659 @@
|
|||||||
|
#ifndef INCLUDE_INJA_PARSER_HPP_
|
||||||
|
#define INCLUDE_INJA_PARSER_HPP_
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <stack>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "exceptions.hpp"
|
||||||
|
#include "function_storage.hpp"
|
||||||
|
#include "lexer.hpp"
|
||||||
|
#include "node.hpp"
|
||||||
|
#include "template.hpp"
|
||||||
|
#include "token.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Class for parsing an inja Template.
|
||||||
|
*/
|
||||||
|
class Parser {
|
||||||
|
const ParserConfig& config;
|
||||||
|
|
||||||
|
Lexer lexer;
|
||||||
|
TemplateStorage& template_storage;
|
||||||
|
const FunctionStorage& function_storage;
|
||||||
|
|
||||||
|
Token tok, peek_tok;
|
||||||
|
bool have_peek_tok {false};
|
||||||
|
|
||||||
|
size_t current_paren_level {0};
|
||||||
|
size_t current_bracket_level {0};
|
||||||
|
size_t current_brace_level {0};
|
||||||
|
|
||||||
|
std::string_view literal_start;
|
||||||
|
|
||||||
|
BlockNode* current_block {nullptr};
|
||||||
|
ExpressionListNode* current_expression_list {nullptr};
|
||||||
|
std::stack<std::pair<FunctionNode*, size_t>> function_stack;
|
||||||
|
std::vector<std::shared_ptr<ExpressionNode>> arguments;
|
||||||
|
|
||||||
|
std::stack<std::shared_ptr<FunctionNode>> operator_stack;
|
||||||
|
std::stack<IfStatementNode*> if_statement_stack;
|
||||||
|
std::stack<ForStatementNode*> for_statement_stack;
|
||||||
|
std::stack<BlockStatementNode*> block_statement_stack;
|
||||||
|
|
||||||
|
inline void throw_parser_error(const std::string& message) const {
|
||||||
|
INJA_THROW(ParserError(message, lexer.current_position()));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void get_next_token() {
|
||||||
|
if (have_peek_tok) {
|
||||||
|
tok = peek_tok;
|
||||||
|
have_peek_tok = false;
|
||||||
|
} else {
|
||||||
|
tok = lexer.scan();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void get_peek_token() {
|
||||||
|
if (!have_peek_tok) {
|
||||||
|
peek_tok = lexer.scan();
|
||||||
|
have_peek_tok = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void add_literal(const char* content_ptr) {
|
||||||
|
std::string_view data_text(literal_start.data(), tok.text.data() - literal_start.data() + tok.text.size());
|
||||||
|
arguments.emplace_back(std::make_shared<LiteralNode>(data_text, data_text.data() - content_ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void add_operator() {
|
||||||
|
auto function = operator_stack.top();
|
||||||
|
operator_stack.pop();
|
||||||
|
|
||||||
|
for (int i = 0; i < function->number_args; ++i) {
|
||||||
|
function->arguments.insert(function->arguments.begin(), arguments.back());
|
||||||
|
arguments.pop_back();
|
||||||
|
}
|
||||||
|
arguments.emplace_back(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_to_template_storage(std::string_view path, std::string& template_name) {
|
||||||
|
if (template_storage.find(template_name) != template_storage.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string original_path = static_cast<std::string>(path);
|
||||||
|
std::string original_name = template_name;
|
||||||
|
|
||||||
|
if (config.search_included_templates_in_files) {
|
||||||
|
// Build the relative path
|
||||||
|
template_name = original_path + original_name;
|
||||||
|
if (template_name.compare(0, 2, "./") == 0) {
|
||||||
|
template_name.erase(0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (template_storage.find(template_name) == template_storage.end()) {
|
||||||
|
// Load file
|
||||||
|
std::ifstream file;
|
||||||
|
file.open(template_name);
|
||||||
|
if (!file.fail()) {
|
||||||
|
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
|
auto include_template = Template(text);
|
||||||
|
template_storage.emplace(template_name, include_template);
|
||||||
|
parse_into_template(template_storage[template_name], template_name);
|
||||||
|
return;
|
||||||
|
} else if (!config.include_callback) {
|
||||||
|
INJA_THROW(FileError("failed accessing file at '" + template_name + "'"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try include callback
|
||||||
|
if (config.include_callback) {
|
||||||
|
auto include_template = config.include_callback(original_path, original_name);
|
||||||
|
template_storage.emplace(template_name, include_template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string parse_filename(const Token& tok) const {
|
||||||
|
if (tok.kind != Token::Kind::String) {
|
||||||
|
throw_parser_error("expected string, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok.text.length() < 2) {
|
||||||
|
throw_parser_error("expected filename, got '" + static_cast<std::string>(tok.text) + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove first and last character ""
|
||||||
|
return std::string {tok.text.substr(1, tok.text.length() - 2)};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_expression(Template& tmpl, Token::Kind closing) {
|
||||||
|
while (tok.kind != closing && tok.kind != Token::Kind::Eof) {
|
||||||
|
// Literals
|
||||||
|
switch (tok.kind) {
|
||||||
|
case Token::Kind::String: {
|
||||||
|
if (current_brace_level == 0 && current_bracket_level == 0) {
|
||||||
|
literal_start = tok.text;
|
||||||
|
add_literal(tmpl.content.c_str());
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Number: {
|
||||||
|
if (current_brace_level == 0 && current_bracket_level == 0) {
|
||||||
|
literal_start = tok.text;
|
||||||
|
add_literal(tmpl.content.c_str());
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Token::Kind::LeftBracket: {
|
||||||
|
if (current_brace_level == 0 && current_bracket_level == 0) {
|
||||||
|
literal_start = tok.text;
|
||||||
|
}
|
||||||
|
current_bracket_level += 1;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::LeftBrace: {
|
||||||
|
if (current_brace_level == 0 && current_bracket_level == 0) {
|
||||||
|
literal_start = tok.text;
|
||||||
|
}
|
||||||
|
current_brace_level += 1;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::RightBracket: {
|
||||||
|
if (current_bracket_level == 0) {
|
||||||
|
throw_parser_error("unexpected ']'");
|
||||||
|
}
|
||||||
|
|
||||||
|
current_bracket_level -= 1;
|
||||||
|
if (current_brace_level == 0 && current_bracket_level == 0) {
|
||||||
|
add_literal(tmpl.content.c_str());
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Token::Kind::RightBrace: {
|
||||||
|
if (current_brace_level == 0) {
|
||||||
|
throw_parser_error("unexpected '}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
current_brace_level -= 1;
|
||||||
|
if (current_brace_level == 0 && current_bracket_level == 0) {
|
||||||
|
add_literal(tmpl.content.c_str());
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Id: {
|
||||||
|
get_peek_token();
|
||||||
|
|
||||||
|
// Data Literal
|
||||||
|
if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") ||
|
||||||
|
tok.text == static_cast<decltype(tok.text)>("null")) {
|
||||||
|
if (current_brace_level == 0 && current_bracket_level == 0) {
|
||||||
|
literal_start = tok.text;
|
||||||
|
add_literal(tmpl.content.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operator
|
||||||
|
} else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") {
|
||||||
|
goto parse_operator;
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
} else if (peek_tok.kind == Token::Kind::LeftParen) {
|
||||||
|
operator_stack.emplace(std::make_shared<FunctionNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
|
||||||
|
function_stack.emplace(operator_stack.top().get(), current_paren_level);
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
} else {
|
||||||
|
arguments.emplace_back(std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Equal:
|
||||||
|
case Token::Kind::NotEqual:
|
||||||
|
case Token::Kind::GreaterThan:
|
||||||
|
case Token::Kind::GreaterEqual:
|
||||||
|
case Token::Kind::LessThan:
|
||||||
|
case Token::Kind::LessEqual:
|
||||||
|
case Token::Kind::Plus:
|
||||||
|
case Token::Kind::Minus:
|
||||||
|
case Token::Kind::Times:
|
||||||
|
case Token::Kind::Slash:
|
||||||
|
case Token::Kind::Power:
|
||||||
|
case Token::Kind::Percent:
|
||||||
|
case Token::Kind::Dot: {
|
||||||
|
|
||||||
|
parse_operator:
|
||||||
|
FunctionStorage::Operation operation;
|
||||||
|
switch (tok.kind) {
|
||||||
|
case Token::Kind::Id: {
|
||||||
|
if (tok.text == "and") {
|
||||||
|
operation = FunctionStorage::Operation::And;
|
||||||
|
} else if (tok.text == "or") {
|
||||||
|
operation = FunctionStorage::Operation::Or;
|
||||||
|
} else if (tok.text == "in") {
|
||||||
|
operation = FunctionStorage::Operation::In;
|
||||||
|
} else if (tok.text == "not") {
|
||||||
|
operation = FunctionStorage::Operation::Not;
|
||||||
|
} else {
|
||||||
|
throw_parser_error("unknown operator in parser.");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Equal: {
|
||||||
|
operation = FunctionStorage::Operation::Equal;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::NotEqual: {
|
||||||
|
operation = FunctionStorage::Operation::NotEqual;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::GreaterThan: {
|
||||||
|
operation = FunctionStorage::Operation::Greater;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::GreaterEqual: {
|
||||||
|
operation = FunctionStorage::Operation::GreaterEqual;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::LessThan: {
|
||||||
|
operation = FunctionStorage::Operation::Less;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::LessEqual: {
|
||||||
|
operation = FunctionStorage::Operation::LessEqual;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Plus: {
|
||||||
|
operation = FunctionStorage::Operation::Add;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Minus: {
|
||||||
|
operation = FunctionStorage::Operation::Subtract;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Times: {
|
||||||
|
operation = FunctionStorage::Operation::Multiplication;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Slash: {
|
||||||
|
operation = FunctionStorage::Operation::Division;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Power: {
|
||||||
|
operation = FunctionStorage::Operation::Power;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Percent: {
|
||||||
|
operation = FunctionStorage::Operation::Modulo;
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Dot: {
|
||||||
|
operation = FunctionStorage::Operation::AtId;
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
throw_parser_error("unknown operator in parser.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto function_node = std::make_shared<FunctionNode>(operation, tok.text.data() - tmpl.content.c_str());
|
||||||
|
|
||||||
|
while (!operator_stack.empty() &&
|
||||||
|
((operator_stack.top()->precedence > function_node->precedence) ||
|
||||||
|
(operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) &&
|
||||||
|
(operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) {
|
||||||
|
add_operator();
|
||||||
|
}
|
||||||
|
|
||||||
|
operator_stack.emplace(function_node);
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Comma: {
|
||||||
|
if (current_brace_level == 0 && current_bracket_level == 0) {
|
||||||
|
if (function_stack.empty()) {
|
||||||
|
throw_parser_error("unexpected ','");
|
||||||
|
}
|
||||||
|
|
||||||
|
function_stack.top().first->number_args += 1;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Token::Kind::Colon: {
|
||||||
|
if (current_brace_level == 0 && current_bracket_level == 0) {
|
||||||
|
throw_parser_error("unexpected ':'");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Token::Kind::LeftParen: {
|
||||||
|
current_paren_level += 1;
|
||||||
|
operator_stack.emplace(std::make_shared<FunctionNode>(FunctionStorage::Operation::ParenLeft, tok.text.data() - tmpl.content.c_str()));
|
||||||
|
|
||||||
|
get_peek_token();
|
||||||
|
if (peek_tok.kind == Token::Kind::RightParen) {
|
||||||
|
if (!function_stack.empty() && function_stack.top().second == current_paren_level - 1) {
|
||||||
|
function_stack.top().first->number_args = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Token::Kind::RightParen: {
|
||||||
|
current_paren_level -= 1;
|
||||||
|
while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) {
|
||||||
|
add_operator();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) {
|
||||||
|
operator_stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_stack.empty() && function_stack.top().second == current_paren_level) {
|
||||||
|
auto func = function_stack.top().first;
|
||||||
|
auto function_data = function_storage.find_function(func->name, func->number_args);
|
||||||
|
if (function_data.operation == FunctionStorage::Operation::None) {
|
||||||
|
throw_parser_error("unknown function " + func->name);
|
||||||
|
}
|
||||||
|
func->operation = function_data.operation;
|
||||||
|
if (function_data.operation == FunctionStorage::Operation::Callback) {
|
||||||
|
func->callback = function_data.callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operator_stack.empty()) {
|
||||||
|
throw_parser_error("internal error at function " + func->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_operator();
|
||||||
|
function_stack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_next_token();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!operator_stack.empty()) {
|
||||||
|
add_operator();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.size() == 1) {
|
||||||
|
current_expression_list->root = arguments[0];
|
||||||
|
arguments = {};
|
||||||
|
} else if (arguments.size() > 1) {
|
||||||
|
throw_parser_error("malformed expression");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_statement(Template& tmpl, Token::Kind closing, std::string_view path) {
|
||||||
|
if (tok.kind != Token::Kind::Id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok.text == static_cast<decltype(tok.text)>("if")) {
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
auto if_statement_node = std::make_shared<IfStatementNode>(current_block, tok.text.data() - tmpl.content.c_str());
|
||||||
|
current_block->nodes.emplace_back(if_statement_node);
|
||||||
|
if_statement_stack.emplace(if_statement_node.get());
|
||||||
|
current_block = &if_statement_node->true_statement;
|
||||||
|
current_expression_list = &if_statement_node->condition;
|
||||||
|
|
||||||
|
if (!parse_expression(tmpl, closing)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (tok.text == static_cast<decltype(tok.text)>("else")) {
|
||||||
|
if (if_statement_stack.empty()) {
|
||||||
|
throw_parser_error("else without matching if");
|
||||||
|
}
|
||||||
|
auto& if_statement_data = if_statement_stack.top();
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
if_statement_data->has_false_statement = true;
|
||||||
|
current_block = &if_statement_data->false_statement;
|
||||||
|
|
||||||
|
// Chained else if
|
||||||
|
if (tok.kind == Token::Kind::Id && tok.text == static_cast<decltype(tok.text)>("if")) {
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
auto if_statement_node = std::make_shared<IfStatementNode>(true, current_block, tok.text.data() - tmpl.content.c_str());
|
||||||
|
current_block->nodes.emplace_back(if_statement_node);
|
||||||
|
if_statement_stack.emplace(if_statement_node.get());
|
||||||
|
current_block = &if_statement_node->true_statement;
|
||||||
|
current_expression_list = &if_statement_node->condition;
|
||||||
|
|
||||||
|
if (!parse_expression(tmpl, closing)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (tok.text == static_cast<decltype(tok.text)>("endif")) {
|
||||||
|
if (if_statement_stack.empty()) {
|
||||||
|
throw_parser_error("endif without matching if");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nested if statements
|
||||||
|
while (if_statement_stack.top()->is_nested) {
|
||||||
|
if_statement_stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& if_statement_data = if_statement_stack.top();
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
current_block = if_statement_data->parent;
|
||||||
|
if_statement_stack.pop();
|
||||||
|
} else if (tok.text == static_cast<decltype(tok.text)>("block")) {
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
if (tok.kind != Token::Kind::Id) {
|
||||||
|
throw_parser_error("expected block name, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string block_name = static_cast<std::string>(tok.text);
|
||||||
|
|
||||||
|
auto block_statement_node = std::make_shared<BlockStatementNode>(current_block, block_name, tok.text.data() - tmpl.content.c_str());
|
||||||
|
current_block->nodes.emplace_back(block_statement_node);
|
||||||
|
block_statement_stack.emplace(block_statement_node.get());
|
||||||
|
current_block = &block_statement_node->block;
|
||||||
|
auto success = tmpl.block_storage.emplace(block_name, block_statement_node);
|
||||||
|
if (!success.second) {
|
||||||
|
throw_parser_error("block with the name '" + block_name + "' does already exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
get_next_token();
|
||||||
|
} else if (tok.text == static_cast<decltype(tok.text)>("endblock")) {
|
||||||
|
if (block_statement_stack.empty()) {
|
||||||
|
throw_parser_error("endblock without matching block");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& block_statement_data = block_statement_stack.top();
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
current_block = block_statement_data->parent;
|
||||||
|
block_statement_stack.pop();
|
||||||
|
} else if (tok.text == static_cast<decltype(tok.text)>("for")) {
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
// options: for a in arr; for a, b in obj
|
||||||
|
if (tok.kind != Token::Kind::Id) {
|
||||||
|
throw_parser_error("expected id, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
Token value_token = tok;
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
// Object type
|
||||||
|
std::shared_ptr<ForStatementNode> for_statement_node;
|
||||||
|
if (tok.kind == Token::Kind::Comma) {
|
||||||
|
get_next_token();
|
||||||
|
if (tok.kind != Token::Kind::Id) {
|
||||||
|
throw_parser_error("expected id, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
Token key_token = std::move(value_token);
|
||||||
|
value_token = tok;
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text), static_cast<std::string>(value_token.text),
|
||||||
|
current_block, tok.text.data() - tmpl.content.c_str());
|
||||||
|
|
||||||
|
// Array type
|
||||||
|
} else {
|
||||||
|
for_statement_node =
|
||||||
|
std::make_shared<ForArrayStatementNode>(static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
current_block->nodes.emplace_back(for_statement_node);
|
||||||
|
for_statement_stack.emplace(for_statement_node.get());
|
||||||
|
current_block = &for_statement_node->body;
|
||||||
|
current_expression_list = &for_statement_node->condition;
|
||||||
|
|
||||||
|
if (tok.kind != Token::Kind::Id || tok.text != static_cast<decltype(tok.text)>("in")) {
|
||||||
|
throw_parser_error("expected 'in', got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
if (!parse_expression(tmpl, closing)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (tok.text == static_cast<decltype(tok.text)>("endfor")) {
|
||||||
|
if (for_statement_stack.empty()) {
|
||||||
|
throw_parser_error("endfor without matching for");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& for_statement_data = for_statement_stack.top();
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
current_block = for_statement_data->parent;
|
||||||
|
for_statement_stack.pop();
|
||||||
|
} else if (tok.text == static_cast<decltype(tok.text)>("include")) {
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
std::string template_name = parse_filename(tok);
|
||||||
|
add_to_template_storage(path, template_name);
|
||||||
|
|
||||||
|
current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
|
||||||
|
|
||||||
|
get_next_token();
|
||||||
|
} else if (tok.text == static_cast<decltype(tok.text)>("extends")) {
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
std::string template_name = parse_filename(tok);
|
||||||
|
add_to_template_storage(path, template_name);
|
||||||
|
|
||||||
|
current_block->nodes.emplace_back(std::make_shared<ExtendsStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
|
||||||
|
|
||||||
|
get_next_token();
|
||||||
|
} else if (tok.text == static_cast<decltype(tok.text)>("set")) {
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
if (tok.kind != Token::Kind::Id) {
|
||||||
|
throw_parser_error("expected variable name, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key = static_cast<std::string>(tok.text);
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
auto set_statement_node = std::make_shared<SetStatementNode>(key, tok.text.data() - tmpl.content.c_str());
|
||||||
|
current_block->nodes.emplace_back(set_statement_node);
|
||||||
|
current_expression_list = &set_statement_node->expression;
|
||||||
|
|
||||||
|
if (tok.text != static_cast<decltype(tok.text)>("=")) {
|
||||||
|
throw_parser_error("expected '=', got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
if (!parse_expression(tmpl, closing)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_into(Template& tmpl, std::string_view path) {
|
||||||
|
lexer.start(tmpl.content);
|
||||||
|
current_block = &tmpl.root;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
get_next_token();
|
||||||
|
switch (tok.kind) {
|
||||||
|
case Token::Kind::Eof: {
|
||||||
|
if (!if_statement_stack.empty()) {
|
||||||
|
throw_parser_error("unmatched if");
|
||||||
|
}
|
||||||
|
if (!for_statement_stack.empty()) {
|
||||||
|
throw_parser_error("unmatched for");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case Token::Kind::Text: {
|
||||||
|
current_block->nodes.emplace_back(std::make_shared<TextNode>(tok.text.data() - tmpl.content.c_str(), tok.text.size()));
|
||||||
|
} break;
|
||||||
|
case Token::Kind::StatementOpen: {
|
||||||
|
get_next_token();
|
||||||
|
if (!parse_statement(tmpl, Token::Kind::StatementClose, path)) {
|
||||||
|
throw_parser_error("expected statement, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
if (tok.kind != Token::Kind::StatementClose) {
|
||||||
|
throw_parser_error("expected statement close, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Token::Kind::LineStatementOpen: {
|
||||||
|
get_next_token();
|
||||||
|
if (!parse_statement(tmpl, Token::Kind::LineStatementClose, path)) {
|
||||||
|
throw_parser_error("expected statement, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
if (tok.kind != Token::Kind::LineStatementClose && tok.kind != Token::Kind::Eof) {
|
||||||
|
throw_parser_error("expected line statement close, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Token::Kind::ExpressionOpen: {
|
||||||
|
get_next_token();
|
||||||
|
|
||||||
|
auto expression_list_node = std::make_shared<ExpressionListNode>(tok.text.data() - tmpl.content.c_str());
|
||||||
|
current_block->nodes.emplace_back(expression_list_node);
|
||||||
|
current_expression_list = expression_list_node.get();
|
||||||
|
|
||||||
|
if (!parse_expression(tmpl, Token::Kind::ExpressionClose)) {
|
||||||
|
throw_parser_error("expected expression, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok.kind != Token::Kind::ExpressionClose) {
|
||||||
|
throw_parser_error("expected expression close, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Token::Kind::CommentOpen: {
|
||||||
|
get_next_token();
|
||||||
|
if (tok.kind != Token::Kind::CommentClose) {
|
||||||
|
throw_parser_error("expected comment close, got '" + tok.describe() + "'");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
throw_parser_error("unexpected token '" + tok.describe() + "'");
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Parser(const ParserConfig& parser_config, const LexerConfig& lexer_config, TemplateStorage& template_storage,
|
||||||
|
const FunctionStorage& function_storage)
|
||||||
|
: config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) {}
|
||||||
|
|
||||||
|
Template parse(std::string_view input, std::string_view path) {
|
||||||
|
auto result = Template(static_cast<std::string>(input));
|
||||||
|
parse_into(result, path);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Template parse(std::string_view input) {
|
||||||
|
return parse(input, "./");
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_into_template(Template& tmpl, std::string_view filename) {
|
||||||
|
std::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
|
||||||
|
|
||||||
|
// StringRef path = sys::path::parent_path(filename);
|
||||||
|
auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage);
|
||||||
|
sub_parser.parse_into(tmpl, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string load_file(const std::string& filename) {
|
||||||
|
std::ifstream file;
|
||||||
|
file.open(filename);
|
||||||
|
if (file.fail()) {
|
||||||
|
INJA_THROW(FileError("failed accessing file at '" + filename + "'"));
|
||||||
|
}
|
||||||
|
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_PARSER_HPP_
|
||||||
633
libi2pd_webconsole/inja/renderer.hpp
Normal file
633
libi2pd_webconsole/inja/renderer.hpp
Normal file
@@ -0,0 +1,633 @@
|
|||||||
|
#ifndef INCLUDE_INJA_RENDERER_HPP_
|
||||||
|
#define INCLUDE_INJA_RENDERER_HPP_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <numeric>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "exceptions.hpp"
|
||||||
|
#include "node.hpp"
|
||||||
|
#include "template.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Class for rendering a Template with data.
|
||||||
|
*/
|
||||||
|
class Renderer : public NodeVisitor {
|
||||||
|
using Op = FunctionStorage::Operation;
|
||||||
|
|
||||||
|
const RenderConfig config;
|
||||||
|
const TemplateStorage& template_storage;
|
||||||
|
const FunctionStorage& function_storage;
|
||||||
|
|
||||||
|
const Template* current_template;
|
||||||
|
size_t current_level {0};
|
||||||
|
std::vector<const Template*> template_stack;
|
||||||
|
std::vector<const BlockStatementNode*> block_statement_stack;
|
||||||
|
|
||||||
|
const json* data_input;
|
||||||
|
std::ostream* output_stream;
|
||||||
|
|
||||||
|
json additional_data;
|
||||||
|
json* current_loop_data = &additional_data["loop"];
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<json>> data_tmp_stack;
|
||||||
|
std::stack<const json*> data_eval_stack;
|
||||||
|
std::stack<const DataNode*> not_found_stack;
|
||||||
|
|
||||||
|
bool break_rendering {false};
|
||||||
|
|
||||||
|
static bool truthy(const json* data) {
|
||||||
|
if (data->is_boolean()) {
|
||||||
|
return data->get<bool>();
|
||||||
|
} else if (data->is_number()) {
|
||||||
|
return (*data != 0);
|
||||||
|
} else if (data->is_null()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !data->empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_data(const std::shared_ptr<json> value) {
|
||||||
|
if (value->is_string()) {
|
||||||
|
*output_stream << value->get_ref<const json::string_t&>();
|
||||||
|
} else if (value->is_number_integer()) {
|
||||||
|
*output_stream << value->get<const json::number_integer_t>();
|
||||||
|
} else if (value->is_null()) {
|
||||||
|
} else {
|
||||||
|
*output_stream << value->dump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::shared_ptr<json> eval_expression_list(const ExpressionListNode& expression_list) {
|
||||||
|
if (!expression_list.root) {
|
||||||
|
throw_renderer_error("empty expression", expression_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
expression_list.root->accept(*this);
|
||||||
|
|
||||||
|
if (data_eval_stack.empty()) {
|
||||||
|
throw_renderer_error("empty expression", expression_list);
|
||||||
|
} else if (data_eval_stack.size() != 1) {
|
||||||
|
throw_renderer_error("malformed expression", expression_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = data_eval_stack.top();
|
||||||
|
data_eval_stack.pop();
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
if (not_found_stack.empty()) {
|
||||||
|
throw_renderer_error("expression could not be evaluated", expression_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto node = not_found_stack.top();
|
||||||
|
not_found_stack.pop();
|
||||||
|
|
||||||
|
throw_renderer_error("variable '" + static_cast<std::string>(node->name) + "' not found", *node);
|
||||||
|
}
|
||||||
|
return std::make_shared<json>(*result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void throw_renderer_error(const std::string& message, const AstNode& node) {
|
||||||
|
SourceLocation loc = get_source_location(current_template->content, node.pos);
|
||||||
|
INJA_THROW(RenderError(message, loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_result(const json&& result) {
|
||||||
|
auto result_ptr = std::make_shared<json>(result);
|
||||||
|
data_tmp_stack.push_back(result_ptr);
|
||||||
|
data_eval_stack.push(result_ptr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N, size_t N_start = 0, bool throw_not_found = true> std::array<const json*, N> get_arguments(const FunctionNode& node) {
|
||||||
|
if (node.arguments.size() < N_start + N) {
|
||||||
|
throw_renderer_error("function needs " + std::to_string(N_start + N) + " variables, but has only found " + std::to_string(node.arguments.size()), node);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = N_start; i < N_start + N; i += 1) {
|
||||||
|
node.arguments[i]->accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_eval_stack.size() < N) {
|
||||||
|
throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<const json*, N> result;
|
||||||
|
for (size_t i = 0; i < N; i += 1) {
|
||||||
|
result[N - i - 1] = data_eval_stack.top();
|
||||||
|
data_eval_stack.pop();
|
||||||
|
|
||||||
|
if (!result[N - i - 1]) {
|
||||||
|
const auto data_node = not_found_stack.top();
|
||||||
|
not_found_stack.pop();
|
||||||
|
|
||||||
|
if (throw_not_found) {
|
||||||
|
throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool throw_not_found = true> Arguments get_argument_vector(const FunctionNode& node) {
|
||||||
|
const size_t N = node.arguments.size();
|
||||||
|
for (auto a : node.arguments) {
|
||||||
|
a->accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_eval_stack.size() < N) {
|
||||||
|
throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node);
|
||||||
|
}
|
||||||
|
|
||||||
|
Arguments result {N};
|
||||||
|
for (size_t i = 0; i < N; i += 1) {
|
||||||
|
result[N - i - 1] = data_eval_stack.top();
|
||||||
|
data_eval_stack.pop();
|
||||||
|
|
||||||
|
if (!result[N - i - 1]) {
|
||||||
|
const auto data_node = not_found_stack.top();
|
||||||
|
not_found_stack.pop();
|
||||||
|
|
||||||
|
if (throw_not_found) {
|
||||||
|
throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const BlockNode& node) {
|
||||||
|
for (auto& n : node.nodes) {
|
||||||
|
n->accept(*this);
|
||||||
|
|
||||||
|
if (break_rendering) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const TextNode& node) {
|
||||||
|
output_stream->write(current_template->content.c_str() + node.pos, node.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const ExpressionNode&) {}
|
||||||
|
|
||||||
|
void visit(const LiteralNode& node) {
|
||||||
|
data_eval_stack.push(&node.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const DataNode& node) {
|
||||||
|
if (additional_data.contains(node.ptr)) {
|
||||||
|
data_eval_stack.push(&(additional_data[node.ptr]));
|
||||||
|
} else if (data_input->contains(node.ptr)) {
|
||||||
|
data_eval_stack.push(&(*data_input)[node.ptr]);
|
||||||
|
} else {
|
||||||
|
// Try to evaluate as a no-argument callback
|
||||||
|
const auto function_data = function_storage.find_function(node.name, 0);
|
||||||
|
if (function_data.operation == FunctionStorage::Operation::Callback) {
|
||||||
|
Arguments empty_args {};
|
||||||
|
const auto value = std::make_shared<json>(function_data.callback(empty_args));
|
||||||
|
data_tmp_stack.push_back(value);
|
||||||
|
data_eval_stack.push(value.get());
|
||||||
|
} else {
|
||||||
|
data_eval_stack.push(nullptr);
|
||||||
|
not_found_stack.emplace(&node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const FunctionNode& node) {
|
||||||
|
switch (node.operation) {
|
||||||
|
case Op::Not: {
|
||||||
|
const auto args = get_arguments<1>(node);
|
||||||
|
make_result(!truthy(args[0]));
|
||||||
|
} break;
|
||||||
|
case Op::And: {
|
||||||
|
make_result(truthy(get_arguments<1, 0>(node)[0]) && truthy(get_arguments<1, 1>(node)[0]));
|
||||||
|
} break;
|
||||||
|
case Op::Or: {
|
||||||
|
make_result(truthy(get_arguments<1, 0>(node)[0]) || truthy(get_arguments<1, 1>(node)[0]));
|
||||||
|
} break;
|
||||||
|
case Op::In: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
make_result(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
|
||||||
|
} break;
|
||||||
|
case Op::Equal: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
make_result(*args[0] == *args[1]);
|
||||||
|
} break;
|
||||||
|
case Op::NotEqual: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
make_result(*args[0] != *args[1]);
|
||||||
|
} break;
|
||||||
|
case Op::Greater: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
make_result(*args[0] > *args[1]);
|
||||||
|
} break;
|
||||||
|
case Op::GreaterEqual: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
make_result(*args[0] >= *args[1]);
|
||||||
|
} break;
|
||||||
|
case Op::Less: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
make_result(*args[0] < *args[1]);
|
||||||
|
} break;
|
||||||
|
case Op::LessEqual: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
make_result(*args[0] <= *args[1]);
|
||||||
|
} break;
|
||||||
|
case Op::Add: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
if (args[0]->is_string() && args[1]->is_string()) {
|
||||||
|
make_result(args[0]->get_ref<const std::string&>() + args[1]->get_ref<const std::string&>());
|
||||||
|
} else if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
|
||||||
|
make_result(args[0]->get<int>() + args[1]->get<int>());
|
||||||
|
} else {
|
||||||
|
make_result(args[0]->get<double>() + args[1]->get<double>());
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Op::Subtract: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
|
||||||
|
make_result(args[0]->get<int>() - args[1]->get<int>());
|
||||||
|
} else {
|
||||||
|
make_result(args[0]->get<double>() - args[1]->get<double>());
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Op::Multiplication: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
|
||||||
|
make_result(args[0]->get<int>() * args[1]->get<int>());
|
||||||
|
} else {
|
||||||
|
make_result(args[0]->get<double>() * args[1]->get<double>());
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Op::Division: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
if (args[1]->get<double>() == 0) {
|
||||||
|
throw_renderer_error("division by zero", node);
|
||||||
|
}
|
||||||
|
make_result(args[0]->get<double>() / args[1]->get<double>());
|
||||||
|
} break;
|
||||||
|
case Op::Power: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
if (args[0]->is_number_integer() && args[1]->get<int>() >= 0) {
|
||||||
|
int result = static_cast<int>(std::pow(args[0]->get<int>(), args[1]->get<int>()));
|
||||||
|
make_result(result);
|
||||||
|
} else {
|
||||||
|
double result = std::pow(args[0]->get<double>(), args[1]->get<int>());
|
||||||
|
make_result(result);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Op::Modulo: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
make_result(args[0]->get<int>() % args[1]->get<int>());
|
||||||
|
} break;
|
||||||
|
case Op::AtId: {
|
||||||
|
const auto container = get_arguments<1, 0, false>(node)[0];
|
||||||
|
node.arguments[1]->accept(*this);
|
||||||
|
if (not_found_stack.empty()) {
|
||||||
|
throw_renderer_error("could not find element with given name", node);
|
||||||
|
}
|
||||||
|
const auto id_node = not_found_stack.top();
|
||||||
|
not_found_stack.pop();
|
||||||
|
data_eval_stack.pop();
|
||||||
|
data_eval_stack.push(&container->at(id_node->name));
|
||||||
|
} break;
|
||||||
|
case Op::At: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
if (args[0]->is_object()) {
|
||||||
|
data_eval_stack.push(&args[0]->at(args[1]->get<std::string>()));
|
||||||
|
} else {
|
||||||
|
data_eval_stack.push(&args[0]->at(args[1]->get<int>()));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Op::Default: {
|
||||||
|
const auto test_arg = get_arguments<1, 0, false>(node)[0];
|
||||||
|
data_eval_stack.push(test_arg ? test_arg : get_arguments<1, 1>(node)[0]);
|
||||||
|
} break;
|
||||||
|
case Op::DivisibleBy: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
const int divisor = args[1]->get<int>();
|
||||||
|
make_result((divisor != 0) && (args[0]->get<int>() % divisor == 0));
|
||||||
|
} break;
|
||||||
|
case Op::Even: {
|
||||||
|
make_result(get_arguments<1>(node)[0]->get<int>() % 2 == 0);
|
||||||
|
} break;
|
||||||
|
case Op::Exists: {
|
||||||
|
auto&& name = get_arguments<1>(node)[0]->get_ref<const std::string&>();
|
||||||
|
make_result(data_input->contains(json::json_pointer(DataNode::convert_dot_to_ptr(name))));
|
||||||
|
} break;
|
||||||
|
case Op::ExistsInObject: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
auto&& name = args[1]->get_ref<const std::string&>();
|
||||||
|
make_result(args[0]->find(name) != args[0]->end());
|
||||||
|
} break;
|
||||||
|
case Op::First: {
|
||||||
|
const auto result = &get_arguments<1>(node)[0]->front();
|
||||||
|
data_eval_stack.push(result);
|
||||||
|
} break;
|
||||||
|
case Op::Float: {
|
||||||
|
make_result(std::stod(get_arguments<1>(node)[0]->get_ref<const std::string&>()));
|
||||||
|
} break;
|
||||||
|
case Op::Int: {
|
||||||
|
make_result(std::stoi(get_arguments<1>(node)[0]->get_ref<const std::string&>()));
|
||||||
|
} break;
|
||||||
|
case Op::Last: {
|
||||||
|
const auto result = &get_arguments<1>(node)[0]->back();
|
||||||
|
data_eval_stack.push(result);
|
||||||
|
} break;
|
||||||
|
case Op::Length: {
|
||||||
|
const auto val = get_arguments<1>(node)[0];
|
||||||
|
if (val->is_string()) {
|
||||||
|
make_result(val->get_ref<const std::string&>().length());
|
||||||
|
} else {
|
||||||
|
make_result(val->size());
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Op::Lower: {
|
||||||
|
std::string result = get_arguments<1>(node)[0]->get<std::string>();
|
||||||
|
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
||||||
|
make_result(std::move(result));
|
||||||
|
} break;
|
||||||
|
case Op::Max: {
|
||||||
|
const auto args = get_arguments<1>(node);
|
||||||
|
const auto result = std::max_element(args[0]->begin(), args[0]->end());
|
||||||
|
data_eval_stack.push(&(*result));
|
||||||
|
} break;
|
||||||
|
case Op::Min: {
|
||||||
|
const auto args = get_arguments<1>(node);
|
||||||
|
const auto result = std::min_element(args[0]->begin(), args[0]->end());
|
||||||
|
data_eval_stack.push(&(*result));
|
||||||
|
} break;
|
||||||
|
case Op::Odd: {
|
||||||
|
make_result(get_arguments<1>(node)[0]->get<int>() % 2 != 0);
|
||||||
|
} break;
|
||||||
|
case Op::Range: {
|
||||||
|
std::vector<int> result(get_arguments<1>(node)[0]->get<int>());
|
||||||
|
std::iota(result.begin(), result.end(), 0);
|
||||||
|
make_result(std::move(result));
|
||||||
|
} break;
|
||||||
|
case Op::Round: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
const int precision = args[1]->get<int>();
|
||||||
|
const double result = std::round(args[0]->get<double>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
|
||||||
|
if (precision == 0) {
|
||||||
|
make_result(int(result));
|
||||||
|
} else {
|
||||||
|
make_result(result);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Op::Sort: {
|
||||||
|
auto result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
|
||||||
|
std::sort(result_ptr->begin(), result_ptr->end());
|
||||||
|
data_tmp_stack.push_back(result_ptr);
|
||||||
|
data_eval_stack.push(result_ptr.get());
|
||||||
|
} break;
|
||||||
|
case Op::Upper: {
|
||||||
|
std::string result = get_arguments<1>(node)[0]->get<std::string>();
|
||||||
|
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
|
||||||
|
make_result(std::move(result));
|
||||||
|
} break;
|
||||||
|
case Op::IsBoolean: {
|
||||||
|
make_result(get_arguments<1>(node)[0]->is_boolean());
|
||||||
|
} break;
|
||||||
|
case Op::IsNumber: {
|
||||||
|
make_result(get_arguments<1>(node)[0]->is_number());
|
||||||
|
} break;
|
||||||
|
case Op::IsInteger: {
|
||||||
|
make_result(get_arguments<1>(node)[0]->is_number_integer());
|
||||||
|
} break;
|
||||||
|
case Op::IsFloat: {
|
||||||
|
make_result(get_arguments<1>(node)[0]->is_number_float());
|
||||||
|
} break;
|
||||||
|
case Op::IsObject: {
|
||||||
|
make_result(get_arguments<1>(node)[0]->is_object());
|
||||||
|
} break;
|
||||||
|
case Op::IsArray: {
|
||||||
|
make_result(get_arguments<1>(node)[0]->is_array());
|
||||||
|
} break;
|
||||||
|
case Op::IsString: {
|
||||||
|
make_result(get_arguments<1>(node)[0]->is_string());
|
||||||
|
} break;
|
||||||
|
case Op::Callback: {
|
||||||
|
auto args = get_argument_vector(node);
|
||||||
|
make_result(node.callback(args));
|
||||||
|
} break;
|
||||||
|
case Op::Super: {
|
||||||
|
const auto args = get_argument_vector(node);
|
||||||
|
const size_t old_level = current_level;
|
||||||
|
const size_t level_diff = (args.size() == 1) ? args[0]->get<int>() : 1;
|
||||||
|
const size_t level = current_level + level_diff;
|
||||||
|
|
||||||
|
if (block_statement_stack.empty()) {
|
||||||
|
throw_renderer_error("super() call is not within a block", node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level < 1 || level > template_stack.size() - 1) {
|
||||||
|
throw_renderer_error("level of super() call does not match parent templates (between 1 and " + std::to_string(template_stack.size() - 1) + ")", node);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto current_block_statement = block_statement_stack.back();
|
||||||
|
const Template* new_template = template_stack.at(level);
|
||||||
|
const Template* old_template = current_template;
|
||||||
|
const auto block_it = new_template->block_storage.find(current_block_statement->name);
|
||||||
|
if (block_it != new_template->block_storage.end()) {
|
||||||
|
current_template = new_template;
|
||||||
|
current_level = level;
|
||||||
|
block_it->second->block.accept(*this);
|
||||||
|
current_level = old_level;
|
||||||
|
current_template = old_template;
|
||||||
|
} else {
|
||||||
|
throw_renderer_error("could not find block with name '" + current_block_statement->name + "'", node);
|
||||||
|
}
|
||||||
|
make_result(nullptr);
|
||||||
|
} break;
|
||||||
|
case Op::Join: {
|
||||||
|
const auto args = get_arguments<2>(node);
|
||||||
|
const auto separator = args[1]->get<std::string>();
|
||||||
|
std::ostringstream os;
|
||||||
|
std::string sep;
|
||||||
|
for (const auto& value : *args[0]) {
|
||||||
|
os << sep;
|
||||||
|
if (value.is_string()) {
|
||||||
|
os << value.get<std::string>(); // otherwise the value is surrounded with ""
|
||||||
|
} else {
|
||||||
|
os << value.dump();
|
||||||
|
}
|
||||||
|
sep = separator;
|
||||||
|
}
|
||||||
|
make_result(os.str());
|
||||||
|
} break;
|
||||||
|
case Op::ParenLeft:
|
||||||
|
case Op::ParenRight:
|
||||||
|
case Op::None:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const ExpressionListNode& node) {
|
||||||
|
print_data(eval_expression_list(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const StatementNode&) {}
|
||||||
|
|
||||||
|
void visit(const ForStatementNode&) {}
|
||||||
|
|
||||||
|
void visit(const ForArrayStatementNode& node) {
|
||||||
|
const auto result = eval_expression_list(node.condition);
|
||||||
|
if (!result->is_array()) {
|
||||||
|
throw_renderer_error("object must be an array", node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current_loop_data->empty()) {
|
||||||
|
auto tmp = *current_loop_data; // Because of clang-3
|
||||||
|
(*current_loop_data)["parent"] = std::move(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
(*current_loop_data)["is_first"] = true;
|
||||||
|
(*current_loop_data)["is_last"] = (result->size() <= 1);
|
||||||
|
for (auto it = result->begin(); it != result->end(); ++it) {
|
||||||
|
additional_data[static_cast<std::string>(node.value)] = *it;
|
||||||
|
|
||||||
|
(*current_loop_data)["index"] = index;
|
||||||
|
(*current_loop_data)["index1"] = index + 1;
|
||||||
|
if (index == 1) {
|
||||||
|
(*current_loop_data)["is_first"] = false;
|
||||||
|
}
|
||||||
|
if (index == result->size() - 1) {
|
||||||
|
(*current_loop_data)["is_last"] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.body.accept(*this);
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
additional_data[static_cast<std::string>(node.value)].clear();
|
||||||
|
if (!(*current_loop_data)["parent"].empty()) {
|
||||||
|
const auto tmp = (*current_loop_data)["parent"];
|
||||||
|
*current_loop_data = std::move(tmp);
|
||||||
|
} else {
|
||||||
|
current_loop_data = &additional_data["loop"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const ForObjectStatementNode& node) {
|
||||||
|
const auto result = eval_expression_list(node.condition);
|
||||||
|
if (!result->is_object()) {
|
||||||
|
throw_renderer_error("object must be an object", node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current_loop_data->empty()) {
|
||||||
|
(*current_loop_data)["parent"] = std::move(*current_loop_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
(*current_loop_data)["is_first"] = true;
|
||||||
|
(*current_loop_data)["is_last"] = (result->size() <= 1);
|
||||||
|
for (auto it = result->begin(); it != result->end(); ++it) {
|
||||||
|
additional_data[static_cast<std::string>(node.key)] = it.key();
|
||||||
|
additional_data[static_cast<std::string>(node.value)] = it.value();
|
||||||
|
|
||||||
|
(*current_loop_data)["index"] = index;
|
||||||
|
(*current_loop_data)["index1"] = index + 1;
|
||||||
|
if (index == 1) {
|
||||||
|
(*current_loop_data)["is_first"] = false;
|
||||||
|
}
|
||||||
|
if (index == result->size() - 1) {
|
||||||
|
(*current_loop_data)["is_last"] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.body.accept(*this);
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
additional_data[static_cast<std::string>(node.key)].clear();
|
||||||
|
additional_data[static_cast<std::string>(node.value)].clear();
|
||||||
|
if (!(*current_loop_data)["parent"].empty()) {
|
||||||
|
*current_loop_data = std::move((*current_loop_data)["parent"]);
|
||||||
|
} else {
|
||||||
|
current_loop_data = &additional_data["loop"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const IfStatementNode& node) {
|
||||||
|
const auto result = eval_expression_list(node.condition);
|
||||||
|
if (truthy(result.get())) {
|
||||||
|
node.true_statement.accept(*this);
|
||||||
|
} else if (node.has_false_statement) {
|
||||||
|
node.false_statement.accept(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const IncludeStatementNode& node) {
|
||||||
|
auto sub_renderer = Renderer(config, template_storage, function_storage);
|
||||||
|
const auto included_template_it = template_storage.find(node.file);
|
||||||
|
if (included_template_it != template_storage.end()) {
|
||||||
|
sub_renderer.render_to(*output_stream, included_template_it->second, *data_input, &additional_data);
|
||||||
|
} else if (config.throw_at_missing_includes) {
|
||||||
|
throw_renderer_error("include '" + node.file + "' not found", node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const ExtendsStatementNode& node) {
|
||||||
|
const auto included_template_it = template_storage.find(node.file);
|
||||||
|
if (included_template_it != template_storage.end()) {
|
||||||
|
const Template* parent_template = &included_template_it->second;
|
||||||
|
render_to(*output_stream, *parent_template, *data_input, &additional_data);
|
||||||
|
break_rendering = true;
|
||||||
|
} else if (config.throw_at_missing_includes) {
|
||||||
|
throw_renderer_error("extends '" + node.file + "' not found", node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const BlockStatementNode& node) {
|
||||||
|
const size_t old_level = current_level;
|
||||||
|
current_level = 0;
|
||||||
|
current_template = template_stack.front();
|
||||||
|
const auto block_it = current_template->block_storage.find(node.name);
|
||||||
|
if (block_it != current_template->block_storage.end()) {
|
||||||
|
block_statement_stack.emplace_back(&node);
|
||||||
|
block_it->second->block.accept(*this);
|
||||||
|
block_statement_stack.pop_back();
|
||||||
|
}
|
||||||
|
current_level = old_level;
|
||||||
|
current_template = template_stack.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const SetStatementNode& node) {
|
||||||
|
std::string ptr = node.key;
|
||||||
|
replace_substring(ptr, ".", "/");
|
||||||
|
ptr = "/" + ptr;
|
||||||
|
additional_data[json::json_pointer(ptr)] = *eval_expression_list(node.expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Renderer(const RenderConfig& config, const TemplateStorage& template_storage, const FunctionStorage& function_storage)
|
||||||
|
: config(config), template_storage(template_storage), function_storage(function_storage) {}
|
||||||
|
|
||||||
|
void render_to(std::ostream& os, const Template& tmpl, const json& data, json* loop_data = nullptr) {
|
||||||
|
output_stream = &os;
|
||||||
|
current_template = &tmpl;
|
||||||
|
data_input = &data;
|
||||||
|
if (loop_data) {
|
||||||
|
additional_data = *loop_data;
|
||||||
|
current_loop_data = &additional_data["loop"];
|
||||||
|
}
|
||||||
|
|
||||||
|
template_stack.emplace_back(current_template);
|
||||||
|
current_template->root.accept(*this);
|
||||||
|
|
||||||
|
data_tmp_stack.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_RENDERER_HPP_
|
||||||
73
libi2pd_webconsole/inja/statistics.hpp
Normal file
73
libi2pd_webconsole/inja/statistics.hpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#ifndef INCLUDE_INJA_STATISTICS_HPP_
|
||||||
|
#define INCLUDE_INJA_STATISTICS_HPP_
|
||||||
|
|
||||||
|
#include "node.hpp"
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief A class for counting statistics on a Template.
|
||||||
|
*/
|
||||||
|
class StatisticsVisitor : public NodeVisitor {
|
||||||
|
void visit(const BlockNode& node) {
|
||||||
|
for (auto& n : node.nodes) {
|
||||||
|
n->accept(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const TextNode&) {}
|
||||||
|
void visit(const ExpressionNode&) {}
|
||||||
|
void visit(const LiteralNode&) {}
|
||||||
|
|
||||||
|
void visit(const DataNode&) {
|
||||||
|
variable_counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const FunctionNode& node) {
|
||||||
|
for (auto& n : node.arguments) {
|
||||||
|
n->accept(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const ExpressionListNode& node) {
|
||||||
|
node.root->accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const StatementNode&) {}
|
||||||
|
void visit(const ForStatementNode&) {}
|
||||||
|
|
||||||
|
void visit(const ForArrayStatementNode& node) {
|
||||||
|
node.condition.accept(*this);
|
||||||
|
node.body.accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const ForObjectStatementNode& node) {
|
||||||
|
node.condition.accept(*this);
|
||||||
|
node.body.accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const IfStatementNode& node) {
|
||||||
|
node.condition.accept(*this);
|
||||||
|
node.true_statement.accept(*this);
|
||||||
|
node.false_statement.accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const IncludeStatementNode&) {}
|
||||||
|
|
||||||
|
void visit(const ExtendsStatementNode&) {}
|
||||||
|
|
||||||
|
void visit(const BlockStatementNode& node) {
|
||||||
|
node.block.accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit(const SetStatementNode&) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
unsigned int variable_counter;
|
||||||
|
|
||||||
|
explicit StatisticsVisitor(): variable_counter(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_STATISTICS_HPP_
|
||||||
37
libi2pd_webconsole/inja/template.hpp
Normal file
37
libi2pd_webconsole/inja/template.hpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef INCLUDE_INJA_TEMPLATE_HPP_
|
||||||
|
#define INCLUDE_INJA_TEMPLATE_HPP_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "node.hpp"
|
||||||
|
#include "statistics.hpp"
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The main inja Template.
|
||||||
|
*/
|
||||||
|
struct Template {
|
||||||
|
BlockNode root;
|
||||||
|
std::string content;
|
||||||
|
std::map<std::string, std::shared_ptr<BlockStatementNode>> block_storage;
|
||||||
|
|
||||||
|
explicit Template() {}
|
||||||
|
explicit Template(const std::string& content): content(content) {}
|
||||||
|
|
||||||
|
/// Return number of variables (total number, not distinct ones) in the template
|
||||||
|
int count_variables() {
|
||||||
|
auto statistic_visitor = StatisticsVisitor();
|
||||||
|
root.accept(statistic_visitor);
|
||||||
|
return statistic_visitor.variable_counter;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using TemplateStorage = std::map<std::string, Template>;
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_TEMPLATE_HPP_
|
||||||
73
libi2pd_webconsole/inja/token.hpp
Normal file
73
libi2pd_webconsole/inja/token.hpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#ifndef INCLUDE_INJA_TOKEN_HPP_
|
||||||
|
#define INCLUDE_INJA_TOKEN_HPP_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Helper-class for the inja Lexer.
|
||||||
|
*/
|
||||||
|
struct Token {
|
||||||
|
enum class Kind {
|
||||||
|
Text,
|
||||||
|
ExpressionOpen, // {{
|
||||||
|
ExpressionClose, // }}
|
||||||
|
LineStatementOpen, // ##
|
||||||
|
LineStatementClose, // \n
|
||||||
|
StatementOpen, // {%
|
||||||
|
StatementClose, // %}
|
||||||
|
CommentOpen, // {#
|
||||||
|
CommentClose, // #}
|
||||||
|
Id, // this, this.foo
|
||||||
|
Number, // 1, 2, -1, 5.2, -5.3
|
||||||
|
String, // "this"
|
||||||
|
Plus, // +
|
||||||
|
Minus, // -
|
||||||
|
Times, // *
|
||||||
|
Slash, // /
|
||||||
|
Percent, // %
|
||||||
|
Power, // ^
|
||||||
|
Comma, // ,
|
||||||
|
Dot, // .
|
||||||
|
Colon, // :
|
||||||
|
LeftParen, // (
|
||||||
|
RightParen, // )
|
||||||
|
LeftBracket, // [
|
||||||
|
RightBracket, // ]
|
||||||
|
LeftBrace, // {
|
||||||
|
RightBrace, // }
|
||||||
|
Equal, // ==
|
||||||
|
NotEqual, // !=
|
||||||
|
GreaterThan, // >
|
||||||
|
GreaterEqual, // >=
|
||||||
|
LessThan, // <
|
||||||
|
LessEqual, // <=
|
||||||
|
Unknown,
|
||||||
|
Eof,
|
||||||
|
};
|
||||||
|
|
||||||
|
Kind kind {Kind::Unknown};
|
||||||
|
std::string_view text;
|
||||||
|
|
||||||
|
explicit constexpr Token() = default;
|
||||||
|
explicit constexpr Token(Kind kind, std::string_view text): kind(kind), text(text) {}
|
||||||
|
|
||||||
|
std::string describe() const {
|
||||||
|
switch (kind) {
|
||||||
|
case Kind::Text:
|
||||||
|
return "<text>";
|
||||||
|
case Kind::LineStatementClose:
|
||||||
|
return "<eol>";
|
||||||
|
case Kind::Eof:
|
||||||
|
return "<eof>";
|
||||||
|
default:
|
||||||
|
return static_cast<std::string>(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_TOKEN_HPP_
|
||||||
70
libi2pd_webconsole/inja/utils.hpp
Normal file
70
libi2pd_webconsole/inja/utils.hpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#ifndef INCLUDE_INJA_UTILS_HPP_
|
||||||
|
#define INCLUDE_INJA_UTILS_HPP_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "exceptions.hpp"
|
||||||
|
|
||||||
|
namespace inja {
|
||||||
|
|
||||||
|
namespace string_view {
|
||||||
|
inline std::string_view slice(std::string_view view, size_t start, size_t end) {
|
||||||
|
start = std::min(start, view.size());
|
||||||
|
end = std::min(std::max(start, end), view.size());
|
||||||
|
return view.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::pair<std::string_view, std::string_view> split(std::string_view view, char Separator) {
|
||||||
|
size_t idx = view.find(Separator);
|
||||||
|
if (idx == std::string_view::npos) {
|
||||||
|
return std::make_pair(view, std::string_view());
|
||||||
|
}
|
||||||
|
return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, std::string_view::npos));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool starts_with(std::string_view view, std::string_view prefix) {
|
||||||
|
return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0);
|
||||||
|
}
|
||||||
|
} // namespace string_view
|
||||||
|
|
||||||
|
inline SourceLocation get_source_location(std::string_view content, size_t pos) {
|
||||||
|
// Get line and offset position (starts at 1:1)
|
||||||
|
auto sliced = string_view::slice(content, 0, pos);
|
||||||
|
std::size_t last_newline = sliced.rfind("\n");
|
||||||
|
|
||||||
|
if (last_newline == std::string_view::npos) {
|
||||||
|
return {1, sliced.length() + 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count newlines
|
||||||
|
size_t count_lines = 0;
|
||||||
|
size_t search_start = 0;
|
||||||
|
while (search_start <= sliced.size()) {
|
||||||
|
search_start = sliced.find("\n", search_start) + 1;
|
||||||
|
if (search_start == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
count_lines += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {count_lines + 1, sliced.length() - last_newline};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void replace_substring(std::string& s, const std::string& f, const std::string& t) {
|
||||||
|
if (f.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto pos = s.find(f); // find first occurrence of f
|
||||||
|
pos != std::string::npos; // make sure f was found
|
||||||
|
s.replace(pos, f.size(), t), // replace with t, and
|
||||||
|
pos = s.find(f, pos + t.size())) // find next occurrence of f
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace inja
|
||||||
|
|
||||||
|
#endif // INCLUDE_INJA_UTILS_HPP_
|
||||||
48
libi2pd_webconsole/nlohmann/adl_serializer.hpp
Normal file
48
libi2pd_webconsole/nlohmann/adl_serializer.hpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <nlohmann/detail/conversions/from_json.hpp>
|
||||||
|
#include <nlohmann/detail/conversions/to_json.hpp>
|
||||||
|
#include <nlohmann/detail/meta/identity_tag.hpp>
|
||||||
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/adl_serializer/
|
||||||
|
template<typename ValueType, typename>
|
||||||
|
struct adl_serializer
|
||||||
|
{
|
||||||
|
/// @brief convert a JSON value to any value type
|
||||||
|
/// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
|
||||||
|
template<typename BasicJsonType, typename TargetType = ValueType>
|
||||||
|
static auto from_json(BasicJsonType && j, TargetType& val) noexcept(
|
||||||
|
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
|
||||||
|
-> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
|
||||||
|
{
|
||||||
|
::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief convert a JSON value to any value type
|
||||||
|
/// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
|
||||||
|
template<typename BasicJsonType, typename TargetType = ValueType>
|
||||||
|
static auto from_json(BasicJsonType && j) noexcept(
|
||||||
|
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))
|
||||||
|
-> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))
|
||||||
|
{
|
||||||
|
return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief convert any value type to a JSON value
|
||||||
|
/// @sa https://json.nlohmann.me/api/adl_serializer/to_json/
|
||||||
|
template<typename BasicJsonType, typename TargetType = ValueType>
|
||||||
|
static auto to_json(BasicJsonType& j, TargetType && val) noexcept(
|
||||||
|
noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))
|
||||||
|
-> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())
|
||||||
|
{
|
||||||
|
::nlohmann::to_json(j, std::forward<TargetType>(val));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace nlohmann
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user