Compare commits

..

8 Commits

Author SHA1 Message Date
R4SAS
31936f6025 [make] change daemon sources list, add unix/win32 depending on system
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-06-08 23:03:52 +03:00
R4SAS
f3dcc5364f [make] update Unix daemon source name
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-06-08 22:57:37 +03:00
R4SAS
fbe2e734c2 [daemon] WIP: rework accessing from webconsole and App
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-06-08 22:51:09 +03:00
R4SAS
78193fc8f8 [daemon] WIP: use callbacks to work with daemon
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-06-08 19:35:23 +03:00
R4SAS
463d43b0bb [cmake] remove HTTPServer.cpp from daemon sources
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-06-08 00:40:50 +03:00
R4SAS
7197fce349 [webconsole] add base templates from current code
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-06-08 00:37:20 +03:00
R4SAS
5ba387ba2b [cmake] add webconsole library
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-06-07 22:50:44 +03:00
R4SAS
a843be75f3 start work on webconsole with templates
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2022-06-07 22:45:53 +03:00
142 changed files with 27706 additions and 6784 deletions

View File

@@ -1,32 +0,0 @@
name: Build Debian packages
on: [push, pull_request]
jobs:
build:
name: ${{ matrix.dist }}
runs-on: ubuntu-latest
strategy:
matrix:
dist: ['buster', 'bullseye', 'bookworm']
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: change debian changelog
run: |
sudo apt-get update
sudo apt-get install devscripts
debchange -v "`git describe --tags`-${{ matrix.dist }}" -b -M --distribution ${{ matrix.dist }} "trunk build"
- uses: jtdor/build-deb-action@v1
with:
docker-image: debian:${{ matrix.dist }}-slim
buildpackage-opts: --build=binary --no-sign
- uses: actions/upload-artifact@v3
with:
name: i2pd_${{ matrix.dist }}
path: debian/artifacts/i2pd_*.deb
- uses: actions/upload-artifact@v3
with:
name: i2pd-dbgsym_${{ matrix.dist }}
path: debian/artifacts/i2pd-dbgsym_*.deb

View File

@@ -4,25 +4,18 @@ on: [push, pull_request]
jobs:
build:
runs-on: macos-12
runs-on: macos-10.15
name: with UPnP
steps:
- uses: actions/checkout@v2
- name: Test in FreeBSD
id: test
uses: vmactions/freebsd-vm@v0.3.0
uses: vmactions/freebsd-vm@v0.1.5
with:
usesh: true
mem: 2048
sync: rsync
copyback: true
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
run: |
cd build
cmake -DWITH_UPNP=ON -DCMAKE_BUILD_TYPE=Release .
gmake -j2
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: i2pd-freebsd
path: build/i2pd

View File

@@ -38,3 +38,51 @@ jobs:
cd build
cmake -DWITH_UPNP=${{ matrix.with_upnp }} .
make -j3
build-deb-stretch:
name: Build package for stretch
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: change debian changelog
run: |
sudo apt-get update
sudo apt-get install devscripts
debchange -v "`git describe --tags`-stretch" -b -M --distribution stretch "trunk build"
- uses: singingwolfboy/build-dpkg-stretch@v1
id: build
with:
args: --unsigned-source --unsigned-changes -b
- uses: actions/upload-artifact@v1
with:
name: ${{ steps.build.outputs.filename }}
path: ${{ steps.build.outputs.filename }}
- uses: actions/upload-artifact@v1
with:
name: ${{ steps.build.outputs.filename-dbgsym }}
path: ${{ steps.build.outputs.filename-dbgsym }}
build-deb-buster:
name: Build package for buster
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: change debian changelog
run: |
sudo apt-get update
sudo apt-get install devscripts
debchange -v "`git describe --tags`-buster" -b -M --distribution buster "trunk build"
- uses: singingwolfboy/build-dpkg-buster@v1
id: build
with:
args: --unsigned-source --unsigned-changes -b
- uses: actions/upload-artifact@v1
with:
name: ${{ steps.build.outputs.filename }}
path: ${{ steps.build.outputs.filename }}
- uses: actions/upload-artifact@v1
with:
name: ${{ steps.build.outputs.filename-dbgsym }}
path: ${{ steps.build.outputs.filename-dbgsym }}

View File

@@ -4,137 +4,67 @@ on:
push:
branches:
- openssl
- docker
tags:
- '*'
jobs:
build:
docker:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
strategy:
matrix:
include: [
{ platform: 'linux/amd64', archname: 'amd64' },
{ platform: 'linux/386', archname: 'i386' },
{ platform: 'linux/arm64', archname: 'arm64' },
{ platform: 'linux/arm/v7', archname: 'armv7' },
]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v2
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry
uses: docker/login-action@v2
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build container for ${{ matrix.archname }}
uses: docker/build-push-action@v3
- name: Build and push trunk container
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
uses: docker/build-push-action@v2
with:
context: ./contrib/docker
file: ./contrib/docker/Dockerfile
platforms: ${{ matrix.platform }}
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
push: true
tags: |
purplei2p/i2pd:latest-${{ matrix.archname }}
ghcr.io/purplei2p/i2pd:latest-${{ matrix.archname }}
purplei2p/i2pd:latest
ghcr.io/purplei2p/i2pd:latest
push:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
needs: build
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push latest manifest image to Docker Hub
uses: Noelware/docker-manifest-action@master
with:
base-image: purplei2p/i2pd:latest
extra-images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
push: true
- name: Create and push latest manifest image to GHCR
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/purplei2p/i2pd:latest
extra-images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
push: true
- name: Store release version to env
- name: Set env
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
- name: Create and push release manifest image to Docker Hub
- name: Build and push release container
if: ${{ startsWith(github.ref, 'refs/tags/') }}
uses: Noelware/docker-manifest-action@master
uses: docker/build-push-action@v2
with:
base-image: purplei2p/i2pd:latest-release
extra-images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
push: true
- name: Create and push release manifest image to GHCR
if: ${{ startsWith(github.ref, 'refs/tags/') }}
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/purplei2p/i2pd:latest-release
extra-images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
push: true
- name: Create and push versioned manifest image to Docker Hub
if: ${{ startsWith(github.ref, 'refs/tags/') }}
uses: Noelware/docker-manifest-action@master
with:
base-image: purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
extra-images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
push: true
- name: Create and push versioned manifest image to GHCR
if: ${{ startsWith(github.ref, 'refs/tags/') }}
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
extra-images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
context: ./contrib/docker
file: ./contrib/docker/Dockerfile
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
push: true
tags: |
purplei2p/i2pd:latest
purplei2p/i2pd:latest-release
purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
ghcr.io/purplei2p/i2pd:latest
ghcr.io/purplei2p/i2pd:latest-release
ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}

3
.gitignore vendored
View File

@@ -8,12 +8,15 @@ netDb
/libi2pd.a
/libi2pdclient.a
/libi2pdlang.a
/libi2pdwebconsole.a
/libi2pd.so
/libi2pdclient.so
/libi2pdlang.so
/libi2pdwebconsole.so
/libi2pd.dll
/libi2pdclient.dll
/libi2pdlang.dll
/libi2pdwebconsole.dll
*.exe

View File

@@ -1,66 +1,6 @@
# for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog
## [2.44.0] - 2022-11-20
### Added
- SSL connection for server I2P tunnels
- Localization to Italian and Spanish
- SSU2 through SOCKS5 UDP proxy
- Reload tunnels through web console
- SSU2 send immediate ack request flag
- SSU2 send and verify path challenge
- Configurable ssu2.mtu4 and ssu2.mtu6
### Changed
- SSU2 is enbaled and SSU is disabled by default
- Separate network status and error
- Random selection between NTCP2 and SSU2 priority
- Added notbob.i2p to jump services
- Remove DoNotTrack flag from HTTP Request header
- Skip addresshelper page if destination was not changed
- SSU2 allow different ports from RelayReponse and HolePunch
- SSU2 resend PeerTest msg 1 and msg 2
- SSU2 Send Retry instead SessionCreated if clock skew detected
### Fixed
- Long HTTP headers for HTTP proxy and HTTP server tunnel
- SSU2 resends and resend limits
- Crash at startup if addressbook is disabled
- NTCP2 ipv6 connection through SOCKS5 proxy
- SSU2 SessionRequest with zero token
- SSU2 MTU less than 1280
- SSU2 port=1
- Incorrect addresses from network interfaces
- Definitions for Darwin PPC; do not use pthread_setname_np
## [2.43.0] - 2022-08-22
### Added
- Complete SSU2 implementation
- Localization to Chinese
- Send RouterInfo update for long live sessions
- Explicit ipv6 ranges of known tunnel brokers for MTU detection
- Always send "Connection: close" and strip out Keep-Alive for server HTTP tunnel
- Show ports for all transports in web console
- Translation of webconsole site title
- Support for Windows ProgramData path when running as service
- Ability to turn off address book
- Handle signals TSTP and CONT to stop and resume network
### Changed
- Case insensitive headers for server HTTP tunnel
- Do not show 'Address registration' line if LeaseSet is encrypted
- SSU2 transports have higher priority than SSU
- Disable ElGamal precalculated table if no SSU
- Deprecate limits.ntcpsoft, limits.ntcphard and limits.ntcpthreads config options
- SSU2 is enabled and SSU is disabled by default for new installations
### Fixed
- Typo with Referer header name in HTTP proxy
- Can't handle garlic message from an exploratory tunnel
- Incorrect encryption key for exploratory lookup reply
- Bound checks issues in LeaseSets code
- MTU detection on Windows
- Crash on stop of active server tunnel
- Send datagram to wrong destination in SAM
- Incorrect static key in RouterInfo if the keys were regenerated
- Duplicated sessions in BOB
## [2.42.1] - 2022-05-24
### Fixed
- Incorrect jump link in HTTP Proxy
@@ -441,7 +381,7 @@
### Added
- Client auth flag for b33 address
### Changed
- Remove incoming NTCP2 session from pending list when established
- Remove incoming NTCP2 session from pending list when established
- Handle errors for NTCP2 SessionConfrimed send
### Fixed
- Failure to start on Windows XP
@@ -745,7 +685,7 @@
### Added
- Datagram i2p tunnels
- Unique local addresses for server tunnels
- Configurable list of reseed servers and initial addressbook
- Configurable list of reseed servers and initial addressbook
- Configurable netid
- Initial iOS support

View File

@@ -12,19 +12,22 @@ endif
SHLIB := libi2pd.$(SHARED_SUFFIX)
ARLIB := libi2pd.a
SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
ARLIB_LANG := libi2pdlang.a
SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX)
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)
ARLIB_WRAP := libi2pdwrapper.a
I2PD := i2pd
LIB_SRC_DIR := libi2pd
LIB_CLIENT_SRC_DIR := libi2pd_client
WRAP_SRC_DIR := libi2pd_wrapper
WEBCONSOLE_SRC_DIR := libi2pd_webconsole
LANG_SRC_DIR := i18n
DAEMON_SRC_DIR := daemon
WRAP_SRC_DIR := libi2pd_wrapper
# import source files lists
include filelist.mk
@@ -48,20 +51,20 @@ else
endif
ifneq (, $(findstring darwin, $(SYS)))
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonUnix.cpp
ifeq ($(HOMEBREW),1)
include Makefile.homebrew
else
include Makefile.osx
endif
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
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
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
include Makefile.mingw
else # not supported
$(error Not supported platform)
@@ -72,31 +75,34 @@ ifeq ($(USE_GIT_VERSION),yes)
NEEDED_CXXFLAGS += -DGITVER=\"$(GIT_VERSION)\"
endif
NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR) -DOPENSSL_SUPPRESS_DEPRECATED
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_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_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))
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
all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD)
all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(ARLIB_WEBCONSOLE) $(I2PD)
mk_obj_dir:
@mkdir -p obj/$(LIB_SRC_DIR)
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
@mkdir -p obj/$(LANG_SRC_DIR)
@mkdir -p obj/$(WEBCONSOLE_SRC_DIR)
@mkdir -p obj/$(DAEMON_SRC_DIR)
@mkdir -p obj/$(WRAP_SRC_DIR)
@mkdir -p obj/Win32
api: $(SHLIB) $(ARLIB)
client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
lang: $(SHLIB_LANG) $(ARLIB_LANG)
api_client: api client lang
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
api: $(SHLIB) $(ARLIB)
client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
lang: $(SHLIB_LANG) $(ARLIB_LANG)
webconsole: $(SHLIB_WEBCONSOLE) $(ARLIB_WEBCONSOLE)
api_client: api client lang webconsole
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
## **without** overwriting the CXXFLAGS which we need in order to build.
@@ -111,7 +117,7 @@ obj/%.o: %.cpp | mk_obj_dir
# '-' is 'ignore if missing' on first run
-include $(DEPS)
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
$(I2PD): $(DAEMON_OBJS) $(ARLIB_WEBCONSOLE) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
$(SHLIB): $(LIB_OBJS) $(SHLIB_LANG)
@@ -124,12 +130,17 @@ ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG)
endif
$(SHLIB_WRAP): $(WRAP_LIB_OBJS)
$(SHLIB_LANG): $(LANG_OBJS)
ifneq ($(USE_STATIC),yes)
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
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)
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
endif
@@ -140,18 +151,21 @@ $(ARLIB): $(LIB_OBJS)
$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
$(AR) -r $@ $^
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
$(ARLIB_LANG): $(LANG_OBJS)
$(AR) -r $@ $^
$(ARLIB_LANG): $(LANG_OBJS)
$(ARLIB_WEBCONSOLE): $(WEBCONSOLE_OBJS)
$(AR) -r $@ $^
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
$(AR) -r $@ $^
clean:
$(RM) -r obj
$(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 $^
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)

View File

@@ -39,18 +39,13 @@ ifeq ($(USE_AESNI),yes)
endif
install: all
install -d ${PREFIX}/bin
install -m 755 ${I2PD} ${PREFIX}/bin
install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d
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 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd
install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
install -d ${PREFIX}/share/i2pd
@cp -R contrib/certificates ${PREFIX}/share/i2pd/
install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1
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
install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd
@ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates
@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/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt

View File

@@ -64,18 +64,13 @@ endif
endif
install: all
install -d ${PREFIX}/bin
install -m 755 ${I2PD} ${PREFIX}/bin
install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d
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
install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
install -d ${PREFIX}/share/i2pd
@cp -R contrib/certificates ${PREFIX}/share/i2pd/
install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1
@gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1
install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd
@ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates
@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

View File

@@ -4,7 +4,7 @@ USE_WIN32_APP := yes
WINDRES = windres
CXXFLAGS := $(CXX_DEBUG) -fPIC -msse
INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32
INCFLAGS = -IWin32
LDFLAGS := ${LD_DEBUG} -static
NEEDED_CXXFLAGS += -std=c++17 -DWIN32_LEAN_AND_MEAN

View File

@@ -18,7 +18,7 @@
#include "Tunnel.h"
#include "version.h"
#include "resource.h"
#include "Daemon.h"
#include "Win32App.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_SEPARATOR, 0, NULL);
if(!i2p::context.AcceptsTunnels())
InsertMenu (hPopup, -1,
i2p::util::DaemonWin32::Instance ().isGraceful ? MF_BYPOSITION | MF_STRING | MF_GRAYED : MF_BYPOSITION | MF_STRING,
ID_ACCEPT_TRANSIT, "Accept &transit");
if(m_getIsGraceful)
if(m_getIsGraceful())
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
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
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");
else
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown");
@@ -154,23 +156,25 @@ namespace win32
case eRouterStatusUnknown: s << "Unk"; break;
case eRouterStatusProxy: s << "Proxy"; break;
case eRouterStatusMesh: s << "Mesh"; break;
default: s << "Unk";
};
if (i2p::context.GetError () != eRouterErrorNone)
{
switch (i2p::context.GetError ())
case eRouterStatusError:
{
case eRouterErrorClockSkew:
s << " - Clock skew";
s << "Err";
switch (i2p::context.GetError ())
{
case eRouterErrorClockSkew:
s << " - Clock skew";
break;
case eRouterErrorOffline:
s << " - Offline";
break;
case eRouterErrorSymmetricNAT:
s << " - Symmetric NAT";
break;
default: ;
}
break;
case eRouterErrorOffline:
s << " - Offline";
break;
case eRouterErrorSymmetricNAT:
s << " - Symmetric NAT";
break;
default: ;
}
default: s << "Unk";
}
}
@@ -268,7 +272,7 @@ namespace win32
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
g_GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
i2p::util::DaemonWin32::Instance ().isGraceful = true;
if (m_setIsGraceful) m_setIsGraceful(true);
return 0;
}
case ID_STOP_GRACEFUL_SHUTDOWN:
@@ -277,7 +281,7 @@ namespace win32
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
g_GracefulShutdownEndtime = 0;
i2p::util::DaemonWin32::Instance ().isGraceful = false;
if (m_setIsGraceful) m_setIsGraceful(false);
return 0;
}
case ID_RELOAD:

View File

@@ -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
*
@@ -22,6 +22,15 @@ namespace win32
int RunWin32App ();
bool GracefulShutdown ();
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__

View File

@@ -10,7 +10,6 @@
#include <assert.h>
#include <windows.h>
#include "Daemon.h"
#include "Log.h"
I2PService *I2PService::s_service = NULL;
@@ -132,7 +131,13 @@ void I2PService::Start(DWORD dwArgc, PSTR *pszArgv)
void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv)
{
LogPrint(eLogInfo, "Win32Service: in OnStart (", EVENTLOG_INFORMATION_TYPE, ")");
Daemon.start();
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));
}
@@ -171,7 +176,11 @@ void I2PService::OnStop()
{
// Log a service stop message to the Application log.
LogPrint(eLogInfo, "Win32Service: in OnStop (", EVENTLOG_INFORMATION_TYPE, ")");
Daemon.stop();
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)
{

View File

@@ -6,9 +6,10 @@
* See full license text in LICENSE file at top of project tree
*/
#ifndef WIN_32_SERVICE_H__
#define WIN_32_SERVICE_H__
#ifndef WIN32SERVICE_H__
#define WIN32SERVICE_H__
#include <functional>
#include <thread>
#include <windows.h>
@@ -29,6 +30,13 @@ class I2PService
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);
@@ -58,6 +66,11 @@ class I2PService
HANDLE m_hStoppedEvent;
std::thread* _worker;
private:
DaemonStart m_daemonStart;
DaemonStop m_daemonStop;
};
#endif // WIN_32_SERVICE_H__
#endif // WIN32SERVICE_H__

View File

@@ -17,13 +17,12 @@ option(WITH_LIBRARY "Build library" ON)
option(WITH_BINARY "Build binary" ON)
option(WITH_STATIC "Static build" OFF)
option(WITH_UPNP "Include support for UPnP client" OFF)
option(WITH_GIT_VERSION "Use git commit info as version" OFF)
option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF)
option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF)
# paths
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
set(CMAKE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
set(CMAKE_SOURCE_DIR "..")
#Handle paths nicely
include(GNUInstallDirs)
@@ -34,11 +33,13 @@ target_architecture(ARCHITECTURE)
set(LIBI2PD_SRC_DIR ../libi2pd)
set(LIBI2PD_CLIENT_SRC_DIR ../libi2pd_client)
set(WEBCONSOLE_SRC_DIR ../libi2pd_webconsole)
set(LANG_SRC_DIR ../i18n)
set(DAEMON_SRC_DIR ../daemon)
include_directories(${LIBI2PD_SRC_DIR})
include_directories(${LIBI2PD_CLIENT_SRC_DIR})
include_directories(${WEBCONSOLE_SRC_DIR})
include_directories(${LANG_SRC_DIR})
include_directories(${DAEMON_SRC_DIR})
@@ -69,6 +70,18 @@ if(WITH_LIBRARY)
COMPONENT Libraries)
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)
add_library(libi2pdlang ${LANG_SRC})
set_target_properties(libi2pdlang PROPERTIES PREFIX "")
@@ -83,9 +96,7 @@ endif()
set(DAEMON_SRC
"${DAEMON_SRC_DIR}/Daemon.cpp"
"${DAEMON_SRC_DIR}/HTTPServer.cpp"
"${DAEMON_SRC_DIR}/I2PControl.cpp"
"${DAEMON_SRC_DIR}/I2PControlHandlers.cpp"
"${DAEMON_SRC_DIR}/i2pd.cpp"
"${DAEMON_SRC_DIR}/UPnP.cpp"
)
@@ -94,12 +105,6 @@ if(WITH_UPNP)
add_definitions(-DUSE_UPNP)
endif()
if(WITH_GIT_VERSION)
include(GetGitRevisionDescription)
git_describe(GIT_VERSION)
add_definitions(-DGITVER="${GIT_VERSION}")
endif()
if(APPLE)
add_definitions(-DMAC_OSX)
endif()
@@ -143,7 +148,7 @@ endif()
# compiler flags customization(by system)
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))
# "'sleep_for' is not a member of 'std::this_thread'" in gcc 4.7/4.8
add_definitions("-D_GLIBCXX_USE_NANOSLEEP=1")
@@ -172,13 +177,6 @@ if(WITH_THREADSANITIZER)
endif()
# Enable usage of STD's Atomic instead of Boost's on PowerPC
# For more information refer to https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111
if(ARCHITECTURE MATCHES "ppc")
add_definitions(-DBOOST_SP_USE_STD_ATOMIC)
endif()
# libraries
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
@@ -186,7 +184,6 @@ find_package(Threads REQUIRED)
if(WITH_STATIC)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON)
set(OPENSSL_USE_STATIC_LIBS ON)
set(BUILD_SHARED_LIBS OFF)
if(${CMAKE_CXX_COMPILER} MATCHES ".*-openwrt-.*")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
@@ -201,6 +198,8 @@ 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)
endif()
target_link_libraries(libi2pdwebconsole libi2pdclient libi2pd libi2pdlang)
find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED)
if(NOT DEFINED Boost_INCLUDE_DIRS)
message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!")
@@ -249,7 +248,6 @@ message(STATUS " LIBRARY : ${WITH_LIBRARY}")
message(STATUS " BINARY : ${WITH_BINARY}")
message(STATUS " STATIC BUILD : ${WITH_STATIC}")
message(STATUS " UPnP : ${WITH_UPNP}")
message(STATUS " GIT VERSION : ${WITH_GIT_VERSION}")
message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}")
message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}")
message(STATUS "---------------------------------------")
@@ -265,17 +263,23 @@ if(WITH_BINARY)
set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-z relro -z now")
endif()
if(WITH_UPNP)
set(UPNP_LIB ${MINIUPNPC_LIBRARY})
endif()
# FindBoost pulls pthread for thread which is broken for static linking at least on Ubuntu 15.04
list(GET Boost_LIBRARIES -1 LAST_Boost_LIBRARIES)
if(${LAST_Boost_LIBRARIES} MATCHES ".*pthread.*")
list(REMOVE_AT Boost_LIBRARIES -1)
endif()
if(WITH_STATIC)
set(DL_LIB ${CMAKE_DL_LIBS})
endif()
target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto ${MINIUPNPC_LIBRARY} ZLIB::ZLIB Threads::Threads ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY})
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)
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")

View File

@@ -1,284 +0,0 @@
# - Returns a version string from Git
#
# These functions force a re-configure on each git commit so that you can
# trust the values of the variables in your build system.
#
# get_git_head_revision(<refspecvar> <hashvar> [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR])
#
# Returns the refspec and sha hash of the current head revision
#
# git_describe(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the source tree, and adjusting
# the output so that it tests false if an error occurs.
#
# git_describe_working_tree(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the working tree (--dirty option),
# and adjusting the output so that it tests false if an error occurs.
#
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe --exact-match on the source tree,
# and adjusting the output so that it tests false if there was no exact
# matching tag.
#
# git_local_changes(<var>)
#
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
# Uses the return code of "git diff-index --quiet HEAD --".
# Does not regard untracked files.
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
# http://academic.cleardefinition.com
#
# Copyright 2009-2013, Iowa State University.
# Copyright 2013-2020, Ryan Pavlik
# Copyright 2013-2020, Contributors
# SPDX-License-Identifier: BSL-1.0
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__get_git_revision_description)
return()
endif()
set(__get_git_revision_description YES)
# We must run the following at "include" time, not at function call time,
# to find the path to this module rather than the path to a calling list file
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
# Function _git_find_closest_git_dir finds the next closest .git directory
# that is part of any directory in the path defined by _start_dir.
# The result is returned in the parent scope variable whose name is passed
# as variable _git_dir_var. If no .git directory can be found, the
# function returns an empty string via _git_dir_var.
#
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
# neither foo nor bar contain a file/directory .git. This wil return
# C:/bla/.git
#
function(_git_find_closest_git_dir _start_dir _git_dir_var)
set(cur_dir "${_start_dir}")
set(git_dir "${_start_dir}/.git")
while(NOT EXISTS "${git_dir}")
# .git dir not found, search parent directories
set(git_previous_parent "${cur_dir}")
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
if(cur_dir STREQUAL git_previous_parent)
# We have reached the root directory, we are not in git
set(${_git_dir_var}
""
PARENT_SCOPE)
return()
endif()
set(git_dir "${cur_dir}/.git")
endwhile()
set(${_git_dir_var}
"${git_dir}"
PARENT_SCOPE)
endfunction()
function(get_git_head_revision _refspecvar _hashvar)
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR")
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE)
else()
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE)
endif()
if(NOT "${GIT_DIR}" STREQUAL "")
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}"
"${GIT_DIR}")
if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR)
# We've gone above the CMake root dir.
set(GIT_DIR "")
endif()
endif()
if("${GIT_DIR}" STREQUAL "")
set(${_refspecvar}
"GITDIR-NOTFOUND"
PARENT_SCOPE)
set(${_hashvar}
"GITDIR-NOTFOUND"
PARENT_SCOPE)
return()
endif()
# Check if the current source dir is a git submodule or a worktree.
# In both cases .git is a file instead of a directory.
#
if(NOT IS_DIRECTORY ${GIT_DIR})
# The following git command will return a non empty string that
# points to the super project working tree if the current
# source dir is inside a git submodule.
# Otherwise the command will return an empty string.
#
execute_process(
COMMAND "${GIT_EXECUTABLE}" rev-parse
--show-superproject-working-tree
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT "${out}" STREQUAL "")
# If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
file(READ ${GIT_DIR} submodule)
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE
${submodule})
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE}
ABSOLUTE)
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
else()
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
file(READ ${GIT_DIR} worktree_ref)
# The .git directory contains a path to the worktree information directory
# inside the parent git repo of the worktree.
#
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir
${worktree_ref})
string(STRIP ${git_worktree_dir} git_worktree_dir)
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
endif()
else()
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
endif()
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
if(NOT EXISTS "${GIT_DATA}")
file(MAKE_DIRECTORY "${GIT_DATA}")
endif()
if(NOT EXISTS "${HEAD_SOURCE_FILE}")
return()
endif()
set(HEAD_FILE "${GIT_DATA}/HEAD")
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
"${GIT_DATA}/grabRef.cmake" @ONLY)
include("${GIT_DATA}/grabRef.cmake")
set(${_refspecvar}
"${HEAD_REF}"
PARENT_SCOPE)
set(${_hashvar}
"${HEAD_HASH}"
PARENT_SCOPE)
endfunction()
function(git_describe _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var}
"HEAD-HASH-NOTFOUND"
PARENT_SCOPE)
return()
endif()
# TODO sanitize
#if((${ARGN}" MATCHES "&&") OR
# (ARGN MATCHES "||") OR
# (ARGN MATCHES "\\;"))
# message("Please report the following error to the project!")
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
#endif()
#message(STATUS "Arguments to execute_process: ${ARGN}")
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_describe_working_tree _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN})
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_local_changes _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var}
"HEAD-HASH-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(res EQUAL 0)
set(${_var}
"CLEAN"
PARENT_SCOPE)
else()
set(${_var}
"DIRTY"
PARENT_SCOPE)
endif()
endfunction()

View File

@@ -1,43 +0,0 @@
#
# Internal file for GetGitRevisionDescription.cmake
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright 2009-2012, Iowa State University
# Copyright 2011-2015, Contributors
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
# SPDX-License-Identifier: BSL-1.0
set(HEAD_HASH)
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
if(HEAD_CONTENTS MATCHES "ref")
# named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
else()
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
set(HEAD_HASH "${CMAKE_MATCH_1}")
endif()
endif()
else()
# detached HEAD
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif()
if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif()

View File

@@ -61,7 +61,7 @@ set(archdetect_c_code "
#else
#error cmake_ARCH mips
#endif
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) || defined(__POWERPC__) \\
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \\
|| defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \\
|| defined(_M_MPPC) || defined(_M_PPC)
#if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)

View File

@@ -1,18 +1,5 @@
#
# Copyright (c) 2017-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
#
FROM alpine:latest
LABEL authors="Mikal Villa <mikal@sigterm.no>, Darknet Villain <supervillain@riseup.net>"
LABEL maintainer="R4SAS <r4sas@i2pmail.org>"
LABEL org.opencontainers.image.source=https://github.com/PurpleI2P/i2pd
LABEL org.opencontainers.image.documentation=https://i2pd.readthedocs.io/en/latest/
LABEL org.opencontainers.image.licenses=BSD3
LABEL authors "Mikal Villa <mikal@sigterm.no>, Darknet Villain <supervillain@riseup.net>"
# Expose git branch, tag and URL variables as arguments
ARG GIT_BRANCH="openssl"
@@ -24,28 +11,27 @@ ENV REPO_URL=${REPO_URL}
ENV I2PD_HOME="/home/i2pd"
ENV DATA_DIR="${I2PD_HOME}/data"
ENV DEFAULT_ARGS=" --datadir=$DATA_DIR"
ENV DEFAULT_ARGS=" --datadir=$DATA_DIR --reseed.verify=true --upnp.enabled=false --http.enabled=true --http.address=0.0.0.0 --httpproxy.enabled=true --httpproxy.address=0.0.0.0 --socksproxy.enabled=true --socksproxy.address=0.0.0.0 --sam.enabled=true --sam.address=0.0.0.0"
RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \
&& adduser -S -h "$I2PD_HOME" i2pd \
&& chown -R i2pd:nobody "$I2PD_HOME"
# 1. Building binary
# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the
# image under 20mb we need to remove all the build dependencies in the same "RUN" / layer.
#
# 1. install deps, clone and build.
# 2. strip binaries.
# 3. Purge all dependencies and other unrelated packages, including build directory.
# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the
# image under 20mb we need to remove all the build dependencies in the same "RUN" / layer.
#
# 1. install deps, clone and build.
# 2. strip binaries.
# 3. Purge all dependencies and other unrelated packages, including build directory.
RUN apk update \
&& apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl miniupnpc-dev git \
&& mkdir -p /tmp/build \
&& cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \
&& cd i2pd \
&& if [ -n "${GIT_TAG}" ]; then git checkout tags/${GIT_TAG}; fi \
&& make -j$(nproc) USE_UPNP=yes \
&& make USE_UPNP=yes \
&& cp -R contrib/certificates /i2pd_certificates \
&& mkdir -p /usr/local/bin \
&& mv i2pd /usr/local/bin \
@@ -59,9 +45,6 @@ RUN apk update \
# 2. Adding required libraries to run i2pd to ensure it will run.
RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl miniupnpc musl-utils libstdc++
# 3. Copy preconfigured config file and entrypoint
COPY i2pd-docker.conf "$I2PD_HOME/i2pd.conf"
RUN chown i2pd:nobody "$I2PD_HOME/i2pd.conf"
COPY entrypoint.sh /entrypoint.sh
RUN chmod a+x /entrypoint.sh

View File

@@ -1,52 +0,0 @@
## Preconfigured i2pd configuration file for a Docker container
## See https://i2pd.readthedocs.io/en/latest/user-guide/configuration/
## for more options you can use in this file.
## Note that for exposing ports outside of container you need to bind all services to 0.0.0.0
log = file
loglevel = none
ipv4 = true
ipv6 = false
# bandwidth = L
# notransit = false
# floodfill = false
[ntcp2]
enabled = true
published = true
[ssu2]
enabled = true
published = true
[http]
enabled = true
address = 0.0.0.0
port = 7070
[httpproxy]
enabled = true
address = 0.0.0.0
port = 4444
[socksproxy]
enabled = true
address = 0.0.0.0
port = 4447
[sam]
enabled = true
address = 0.0.0.0
port = 7656
[upnp]
enabled = false
[reseed]
verify = true
[limits]
# transittunnels = 2500

View File

@@ -1,13 +1,13 @@
# i2pd
# Copyright (C) 2021-2022 PurpleI2P team
# Copyright (C) 2021 PurpleI2P team
# This file is distributed under the same license as the i2pd package.
# R4SAS <r4sas@i2pmail.org>, 2021-2022.
# R4SAS <r4sas@i2pmail.org>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: i2pd\n"
"Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n"
"POT-Creation-Date: 2022-07-26 21:22\n"
"POT-Creation-Date: 2021-08-06 17:12\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -18,712 +18,706 @@ msgstr ""
"X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n"
"X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n"
#: daemon/HTTPServer.cpp:108
#: daemon/HTTPServer.cpp:177
msgid "day"
msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:112
#: daemon/HTTPServer.cpp:181
msgid "hour"
msgid_plural "hours"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:116
#: daemon/HTTPServer.cpp:185
msgid "minute"
msgid_plural "minutes"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:119
#: daemon/HTTPServer.cpp:188
msgid "second"
msgid_plural "seconds"
msgstr[0] ""
msgstr[1] ""
#. tr: Kibibit
#: daemon/HTTPServer.cpp:127 daemon/HTTPServer.cpp:155
#: daemon/HTTPServer.cpp:196 daemon/HTTPServer.cpp:224
msgid "KiB"
msgstr ""
#. tr: Mebibit
#: daemon/HTTPServer.cpp:129
#: daemon/HTTPServer.cpp:198
msgid "MiB"
msgstr ""
#. tr: Gibibit
#: daemon/HTTPServer.cpp:131
#: daemon/HTTPServer.cpp:200
msgid "GiB"
msgstr ""
#: daemon/HTTPServer.cpp:148
#: daemon/HTTPServer.cpp:217
msgid "building"
msgstr ""
#: daemon/HTTPServer.cpp:149
#: daemon/HTTPServer.cpp:218
msgid "failed"
msgstr ""
#: daemon/HTTPServer.cpp:150
#: daemon/HTTPServer.cpp:219
msgid "expiring"
msgstr ""
#: daemon/HTTPServer.cpp:151
#: daemon/HTTPServer.cpp:220
msgid "established"
msgstr ""
#: daemon/HTTPServer.cpp:152
#: daemon/HTTPServer.cpp:221
msgid "unknown"
msgstr ""
#: daemon/HTTPServer.cpp:154
#: daemon/HTTPServer.cpp:223
msgid "exploratory"
msgstr ""
#. tr: Webconsole page title
#: daemon/HTTPServer.cpp:185
msgid "Purple I2P Webconsole"
msgstr ""
#: daemon/HTTPServer.cpp:190
#: daemon/HTTPServer.cpp:259
msgid "<b>i2pd</b> webconsole"
msgstr ""
#: daemon/HTTPServer.cpp:193
#: daemon/HTTPServer.cpp:262
msgid "Main page"
msgstr ""
#: daemon/HTTPServer.cpp:194 daemon/HTTPServer.cpp:700
#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:725
msgid "Router commands"
msgstr ""
#: daemon/HTTPServer.cpp:195 daemon/HTTPServer.cpp:382
#: daemon/HTTPServer.cpp:394
#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:448
#: daemon/HTTPServer.cpp:460
msgid "Local Destinations"
msgstr ""
#: daemon/HTTPServer.cpp:197 daemon/HTTPServer.cpp:352
#: daemon/HTTPServer.cpp:438 daemon/HTTPServer.cpp:444
#: daemon/HTTPServer.cpp:597 daemon/HTTPServer.cpp:640
#: daemon/HTTPServer.cpp:644
#: daemon/HTTPServer.cpp:266 daemon/HTTPServer.cpp:418
#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:510
#: daemon/HTTPServer.cpp:641 daemon/HTTPServer.cpp:684
#: daemon/HTTPServer.cpp:688
msgid "LeaseSets"
msgstr ""
#: daemon/HTTPServer.cpp:199 daemon/HTTPServer.cpp:650
#: daemon/HTTPServer.cpp:268 daemon/HTTPServer.cpp:694
msgid "Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:201 daemon/HTTPServer.cpp:359
#: daemon/HTTPServer.cpp:770 daemon/HTTPServer.cpp:786
#: daemon/HTTPServer.cpp:269 daemon/HTTPServer.cpp:425
#: daemon/HTTPServer.cpp:787 daemon/HTTPServer.cpp:803
msgid "Transit Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:203 daemon/HTTPServer.cpp:839
#: daemon/HTTPServer.cpp:270 daemon/HTTPServer.cpp:852
msgid "Transports"
msgstr ""
#: daemon/HTTPServer.cpp:204
#: daemon/HTTPServer.cpp:271
msgid "I2P tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:206 daemon/HTTPServer.cpp:908
#: daemon/HTTPServer.cpp:918
#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:914
#: daemon/HTTPServer.cpp:924
msgid "SAM sessions"
msgstr ""
#: daemon/HTTPServer.cpp:222 daemon/HTTPServer.cpp:1302
#: daemon/HTTPServer.cpp:1305 daemon/HTTPServer.cpp:1308
#: daemon/HTTPServer.cpp:1322 daemon/HTTPServer.cpp:1367
#: daemon/HTTPServer.cpp:1370 daemon/HTTPServer.cpp:1373
#: daemon/HTTPServer.cpp:289 daemon/HTTPServer.cpp:1306
#: daemon/HTTPServer.cpp:1309 daemon/HTTPServer.cpp:1312
#: daemon/HTTPServer.cpp:1326 daemon/HTTPServer.cpp:1371
#: daemon/HTTPServer.cpp:1374 daemon/HTTPServer.cpp:1377
msgid "ERROR"
msgstr ""
#: daemon/HTTPServer.cpp:229
#: daemon/HTTPServer.cpp:296
msgid "OK"
msgstr ""
#: daemon/HTTPServer.cpp:230
#: daemon/HTTPServer.cpp:297
msgid "Testing"
msgstr ""
#: daemon/HTTPServer.cpp:231
#: daemon/HTTPServer.cpp:298
msgid "Firewalled"
msgstr ""
#: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:253
#: daemon/HTTPServer.cpp:325
#: daemon/HTTPServer.cpp:299 daemon/HTTPServer.cpp:320
#: daemon/HTTPServer.cpp:406
msgid "Unknown"
msgstr ""
#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:369
#: daemon/HTTPServer.cpp:370 daemon/HTTPServer.cpp:976
#: daemon/HTTPServer.cpp:985
#: daemon/HTTPServer.cpp:300 daemon/HTTPServer.cpp:435
#: daemon/HTTPServer.cpp:436 daemon/HTTPServer.cpp:982
#: daemon/HTTPServer.cpp:991
msgid "Proxy"
msgstr ""
#: daemon/HTTPServer.cpp:234
#: daemon/HTTPServer.cpp:301
msgid "Mesh"
msgstr ""
#: daemon/HTTPServer.cpp:237
#: daemon/HTTPServer.cpp:304
msgid "Error"
msgstr ""
#: daemon/HTTPServer.cpp:241
#: daemon/HTTPServer.cpp:308
msgid "Clock skew"
msgstr ""
#: daemon/HTTPServer.cpp:244
#: daemon/HTTPServer.cpp:311
msgid "Offline"
msgstr ""
#: daemon/HTTPServer.cpp:247
#: daemon/HTTPServer.cpp:314
msgid "Symmetric NAT"
msgstr ""
#: daemon/HTTPServer.cpp:259
#: daemon/HTTPServer.cpp:326
msgid "Uptime"
msgstr ""
#: daemon/HTTPServer.cpp:262
#: daemon/HTTPServer.cpp:329
msgid "Network status"
msgstr ""
#: daemon/HTTPServer.cpp:267
#: daemon/HTTPServer.cpp:334
msgid "Network status v6"
msgstr ""
#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:280
#: daemon/HTTPServer.cpp:340 daemon/HTTPServer.cpp:347
msgid "Stopping in"
msgstr ""
#: daemon/HTTPServer.cpp:287
#: daemon/HTTPServer.cpp:354
msgid "Family"
msgstr ""
#: daemon/HTTPServer.cpp:288
#: daemon/HTTPServer.cpp:355
msgid "Tunnel creation success rate"
msgstr ""
#: daemon/HTTPServer.cpp:289
#: daemon/HTTPServer.cpp:356
msgid "Received"
msgstr ""
#. tr: Kibibit/s
#: daemon/HTTPServer.cpp:291 daemon/HTTPServer.cpp:294
#: daemon/HTTPServer.cpp:297
#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:361
#: daemon/HTTPServer.cpp:364
msgid "KiB/s"
msgstr ""
#: daemon/HTTPServer.cpp:292
#: daemon/HTTPServer.cpp:359
msgid "Sent"
msgstr ""
#: daemon/HTTPServer.cpp:295
#: daemon/HTTPServer.cpp:362
msgid "Transit"
msgstr ""
#: daemon/HTTPServer.cpp:298
#: daemon/HTTPServer.cpp:365
msgid "Data path"
msgstr ""
#: daemon/HTTPServer.cpp:301
#: daemon/HTTPServer.cpp:368
msgid "Hidden content. Press on text to see."
msgstr ""
#: daemon/HTTPServer.cpp:304
#: daemon/HTTPServer.cpp:371
msgid "Router Ident"
msgstr ""
#: daemon/HTTPServer.cpp:306
#: daemon/HTTPServer.cpp:373
msgid "Router Family"
msgstr ""
#: daemon/HTTPServer.cpp:307
#: daemon/HTTPServer.cpp:374
msgid "Router Caps"
msgstr ""
#: daemon/HTTPServer.cpp:308
#: daemon/HTTPServer.cpp:375
msgid "Version"
msgstr ""
#: daemon/HTTPServer.cpp:309
#: daemon/HTTPServer.cpp:376
msgid "Our external address"
msgstr ""
#: daemon/HTTPServer.cpp:337
#: daemon/HTTPServer.cpp:384
msgid "supported"
msgstr ""
#: daemon/HTTPServer.cpp:350
#: daemon/HTTPServer.cpp:416
msgid "Routers"
msgstr ""
#: daemon/HTTPServer.cpp:351
#: daemon/HTTPServer.cpp:417
msgid "Floodfills"
msgstr ""
#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:962
#: daemon/HTTPServer.cpp:424 daemon/HTTPServer.cpp:968
msgid "Client Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:368
#: daemon/HTTPServer.cpp:434
msgid "Services"
msgstr ""
#: daemon/HTTPServer.cpp:369 daemon/HTTPServer.cpp:370
#: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:372
#: daemon/HTTPServer.cpp:373 daemon/HTTPServer.cpp:374
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
msgid "Enabled"
msgstr ""
#: daemon/HTTPServer.cpp:369 daemon/HTTPServer.cpp:370
#: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:372
#: daemon/HTTPServer.cpp:373 daemon/HTTPServer.cpp:374
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
msgid "Disabled"
msgstr ""
#: daemon/HTTPServer.cpp:417
#: daemon/HTTPServer.cpp:483
msgid "Encrypted B33 address"
msgstr ""
#: daemon/HTTPServer.cpp:426
#: daemon/HTTPServer.cpp:492
msgid "Address registration line"
msgstr ""
#: daemon/HTTPServer.cpp:431
#: daemon/HTTPServer.cpp:497
msgid "Domain"
msgstr ""
#: daemon/HTTPServer.cpp:432
#: daemon/HTTPServer.cpp:498
msgid "Generate"
msgstr ""
#: daemon/HTTPServer.cpp:433
#: daemon/HTTPServer.cpp:499
msgid ""
"<b>Note:</b> result string can be used only for registering 2LD domains "
"(example.i2p). For registering subdomains please use i2pd-tools."
msgstr ""
#: daemon/HTTPServer.cpp:439
#: daemon/HTTPServer.cpp:505
msgid "Address"
msgstr ""
#: daemon/HTTPServer.cpp:439
#: daemon/HTTPServer.cpp:505
msgid "Type"
msgstr ""
#: daemon/HTTPServer.cpp:439
#: daemon/HTTPServer.cpp:505
msgid "EncType"
msgstr ""
#: daemon/HTTPServer.cpp:449 daemon/HTTPServer.cpp:655
#: daemon/HTTPServer.cpp:515 daemon/HTTPServer.cpp:699
msgid "Inbound tunnels"
msgstr ""
#. tr: Milliseconds
#: daemon/HTTPServer.cpp:464 daemon/HTTPServer.cpp:484
#: daemon/HTTPServer.cpp:669 daemon/HTTPServer.cpp:689
#: daemon/HTTPServer.cpp:520 daemon/HTTPServer.cpp:530
#: daemon/HTTPServer.cpp:704 daemon/HTTPServer.cpp:714
msgid "ms"
msgstr ""
#: daemon/HTTPServer.cpp:469 daemon/HTTPServer.cpp:674
#: daemon/HTTPServer.cpp:525 daemon/HTTPServer.cpp:709
msgid "Outbound tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:491
#: daemon/HTTPServer.cpp:537
msgid "Tags"
msgstr ""
#: daemon/HTTPServer.cpp:491
#: daemon/HTTPServer.cpp:537
msgid "Incoming"
msgstr ""
#: daemon/HTTPServer.cpp:498 daemon/HTTPServer.cpp:501
#: daemon/HTTPServer.cpp:544 daemon/HTTPServer.cpp:547
msgid "Outgoing"
msgstr ""
#: daemon/HTTPServer.cpp:499 daemon/HTTPServer.cpp:515
#: daemon/HTTPServer.cpp:545 daemon/HTTPServer.cpp:561
msgid "Destination"
msgstr ""
#: daemon/HTTPServer.cpp:499
#: daemon/HTTPServer.cpp:545
msgid "Amount"
msgstr ""
#: daemon/HTTPServer.cpp:506
#: daemon/HTTPServer.cpp:552
msgid "Incoming Tags"
msgstr ""
#: daemon/HTTPServer.cpp:514 daemon/HTTPServer.cpp:517
#: daemon/HTTPServer.cpp:560 daemon/HTTPServer.cpp:563
msgid "Tags sessions"
msgstr ""
#: daemon/HTTPServer.cpp:515
#: daemon/HTTPServer.cpp:561
msgid "Status"
msgstr ""
#: daemon/HTTPServer.cpp:524 daemon/HTTPServer.cpp:582
#: daemon/HTTPServer.cpp:570 daemon/HTTPServer.cpp:626
msgid "Local Destination"
msgstr ""
#: daemon/HTTPServer.cpp:535 daemon/HTTPServer.cpp:941
#: daemon/HTTPServer.cpp:580 daemon/HTTPServer.cpp:947
msgid "Streams"
msgstr ""
#: daemon/HTTPServer.cpp:558
#: daemon/HTTPServer.cpp:602
msgid "Close stream"
msgstr ""
#: daemon/HTTPServer.cpp:587
#: daemon/HTTPServer.cpp:631
msgid "I2CP session not found"
msgstr ""
#: daemon/HTTPServer.cpp:590
#: daemon/HTTPServer.cpp:634
msgid "I2CP is not enabled"
msgstr ""
#: daemon/HTTPServer.cpp:616
#: daemon/HTTPServer.cpp:660
msgid "Invalid"
msgstr ""
#: daemon/HTTPServer.cpp:619
#: daemon/HTTPServer.cpp:663
msgid "Store type"
msgstr ""
#: daemon/HTTPServer.cpp:620
#: daemon/HTTPServer.cpp:664
msgid "Expires"
msgstr ""
#: daemon/HTTPServer.cpp:625
#: daemon/HTTPServer.cpp:669
msgid "Non Expired Leases"
msgstr ""
#: daemon/HTTPServer.cpp:628
#: daemon/HTTPServer.cpp:672
msgid "Gateway"
msgstr ""
#: daemon/HTTPServer.cpp:629
#: daemon/HTTPServer.cpp:673
msgid "TunnelID"
msgstr ""
#: daemon/HTTPServer.cpp:630
#: daemon/HTTPServer.cpp:674
msgid "EndDate"
msgstr ""
#: daemon/HTTPServer.cpp:640
#: daemon/HTTPServer.cpp:684
msgid "not floodfill"
msgstr ""
#: daemon/HTTPServer.cpp:651
#: daemon/HTTPServer.cpp:695
msgid "Queue size"
msgstr ""
#: daemon/HTTPServer.cpp:701
#: daemon/HTTPServer.cpp:726
msgid "Run peer test"
msgstr ""
#: daemon/HTTPServer.cpp:706
#: daemon/HTTPServer.cpp:731
msgid "Decline transit tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:708
#: daemon/HTTPServer.cpp:733
msgid "Accept transit tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:712 daemon/HTTPServer.cpp:717
#: daemon/HTTPServer.cpp:737 daemon/HTTPServer.cpp:742
msgid "Cancel graceful shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:714 daemon/HTTPServer.cpp:719
#: daemon/HTTPServer.cpp:739 daemon/HTTPServer.cpp:744
msgid "Start graceful shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:722
#: daemon/HTTPServer.cpp:747
msgid "Force shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:723
#: daemon/HTTPServer.cpp:748
msgid "Reload external CSS styles"
msgstr ""
#: daemon/HTTPServer.cpp:726
#: daemon/HTTPServer.cpp:751
msgid ""
"<b>Note:</b> any action done here are not persistent and not changes your "
"config files."
msgstr ""
#: daemon/HTTPServer.cpp:728
#: daemon/HTTPServer.cpp:753
msgid "Logging level"
msgstr ""
#: daemon/HTTPServer.cpp:736
#: daemon/HTTPServer.cpp:761
msgid "Transit tunnels limit"
msgstr ""
#: daemon/HTTPServer.cpp:741 daemon/HTTPServer.cpp:760
#: daemon/HTTPServer.cpp:766 daemon/HTTPServer.cpp:778
msgid "Change"
msgstr ""
#: daemon/HTTPServer.cpp:748
#: daemon/HTTPServer.cpp:770
msgid "Change language"
msgstr ""
#: daemon/HTTPServer.cpp:786
#: daemon/HTTPServer.cpp:803
msgid "no transit tunnels currently built"
msgstr ""
#: daemon/HTTPServer.cpp:902 daemon/HTTPServer.cpp:925
#: daemon/HTTPServer.cpp:908 daemon/HTTPServer.cpp:931
msgid "SAM disabled"
msgstr ""
#: daemon/HTTPServer.cpp:918
#: daemon/HTTPServer.cpp:924
msgid "no sessions currently running"
msgstr ""
#: daemon/HTTPServer.cpp:931
#: daemon/HTTPServer.cpp:937
msgid "SAM session not found"
msgstr ""
#: daemon/HTTPServer.cpp:936
#: daemon/HTTPServer.cpp:942
msgid "SAM Session"
msgstr ""
#: daemon/HTTPServer.cpp:993
#: daemon/HTTPServer.cpp:999
msgid "Server Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:1009
#: daemon/HTTPServer.cpp:1015
msgid "Client Forwards"
msgstr ""
#: daemon/HTTPServer.cpp:1023
#: daemon/HTTPServer.cpp:1029
msgid "Server Forwards"
msgstr ""
#: daemon/HTTPServer.cpp:1223
#: daemon/HTTPServer.cpp:1227
msgid "Unknown page"
msgstr ""
#: daemon/HTTPServer.cpp:1242
#: daemon/HTTPServer.cpp:1246
msgid "Invalid token"
msgstr ""
#: daemon/HTTPServer.cpp:1300 daemon/HTTPServer.cpp:1357
#: daemon/HTTPServer.cpp:1397
#: daemon/HTTPServer.cpp:1304 daemon/HTTPServer.cpp:1361
#: daemon/HTTPServer.cpp:1401
msgid "SUCCESS"
msgstr ""
#: daemon/HTTPServer.cpp:1300
#: daemon/HTTPServer.cpp:1304
msgid "Stream closed"
msgstr ""
#: daemon/HTTPServer.cpp:1302
#: daemon/HTTPServer.cpp:1306
msgid "Stream not found or already was closed"
msgstr ""
#: daemon/HTTPServer.cpp:1305
#: daemon/HTTPServer.cpp:1309
msgid "Destination not found"
msgstr ""
#: daemon/HTTPServer.cpp:1308
#: daemon/HTTPServer.cpp:1312
msgid "StreamID can't be null"
msgstr ""
#: daemon/HTTPServer.cpp:1310 daemon/HTTPServer.cpp:1375
#: daemon/HTTPServer.cpp:1314 daemon/HTTPServer.cpp:1379
msgid "Return to destination page"
msgstr ""
#: daemon/HTTPServer.cpp:1311 daemon/HTTPServer.cpp:1324
#: daemon/HTTPServer.cpp:1399
#: daemon/HTTPServer.cpp:1315 daemon/HTTPServer.cpp:1328
#: daemon/HTTPServer.cpp:1403
msgid "You will be redirected in 5 seconds"
msgstr ""
#: daemon/HTTPServer.cpp:1322
#: daemon/HTTPServer.cpp:1326
msgid "Transit tunnels count must not exceed 65535"
msgstr ""
#: daemon/HTTPServer.cpp:1323 daemon/HTTPServer.cpp:1398
#: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1402
msgid "Back to commands list"
msgstr ""
#: daemon/HTTPServer.cpp:1359
#: daemon/HTTPServer.cpp:1363
msgid "Register at reg.i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1360
#: daemon/HTTPServer.cpp:1364
msgid "Description"
msgstr ""
#: daemon/HTTPServer.cpp:1360
#: daemon/HTTPServer.cpp:1364
msgid "A bit information about service on domain"
msgstr ""
#: daemon/HTTPServer.cpp:1361
#: daemon/HTTPServer.cpp:1365
msgid "Submit"
msgstr ""
#: daemon/HTTPServer.cpp:1367
#: daemon/HTTPServer.cpp:1371
msgid "Domain can't end with .b32.i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1370
#: daemon/HTTPServer.cpp:1374
msgid "Domain must end with .i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1373
#: daemon/HTTPServer.cpp:1377
msgid "Such destination is not found"
msgstr ""
#: daemon/HTTPServer.cpp:1393
#: daemon/HTTPServer.cpp:1397
msgid "Unknown command"
msgstr ""
#: daemon/HTTPServer.cpp:1397
#: daemon/HTTPServer.cpp:1401
msgid "Command accepted"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:163
#: libi2pd_client/HTTPProxy.cpp:157
msgid "Proxy error"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:171
#: libi2pd_client/HTTPProxy.cpp:165
msgid "Proxy info"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:179
#: libi2pd_client/HTTPProxy.cpp:173
msgid "Proxy error: Host not found"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:180
#: libi2pd_client/HTTPProxy.cpp:174
msgid "Remote host not found in router's addressbook"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:181
#: libi2pd_client/HTTPProxy.cpp:175
msgid "You may try to find this host on jump services below"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:282 libi2pd_client/HTTPProxy.cpp:297
#: libi2pd_client/HTTPProxy.cpp:331 libi2pd_client/HTTPProxy.cpp:372
#: libi2pd_client/HTTPProxy.cpp:273 libi2pd_client/HTTPProxy.cpp:288
#: libi2pd_client/HTTPProxy.cpp:322 libi2pd_client/HTTPProxy.cpp:365
msgid "Invalid request"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:282
#: libi2pd_client/HTTPProxy.cpp:273
msgid "Proxy unable to parse your request"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:297
#: libi2pd_client/HTTPProxy.cpp:288
msgid "addresshelper is not supported"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:306 libi2pd_client/HTTPProxy.cpp:315
#: libi2pd_client/HTTPProxy.cpp:392
#: libi2pd_client/HTTPProxy.cpp:297 libi2pd_client/HTTPProxy.cpp:306
#: libi2pd_client/HTTPProxy.cpp:385
msgid "Host"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:306
#: libi2pd_client/HTTPProxy.cpp:297
msgid "added to router's addressbook from helper"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:307
#: libi2pd_client/HTTPProxy.cpp:298
msgid "Click here to proceed:"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:307 libi2pd_client/HTTPProxy.cpp:317
#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308
msgid "Continue"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:308 libi2pd_client/HTTPProxy.cpp:318
#: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309
msgid "Addresshelper found"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:315
#: libi2pd_client/HTTPProxy.cpp:306
msgid "already in router's addressbook"
msgstr ""
#. tr: The "record" means addressbook's record. That message appears when domain was already added to addressbook, but helper link is opened for it.
#: libi2pd_client/HTTPProxy.cpp:316
#: libi2pd_client/HTTPProxy.cpp:307
msgid "Click here to update record:"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:331
#: libi2pd_client/HTTPProxy.cpp:322
msgid "invalid request uri"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:372
#: libi2pd_client/HTTPProxy.cpp:365
msgid "Can't detect destination host from request"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:389 libi2pd_client/HTTPProxy.cpp:393
#: libi2pd_client/HTTPProxy.cpp:382 libi2pd_client/HTTPProxy.cpp:386
msgid "Outproxy failure"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:389
#: libi2pd_client/HTTPProxy.cpp:382
msgid "bad outproxy settings"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:392
#: libi2pd_client/HTTPProxy.cpp:385
msgid "not inside I2P network, but outproxy is not enabled"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:482
#: libi2pd_client/HTTPProxy.cpp:474
msgid "unknown outproxy url"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:490
#: libi2pd_client/HTTPProxy.cpp:480
msgid "cannot resolve upstream proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:498
#: libi2pd_client/HTTPProxy.cpp:488
msgid "hostname too long"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:525
#: libi2pd_client/HTTPProxy.cpp:515
msgid "cannot connect to upstream socks proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:531
#: libi2pd_client/HTTPProxy.cpp:521
msgid "Cannot negotiate with socks proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:573
#: libi2pd_client/HTTPProxy.cpp:563
msgid "CONNECT error"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:573
#: libi2pd_client/HTTPProxy.cpp:563
msgid "Failed to Connect"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:584 libi2pd_client/HTTPProxy.cpp:610
#: libi2pd_client/HTTPProxy.cpp:574 libi2pd_client/HTTPProxy.cpp:600
msgid "socks proxy error"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:592
#: libi2pd_client/HTTPProxy.cpp:582
msgid "failed to send request to upstream"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:613
#: libi2pd_client/HTTPProxy.cpp:603
msgid "No Reply From socks proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:620
#: libi2pd_client/HTTPProxy.cpp:610
msgid "cannot connect"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:620
#: libi2pd_client/HTTPProxy.cpp:610
msgid "http out proxy not implemented"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:621
#: libi2pd_client/HTTPProxy.cpp:611
msgid "cannot connect to upstream http proxy"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:654
#: libi2pd_client/HTTPProxy.cpp:644
msgid "Host is down"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:654
#: libi2pd_client/HTTPProxy.cpp:644
msgid ""
"Can't create connection to requested host, it may be down. Please try again "
"later."

View File

@@ -9,8 +9,8 @@ Regex for transforming gettext translations to our format:
---
```
in: msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\n(msgstr\[1\]\ \"(.*)\"\n)?(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)?
out: #{"$2", {"$3", "$5", "$7", "$9", "$11"}},\n
in: msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\nmsgstr\[1\]\ \"(.*)\"\n(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)?
out: #{"$2", {"$3", "$4", "$6", "$8", "$10"}},\n
```
```

View File

@@ -75,8 +75,8 @@ ipv4 = true
## Enable communication through ipv6
ipv6 = false
## Enable SSU transport
ssu = false
## Enable SSU transport (default = true)
# ssu = true
## Bandwidth configuration
## L limit bandwidth to 32KBs/sec, O - to 256KBs/sec, P - to 2048KBs/sec,
@@ -96,22 +96,6 @@ ssu = false
## Note: that mode uses much more network connections and CPU!
# floodfill = true
[ntcp2]
## Enable NTCP2 transport (default = true)
# enabled = true
## Publish address in RouterInfo (default = true)
# published = true
## Port for incoming connections (default is global port option value)
# port = 4567
[ssu2]
## Enable SSU2 transport
# enabled = true
## Publish address in RouterInfo
# published = true
## Port for incoming connections (default is global port option value or port + 1 if SSU is enabled)
# port = 4567
[http]
## Web Console settings
## Uncomment and set to 'false' to disable Web Console
@@ -126,8 +110,8 @@ port = 7070
# user = i2pd
# pass = changeme
## Select webconsole language
## Currently supported english (default), afrikaans, armenian, chinese, french,
## german, italian, russian, spanish, turkmen, ukrainian and uzbek languages
## Currently supported english (default), afrikaans, armenian, french, german,
## russian, turkmen, ukrainian and uzbek languages
# lang = english
[httpproxy]

View File

@@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git
Version: 2.44.0
Version: 2.42.1
Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd
@@ -62,7 +62,9 @@ pushd redhat-linux-build
%endif
%if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build
%endif
%else
%if 0%{?fedora} >= 33
pushd %{_target_platform}
@@ -80,8 +82,10 @@ popd
%endif
%if 0%{?fedora} >= 33
%if 0%{?fedora} < 37
popd
%endif
%endif
%if 0%{?mageia} > 7
popd
@@ -95,7 +99,9 @@ pushd redhat-linux-build
%endif
%if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build
%endif
%else
%if 0%{?fedora} >= 33
pushd %{_target_platform}
@@ -158,12 +164,6 @@ getent passwd i2pd >/dev/null || \
%changelog
* Sun Nov 20 2022 orignal <orignal@i2pmail.org> - 2.44.0
- update to 2.44.0
* Mon Aug 22 2022 orignal <orignal@i2pmail.org> - 2.43.0
- update to 2.43.0
* Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1
- update to 2.42.1

View File

@@ -1,5 +1,5 @@
Name: i2pd
Version: 2.44.0
Version: 2.42.1
Release: 1%{?dist}
Summary: I2P router written in C++
Conflicts: i2pd-git
@@ -59,7 +59,9 @@ pushd redhat-linux-build
%endif
%if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build
%endif
%else
%if 0%{?fedora} >= 33
pushd %{_target_platform}
@@ -77,8 +79,10 @@ popd
%endif
%if 0%{?fedora} >= 33
%if 0%{?fedora} < 37
popd
%endif
%endif
%if 0%{?mageia} > 7
popd
@@ -92,7 +96,9 @@ pushd redhat-linux-build
%endif
%if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build
%endif
%else
%if 0%{?fedora} >= 33
pushd %{_target_platform}
@@ -155,12 +161,6 @@ getent passwd i2pd >/dev/null || \
%changelog
* Sun Nov 20 2022 orignal <orignal@i2pmail.org> - 2.44.0
- update to 2.44.0
* Mon Aug 22 2022 orignal <orignal@i2pmail.org> - 2.43.0
- update to 2.43.0
* Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1
- update to 2.42.1

View File

@@ -31,6 +31,7 @@
#include "Crypto.h"
#include "UPnP.h"
#include "Timestamp.h"
#include "util.h"
#include "I18N.h"
namespace i2p
@@ -152,18 +153,115 @@ namespace util
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
bool avx; i2p::config::GetOption("cpuext.avx", avx);
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
bool ssu; i2p::config::GetOption("ssu", ssu);
if (!ssu && i2p::config::IsDefault ("precomputation.elgamal"))
precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly
i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt);
i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces
int netID; i2p::config::GetOption("netid", netID);
i2p::context.SetNetID (netID);
i2p::context.Init ();
i2p::transport::InitTransports ();
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
// ifname -> address
std::string ifname; i2p::config::GetOption("ifname", ifname);
if (ipv4 && i2p::config::IsDefault ("address4"))
{
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
if (!ifname4.empty ())
i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname4, false).to_string ()); // v4
else if (!ifname.empty ())
i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname, false).to_string ()); // v4
}
if (ipv6 && i2p::config::IsDefault ("address6"))
{
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
if (!ifname6.empty ())
i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname6, true).to_string ()); // v6
else if (!ifname.empty ())
i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname, true).to_string ()); // v6
}
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
boost::asio::ip::address_v6 yggaddr;
if (ygg)
{
std::string yggaddress; i2p::config::GetOption ("meshnets.yggaddress", yggaddress);
if (!yggaddress.empty ())
{
yggaddr = boost::asio::ip::address_v6::from_string (yggaddress);
if (yggaddr.is_unspecified () || !i2p::util::net::IsYggdrasilAddress (yggaddr) ||
!i2p::util::net::IsLocalAddress (yggaddr))
{
LogPrint(eLogWarning, "Daemon: Can't find Yggdrasil address ", yggaddress);
ygg = false;
}
}
else
{
yggaddr = i2p::util::net::GetYggdrasilAddress ();
if (yggaddr.is_unspecified ())
{
LogPrint(eLogWarning, "Daemon: Yggdrasil is not running. Disabled");
ygg = false;
}
}
}
uint16_t port; i2p::config::GetOption("port", port);
if (!i2p::config::IsDefault("port"))
{
LogPrint(eLogInfo, "Daemon: Accepting incoming connections at port ", port);
i2p::context.UpdatePort (port);
}
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4);
i2p::context.SetSupportsMesh (ygg, yggaddr);
i2p::context.RemoveNTCPAddress (!ipv6); // TODO: remove later
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2)
{
bool published; i2p::config::GetOption("ntcp2.published", published);
if (published)
{
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
if (!ntcp2proxy.empty ()) published = false;
}
if (published)
{
uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port);
if (!ntcp2port) ntcp2port = port; // use standard port
i2p::context.PublishNTCP2Address (ntcp2port, true, ipv4, ipv6, false); // publish
if (ipv6)
{
std::string ipv6Addr; i2p::config::GetOption("ntcp2.addressv6", ipv6Addr);
auto addr = boost::asio::ip::address_v6::from_string (ipv6Addr);
if (!addr.is_unspecified () && addr != boost::asio::ip::address_v6::any ())
i2p::context.UpdateNTCP2V6Address (addr); // set ipv6 address if configured
}
}
else
i2p::context.PublishNTCP2Address (port, false, ipv4, ipv6, false); // unpublish
}
if (ygg)
{
i2p::context.PublishNTCP2Address (port, true, false, false, true);
i2p::context.UpdateNTCP2V6Address (yggaddr);
if (!ipv4 && !ipv6)
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);
i2p::context.SetAcceptsTunnels (!transit);
@@ -306,7 +404,7 @@ namespace util
i2p::transport::transports.SetCheckReserved(checkInReserved);
i2p::transport::transports.Start(ntcp2, ssu, ssu2);
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2())
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2())
LogPrint(eLogInfo, "Daemon: Transports started");
else
{
@@ -325,6 +423,10 @@ namespace util
try
{
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();
}
catch (std::exception& ex)

View File

@@ -75,14 +75,17 @@ namespace util
return instance;
}
bool init(int argc, char* argv[]);
bool start();
bool stop();
bool init (int argc, char* argv[]);
bool start ();
bool stop ();
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))
#define Daemon i2p::util::DaemonAndroid::Instance()
@@ -109,8 +112,8 @@ namespace util
return instance;
}
bool start();
bool stop();
bool start ();
bool stop ();
void run ();
private:

View File

@@ -24,7 +24,6 @@
#include "Tunnel.h"
#include "RouterContext.h"
#include "ClientContext.h"
#include "Transports.h"
void handle_signal(int sig)
{
@@ -55,14 +54,6 @@ void handle_signal(int sig)
case SIGPIPE:
LogPrint(eLogInfo, "SIGPIPE received");
break;
case SIGTSTP:
LogPrint(eLogInfo, "Daemon: Got SIGTSTP, disconnecting from network...");
i2p::transport::transports.SetOnline(false);
break;
case SIGCONT:
LogPrint(eLogInfo, "Daemon: Got SIGCONT, restoring connection to network...");
i2p::transport::transports.SetOnline(true);
break;
}
}
@@ -180,9 +171,6 @@ namespace i2p
}
gracefulShutdownInterval = 0; // not specified
// handle signal TSTP
bool handleTSTP; i2p::config::GetOption("unix.handle_sigtstp", handleTSTP);
// Signal handler
struct sigaction sa;
sa.sa_handler = handle_signal;
@@ -194,11 +182,6 @@ namespace i2p
sigaction(SIGTERM, &sa, 0);
sigaction(SIGINT, &sa, 0);
sigaction(SIGPIPE, &sa, 0);
if (handleTSTP)
{
sigaction(SIGTSTP, &sa, 0);
sigaction(SIGCONT, &sa, 0);
}
return Daemon_Singleton::start();
}

View File

@@ -6,6 +6,8 @@
* See full license text in LICENSE file at top of project tree
*/
#ifdef _WIN32
#include <thread>
#include <clocale>
#include "Config.h"
@@ -13,7 +15,6 @@
#include "util.h"
#include "Log.h"
#ifdef _WIN32
#include "Win32Service.h"
#ifdef WIN32_APP
#include <windows.h>
@@ -38,6 +39,9 @@ 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))
return false;
@@ -45,6 +49,8 @@ namespace util
{
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());

View File

@@ -14,17 +14,25 @@
// Use global placeholders from boost introduced when local_time.hpp is loaded
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/lexical_cast.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/lexical_cast.hpp>
#include "Crypto.h"
#include "FS.h"
#include "Log.h"
#include "Config.h"
#include "NetDb.hpp"
#include "Tunnel.h"
#include "RouterContext.h"
#include "Daemon.h"
#include "Tunnel.h"
#include "Timestamp.h"
#include "Transports.h"
#include "version.h"
#include "util.h"
#include "ClientContext.h"
#include "I2PControl.h"
namespace i2p
@@ -61,18 +69,44 @@ namespace client
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler;
m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler;
m_MethodHandlers["RouterInfo"] = &I2PControlHandlers::RouterInfoHandler;
m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler;
m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler;
m_MethodHandlers["NetworkSetting"] = &I2PControlHandlers::NetworkSettingHandler;
m_MethodHandlers["ClientServicesInfo"] = &I2PControlHandlers::ClientServicesInfoHandler;
m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler;
m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler;
// I2PControl
m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler;
// RouterInfo
m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler;
m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler;
m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler;
m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler;
m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler;
m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S;
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler;
m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = &I2PControlService::TunnelsSuccessRateHandler;
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
// RouterManager
m_RouterManagerHandlers["Reseed"] = &I2PControlService::ReseedHandler;
m_RouterManagerHandlers["Shutdown"] = &I2PControlService::ShutdownHandler;
m_RouterManagerHandlers["ShutdownGraceful"] = &I2PControlService::ShutdownGracefulHandler;
// NetworkSetting
m_NetworkSettingHandlers["i2p.router.net.bw.in"] = &I2PControlService::InboundBandwidthLimit;
m_NetworkSettingHandlers["i2p.router.net.bw.out"] = &I2PControlService::OutboundBandwidthLimit;
// ClientServicesInfo
m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlService::I2PTunnelInfoHandler;
m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlService::HTTPProxyInfoHandler;
m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler;
m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler;
m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler;
m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler;
}
I2PControlService::~I2PControlService ()
@@ -246,6 +280,37 @@ namespace client
}
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
{
ss << "\"" << name << "\":" << value;
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value, bool quotes) const
{
ss << "\"" << name << "\":";
if (value.length () > 0)
{
if (quotes)
ss << "\"" << value << "\"";
else
ss << value;
}
else
ss << "null";
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const
{
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
}
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const
{
std::ostringstream buf;
boost::property_tree::write_json (buf, value, false);
ss << "\"" << name << "\":" << buf.str();
}
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
{
@@ -331,6 +396,91 @@ namespace client
m_Tokens.clear ();
}
// RouterInfo
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
bool first = true;
for (auto it = params.begin (); it != params.end (); it++)
{
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
auto it1 = m_RouterInfoHandlers.find (it->first);
if (it1 != m_RouterInfoHandlers.end ())
{
if (!first) results << ",";
else first = false;
(this->*(it1->second))(results);
}
else
LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first);
}
}
void I2PControlService::UptimeHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL), false);
}
void I2PControlService::VersionHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.version", VERSION);
}
void I2PControlService::StatusHandler (std::ostringstream& results)
{
auto dest = i2p::client::context.GetSharedLocalDestination ();
InsertParam (results, "i2p.router.status", (dest && dest->IsReady ()) ? "1" : "0");
}
void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.netdb.knownpeers", i2p::data::netdb.GetNumRouters ());
}
void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.netdb.activepeers", (int)i2p::transport::transports.GetPeers ().size ());
}
void I2PControlService::NetStatusHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ());
}
void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results)
{
int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size ();
InsertParam (results, "i2p.router.net.tunnels.participating", transit);
}
void I2PControlService::TunnelsSuccessRateHandler (std::ostringstream& results)
{
int rate = i2p::tunnel::tunnels.GetTunnelCreationSuccessRate ();
InsertParam (results, "i2p.router.net.tunnels.successrate", rate);
}
void I2PControlService::InboundBandwidth1S (std::ostringstream& results)
{
double bw = i2p::transport::transports.GetInBandwidth ();
InsertParam (results, "i2p.router.net.bw.inbound.1s", bw);
}
void I2PControlService::OutboundBandwidth1S (std::ostringstream& results)
{
double bw = i2p::transport::transports.GetOutBandwidth ();
InsertParam (results, "i2p.router.net.bw.outbound.1s", bw);
}
void I2PControlService::NetTotalReceivedBytes (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.total.received.bytes", (double)i2p::transport::transports.GetTotalReceivedBytes ());
}
void I2PControlService::NetTotalSentBytes (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ());
}
// RouterManager
@@ -382,6 +532,37 @@ namespace client
i2p::data::netdb.Reseed ();
}
// network setting
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
{
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
auto it1 = m_NetworkSettingHandlers.find (it->first);
if (it1 != m_NetworkSettingHandlers.end ()) {
if (it != params.begin ()) results << ",";
(this->*(it1->second))(it->second.data (), results);
} else
LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first);
}
}
void I2PControlService::InboundBandwidthLimit (const std::string& value, std::ostringstream& results)
{
if (value != "null")
i2p::context.SetBandwidth (std::atoi(value.c_str()));
int bw = i2p::context.GetBandwidthLimit();
InsertParam (results, "i2p.router.net.bw.in", bw);
}
void I2PControlService::OutboundBandwidthLimit (const std::string& value, std::ostringstream& results)
{
if (value != "null")
i2p::context.SetBandwidth (std::atoi(value.c_str()));
int bw = i2p::context.GetBandwidthLimit();
InsertParam (results, "i2p.router.net.bw.out", bw);
}
// certificate
void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path)
{
@@ -430,5 +611,178 @@ namespace client
}
EVP_PKEY_free (pkey);
}
// ClientServicesInfo
void I2PControlService::ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
{
LogPrint (eLogDebug, "I2PControl: ClientServicesInfo request: ", it->first);
auto it1 = m_ClientServicesInfoHandlers.find (it->first);
if (it1 != m_ClientServicesInfoHandlers.end ())
{
if (it != params.begin ()) results << ",";
(this->*(it1->second))(results);
}
else
LogPrint (eLogError, "I2PControl: ClientServicesInfo unknown request ", it->first);
}
}
void I2PControlService::I2PTunnelInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
boost::property_tree::ptree client_tunnels, server_tunnels;
for (auto& it: i2p::client::context.GetClientTunnels ())
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
boost::property_tree::ptree ct;
ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
client_tunnels.add_child(it.second->GetName (), ct);
}
auto& serverTunnels = i2p::client::context.GetServerTunnels ();
if (!serverTunnels.empty ()) {
for (auto& it: serverTunnels)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
boost::property_tree::ptree st;
st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
st.put("port", it.second->GetLocalPort ());
server_tunnels.add_child(it.second->GetName (), st);
}
}
auto& clientForwards = i2p::client::context.GetClientForwards ();
if (!clientForwards.empty ())
{
for (auto& it: clientForwards)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
boost::property_tree::ptree ct;
ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
client_tunnels.add_child(it.second->GetName (), ct);
}
}
auto& serverForwards = i2p::client::context.GetServerForwards ();
if (!serverForwards.empty ())
{
for (auto& it: serverForwards)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
boost::property_tree::ptree st;
st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
server_tunnels.add_child(it.second->GetName (), st);
}
}
pt.add_child("client", client_tunnels);
pt.add_child("server", server_tunnels);
InsertParam (results, "I2PTunnel", pt);
}
void I2PControlService::HTTPProxyInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
auto httpProxy = i2p::client::context.GetHttpProxy ();
if (httpProxy)
{
auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
pt.put("enabled", true);
pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
}
else
pt.put("enabled", false);
InsertParam (results, "HTTPProxy", pt);
}
void I2PControlService::SOCKSInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
auto socksProxy = i2p::client::context.GetSocksProxy ();
if (socksProxy)
{
auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash();
pt.put("enabled", true);
pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
}
else
pt.put("enabled", false);
InsertParam (results, "SOCKS", pt);
}
void I2PControlService::SAMInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
auto sam = i2p::client::context.GetSAMBridge ();
if (sam)
{
pt.put("enabled", true);
boost::property_tree::ptree sam_sessions;
for (auto& it: sam->GetSessions ())
{
boost::property_tree::ptree sam_session, sam_session_sockets;
auto& name = it.second->GetLocalDestination ()->GetNickname ();
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
sam_session.put("name", name);
sam_session.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
for (const auto& socket: sam->ListSockets(it.first))
{
boost::property_tree::ptree stream;
stream.put("type", socket->GetSocketType ());
stream.put("peer", socket->GetSocket ().remote_endpoint());
sam_session_sockets.push_back(std::make_pair("", stream));
}
sam_session.add_child("sockets", sam_session_sockets);
sam_sessions.add_child(it.first, sam_session);
}
pt.add_child("sessions", sam_sessions);
}
else
pt.put("enabled", false);
InsertParam (results, "SAM", pt);
}
void I2PControlService::BOBInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
auto bob = i2p::client::context.GetBOBCommandChannel ();
if (bob)
{
/* TODO more info */
pt.put("enabled", true);
}
else
pt.put("enabled", false);
InsertParam (results, "BOB", pt);
}
void I2PControlService::I2CPInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
auto i2cp = i2p::client::context.GetI2CPServer ();
if (i2cp)
{
/* TODO more info */
pt.put("enabled", true);
}
else
pt.put("enabled", false);
InsertParam (results, "I2CP", pt);
}
}
}

View File

@@ -20,7 +20,6 @@
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/property_tree/ptree.hpp>
#include "I2PControlHandlers.h"
namespace i2p
{
@@ -33,7 +32,7 @@ namespace client
const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol";
const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P";
class I2PControlService: public I2PControlHandlers
class I2PControlService
{
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
@@ -64,24 +63,61 @@ namespace client
private:
void InsertParam (std::ostringstream& ss, const std::string& name, int value) const;
void InsertParam (std::ostringstream& ss, const std::string& name, double value) const;
void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value, bool quotes = true) const;
void InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const;
// methods
typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results);
void AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
// I2PControl
typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value);
void PasswordHandler (const std::string& value);
// RouterInfo
typedef void (I2PControlService::*RouterInfoRequestHandler)(std::ostringstream& results);
void UptimeHandler (std::ostringstream& results);
void VersionHandler (std::ostringstream& results);
void StatusHandler (std::ostringstream& results);
void NetDbKnownPeersHandler (std::ostringstream& results);
void NetDbActivePeersHandler (std::ostringstream& results);
void NetStatusHandler (std::ostringstream& results);
void TunnelsParticipatingHandler (std::ostringstream& results);
void TunnelsSuccessRateHandler (std::ostringstream& results);
void InboundBandwidth1S (std::ostringstream& results);
void OutboundBandwidth1S (std::ostringstream& results);
void NetTotalReceivedBytes (std::ostringstream& results);
void NetTotalSentBytes (std::ostringstream& results);
// RouterManager
typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results);
void ShutdownHandler (std::ostringstream& results);
void ShutdownGracefulHandler (std::ostringstream& results);
void ReseedHandler (std::ostringstream& results);
// NetworkSetting
typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results);
void InboundBandwidthLimit (const std::string& value, std::ostringstream& results);
void OutboundBandwidthLimit (const std::string& value, std::ostringstream& results);
// ClientServicesInfo
typedef void (I2PControlService::*ClientServicesInfoRequestHandler)(std::ostringstream& results);
void I2PTunnelInfoHandler (std::ostringstream& results);
void HTTPProxyInfoHandler (std::ostringstream& results);
void SOCKSInfoHandler (std::ostringstream& results);
void SAMInfoHandler (std::ostringstream& results);
void BOBInfoHandler (std::ostringstream& results);
void I2CPInfoHandler (std::ostringstream& results);
private:
std::string m_Password;
@@ -96,7 +132,10 @@ namespace client
std::map<std::string, MethodHandler> m_MethodHandlers;
std::map<std::string, I2PControlRequestHandler> m_I2PControlHandlers;
std::map<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;
std::map<std::string, RouterManagerRequestHandler> m_RouterManagerHandlers;
std::map<std::string, NetworkSettingRequestHandler> m_NetworkSettingHandlers;
std::map<std::string, ClientServicesInfoRequestHandler> m_ClientServicesInfoHandlers;
};
}
}

View File

@@ -1,376 +0,0 @@
/*
* 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 <iomanip>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/lexical_cast.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "Log.h"
#include "RouterContext.h"
#include "NetDb.hpp"
#include "Tunnel.h"
#include "Transports.h"
#include "version.h"
#include "ClientContext.h"
#include "I2PControlHandlers.h"
namespace i2p
{
namespace client
{
I2PControlHandlers::I2PControlHandlers ()
{
// RouterInfo
m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlHandlers::UptimeHandler;
m_RouterInfoHandlers["i2p.router.version"] = &I2PControlHandlers::VersionHandler;
m_RouterInfoHandlers["i2p.router.status"] = &I2PControlHandlers::StatusHandler;
m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlHandlers::NetDbKnownPeersHandler;
m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlHandlers::NetDbActivePeersHandler;
m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlHandlers::InboundBandwidth1S;
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlHandlers::OutboundBandwidth1S;
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlHandlers::NetStatusHandler;
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlHandlers::TunnelsParticipatingHandler;
m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = &I2PControlHandlers::TunnelsSuccessRateHandler;
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlHandlers::NetTotalReceivedBytes;
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlHandlers::NetTotalSentBytes;
// NetworkSetting
m_NetworkSettingHandlers["i2p.router.net.bw.in"] = &I2PControlHandlers::InboundBandwidthLimit;
m_NetworkSettingHandlers["i2p.router.net.bw.out"] = &I2PControlHandlers::OutboundBandwidthLimit;
// ClientServicesInfo
m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlHandlers::I2PTunnelInfoHandler;
m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlHandlers::HTTPProxyInfoHandler;
m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlHandlers::SOCKSInfoHandler;
m_ClientServicesInfoHandlers["SAM"] = &I2PControlHandlers::SAMInfoHandler;
m_ClientServicesInfoHandlers["BOB"] = &I2PControlHandlers::BOBInfoHandler;
m_ClientServicesInfoHandlers["I2CP"] = &I2PControlHandlers::I2CPInfoHandler;
}
void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
{
ss << "\"" << name << "\":" << value;
}
void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value, bool quotes) const
{
ss << "\"" << name << "\":";
if (value.length () > 0)
{
if (quotes)
ss << "\"" << value << "\"";
else
ss << value;
}
else
ss << "null";
}
void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, double value) const
{
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
}
void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const
{
std::ostringstream buf;
boost::property_tree::write_json (buf, value, false);
ss << "\"" << name << "\":" << buf.str();
}
// RouterInfo
void I2PControlHandlers::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
bool first = true;
for (auto it = params.begin (); it != params.end (); it++)
{
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
auto it1 = m_RouterInfoHandlers.find (it->first);
if (it1 != m_RouterInfoHandlers.end ())
{
if (!first) results << ",";
else first = false;
(this->*(it1->second))(results);
}
else
LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first);
}
}
void I2PControlHandlers::UptimeHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL), false);
}
void I2PControlHandlers::VersionHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.version", VERSION);
}
void I2PControlHandlers::StatusHandler (std::ostringstream& results)
{
auto dest = i2p::client::context.GetSharedLocalDestination ();
InsertParam (results, "i2p.router.status", (dest && dest->IsReady ()) ? "1" : "0");
}
void I2PControlHandlers::NetDbKnownPeersHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.netdb.knownpeers", i2p::data::netdb.GetNumRouters ());
}
void I2PControlHandlers::NetDbActivePeersHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.netdb.activepeers", (int)i2p::transport::transports.GetPeers ().size ());
}
void I2PControlHandlers::NetStatusHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ());
}
void I2PControlHandlers::TunnelsParticipatingHandler (std::ostringstream& results)
{
int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size ();
InsertParam (results, "i2p.router.net.tunnels.participating", transit);
}
void I2PControlHandlers::TunnelsSuccessRateHandler (std::ostringstream& results)
{
int rate = i2p::tunnel::tunnels.GetTunnelCreationSuccessRate ();
InsertParam (results, "i2p.router.net.tunnels.successrate", rate);
}
void I2PControlHandlers::InboundBandwidth1S (std::ostringstream& results)
{
double bw = i2p::transport::transports.GetInBandwidth ();
InsertParam (results, "i2p.router.net.bw.inbound.1s", bw);
}
void I2PControlHandlers::OutboundBandwidth1S (std::ostringstream& results)
{
double bw = i2p::transport::transports.GetOutBandwidth ();
InsertParam (results, "i2p.router.net.bw.outbound.1s", bw);
}
void I2PControlHandlers::NetTotalReceivedBytes (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.total.received.bytes", (double)i2p::transport::transports.GetTotalReceivedBytes ());
}
void I2PControlHandlers::NetTotalSentBytes (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ());
}
// network setting
void I2PControlHandlers::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
{
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
auto it1 = m_NetworkSettingHandlers.find (it->first);
if (it1 != m_NetworkSettingHandlers.end ()) {
if (it != params.begin ()) results << ",";
(this->*(it1->second))(it->second.data (), results);
} else
LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first);
}
}
void I2PControlHandlers::InboundBandwidthLimit (const std::string& value, std::ostringstream& results)
{
if (value != "null")
i2p::context.SetBandwidth (std::atoi(value.c_str()));
int bw = i2p::context.GetBandwidthLimit();
InsertParam (results, "i2p.router.net.bw.in", bw);
}
void I2PControlHandlers::OutboundBandwidthLimit (const std::string& value, std::ostringstream& results)
{
if (value != "null")
i2p::context.SetBandwidth (std::atoi(value.c_str()));
int bw = i2p::context.GetBandwidthLimit();
InsertParam (results, "i2p.router.net.bw.out", bw);
}
// ClientServicesInfo
void I2PControlHandlers::ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
{
for (auto it = params.begin (); it != params.end (); it++)
{
LogPrint (eLogDebug, "I2PControl: ClientServicesInfo request: ", it->first);
auto it1 = m_ClientServicesInfoHandlers.find (it->first);
if (it1 != m_ClientServicesInfoHandlers.end ())
{
if (it != params.begin ()) results << ",";
(this->*(it1->second))(results);
}
else
LogPrint (eLogError, "I2PControl: ClientServicesInfo unknown request ", it->first);
}
}
void I2PControlHandlers::I2PTunnelInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
boost::property_tree::ptree client_tunnels, server_tunnels;
for (auto& it: i2p::client::context.GetClientTunnels ())
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
boost::property_tree::ptree ct;
ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
client_tunnels.add_child(it.second->GetName (), ct);
}
auto& serverTunnels = i2p::client::context.GetServerTunnels ();
if (!serverTunnels.empty ()) {
for (auto& it: serverTunnels)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
boost::property_tree::ptree st;
st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
st.put("port", it.second->GetLocalPort ());
server_tunnels.add_child(it.second->GetName (), st);
}
}
auto& clientForwards = i2p::client::context.GetClientForwards ();
if (!clientForwards.empty ())
{
for (auto& it: clientForwards)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
boost::property_tree::ptree ct;
ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
client_tunnels.add_child(it.second->GetName (), ct);
}
}
auto& serverForwards = i2p::client::context.GetServerForwards ();
if (!serverForwards.empty ())
{
for (auto& it: serverForwards)
{
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
boost::property_tree::ptree st;
st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
server_tunnels.add_child(it.second->GetName (), st);
}
}
pt.add_child("client", client_tunnels);
pt.add_child("server", server_tunnels);
InsertParam (results, "I2PTunnel", pt);
}
void I2PControlHandlers::HTTPProxyInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
auto httpProxy = i2p::client::context.GetHttpProxy ();
if (httpProxy)
{
auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
pt.put("enabled", true);
pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
}
else
pt.put("enabled", false);
InsertParam (results, "HTTPProxy", pt);
}
void I2PControlHandlers::SOCKSInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
auto socksProxy = i2p::client::context.GetSocksProxy ();
if (socksProxy)
{
auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash();
pt.put("enabled", true);
pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
}
else
pt.put("enabled", false);
InsertParam (results, "SOCKS", pt);
}
void I2PControlHandlers::SAMInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
auto sam = i2p::client::context.GetSAMBridge ();
if (sam)
{
pt.put("enabled", true);
boost::property_tree::ptree sam_sessions;
for (auto& it: sam->GetSessions ())
{
boost::property_tree::ptree sam_session, sam_session_sockets;
auto& name = it.second->GetLocalDestination ()->GetNickname ();
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
sam_session.put("name", name);
sam_session.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident));
for (const auto& socket: sam->ListSockets(it.first))
{
boost::property_tree::ptree stream;
stream.put("type", socket->GetSocketType ());
stream.put("peer", socket->GetSocket ().remote_endpoint());
sam_session_sockets.push_back(std::make_pair("", stream));
}
sam_session.add_child("sockets", sam_session_sockets);
sam_sessions.add_child(it.first, sam_session);
}
pt.add_child("sessions", sam_sessions);
}
else
pt.put("enabled", false);
InsertParam (results, "SAM", pt);
}
void I2PControlHandlers::BOBInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
auto bob = i2p::client::context.GetBOBCommandChannel ();
if (bob)
{
/* TODO more info */
pt.put("enabled", true);
}
else
pt.put("enabled", false);
InsertParam (results, "BOB", pt);
}
void I2PControlHandlers::I2CPInfoHandler (std::ostringstream& results)
{
boost::property_tree::ptree pt;
auto i2cp = i2p::client::context.GetI2CPServer ();
if (i2cp)
{
/* TODO more info */
pt.put("enabled", true);
}
else
pt.put("enabled", false);
InsertParam (results, "I2CP", pt);
}
}
}

View File

@@ -1,80 +0,0 @@
/*
* 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 I2P_CONTROL_HANDLERS_H__
#define I2P_CONTROL_HANDLERS_H__
#include <sstream>
#include <map>
#include <string>
#include <boost/property_tree/ptree.hpp>
namespace i2p
{
namespace client
{
class I2PControlHandlers
{
public:
I2PControlHandlers ();
// methods
// TODO: make protected
void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
protected:
void InsertParam (std::ostringstream& ss, const std::string& name, int value) const;
void InsertParam (std::ostringstream& ss, const std::string& name, double value) const;
void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value, bool quotes = true) const;
void InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const;
private:
// RouterInfo
typedef void (I2PControlHandlers::*RouterInfoRequestHandler)(std::ostringstream& results);
void UptimeHandler (std::ostringstream& results);
void VersionHandler (std::ostringstream& results);
void StatusHandler (std::ostringstream& results);
void NetDbKnownPeersHandler (std::ostringstream& results);
void NetDbActivePeersHandler (std::ostringstream& results);
void NetStatusHandler (std::ostringstream& results);
void TunnelsParticipatingHandler (std::ostringstream& results);
void TunnelsSuccessRateHandler (std::ostringstream& results);
void InboundBandwidth1S (std::ostringstream& results);
void OutboundBandwidth1S (std::ostringstream& results);
void NetTotalReceivedBytes (std::ostringstream& results);
void NetTotalSentBytes (std::ostringstream& results);
// NetworkSetting
typedef void (I2PControlHandlers::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results);
void InboundBandwidthLimit (const std::string& value, std::ostringstream& results);
void OutboundBandwidthLimit (const std::string& value, std::ostringstream& results);
// ClientServicesInfo
typedef void (I2PControlHandlers::*ClientServicesInfoRequestHandler)(std::ostringstream& results);
void I2PTunnelInfoHandler (std::ostringstream& results);
void HTTPProxyInfoHandler (std::ostringstream& results);
void SOCKSInfoHandler (std::ostringstream& results);
void SAMInfoHandler (std::ostringstream& results);
void BOBInfoHandler (std::ostringstream& results);
void I2CPInfoHandler (std::ostringstream& results);
private:
std::map<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;
std::map<std::string, NetworkSettingRequestHandler> m_NetworkSettingHandlers;
std::map<std::string, ClientServicesInfoRequestHandler> m_ClientServicesInfoHandlers;
};
}
}
#endif

12
debian/changelog vendored
View File

@@ -1,15 +1,3 @@
i2pd (2.44.0-1) unstable; urgency=medium
* updated to version 2.44.0/0.9.56
-- orignal <orignal@i2pmail.org> Sun, 20 Nov 2022 19:00:00 +0000
i2pd (2.43.0-1) unstable; urgency=medium
* updated to version 2.43.0/0.9.55
-- orignal <orignal@i2pmail.org> Mon, 22 Aug 2022 16:00:00 +0000
i2pd (2.42.1-1) unstable; urgency=medium
* updated to version 2.42.1/0.9.54

View File

@@ -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_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)
# 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)
WEBCONSOLE_SRC = $(wildcard $(WEBCONSOLE_SRC_DIR)/*.cpp)
WRAP_LIB_SRC = $(wildcard $(WRAP_SRC_DIR)/*.cpp)
DAEMON_SRC = $(wildcard $(DAEMON_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

View File

@@ -1,217 +0,0 @@
/*
* 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"
// Simplified Chinese localization file
// This is an example translation file without strings in it.
namespace i2p
{
namespace i18n
{
namespace chinese // language namespace
{
// language name in lowercase
static std::string language = "chinese";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
return 0;
}
static std::map<std::string, std::string> strings
{
{"KiB", "KiB"},
{"MiB", "MiB"},
{"GiB", "GiB"},
{"building", "正在构建"},
{"failed", "连接失败"},
{"expiring", "即将过期"},
{"established", "连接成功"},
{"unknown", "未知"},
{"exploratory", "探索"},
{"Purple I2P Webconsole", "Purple I2P 网页控制台"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> 网页控制台"},
{"Main page", "主页"},
{"Router commands", "路由命令"},
{"Local Destinations", "本地目标"},
{"LeaseSets", "租契集"},
{"Tunnels", "隧道"},
{"Transit Tunnels", "中转隧道"},
{"Transports", "传输"},
{"I2P tunnels", "I2P 隧道"},
{"SAM sessions", "SAM 会话"},
{"ERROR", "错误"},
{"OK", "良好"},
{"Testing", "测试中"},
{"Firewalled", "受到防火墙限制"},
{"Unknown", "未知"},
{"Proxy", "代理"},
{"Mesh", "Mesh组网"},
{"Error", "错误"},
{"Clock skew", "时钟偏移"},
{"Offline", "离线"},
{"Symmetric NAT", "对称 NAT"},
{"Uptime", "运行时间"},
{"Network status", "IPv4 网络状态"},
{"Network status v6", "IPv6 网络状态"},
{"Stopping in", "距停止还有:"},
{"Family", "家族"},
{"Tunnel creation success rate", "隧道创建成功率"},
{"Received", "已接收"},
{"KiB/s", "KiB/s"},
{"Sent", "已发送"},
{"Transit", "中转"},
{"Data path", "数据文件路径"},
{"Hidden content. Press on text to see.", "隐藏内容 请点击此处查看。"},
{"Router Ident", "路由身份"},
{"Router Family", "路由器家族"},
{"Router Caps", "路由器类型"},
{"Version", "版本"},
{"Our external address", "外部地址"},
{"supported", "支持"},
{"Routers", "路由节点"},
{"Floodfills", "洪泛节点"},
{"Client Tunnels", "客户端隧道"},
{"Services", "服务"},
{"Enabled", "启用"},
{"Disabled", "禁用"},
{"Encrypted B33 address", "加密的 B33 地址"},
{"Address registration line", "地址域名注册"},
{"Domain", "域名"},
{"Generate", "生成"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>注意:</b> 结果字符串只能用于注册次级域名(例如example.i2p)。若需注册子域名,请使用 i2pd-tools。"},
{"Address", "地址"},
{"Type", "类型"},
{"EncType", "加密类型"},
{"Inbound tunnels", "入站隧道"},
{"ms", "毫秒"},
{"Outbound tunnels", "出站隧道"},
{"Tags", "标签"},
{"Incoming", "传入"},
{"Outgoing", "传出"},
{"Destination", "目标"},
{"Amount", "数量"},
{"Incoming Tags", "传入标签"},
{"Tags sessions", "标签会话"},
{"Status", "状态"},
{"Local Destination", "本地目标"},
{"Streams", ""},
{"Close stream", "断开流"},
{"I2CP session not found", "未找到 I2CP 会话"},
{"I2CP is not enabled", "I2CP 未启用"},
{"Invalid", "无效"},
{"Store type", "存储类型"},
{"Expires", "过期时间"},
{"Non Expired Leases", "未到期的租约"},
{"Gateway", "网关"},
{"TunnelID", "隧道 ID"},
{"EndDate", "结束日期"},
{"not floodfill", "非洪泛"},
{"Queue size", "队列大小"},
{"Run peer test", "运行节点测试"},
{"Decline transit tunnels", "拒绝中转隧道"},
{"Accept transit tunnels", "允许中转隧道"},
{"Cancel graceful shutdown", "取消平滑关闭"},
{"Start graceful shutdown", "平滑关闭"},
{"Force shutdown", "强制停止"},
{"Reload external CSS styles", "重载外部 CSS 样式"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>注意:</b> 此处完成的任何操作都不是永久的,不会更改您的配置文件。"},
{"Logging level", "日志记录级别"},
{"Transit tunnels limit", "中转隧道限制"},
{"Change", "修改"},
{"Change language", "更改语言"},
{"no transit tunnels currently built", "目前未构建中转隧道"},
{"SAM disabled", "SAM 已禁用"},
{"no sessions currently running", "没有正在运行的会话"},
{"SAM session not found", "未找到 SAM 会话"},
{"SAM Session", "SAM 会话"},
{"Server Tunnels", "服务器隧道"},
{"Client Forwards", "客户端转发"},
{"Server Forwards", "服务器转发"},
{"Unknown page", "未知页面"},
{"Invalid token", "无效令牌"},
{"SUCCESS", "成功"},
{"Stream closed", "流已关闭"},
{"Stream not found or already was closed", "流未找到或已关闭"},
{"Destination not found", "找不到目标"},
{"StreamID can't be null", "StreamID 不能为空"},
{"Return to destination page", "返回目标页面"},
{"You will be redirected in 5 seconds", "您将在5秒内被重定向"},
{"Transit tunnels count must not exceed 65535", "中转隧道数量不能超过 65535"},
{"Back to commands list", "返回命令列表"},
{"Register at reg.i2p", "在 reg.i2p 注册域名"},
{"Description", "描述"},
{"A bit information about service on domain", "在此域名上运行的服务的一些信息"},
{"Submit", "提交"},
{"Domain can't end with .b32.i2p", "域名不能以 .b32.i2p 结尾"},
{"Domain must end with .i2p", "域名必须以 .i2p 结尾"},
{"Such destination is not found", "找不到此目标"},
{"Unknown command", "未知指令"},
{"Command accepted", "已接受指令"},
{"Proxy error", "代理错误"},
{"Proxy info", "代理信息"},
{"Proxy error: Host not found", "代理错误:未找到主机"},
{"Remote host not found in router's addressbook", "在路由地址簿中未找到远程主机"},
{"You may try to find this host on jump services below", "您可以尝试在下方的跳转服务中找到该主机"},
{"Invalid request", "无效请求"},
{"Proxy unable to parse your request", "代理无法解析您的请求"},
{"addresshelper is not supported", "不支持地址助手"},
{"Host", "主机"},
{"added to router's addressbook from helper", "将此地址从地址助手添加到路由地址簿"},
{"Click here to proceed:", "点击此处继续:"},
{"Continue", "继续"},
{"Addresshelper found", "已找到地址助手"},
{"already in router's addressbook", "已在路由地址簿中"},
{"Click here to update record:", "点击此处更新地址簿记录"},
{"invalid request uri", "无效的 URL 请求"},
{"Can't detect destination host from request", "无法从请求中检测到目标主机"},
{"Outproxy failure", "出口代理故障"},
{"bad outproxy settings", "错误的出口代理设置"},
{"not inside I2P network, but outproxy is not enabled", "该地址不在 I2P 网络内,但未启用出口代理"},
{"unknown outproxy url", "未知的出口代理地址"},
{"cannot resolve upstream proxy", "无法解析上游代理"},
{"hostname too long", "主机名过长"},
{"cannot connect to upstream socks proxy", "无法连接到上游 socks 代理"},
{"Cannot negotiate with socks proxy", "无法与 socks 代理协商"},
{"CONNECT error", "连接错误"},
{"Failed to Connect", "连接失败"},
{"socks proxy error", "socks 代理错误"},
{"failed to send request to upstream", "向上游发送请求失败"},
{"No Reply From socks proxy", "没有来自 socks 代理的回复"},
{"cannot connect", "无法连接"},
{"http out proxy not implemented", "http 出口代理未实现"},
{"cannot connect to upstream http proxy", "无法连接到上游 http 代理"},
{"Host is down", "主机已关闭"},
{"Can't create connection to requested host, it may be down. Please try again later.", "无法创建到目标主机的连接。主机可能已下线,请稍后再试。"},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"days", {""}},
{"hours", {""}},
{"minutes", {""}},
{"seconds", {""}},
{"", {""}},
};
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

View File

@@ -35,33 +35,24 @@ namespace french // language namespace
{"MiB", "Mio"},
{"GiB", "Gio"},
{"building", "En construction"},
{"failed", "échoué"},
{"failed", "echoué"},
{"expiring", "expiré"},
{"established", "établi"},
{"unknown", "inconnu"},
{"exploratory", "exploratoire"},
{"Purple I2P Webconsole", "Console web Purple I2P"},
{"<b>i2pd</b> webconsole", "Console web <b>i2pd</b>"},
{"Main page", "Page principale"},
{"Router commands", "Commandes du routeur"},
{"Local Destinations", "Destinations locales"},
{"LeaseSets", "Jeu de baux"},
{"Tunnels", "Tunnels"},
{"Transit Tunnels", "Tunnels transitoires"},
{"Transports", "Transports"},
{"I2P tunnels", "Tunnels I2P"},
{"SAM sessions", "Sessions SAM"},
{"ERROR", "ERREUR"},
{"OK", "OK"},
{"Testing", "Test en cours"},
{"Firewalled", "Derrière un pare-feu"},
{"Unknown", "Inconnu"},
{"Proxy", "Proxy"},
{"Mesh", "Maillé"},
{"Error", "Erreur"},
{"Clock skew", "Horloge décalée"},
{"Offline", "Hors ligne"},
{"Symmetric NAT", "NAT symétrique"},
{"Uptime", "Temps de fonctionnement"},
{"Network status", "État du réseau"},
{"Network status v6", "État du réseau v6"},
@@ -71,124 +62,24 @@ namespace french // language namespace
{"Received", "Reçu"},
{"KiB/s", "kio/s"},
{"Sent", "Envoyé"},
{"Transit", "Transité"},
{"Data path", "Emplacement des données"},
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour afficher."},
{"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"},
{"Router Caps", "Limiteurs du routeur"},
{"Version", "Version"},
{"Our external address", "Notre adresse externe"},
{"supported", "supporté"},
{"Routers", "Routeurs"},
{"Client Tunnels", "Tunnels clients"},
{"Services", "Services"},
{"Enabled", "Activé"},
{"Disabled", "Désactivé"},
{"Encrypted B33 address", "Adresse B33 chiffrée"},
{"Address registration line", "Ligne d'inscription de l'adresse"},
{"Domain", "Domaine"},
{"Generate", "Générer"},
{"<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"},
{"Type", "Type"},
{"Inbound tunnels", "Tunnels entrants"},
{"ms", "ms"},
{"Outbound tunnels", "Tunnels sortants"},
{"Tags", "Balises"},
{"Incoming", "Entrant"},
{"Outgoing", "Sortant"},
{"Destination", "Destination"},
{"Amount", "Quantité"},
{"Incoming Tags", "Balises entrantes"},
{"Tags sessions", "Sessions des balises"},
{"Status", "Statut"},
{"Local Destination", "Destination locale"},
{"Streams", "Flux"},
{"Close stream", "Fermer le flux"},
{"I2CP session not found", "Session I2CP introuvable"},
{"I2CP is not enabled", "I2CP est désactivé"},
{"Invalid", "Invalide"},
{"Store type", "Type de stockage"},
{"Expires", "Expire"},
{"Non Expired Leases", "Baux non expirés"},
{"Gateway", "Passerelle"},
{"TunnelID", "ID du tunnel"},
{"EndDate", "Date de fin"},
{"Queue size", "Longueur de la file"},
{"Run peer test", "Lancer test des pairs"},
{"Decline transit tunnels", "Refuser les tunnels transitoires"},
{"Accept transit tunnels", "Accepter les tunnels transitoires"},
{"Cancel graceful shutdown", "Annuler l'arrêt gracieux"},
{"Start graceful shutdown", "Démarrer l'arrêt gracieux"},
{"Force shutdown", "Forcer l'arrêt"},
{"Reload external CSS styles", "Rafraîchir les styles CSS externes"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Note:</b> Toute action effectuée ici n'est pas permanente et ne modifie pas vos fichiers de configuration."},
{"Logging level", "Niveau de journalisation"},
{"Transit tunnels limit", "Limite sur les tunnels transitoires"},
{"Change", "Changer"},
{"Change language", "Changer la langue"},
{"no transit tunnels currently built", "aucun tunnel transitoire présentement établi"},
{"SAM disabled", "SAM désactivé"},
{"no sessions currently running", "aucune session présentement en cours"},
{"SAM session not found", "session SAM introuvable"},
{"SAM Session", "Session SAM"},
{"Server Tunnels", "Tunnels serveurs"},
{"Unknown page", "Page inconnue"},
{"Invalid token", "Jeton invalide"},
{"SUCCESS", "SUCCÈS"},
{"Stream closed", "Flux fermé"},
{"Stream not found or already was closed", "Flux introuvable ou déjà fermé"},
{"Destination not found", "Destination introuvable"},
{"StreamID can't be null", "StreamID ne peut pas être vide"},
{"Return to destination page", "Retourner à la page de destination"},
{"You will be redirected in 5 seconds", "Vous allez être redirigé dans cinq secondes"},
{"Transit tunnels count must not exceed 65535", "Le nombre de tunnels transitoires ne doit pas dépasser 65535"},
{"Back to commands list", "Retour à la liste des commandes"},
{"Register at reg.i2p", "Inscription à reg.i2p"},
{"Description", "Description"},
{"A bit information about service on domain", "Un peu d'information à propos des services disponibles dans le domaine"},
{"Submit", "Soumettre"},
{"Domain can't end with .b32.i2p", "Le domaine ne peut pas terminer par .b32.i2p"},
{"Domain must end with .i2p", "Le domaine doit terminer par .i2p"},
{"Such destination is not found", "Cette destination est introuvable"},
{"Unknown command", "Commande inconnue"},
{"Command accepted", "Commande acceptée"},
{"Proxy error", "Erreur de proxy"},
{"Proxy info", "Information sur le proxy"},
{"Proxy error: Host not found", "Erreur de proxy: Hôte introuvable"},
{"Remote host not found in router's addressbook", "Hôte distant introuvable dans le carnet d'adresse du routeur"},
{"You may try to find this host on jump services below", "Vous pouvez essayer de trouver cet hôte sur des services de redirection ci-dessous"},
{"Invalid request", "Requête invalide"},
{"Proxy unable to parse your request", "Proxy incapable de comprendre votre requête"},
{"addresshelper is not supported", "Assistant d'adresse non supporté"},
{"Host", "Hôte"},
{"added to router's addressbook from helper", "Ajouté au carnet d'adresse du routeur par l'assistant"},
{"Click here to proceed:", "Cliquez ici pour continuer:"},
{"Continue", "Continuer"},
{"Addresshelper found", "Assistant d'adresse trouvé"},
{"already in router's addressbook", "déjà dans le carnet d'adresses du routeur"},
{"Click here to update record:", "Cliquez ici pour mettre à jour le carnet d'adresse:"},
{"invalid request uri", "uri de la requête invalide"},
{"Can't detect destination host from request", "Impossible de détecter l'hôte de destination à partir de la requête"},
{"Outproxy failure", "Échec de proxy de sortie"},
{"bad outproxy settings", "Mauvaise configuration du proxy de sortie"},
{"not inside I2P network, but outproxy is not enabled", "pas dans le réseau I2P, mais le proxy de sortie n'est pas activé"},
{"unknown outproxy url", "URL du proxy de sortie inconnu"},
{"cannot resolve upstream proxy", "impossible de résoudre l'adresse du proxy en amont"},
{"hostname too long", "nom d'hôte trop long"},
{"cannot connect to upstream socks proxy", "impossible de se connecter au proxy socks en amont"},
{"Cannot negotiate with socks proxy", "Impossible de négocier avec le proxy socks"},
{"CONNECT error", "Erreur de connexion"},
{"Failed to Connect", "Échec de connexion"},
{"socks proxy error", "Erreur de proxy socks"},
{"failed to send request to upstream", "Erreur lors de l'envoie de la requête en amont"},
{"No Reply From socks proxy", "Pas de réponse du proxy socks"},
{"cannot connect", "impossible de connecter"},
{"http out proxy not implemented", "Proxy de sortie HTTP non implémenté"},
{"cannot connect to upstream http proxy", "impossible de se connecter au proxy HTTP en amont"},
{"Host is down", "Hôte hors service"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Impossible d'établir une connexion avec l'hôte, il est peut-être hors service. Veuillez réessayer plus tard."},
{"", ""},
};

View File

@@ -36,15 +36,14 @@ namespace german // language namespace
{"GiB", "GiB"},
{"building", "In Bau"},
{"failed", "fehlgeschlagen"},
{"expiring", "läuft ab"},
{"expiring", "läuft ab in"},
{"established", "hergestellt"},
{"unknown", "Unbekannt"},
{"exploratory", "erforschend"},
{"Purple I2P Webconsole", "Purple I2P-Webkonsole"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b>-Webkonsole"},
{"exploratory", "erforschende"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> Webkonsole"},
{"Main page", "Startseite"},
{"Router commands", "Routerbefehle"},
{"Local Destinations", "Lokale Ziele"},
{"Router commands", "Router Befehle"},
{"Local Destinations", "Lokale Destination"},
{"LeaseSets", "LeaseSets"},
{"Tunnels", "Tunnel"},
{"Transit Tunnels", "Transittunnel"},
@@ -54,7 +53,7 @@ namespace german // language namespace
{"ERROR", "FEHLER"},
{"OK", "OK"},
{"Testing", "Testen"},
{"Firewalled", "Hinter einer Firewall"},
{"Firewalled", "Hinter eine Firewall"},
{"Unknown", "Unbekannt"},
{"Proxy", "Proxy"},
{"Mesh", "Mesh"},
@@ -82,15 +81,15 @@ namespace german // language namespace
{"supported", "unterstützt"},
{"Routers", "Router"},
{"Floodfills", "Floodfills"},
{"Client Tunnels", "Clienttunnel"},
{"Client Tunnels", "Klienttunnel"},
{"Services", "Services"},
{"Enabled", "Aktiviert"},
{"Disabled", "Deaktiviert"},
{"Encrypted B33 address", "Verschlüsselte B33-Adresse"},
{"Address registration line", "Adressregistrierungszeile"},
{"Encrypted B33 address", "Verschlüsselte B33 Adresse"},
{"Address registration line", "Adresseregistrierungszeile"},
{"Domain", "Domain"},
{"Generate", "Generieren"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Hinweis:</b> Der resultierende String kann nur für die Registrierung einer 2LD-Domain (beispiel.i2p) benutzt werden. Für die Registrierung von Subdomains kann i2pd-tools verwendet werden."},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Hinweis:</b> Der resultierende String kann nur für die Registrierung einer 2LD Domain (beispiel.i2p) benutzt werden. Für die Registrierung von Subdomains kann i2pd-tools verwendet werden."},
{"Address", "Adresse"},
{"Type", "Typ"},
{"EncType", "Verschlüsselungstyp"},
@@ -100,15 +99,15 @@ namespace german // language namespace
{"Tags", "Tags"},
{"Incoming", "Eingehend"},
{"Outgoing", "Ausgehend"},
{"Destination", "Ziel"},
{"Destination", "Destination"},
{"Amount", "Anzahl"},
{"Incoming Tags", "Eingehende Tags"},
{"Tags sessions", "Tags-Sitzungen"},
{"Tags sessions", "Tags Sitzungen"},
{"Status", "Status"},
{"Local Destination", "Lokales Ziel"},
{"Local Destination", "Lokale Destination"},
{"Streams", "Streams"},
{"Close stream", "Stream schließen"},
{"I2CP session not found", "I2CP-Sitzung nicht gefunden"},
{"I2CP session not found", "I2CP Sitzung nicht gefunden"},
{"I2CP is not enabled", "I2CP ist nicht aktiviert"},
{"Invalid", "Ungültig"},
{"Store type", "Speichertyp"},
@@ -118,67 +117,67 @@ namespace german // language namespace
{"TunnelID", "TunnelID"},
{"EndDate", "Enddatum"},
{"not floodfill", "kein Floodfill"},
{"Queue size", "Größe der Warteschlange"},
{"Run peer test", "Peer-Test durchführen"},
{"Queue size", "Warteschlangengröße"},
{"Run peer test", "Peer-Test ausführen"},
{"Decline transit tunnels", "Transittunnel ablehnen"},
{"Accept transit tunnels", "Transittunnel akzeptieren"},
{"Cancel graceful shutdown", "Beende das kontrollierte Herunterfahren"},
{"Cancel graceful shutdown", "Beende das kontrollierte herunterfahren"},
{"Start graceful shutdown", "Starte das kontrollierte Herunterfahren"},
{"Force shutdown", "Herunterfahren erzwingen"},
{"Reload external CSS styles", "Lade externe CSS-Stile neu"},
{"Reload external CSS styles", "Lade externe CSS-Styles neu"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Hinweis:</b> Alle hier durchgeführten Aktionen sind nicht dauerhaft und ändern die Konfigurationsdateien nicht."},
{"Logging level", "Protokollierungslevel"},
{"Transit tunnels limit", "Limit für Transittunnel"},
{"Change", "Ändern"},
{"Change", "Verändern"},
{"Change language", "Sprache ändern"},
{"no transit tunnels currently built", "derzeit keine Transittunnel aufgebaut"},
{"SAM disabled", "SAM deaktiviert"},
{"no sessions currently running", "Derzeit keine laufenden Sitzungen"},
{"SAM session not found", "SAM-Sitzung nicht gefunden"},
{"SAM Session", "SAM-Sitzung"},
{"SAM session not found", "SAM Sitzung nicht gefunden"},
{"SAM Session", "SAM Sitzung"},
{"Server Tunnels", "Servertunnel"},
{"Client Forwards", "Client-Weiterleitungen"},
{"Client Forwards", "Klient-Weiterleitungen"},
{"Server Forwards", "Server-Weiterleitungen"},
{"Unknown page", "Unbekannte Seite"},
{"Invalid token", "Ungültiger Token"},
{"SUCCESS", "ERFOLGREICH"},
{"Stream closed", "Stream geschlossen"},
{"Stream not found or already was closed", "Stream nicht gefunden oder bereits geschlossen"},
{"Destination not found", "Ziel nicht gefunden"},
{"Destination not found", "Destination nicht gefunden"},
{"StreamID can't be null", "StreamID kann nicht null sein"},
{"Return to destination page", "Zurück zur Ziel-Seite"},
{"Return to destination page", "Zurück zur Destination-Seite"},
{"You will be redirected in 5 seconds", "Du wirst in 5 Sekunden weitergeleitet"},
{"Transit tunnels count must not exceed 65535", "Es darf maximal 65535 Transittunnel geben"},
{"Back to commands list", "Zurück zur Befehlsliste"},
{"Back to commands list", "Zurück zur Kommandoliste"},
{"Register at reg.i2p", "Auf reg.i2p registrieren"},
{"Description", "Beschreibung"},
{"A bit information about service on domain", "Ein paar Informationen über den Service auf der Domain"},
{"Submit", "Absenden"},
{"Domain can't end with .b32.i2p", "Domain kann nicht auf .b32.i2p enden"},
{"Domain must end with .i2p", "Domain muss auf .i2p enden"},
{"Such destination is not found", "Ein solches Ziel konnte nicht gefunden werden"},
{"A bit information about service on domain", "Ein bisschen Informationen über den Service auf der Domain"},
{"Submit", "Einreichen"},
{"Domain can't end with .b32.i2p", "Domain kann nicht mit .b32.i2p enden"},
{"Domain must end with .i2p", "Domain muss mit .i2p enden"},
{"Such destination is not found", "Eine solche Destination konnte nicht gefunden werden"},
{"Unknown command", "Unbekannter Befehl"},
{"Command accepted", "Befehl akzeptiert"},
{"Proxy error", "Proxy-Fehler"},
{"Proxy info", "Proxy-Info"},
{"Proxy error: Host not found", "Proxy-Fehler: Host nicht gefunden"},
{"Remote host not found in router's addressbook", "Remote-Host nicht im Router-Adressbuch gefunden"},
{"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einem der nachfolgenden Jump-Services finden"},
{"Remote host not found in router's addressbook", "Remote-Host nicht im Router Adressbuch gefunden"},
{"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einen der Jump-Services unten finden"},
{"Invalid request", "Ungültige Anfrage"},
{"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht verarbeiten"},
{"addresshelper is not supported", "Addresshelfer wird nicht unterstützt"},
{"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht interpretieren"},
{"addresshelper is not supported", "addresshelper wird nicht unterstützt"},
{"Host", "Host"},
{"added to router's addressbook from helper", "vom Helfer zum Router-Adressbuch hinzugefügt"},
{"added to router's addressbook from helper", "vom Helfer zum Router Adressbuch hinzugefügt"},
{"Click here to proceed:", "Klicke hier um fortzufahren:"},
{"Continue", "Fortsetzen"},
{"Addresshelper found", "Adresshelfer gefunden"},
{"already in router's addressbook", "bereits im Adressbuch des Routers"},
{"Click here to update record:", "Klicke hier, um den Eintrag zu aktualisieren:"},
{"invalid request uri", "ungültige Anfrage-URI"},
{"Can't detect destination host from request", "Kann den Ziel-Host von der Anfrage nicht erkennen"},
{"Can't detect destination host from request", "Kann Anhand der Anfrage den Destination-Host nicht erkennen"},
{"Outproxy failure", "Outproxy-Fehler"},
{"bad outproxy settings", "ungültige Outproxy-Einstellungen"},
{"not inside I2P network, but outproxy is not enabled", "außerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"},
{"not inside I2P network, but outproxy is not enabled", "nicht innerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"},
{"unknown outproxy url", "unbekannte Outproxy-URL"},
{"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"},
{"hostname too long", "Hostname zu lang"},
@@ -193,7 +192,7 @@ namespace german // language namespace
{"http out proxy not implemented", "HTTP-Outproxy nicht implementiert"},
{"cannot connect to upstream http proxy", "Kann nicht zu Upstream-HTTP-Proxy verbinden"},
{"Host is down", "Host ist offline"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Konnte keine Verbindung zum angefragten Host aufbauen, vielleicht ist er offline. Versuche es später noch mal."},
{"Can't create connection to requested host, it may be down. Please try again later.", "Konnte keine Verbindung zum angefragten Host aufbaunen, vielleicht ist es offline. Versuche es später noch einmal."},
{"", ""},
};

View File

@@ -73,13 +73,10 @@ namespace i18n
// Add localization here with language name as namespace
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace chinese { 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 italian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace spanish { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
@@ -90,16 +87,13 @@ namespace i18n
static std::map<std::string, langData> languages
{
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
{ "armenian", {"hայերէն", "hy", i2p::i18n::armenian::GetLocale} },
{ "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} },
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} },
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
{ "italian", {"Italiano", "it", i2p::i18n::italian::GetLocale} },
{ "russian", {"Русский язык", "ru", i2p::i18n::russian::GetLocale} },
{ "spanish", {"Español", "es", i2p::i18n::spanish::GetLocale} },
{ "turkmen", {"Türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
{ "ukrainian", {"Украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} },
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
{ "ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
{ "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} },
};

View File

@@ -1,216 +0,0 @@
/*
* 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"
// Italian localization file
namespace i2p
{
namespace i18n
{
namespace italian // language namespace
{
// language name in lowercase
static std::string language = "italian";
// 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", "KiB"},
{"MiB", "MiB"},
{"GiB", "GiB"},
{"building", "in costruzione"},
{"failed", "fallito"},
{"expiring", "in scadenza"},
{"established", "stabilita"},
{"unknown", "sconosciuto"},
{"exploratory", "esplorativo"},
{"Purple I2P Webconsole", "Terminale web Purple I2P"},
{"<b>i2pd</b> webconsole", "Terminal web <b>i2pd</b>"},
{"Main page", "Pagina principale"},
{"Router commands", "Comandi router"},
{"Local Destinations", "Destinazioni locali"},
{"LeaseSets", "LeaseSets"},
{"Tunnels", "Tunnel"},
{"Transit Tunnels", "Tunnel di transito"},
{"Transports", "Trasporti"},
{"I2P tunnels", "Tunnel I2P"},
{"SAM sessions", "Sessioni SAM"},
{"ERROR", "ERRORE"},
{"OK", "OK"},
{"Testing", "Testando"},
{"Firewalled", "Protetto da firewall"},
{"Unknown", "Sconosciuto"},
{"Proxy", "Proxy"},
{"Mesh", "Mesh"},
{"Error", "Errore"},
{"Clock skew", "Orologio disallineato"},
{"Offline", "Disconnesso"},
{"Symmetric NAT", "NAT simmetrico"},
{"Uptime", "In funzione da"},
{"Network status", "Stato della rete"},
{"Network status v6", "Stato della rete v6"},
{"Stopping in", "Arresto in"},
{"Family", "Famiglia"},
{"Tunnel creation success rate", "Percentuale di tunnel creati con successo"},
{"Received", "Ricevuti"},
{"KiB/s", "KiB/s"},
{"Sent", "Inviati"},
{"Transit", "Transitati"},
{"Data path", "Percorso dati"},
{"Hidden content. Press on text to see.", "Contenuto nascosto. Premi sul testo per vedere."},
{"Router Ident", "Identificativo del router"},
{"Router Family", "Famiglia del router"},
{"Router Caps", "Limiti del router"},
{"Version", "Versione"},
{"Our external address", "Il nostro indirizzo esterno"},
{"supported", "supportato"},
{"Routers", "Router"},
{"Floodfills", "Floodfill"},
{"Client Tunnels", "Tunnel client"},
{"Services", "Servizi"},
{"Enabled", "Abilitato"},
{"Disabled", "Disabilitato"},
{"Encrypted B33 address", "Indirizzo criptato B33"},
{"Address registration line", "Linea di registrazione indirizzo"},
{"Domain", "Dominio"},
{"Generate", "Genera"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Nota:</b> la stringa risultante può essere utilizzata solo per registrare domini 2LD (example.i2p). Per registrare i sottodomini, si prega di utilizzare i2pd-tools."},
{"Address", "Indirizzo"},
{"Type", "Tipologia"},
{"EncType", "Tipo di crittografia"},
{"Inbound tunnels", "Tunnel in entrata"},
{"ms", "ms"},
{"Outbound tunnels", "Tunnel in uscita"},
{"Tags", "Tag"},
{"Incoming", "In entrata"},
{"Outgoing", "In uscita"},
{"Destination", "Destinazione"},
{"Amount", "Quantità"},
{"Incoming Tags", "Tag in entrata"},
{"Tags sessions", "Sessioni dei tag"},
{"Status", "Stato"},
{"Local Destination", "Destinazione locale"},
{"Streams", "Flussi"},
{"Close stream", "Interrompi il flusso"},
{"I2CP session not found", "Sessione I2CP non trovata"},
{"I2CP is not enabled", "I2CP non è abilitato"},
{"Invalid", "Invalido"},
{"Store type", "Tipologia di archivio"},
{"Expires", "Scade"},
{"Non Expired Leases", "Lease non scaduti"},
{"Gateway", "Gateway"},
{"TunnelID", "TunnelID"},
{"EndDate", "Data di fine"},
{"not floodfill", "no floodfill"},
{"Queue size", "Dimensione della coda"},
{"Run peer test", "Esegui il test dei peer"},
{"Decline transit tunnels", "Rifiuta tunnel di transito"},
{"Accept transit tunnels", "Accetta tunnel di transito"},
{"Cancel graceful shutdown", "Annulla l'interruzione controllata"},
{"Start graceful shutdown", "Avvia l'interruzione controllata"},
{"Force shutdown", "Forza l'arresto"},
{"Reload external CSS styles", "Ricarica gli stili CSS esterni"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Nota:</b> qualsiasi azione effettuata qui non è persistente e non modifica i file di configurazione."},
{"Logging level", "Livello di log"},
{"Transit tunnels limit", "Limite di tunnel di transito"},
{"Change", "Modifica"},
{"Change language", "Modifica linguaggio"},
{"no transit tunnels currently built", "Attualmente non ci sono tunnel di transito instaurati"},
{"SAM disabled", "SAM disabilitato"},
{"no sessions currently running", "Attualmente non ci sono sessioni attive"},
{"SAM session not found", "Sessione SAM non trovata"},
{"SAM Session", "Sessione SAM"},
{"Server Tunnels", "Tunnel server"},
{"Client Forwards", "Client di inoltro"},
{"Server Forwards", "Server di inoltro"},
{"Unknown page", "Pagina sconosciuta"},
{"Invalid token", "Token non valido"},
{"SUCCESS", "SUCCESSO"},
{"Stream closed", "Flusso terminato"},
{"Stream not found or already was closed", "Il flusso non è stato trovato oppure è già stato terminato"},
{"Destination not found", "Destinazione non trovata"},
{"StreamID can't be null", "Lo StreamID non può essere null"},
{"Return to destination page", "Ritorna alla pagina di destinazione"},
{"You will be redirected in 5 seconds", "Verrai reindirizzato in 5 secondi"},
{"Transit tunnels count must not exceed 65535", "Il numero di tunnel di transito non può superare i 65535"},
{"Back to commands list", "Ritorna alla lista dei comandi"},
{"Register at reg.i2p", "Registra a reg.i2p"},
{"Description", "Descrizione"},
{"A bit information about service on domain", "Alcune informazioni riguardo il servizio sul dominio"},
{"Submit", "Invia"},
{"Domain can't end with .b32.i2p", "I domini non possono terminare con .b32.i2p"},
{"Domain must end with .i2p", "I domini devono terminare con .i2p"},
{"Such destination is not found", "Questa destinazione non è stata trovata"},
{"Unknown command", "Comando sconosciuto"},
{"Command accepted", "Comando accettato"},
{"Proxy error", "Errore del proxy"},
{"Proxy info", "Informazioni del proxy"},
{"Proxy error: Host not found", "Errore del proxy: Host non trovato"},
{"Remote host not found in router's addressbook", "L'host remoto non è stato trovato nella rubrica del router"},
{"You may try to find this host on jump services below", "Si può provare a trovare questo host sui servizi di salto qui sotto"},
{"Invalid request", "Richiesta non valida"},
{"Proxy unable to parse your request", "Il proxy non è in grado di elaborare la tua richiesta"},
{"addresshelper is not supported", "addresshelper non è supportato"},
{"Host", "Host"},
{"added to router's addressbook from helper", "aggiunto alla rubrica tramite l'helper"},
{"Click here to proceed:", "Clicca qui per procedere:"},
{"Continue", "Continua"},
{"Addresshelper found", "Addresshelper trovato"},
{"already in router's addressbook", "già presente nella rubrica del router"},
{"Click here to update record:", "Clicca qui per aggiornare l'elemento:"},
{"invalid request uri", "uri della richiesta non valido"},
{"Can't detect destination host from request", "Impossibile determinare l'host di destinazione dalla richiesta"},
{"Outproxy failure", "Fallimento del proxy di uscita"},
{"bad outproxy settings", "impostazioni errate del proxy di uscita"},
{"not inside I2P network, but outproxy is not enabled", "non all'interno della rete I2P, ma il proxy di uscita non è abilitato"},
{"unknown outproxy url", "url del proxy di uscita sconosciuto"},
{"cannot resolve upstream proxy", "impossibile identificare il flusso a monte del proxy"},
{"hostname too long", "il nome dell'host è troppo lungo"},
{"cannot connect to upstream socks proxy", "impossibile connettersi al flusso a monte del proxy socks"},
{"Cannot negotiate with socks proxy", "Impossibile negoziare con il proxy socks"},
{"CONNECT error", "Errore di connessione"},
{"Failed to Connect", "Connessione fallita"},
{"socks proxy error", "errore del proxy socks"},
{"failed to send request to upstream", "invio della richiesta a monte non riuscito"},
{"No Reply From socks proxy", "Nessuna risposta dal proxy socks"},
{"cannot connect", "impossibile connettersi"},
{"http out proxy not implemented", "proxy http di uscita non implementato"},
{"cannot connect to upstream http proxy", "impossibile connettersi al proxy http a monte"},
{"Host is down", "L'host è offline"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Impossibile creare la connessione all'host richiesto, probabilmente è offline. Riprova più tardi."},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"days", {"giorno", "giorni"}},
{"hours", {"ora", "ore"}},
{"minutes", {"minuto", "minuti"}},
{"seconds", {"secondo", "secondi"}},
{"", {"", ""}},
};
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

View File

@@ -1,216 +0,0 @@
/*
* 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"
// Spanish localization file
namespace i2p
{
namespace i18n
{
namespace spanish // language namespace
{
// language name in lowercase
static std::string language = "spanish";
// 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", "KiB"},
{"MiB", "MiB"},
{"GiB", "GiB"},
{"building", "pendiente"},
{"failed", "fallido"},
{"expiring", "expiró"},
{"established", "establecido"},
{"unknown", "desconocido"},
{"exploratory", "exploratorio"},
{"Purple I2P Webconsole", "Consola web de Purple I2P"},
{"<b>i2pd</b> webconsole", "Consola web de <b>i2pd</b>"},
{"Main page", "Inicio"},
{"Router commands", "Comandos de enrutador"},
{"Local Destinations", "Destinos locales"},
{"LeaseSets", "LeaseSets"},
{"Tunnels", "Túneles"},
{"Transit Tunnels", "Túneles de Tránsito"},
{"Transports", "Transportes"},
{"I2P tunnels", "Túneles I2P"},
{"SAM sessions", "Sesiones SAM"},
{"ERROR", "ERROR"},
{"OK", "VALE"},
{"Testing", "Probando"},
{"Firewalled", "Con cortafuegos"},
{"Unknown", "Desconocido"},
{"Proxy", "Proxy"},
{"Mesh", "Malla"},
{"Error", "Error"},
{"Clock skew", "Reloj desfasado"},
{"Offline", "Desconectado"},
{"Symmetric NAT", "NAT simétrico"},
{"Uptime", "Tiempo en línea"},
{"Network status", "Estado de red"},
{"Network status v6", "Estado de red v6"},
{"Stopping in", "Parando en"},
{"Family", "Familia"},
{"Tunnel creation success rate", "Tasa de éxito de creación de túneles"},
{"Received", "Recibido"},
{"KiB/s", "KiB/s"},
{"Sent", "Enviado"},
{"Transit", "Tránsito"},
{"Data path", "Ruta de datos"},
{"Hidden content. Press on text to see.", "Contenido oculto. Presione para ver."},
{"Router Ident", "Ident del Enrutador"},
{"Router Family", "Familia de enrutador"},
{"Router Caps", "Atributos del Enrutador"},
{"Version", "Versión"},
{"Our external address", "Nuestra dirección externa"},
{"supported", "soportado"},
{"Routers", "Enrutadores"},
{"Floodfills", "Inundaciones"},
{"Client Tunnels", "Túneles de cliente"},
{"Services", "Servicios"},
{"Enabled", "Activado"},
{"Disabled", "Desactivado"},
{"Encrypted B33 address", "Dirección encriptada B33"},
{"Address registration line", "Línea para registrar direcciones"},
{"Domain", "Dominio"},
{"Generate", "Generar"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Nota:</b> la cadena resultante solo se puede usar para registrar dominios 2LD (ejemplo.i2p). Para registrar subdominios, por favor utilice i2pd-tools."},
{"Address", "Dirección"},
{"Type", "Tipo"},
{"EncType", "TipoEncrip"},
{"Inbound tunnels", "Túneles entrantes"},
{"ms", "ms"},
{"Outbound tunnels", "Túneles salientes"},
{"Tags", "Etiquetas"},
{"Incoming", "Entrante"},
{"Outgoing", "Saliente"},
{"Destination", "Destino"},
{"Amount", "Cantidad"},
{"Incoming Tags", "Etiquetas entrantes"},
{"Tags sessions", "Sesiones de etiquetas"},
{"Status", "Estado"},
{"Local Destination", "Destino Local"},
{"Streams", "Flujos"},
{"Close stream", "Cerrar flujo"},
{"I2CP session not found", "Sesión I2CP no encontrada"},
{"I2CP is not enabled", "I2CP no está activado"},
{"Invalid", "Inválido"},
{"Store type", "Tipo de almacenamiento"},
{"Expires", "Caduca"},
{"Non Expired Leases", "Sesiones No Expiradas"},
{"Gateway", "Puerta de enlace"},
{"TunnelID", "TunnelID"},
{"EndDate", "FechaVenc"},
{"not floodfill", "no inundado"},
{"Queue size", "Tamaño de cola"},
{"Run peer test", "Ejecutar prueba de par"},
{"Decline transit tunnels", "Rechazar túneles de tránsito"},
{"Accept transit tunnels", "Aceptar túneles de tránsito"},
{"Cancel graceful shutdown", "Cancelar apagado con gracia"},
{"Start graceful shutdown", "Iniciar apagado con gracia"},
{"Force shutdown", "Forzar apagado"},
{"Reload external CSS styles", "Recargar estilos CSS externos"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Nota:</b> cualquier acción hecha aquí no es persistente y no cambia tus archivos de configuración."},
{"Logging level", "Nivel de registro de errores"},
{"Transit tunnels limit", "Límite de túneles de tránsito"},
{"Change", "Cambiar"},
{"Change language", "Cambiar idioma"},
{"no transit tunnels currently built", "no hay túneles de tránsito actualmente construidos"},
{"SAM disabled", "SAM desactivado"},
{"no sessions currently running", "no hay sesiones ejecutándose ahora"},
{"SAM session not found", "Sesión SAM no encontrada"},
{"SAM Session", "Sesión SAM"},
{"Server Tunnels", "Túneles de Servidor"},
{"Client Forwards", "Redirecciones de Cliente"},
{"Server Forwards", "Redirecciones de Servidor"},
{"Unknown page", "Página desconocida"},
{"Invalid token", "Token inválido"},
{"SUCCESS", "ÉXITO"},
{"Stream closed", "Transmisión cerrada"},
{"Stream not found or already was closed", "No se encontró la transmisión o ya se cerró"},
{"Destination not found", "Destino no encontrado"},
{"StreamID can't be null", "StreamID no puede ser nulo"},
{"Return to destination page", "Volver a la página de destino"},
{"You will be redirected in 5 seconds", "Serás redirigido en 5 segundos"},
{"Transit tunnels count must not exceed 65535", "La cantidad de túneles de tránsito no puede exceder 65535"},
{"Back to commands list", "Volver a lista de comandos"},
{"Register at reg.i2p", "Registrar en reg.i2p"},
{"Description", "Descripción"},
{"A bit information about service on domain", "Un poco de información sobre el servicio en el dominio"},
{"Submit", "Enviar"},
{"Domain can't end with .b32.i2p", "El dominio no puede terminar con .b32.i2p"},
{"Domain must end with .i2p", "El dominio debe terminar con .i2p"},
{"Such destination is not found", "No se encontró el destino"},
{"Unknown command", "Comando desconocido"},
{"Command accepted", "Comando aceptado"},
{"Proxy error", "Error de proxy"},
{"Proxy info", "Información del proxy"},
{"Proxy error: Host not found", "Error de proxy: Host no encontrado"},
{"Remote host not found in router's addressbook", "Servidor remoto no encontrado en la libreta de direcciones del enrutador"},
{"You may try to find this host on jump services below", "Puede intentar encontrar este dominio en los siguientes servicios de salto"},
{"Invalid request", "Solicitud inválida"},
{"Proxy unable to parse your request", "Proxy no puede procesar su solicitud"},
{"addresshelper is not supported", "ayudante de dirección no soportado"},
{"Host", "Dominio"},
{"added to router's addressbook from helper", "añadido a la libreta de direcciones desde el ayudante"},
{"Click here to proceed:", "Haga clic aquí para continuar:"},
{"Continue", "Continuar"},
{"Addresshelper found", "Se encontró ayudante de dirección"},
{"already in router's addressbook", "ya se encontró en libreta de direcciones"},
{"Click here to update record:", "Haga clic aquí para actualizar el registro:"},
{"invalid request uri", "uri de solicitud inválida"},
{"Can't detect destination host from request", "No se puede detectar el host de destino de la solicitud"},
{"Outproxy failure", "Fallo en el proxy saliente"},
{"bad outproxy settings", "configuración de outproxy incorrecta"},
{"not inside I2P network, but outproxy is not enabled", "no está dentro de la red I2P, pero el proxy de salida no está activado"},
{"unknown outproxy url", "url de proxy outproxy desconocido"},
{"cannot resolve upstream proxy", "no se puede resolver el proxy de upstream"},
{"hostname too long", "nombre de dominio muy largo"},
{"cannot connect to upstream socks proxy", "no se puede conectar al proxy socks principal"},
{"Cannot negotiate with socks proxy", "No se puede negociar con el proxy socks"},
{"CONNECT error", "Error de CONNECT"},
{"Failed to Connect", "Error al Conectar"},
{"socks proxy error", "error de proxy socks"},
{"failed to send request to upstream", "no se pudo enviar petición al principal"},
{"No Reply From socks proxy", "Sin respuesta del proxy socks"},
{"cannot connect", "no se puede conectar"},
{"http out proxy not implemented", "proxy externo http no implementado"},
{"cannot connect to upstream http proxy", "no se puede conectar al proxy http principal"},
{"Host is down", "Servidor caído"},
{"Can't create connection to requested host, it may be down. Please try again later.", "No se puede crear la conexión al servidor solicitado, puede estar caído. Intente de nuevo más tarde."},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"days", {"día", "días"}},
{"hours", {"hora", "horas"}},
{"minutes", {"minuto", "minutos"}},
{"seconds", {"segundo", "segundos"}},
{"", {"", ""}},
};
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

View File

@@ -64,7 +64,7 @@ namespace config {
("bandwidth", value<std::string>()->default_value(""), "Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
("ssu", bool_switch()->default_value(false), "Enable SSU transport (default: disabled)")
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
#ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Ignored")
@@ -274,12 +274,9 @@ namespace config {
options_description ssu2("SSU2 Options");
ssu2.add_options()
("ssu2.enabled", value<bool>()->default_value(true), "Enable SSU2 (default: enabled)")
("ssu2.published", value<bool>()->default_value(true), "Publish SSU2 (default: enabled)")
("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)")
("ssu2.mtu4", value<uint16_t>()->default_value(0), "MTU for ipv4 address (default: detect)")
("ssu2.mtu6", value<uint16_t>()->default_value(0), "MTU for ipv6 address (default: detect)")
("ssu2.proxy", value<std::string>()->default_value(""), "Socks5 proxy URL for SSU2 transport")
;
options_description nettime("Time sync options");
@@ -314,13 +311,6 @@ namespace config {
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish")
;
#ifdef __linux__
options_description unix_specific("UNIX-specific options");
unix_specific.add_options()
("unix.handle_sigtstp", bool_switch()->default_value(false), "Handle SIGTSTP and SIGCONT signals (default: disabled)")
;
#endif
m_OptionsDesc
.add(general)
.add(limits)
@@ -344,9 +334,6 @@ namespace config {
.add(persist)
.add(cpuext)
.add(meshnets)
#ifdef __linux__
.add(unix_specific)
#endif
;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -15,7 +15,6 @@
#include <map>
#include <vector>
#include "Base.h"
#include "Gzip.h"
#include "Identity.h"
#include "LeaseSet.h"
#include "I2NPProtocol.h"

View File

@@ -1096,13 +1096,7 @@ namespace client
}
auto leaseSet = FindLeaseSet (dest);
if (leaseSet)
{
auto stream = CreateStream (leaseSet, port);
GetService ().post ([streamRequestComplete, stream]()
{
streamRequestComplete(stream);
});
}
streamRequestComplete(CreateStream (leaseSet, port));
else
{
auto s = GetSharedFromThis ();
@@ -1135,35 +1129,6 @@ namespace client
});
}
template<typename Dest>
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStreamSync (const Dest& dest, int port)
{
std::shared_ptr<i2p::stream::Stream> stream;
std::condition_variable streamRequestComplete;
std::mutex streamRequestCompleteMutex;
std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
CreateStream (
[&streamRequestComplete, &streamRequestCompleteMutex, &stream](std::shared_ptr<i2p::stream::Stream> s)
{
stream = s;
std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
streamRequestComplete.notify_all ();
},
dest, port);
streamRequestComplete.wait (l);
return stream;
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (const i2p::data::IdentHash& dest, int port)
{
return CreateStreamSync (dest, port);
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port)
{
return CreateStreamSync (dest, port);
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{
if (m_StreamingDestination)

View File

@@ -247,8 +247,6 @@ namespace client
// following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (const i2p::data::IdentHash& dest, int port = 0); // sync
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0); // sync
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void SendPing (const i2p::data::IdentHash& to);
void SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to);
@@ -284,9 +282,6 @@ namespace client
void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey);
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
template<typename Dest>
std::shared_ptr<i2p::stream::Stream> CreateStreamSync (const Dest& dest, int port);
private:
i2p::data::PrivateKeys m_Keys;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -135,7 +135,6 @@ namespace http
? url.substr(pos_p, std::string::npos)
: url.substr(pos_p, pos_c - pos_p);
/* stoi throws exception on failure, we don't need it */
port = 0;
for (char c : port_str) {
if (c < '0' || c > '9')
return false;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
* Copyright (c) 2013-2020, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -49,30 +49,13 @@ namespace data
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType)
{
/*uint8_t randomPaddingBlock[32];
RAND_bytes (randomPaddingBlock, 32);*/
if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
{
/*memcpy (m_StandardIdentity.publicKey, publicKey ? publicKey : randomPaddingBlock, 32);
for (int i = 0; i < 7; i++) // 224 bytes
memcpy (m_StandardIdentity.publicKey + 32*(i + 1), randomPaddingBlock, 32);*/
if (publicKey)
{
memcpy (m_StandardIdentity.publicKey, publicKey, 32);
RAND_bytes (m_StandardIdentity.publicKey + 32, 224);
}
else
RAND_bytes (m_StandardIdentity.publicKey, 256);
memcpy (m_StandardIdentity.publicKey, publicKey, 32);
RAND_bytes (m_StandardIdentity.publicKey + 32, 224);
}
else
{
if (publicKey)
memcpy (m_StandardIdentity.publicKey, publicKey, 256);
else
RAND_bytes (m_StandardIdentity.publicKey, 256);
/*for (int i = 0; i < 8; i++) // 256 bytes
memcpy (m_StandardIdentity.publicKey + 32*i, randomPaddingBlock, 32);*/
}
memcpy (m_StandardIdentity.publicKey, publicKey, 256);
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{
size_t excessLen = 0;
@@ -110,9 +93,7 @@ namespace data
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
{
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
/*for (int i = 0; i < 3; i++) // 96 bytes
memcpy (m_StandardIdentity.signingKey + 32*i, randomPaddingBlock, 32);*/
RAND_bytes (m_StandardIdentity.signingKey, 96);
RAND_bytes (m_StandardIdentity.signingKey, padding);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
break;
}
@@ -714,7 +695,7 @@ namespace data
return nullptr;
}
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type, CryptoKeyType cryptoType, bool isDestination)
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type, CryptoKeyType cryptoType)
{
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{
@@ -724,12 +705,9 @@ namespace data
GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, signingPublicKey);
// encryption
uint8_t publicKey[256];
if (isDestination)
RAND_bytes (keys.m_PrivateKey, 256);
else
GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey);
GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey);
// identity
keys.m_Public = std::make_shared<IdentityEx> (isDestination ? nullptr : publicKey, signingPublicKey, type, cryptoType);
keys.m_Public = std::make_shared<IdentityEx> (publicKey, signingPublicKey, type, cryptoType);
keys.CreateSigner ();
return keys;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -171,7 +171,7 @@ namespace data
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const;
static std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key);
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL, bool isDestination = false);
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL);
static void GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub);
static void GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub); // priv and pub are 256 bytes long
static i2p::crypto::Signer * CreateSigner (SigningKeyType keyType, const uint8_t * priv);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -59,9 +59,9 @@ namespace data
if (readIdentity || !m_Identity)
m_Identity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
size_t size = m_Identity->GetFullLen ();
if (size + 256 > m_BufferLen)
if (size > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: Identity length ", int(size), " exceeds buffer size ", int(m_BufferLen));
LogPrint (eLogError, "LeaseSet: Identity length ", size, " exceeds buffer size ", m_BufferLen);
m_IsValid = false;
return;
}
@@ -74,7 +74,7 @@ namespace data
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
if (size + 1 > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen));
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen);
m_IsValid = false;
return;
}
@@ -89,7 +89,7 @@ namespace data
}
if (size + num*LEASE_SIZE > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen));
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen);
m_IsValid = false;
return;
}
@@ -125,7 +125,7 @@ namespace data
auto signedSize = leases - m_Buffer;
if (signedSize + m_Identity->GetSignatureLen () > m_BufferLen)
{
LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", int(m_BufferLen));
LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", m_BufferLen);
m_IsValid = false;
}
else if (!m_Identity->Verify (m_Buffer, signedSize, leases))
@@ -172,7 +172,7 @@ namespace data
m_ExpirationTime = lease.endDate;
if (m_StoreLeases)
{
auto ret = m_Leases.insert (i2p::data::netdb.NewLease (lease));
auto ret = m_Leases.insert (std::make_shared<Lease>(lease));
if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing
(*ret.first)->isUpdated = true;
}
@@ -274,7 +274,7 @@ namespace data
{
if (len <= m_BufferLen) m_BufferLen = len;
else
LogPrint (eLogError, "LeaseSet2: Actual buffer size ", int(len) , " exceeds full buffer size ", int(m_BufferLen));
LogPrint (eLogError, "LeaseSet2: Actual buffer size ", len , " exceeds full buffer size ", m_BufferLen);
}
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto):
@@ -320,7 +320,7 @@ namespace data
else
identity = GetIdentity ();
size_t offset = identity->GetFullLen ();
if (offset + 8 > len) return;
if (offset + 8 >= len) return;
m_PublishedTimestamp = bufbe32toh (buf + offset); offset += 4; // published timestamp (seconds)
uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds)
SetExpirationTime ((m_PublishedTimestamp + expires)*1000LL); // in milliseconds
@@ -364,10 +364,6 @@ namespace data
SetIsValid (verified);
}
offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen ();
if (offset > len) {
LogPrint (eLogWarning, "LeaseSet2: short buffer: wanted ", int(offset), "bytes, have ", int(len));
return;
}
SetBufferLen (offset);
}
@@ -392,17 +388,17 @@ namespace data
// properties
uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2;
offset += propertiesLen; // skip for now. TODO: implement properties
if (offset + 1 >= len) return 0;
// key sections
CryptoKeyType preferredKeyType = m_EncryptionType;
bool preferredKeyFound = false;
if (offset + 1 > len) return 0;
int numKeySections = buf[offset]; offset++;
for (int i = 0; i < numKeySections; i++)
{
if (offset + 4 > len) return 0;
uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption key type
if (offset + 2 >= len) return 0;
uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2;
if (offset + encryptionKeyLen > len) return 0;
if (offset + encryptionKeyLen >= len) return 0;
if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only
{
// we pick first valid key if preferred not found
@@ -417,7 +413,7 @@ namespace data
offset += encryptionKeyLen;
}
// leases
if (offset + 1 > len) return 0;
if (offset + 1 >= len) return 0;
int numLeases = buf[offset]; offset++;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (IsStoreLeases ())
@@ -436,8 +432,7 @@ namespace data
}
else
offset += numLeases*LEASE2_SIZE; // 40 bytes per lease
return (offset > len ? 0 : offset);
return offset;
}
size_t LeaseSet2::ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len)
@@ -447,18 +442,18 @@ namespace data
uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2;
offset += propertiesLen; // skip for now. TODO: implement properties
// entries
if (offset + 1 > len) return 0;
if (offset + 1 >= len) return 0;
int numEntries = buf[offset]; offset++;
for (int i = 0; i < numEntries; i++)
{
if (offset + LEASE2_SIZE > len) return 0;
if (offset + 40 >= len) return 0;
offset += 32; // hash
offset += 3; // flags
offset += 1; // cost
offset += 4; // expires
}
// revocations
if (offset + 1 > len) return 0;
if (offset + 1 >= len) return 0;
int numRevocations = buf[offset]; offset++;
for (int i = 0; i < numRevocations; i++)
{

View File

@@ -1169,9 +1169,9 @@ namespace transport
}
}
void NTCP2Session::SendLocalRouterInfo (bool update)
void NTCP2Session::SendLocalRouterInfo ()
{
if (update || !IsOutgoing ()) // we send it in SessionConfirmed for ougoing session
if (!IsOutgoing ()) // we send it in SessionConfirmed
m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ()));
}
@@ -1558,7 +1558,7 @@ namespace transport
case eSocksProxy:
{
// TODO: support username/password auth etc
static const uint8_t buff[3] = {SOCKS5_VER, 0x01, 0x00};
static const uint8_t buff[3] = {0x05, 0x01, 0x00};
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(),
[] (const boost::system::error_code & ec, std::size_t transferred)
{
@@ -1672,21 +1672,21 @@ namespace transport
size_t sz = 6; // header + port
auto buff = std::make_shared<std::vector<int8_t> >(256);
auto readbuff = std::make_shared<std::vector<int8_t> >(256);
(*buff)[0] = SOCKS5_VER;
(*buff)[1] = SOCKS5_CMD_CONNECT;
(*buff)[0] = 0x05;
(*buff)[1] = 0x01;
(*buff)[2] = 0x00;
auto& ep = conn->GetRemoteEndpoint ();
if(ep.address ().is_v4 ())
{
(*buff)[3] = SOCKS5_ATYP_IPV4;
(*buff)[3] = 0x01;
auto addrbytes = ep.address ().to_v4().to_bytes();
sz += 4;
memcpy(buff->data () + 4, addrbytes.data(), 4);
}
else if (ep.address ().is_v6 ())
{
(*buff)[3] = SOCKS5_ATYP_IPV6;
(*buff)[3] = 0x04;
auto addrbytes = ep.address ().to_v6().to_bytes();
sz += 16;
memcpy(buff->data () + 4, addrbytes.data(), 16);
@@ -1708,24 +1708,22 @@ namespace transport
}
});
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE), // read min reply size
boost::asio::transfer_all(),
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 10),
[timer, conn, sz, readbuff](const boost::system::error_code & e, std::size_t transferred)
{
if (e)
LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message());
else if (!(*readbuff)[1]) // succeeded
if(e)
{
boost::system::error_code ec;
size_t moreBytes = conn->GetSocket ().available(ec);
if (moreBytes) // read remaining portion of reply if ipv6 received
boost::asio::read (conn->GetSocket (), boost::asio::buffer(readbuff->data (), moreBytes), boost::asio::transfer_all (), ec);
timer->cancel();
conn->ClientLogin();
return;
LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message());
}
else if(transferred == sz)
{
if((*readbuff)[1] == 0x00)
{
timer->cancel();
conn->ClientLogin();
return;
}
}
else
LogPrint(eLogError, "NTCP2: Proxy reply error ", (int)(*readbuff)[1]);
timer->cancel();
conn->Terminate();
});

View File

@@ -148,7 +148,7 @@ namespace transport
void ClientLogin (); // Alice
void ServerLogin (); // Bob
void SendLocalRouterInfo (bool update); // after handshake or by update
void SendLocalRouterInfo (); // after handshake
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
private:

View File

@@ -240,10 +240,11 @@ namespace data
m_HiddenMode = hide;
}
std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len)
bool NetDb::AddRouterInfo (const uint8_t * buf, int len)
{
bool updated;
return AddRouterInfo (buf, len, updated);
AddRouterInfo (buf, len, updated);
return updated;
}
std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len, bool& updated)
@@ -271,10 +272,7 @@ namespace data
if (r->IsNewer (buf, len))
{
bool wasFloodfill = r->IsFloodfill ();
{
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
r->Update (buf, len);
}
r->Update (buf, len);
LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64());
if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated
{
@@ -438,15 +436,12 @@ namespace data
// try reseeding from floodfill first if specified
std::string riPath;
if(i2p::config::GetOption("reseed.floodfill", riPath))
{
if(i2p::config::GetOption("reseed.floodfill", riPath)) {
auto ri = std::make_shared<RouterInfo>(riPath);
if (ri->IsFloodfill())
{
if (ri->IsFloodfill()) {
const uint8_t * riData = ri->GetBuffer();
int riLen = ri->GetBufferLen();
if (!i2p::data::netdb.AddRouterInfo(riData, riLen))
{
if(!i2p::data::netdb.AddRouterInfo(riData, riLen)) {
// bad router info
LogPrint(eLogError, "NetDb: Bad router info");
return;
@@ -628,8 +623,7 @@ namespace data
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
it.second->SetUnreachable (false);
// find & mark expired routers
if (!it.second->IsReachable () && (it.second->GetCompatibleTransports (true) & (RouterInfo::eSSUV4 | RouterInfo::eSSU2V4)))
// non-reachable router, but reachable by ipv4 SSU or SSU2 means introducers
if (!it.second->IsReachable () && it.second->IsSSU (false))
{
if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)
// RouterInfo expires after 1 hour if uses introducer
@@ -946,9 +940,9 @@ namespace data
}
uint16_t numExcluded = bufbe16toh (excluded);
excluded += 2;
if (numExcluded > 512 || (excluded - buf) + numExcluded*32 > (int)msg->GetPayloadLength ())
if (numExcluded > 512)
{
LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " is too much");
LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " exceeds 512");
return;
}
@@ -957,11 +951,10 @@ namespace data
{
LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters;
const uint8_t * excluded_ident = excluded;
for (int i = 0; i < numExcluded; i++)
{
excludedRouters.insert (excluded_ident);
excluded_ident += 32;
excludedRouters.insert (excluded);
excluded += 32;
}
std::vector<IdentHash> routers;
for (int i = 0; i < 3; i++)
@@ -984,7 +977,8 @@ namespace data
if (router)
{
LogPrint (eLogDebug, "NetDb: Requested RouterInfo ", key, " found");
PopulateRouterInfoBuffer (router);
if (!router->GetBuffer ())
router->LoadBuffer (m_Storage.Path (router->GetIdentHashBase64 ()));
if (router->GetBuffer ())
replyMsg = CreateDatabaseStoreMsg (router);
}
@@ -1019,7 +1013,7 @@ namespace data
if (closestFloodfills.empty ())
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
}
}
}
excluded += numExcluded * 32;
if (replyMsg)
@@ -1218,7 +1212,7 @@ namespace data
router->IsSSU2PeerTesting (v4) && !excluded.count (router->GetIdentHash ());
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSUV6Router () const
{
return GetRandomRouter (
@@ -1238,16 +1232,6 @@ namespace data
});
}
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const
{
return GetRandomRouter (
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router->IsSSU2Introducer (v4) &&
!excluded.count (router->GetIdentHash ());
});
}
std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const
{
return GetRandomRouter (
@@ -1448,13 +1432,6 @@ namespace data
else
++it;
}
m_LeasesPool.CleanUpMt ();
}
void NetDb::PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r)
{
if (!r || r->GetBuffer ()) return;
r->LoadBuffer (m_Storage.Path (r->GetIdentHashBase64 ()));
}
}
}

View File

@@ -69,7 +69,7 @@ namespace data
void Start ();
void Stop ();
std::shared_ptr<const RouterInfo> AddRouterInfo (const uint8_t * buf, int len);
bool AddRouterInfo (const uint8_t * buf, int len);
bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType);
@@ -93,7 +93,6 @@ namespace data
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> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (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::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
@@ -125,8 +124,6 @@ namespace data
void ClearRouterInfos () { m_RouterInfos.clear (); };
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); };
void PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); };
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; };
@@ -183,7 +180,6 @@ namespace data
uint32_t m_PublishReplyToken = 0;
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
i2p::util::MemoryPoolMt<Lease> m_LeasesPool;
};
extern NetDb netdb;

View File

@@ -29,7 +29,7 @@ namespace i2p
RouterContext::RouterContext ():
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown),
m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone), m_NetID (I2PD_NET_ID)
m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID)
{
}
@@ -60,7 +60,11 @@ namespace i2p
i2p::data::LocalRouterInfo routerInfo;
routerInfo.SetRouterIdentity (GetIdentity ());
uint16_t port; i2p::config::GetOption("port", port);
if (!port) port = SelectRandomPort ();
if (!port)
{
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
}
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ssu; i2p::config::GetOption("ssu", ssu);
@@ -117,11 +121,7 @@ namespace i2p
if (ssu2)
{
if (ssu2Published)
{
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), ssu2Port);
}
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), port);
else
{
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4;
@@ -166,11 +166,7 @@ namespace i2p
if (ssu2)
{
if (ssu2Published)
{
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), ssu2Port);
}
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
@@ -196,13 +192,6 @@ namespace i2p
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
}
uint16_t RouterContext::SelectRandomPort () const
{
uint16_t port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
return port;
}
void RouterContext::UpdateRouterInfo ()
{
m_RouterInfo.CreateBuffer (m_Keys);
@@ -236,13 +225,6 @@ namespace i2p
fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys));
}
bool RouterContext::IsSSU2Only () const
{
auto transports = m_RouterInfo.GetCompatibleTransports (false);
return (transports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) &&
!(transports & (i2p::data::RouterInfo::eSSUV4 | i2p::data::RouterInfo::eSSUV6));
}
void RouterContext::SetStatus (RouterStatus status)
{
if (status != m_Status)
@@ -263,18 +245,11 @@ namespace i2p
}
}
void RouterContext::SetStatusSSU2 (RouterStatus status)
{
if (IsSSU2Only ())
SetStatus (status);
}
void RouterContext::SetStatusV6 (RouterStatus status)
{
if (status != m_StatusV6)
{
m_StatusV6 = status;
m_ErrorV6 = eRouterErrorNone;
switch (m_StatusV6)
{
case eRouterStatusOK:
@@ -289,18 +264,12 @@ namespace i2p
}
}
void RouterContext::SetStatusV6SSU2 (RouterStatus status)
{
if (IsSSU2Only ())
SetStatusV6 (status);
}
void RouterContext::UpdatePort (int port)
{
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address->port != port && (address->transportStyle == i2p::data::RouterInfo::eTransportSSU || IsSSU2Only ()))
if (!address->IsNTCP2 () && !address->IsSSU2 () && address->port != port)
{
address->port = port;
updated = true;
@@ -328,7 +297,12 @@ namespace i2p
}
if (isAddr)
{
if (!port && !address->port) port = SelectRandomPort ();
if (!port && !address->port)
{
// select random port only if address's port is not set
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
}
if (port) address->port = port;
address->published = publish;
memcpy (address->i, m_NTCP2Keys->iv, 16);
@@ -344,23 +318,18 @@ namespace i2p
{
auto& addresses = m_RouterInfo.GetAddresses ();
bool found = false, updated = false;
for (auto it = addresses.begin (); it != addresses.end ();)
for (auto it = addresses.begin (); it != addresses.end (); ++it)
{
if ((*it)->IsNTCP2 ())
{
found = true;
if (enable)
if (!enable)
{
(*it)->s = m_NTCP2Keys->staticPublicKey;
memcpy ((*it)->i, m_NTCP2Keys->iv, 16);
it++;
addresses.erase (it);
updated= true;
}
else
it = addresses.erase (it);
updated = true;
break;
}
else
it++;
}
if (enable && !found)
{
@@ -373,31 +342,19 @@ namespace i2p
void RouterContext::PublishSSU2Address (int port, bool publish, bool v4, bool v6)
{
if (!m_SSU2Keys) return;
int newPort = 0;
if (!port)
{
for (const auto& address : m_RouterInfo.GetAddresses ())
if (address->port)
{
newPort = address->port;
break;
}
if (!newPort) newPort = SelectRandomPort ();
}
if (!m_SSU2Keys || (publish && !port)) return;
bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ())
{
if (address->IsSSU2 () && (!address->port || address->port != port || address->published != publish) &&
if (address->IsSSU2 () && (address->port != port || address->published != publish) &&
((v4 && address->IsV4 ()) || (v6 && address->IsV6 ())))
{
if (port) address->port = port;
else if (!address->port) address->port = newPort;
address->port = port;
address->published = publish;
if (publish)
address->caps |= (i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting);
address->caps |= i2p::data::RouterInfo::eSSUIntroducer;
else
address->caps &= ~(i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting);
address->caps &= ~i2p::data::RouterInfo::eSSUIntroducer;
updated = true;
}
}
@@ -409,41 +366,27 @@ namespace i2p
{
auto& addresses = m_RouterInfo.GetAddresses ();
bool found = false, updated = false;
for (auto it = addresses.begin (); it != addresses.end ();)
for (auto it = addresses.begin (); it != addresses.end (); ++it)
{
if ((*it)->IsSSU2 ())
{
found = true;
if (enable)
if (!enable)
{
(*it)->s = m_SSU2Keys->staticPublicKey;
(*it)->i = m_SSU2Keys->intro;
it++;
addresses.erase (it);
updated= true;
}
else
it = addresses.erase (it);
updated = true;
break;
}
else
it++;
}
if (enable && !found)
{
uint8_t addressCaps = 0;
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool published; i2p::config::GetOption("ntcp2.published", published);
if (published)
{
if (ipv4) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV4);
if (ipv6) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV6);
}
else
{
uint8_t addressCaps = 0;
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);
}
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)
@@ -458,29 +401,22 @@ namespace i2p
if (address->host != host && address->IsCompatible (host) &&
!i2p::util::net::IsYggdrasilAddress (address->host))
{
// update host
address->host = host;
updated = true;
}
if (host.is_v6 () && address->IsV6 () && address->ssu &&
(!address->ssu->mtu || updated) && m_StatusV6 != eRouterStatusProxy)
{
// update MTU
auto mtu = i2p::util::net::GetMTU (host);
if (mtu)
if (host.is_v6 () && address->transportStyle == i2p::data::RouterInfo::eTransportSSU)
{
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
int maxMTU = i2p::util::net::GetMaxMTU (host.to_v6 ());
if (mtu > maxMTU)
// update MTU
auto mtu = i2p::util::net::GetMTU (host);
if (mtu)
{
mtu = maxMTU;
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of ", maxMTU, " bytes");
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
if (mtu > 1472) { // TODO: magic constant
mtu = 1472;
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of 1472 bytes");
}
if (address->ssu) address->ssu->mtu = mtu;
}
if (mtu && !address->IsSSU2 ()) // SSU1
mtu = (mtu >> 4) << 4; // round to multiple of 16
address->ssu->mtu = mtu;
updated = true;
}
updated = true;
}
}
auto ts = i2p::util::GetSecondsSinceEpoch ();
@@ -502,37 +438,6 @@ namespace i2p
UpdateRouterInfo ();
}
bool RouterContext::AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4)
{
if (!IsSSU2Only ()) return false;
bool ret = m_RouterInfo.AddSSU2Introducer (introducer, v4);
if (ret)
UpdateRouterInfo ();
return ret;
}
void RouterContext::RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4)
{
if (!IsSSU2Only ()) return;
if (m_RouterInfo.RemoveSSU2Introducer (h, v4))
UpdateRouterInfo ();
}
void RouterContext::ClearSSU2Introducers (bool v4)
{
bool updated = false;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses)
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())) &&
addr->ssu && !addr->ssu->introducers.empty ())
{
addr->ssu->introducers.clear ();
updated = true;
}
if (updated)
UpdateRouterInfo ();
}
void RouterContext::SetFloodfill (bool floodfill)
{
m_IsFloodfill = floodfill;
@@ -633,7 +538,6 @@ namespace i2p
void RouterContext::RemoveNTCPAddress (bool v4only)
{
bool updated = false;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto it = addresses.begin (); it != addresses.end ();)
{
@@ -641,38 +545,11 @@ namespace i2p
(!v4only || (*it)->host.is_v4 ()))
{
it = addresses.erase (it);
updated = true;
if (v4only) break; // otherwise might be more than one address
}
else
++it;
}
if (updated)
m_RouterInfo.UpdateSupportedTransports ();
}
void RouterContext::RemoveSSUAddress ()
{
bool updated = false;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto it = addresses.begin (); it != addresses.end ();)
{
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportSSU)
{
it = addresses.erase (it);
updated = true;
}
else
++it;
}
if (updated)
m_RouterInfo.UpdateSupportedTransports ();
}
void RouterContext::SetUnreachableSSU2 (bool v4, bool v6)
{
if (IsSSU2Only ())
SetUnreachable (v4, v6);
}
void RouterContext::SetUnreachable (bool v4, bool v6)
@@ -691,8 +568,7 @@ namespace i2p
// delete previous introducers
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses)
if (addr->ssu && (!addr->IsSSU2 () || IsSSU2Only ()) &&
((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
{
addr->published = false;
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
@@ -722,19 +598,14 @@ namespace i2p
}
uint16_t port = 0;
// delete previous introducers
bool isSSU2Published = IsSSU2Only (); // TODO
if (isSSU2Published)
i2p::config::GetOption ("ssu2.published", isSSU2Published);
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses)
if (addr->ssu && (!addr->IsSSU2 () || isSSU2Published) &&
((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
{
addr->published = true;
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
addr->ssu->introducers.clear ();
if (addr->port && (!addr->IsSSU2 () || IsSSU2Only ()))
port = addr->port;
port = addr->port;
}
// publish NTCP2
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
@@ -781,17 +652,16 @@ namespace i2p
}
port = addr->port;
}
if (!port)
{
i2p::config::GetOption("port", port);
if (!port) port = SelectRandomPort ();
}
if (!port) i2p::config::GetOption("port", port);
// SSU
bool ssu; i2p::config::GetOption("ssu", ssu);
if (!foundSSU && ssu)
if (!foundSSU)
{
std::string host = "::1"; // TODO: read host
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
bool ssu; i2p::config::GetOption("ssu", ssu);
if (ssu)
{
std::string host = "::1"; // TODO: read host
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
}
}
// NTCP2
if (!foundNTCP2)
@@ -825,7 +695,6 @@ namespace i2p
if (ssu2Published)
{
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("::1"), ssu2Port);
}
else
@@ -871,16 +740,14 @@ namespace i2p
}
if (addr->port) port = addr->port;
}
if (!port)
{
i2p::config::GetOption("port", port);
if (!port) port = SelectRandomPort ();
}
if (!port) i2p::config::GetOption("port", port);
// SSU
bool ssu; i2p::config::GetOption("ssu", ssu);
if (!foundSSU && ssu)
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
if (!foundSSU)
{
bool ssu; i2p::config::GetOption("ssu", ssu);
if (ssu)
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
}
// NTCP2
if (!foundNTCP2)
{
@@ -908,11 +775,10 @@ namespace i2p
if (ssu2Published)
{
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
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::eV4);
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV6);
}
}
m_RouterInfo.EnableV4 ();
@@ -949,43 +815,6 @@ namespace i2p
UpdateRouterInfo ();
}
void RouterContext::SetMTU (int mtu, bool v4)
{
if (mtu < 1280 || mtu > 1500) return;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr: addresses)
{
if (addr->ssu && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
{
if (!addr->IsSSU2 ()) // SSU1
{
// round to multiple of 16
if (v4)
{
if (mtu > 1484) mtu = 1484;
else
{
mtu -= 12;
mtu = (mtu >> 4) << 4;
mtu += 12;
}
}
else
{
if (mtu > 1488) mtu = 1488;
else
mtu = (mtu >> 4) << 4;
}
}
if (mtu)
{
addr->ssu->mtu = mtu;
LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu);
}
}
}
}
void RouterContext::UpdateNTCP2V6Address (const boost::asio::ip::address& host)
{
bool isYgg = i2p::util::net::IsYggdrasilAddress (host);

View File

@@ -37,9 +37,10 @@ namespace garlic
eRouterStatusOK = 0,
eRouterStatusTesting = 1,
eRouterStatusFirewalled = 2,
eRouterStatusUnknown = 3,
eRouterStatusProxy = 4,
eRouterStatusMesh = 5
eRouterStatusError = 3,
eRouterStatusUnknown = 4,
eRouterStatusProxy = 5,
eRouterStatusMesh = 6
};
enum RouterError
@@ -102,14 +103,10 @@ namespace garlic
uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; };
RouterStatus GetStatus () const { return m_Status; };
void SetStatus (RouterStatus status);
void SetStatusSSU2 (RouterStatus status);
RouterError GetError () const { return m_Error; };
void SetError (RouterError error) { m_Error = error; };
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
RouterStatus GetStatusV6 () const { return m_StatusV6; };
void SetStatusV6 (RouterStatus status);
void SetStatusV6SSU2 (RouterStatus status);
RouterError GetErrorV6 () const { return m_ErrorV6; };
void SetErrorV6 (RouterError error) { m_ErrorV6 = error; };
int GetNetID () const { return m_NetID; };
void SetNetID (int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
@@ -122,15 +119,10 @@ namespace garlic
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 RemoveSSUAddress (); // delete SSU address for older routers
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4);
void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4);
void ClearSSU2Introducers (bool v4);
bool IsUnreachable () const;
void SetUnreachable (bool v4, bool v6);
void SetUnreachableSSU2 (bool v4, bool v6);
void SetReachable (bool v4, bool v6);
bool IsFloodfill () const { return m_IsFloodfill; };
void SetFloodfill (bool floodfill);
@@ -147,7 +139,6 @@ namespace garlic
void SetSupportsV6 (bool supportsV6);
void SetSupportsV4 (bool supportsV4);
void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host);
void SetMTU (int mtu, bool v4);
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
@@ -182,10 +173,8 @@ namespace garlic
void UpdateRouterInfo ();
void NewNTCP2Keys ();
void NewSSU2Keys ();
bool IsSSU2Only () const; // SSU2 and no SSU
bool Load ();
void SaveKeys ();
uint16_t SelectRandomPort () const;
bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize);
@@ -201,7 +190,7 @@ namespace garlic
uint64_t m_BandwidthLimit; // allowed bandwidth
int m_ShareRatio;
RouterStatus m_Status, m_StatusV6;
RouterError m_Error, m_ErrorV6;
RouterError m_Error;
int m_NetID;
std::mutex m_GarlicMutex;
std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;

View File

@@ -257,38 +257,20 @@ namespace data
if (!ecode && !address->host.is_unspecified ()) isHost = true;
}
else if (!strcmp (key, "port"))
{
try
{
address->port = boost::lexical_cast<int>(value);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'port' exception ", ex.what ());
}
}
address->port = boost::lexical_cast<int>(value);
else if (!strcmp (key, "mtu"))
{
if (address->ssu)
{
try
{
address->ssu->mtu = boost::lexical_cast<int>(value);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'mtu' exception ", ex.what ());
}
}
address->ssu->mtu = boost::lexical_cast<int>(value);
else
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP");
}
else if (!strcmp (key, "key"))
{
if (address->ssu)
isIntroKey = (Base64ToByteStream (value, strlen (value), address->i, 32) == 32);
else
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP2");
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP");
}
else if (!strcmp (key, "caps"))
address->caps = ExtractAddressCaps (value);
@@ -345,40 +327,13 @@ namespace data
introducer.iHost = boost::asio::ip::address::from_string (value, ecode);
}
else if (!strcmp (key, "iport"))
{
try
{
introducer.iPort = boost::lexical_cast<int>(value);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'iport' exception ", ex.what ());
}
}
introducer.iPort = boost::lexical_cast<int>(value);
else if (!strcmp (key, "itag"))
{
try
{
introducer.iTag = boost::lexical_cast<uint32_t>(value);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ());
}
}
introducer.iTag = boost::lexical_cast<uint32_t>(value);
else if (!strcmp (key, "ikey") || !strcmp (key, "ih"))
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
else if (!strcmp (key, "iexp"))
{
try
{
introducer.iExp = boost::lexical_cast<uint32_t>(value);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'iexp' exception ", ex.what ());
}
}
introducer.iExp = boost::lexical_cast<uint32_t>(value);
}
if (!s) return;
}
@@ -431,10 +386,7 @@ namespace data
((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ())))
numValid++;
else
{
it.iPort = 0;
if (isV2) numValid++;
}
}
if (numValid)
m_ReachableTransports |= supportedTransports;
@@ -457,53 +409,28 @@ namespace data
if (address->host.is_v4 ()) m_ReachableTransports |= eSSU2V4;
if (address->host.is_v6 ()) m_ReachableTransports |= eSSU2V6;
}
if (address->transportStyle == eTransportSSU2)
{
if (address->port) address->published = true;
if (address->ssu && !address->ssu->introducers.empty ())
{
// exclude invalid introducers
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
int numValid = 0;
for (auto& it: address->ssu->introducers)
{
if (it.iTag && ts <= it.iExp)
numValid++;
else
it.iTag = 0;
}
if (numValid)
m_ReachableTransports |= supportedTransports;
else
address->ssu->introducers.resize (0);
}
}
else
{
// 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;
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
if (!address->ssu->introducers.empty ())
{
for (const auto& introducer: address->ssu->introducers)
if (!introducer.iPort && introducer.iHost.is_unspecified () && ts < introducer.iExp) // SSU2
ssu2addr->ssu->introducers.push_back (introducer);
if (!ssu2addr->ssu->introducers.empty ())
m_ReachableTransports |= supportedTransports;
}
addresses->push_back(ssu2addr);
}
}
if (supportedTransports)
{
if (!(m_SupportedTransports & supportedTransports)) // avoid duplicates
{
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;
}
}
@@ -753,7 +680,6 @@ namespace data
{
auto addr = std::make_shared<Address>();
addr->transportStyle = eTransportSSU2;
addr->port = 0;
addr->caps = caps;
addr->date = 0;
addr->ssu.reset (new SSUExt ());
@@ -773,7 +699,7 @@ namespace data
addr->host = host;
addr->port = port;
addr->published = true;
addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC;
addr->caps = 0;
addr->date = 0;
addr->ssu.reset (new SSUExt ());
addr->ssu->mtu = 0;
@@ -979,21 +905,6 @@ namespace data
});
}
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSU2Address (bool v4) const
{
if (v4)
{
if (m_SupportedTransports & eSSU2V4)
return GetSSU2V4Address ();
}
else
{
if (m_SupportedTransports & eSSU2V6)
return GetSSU2V6Address ();
}
return nullptr;
}
template<typename Filter>
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (Filter filter) const
{
@@ -1025,8 +936,7 @@ namespace data
return GetAddress (
[key, isV6](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return address->IsSSU2 () && !memcmp (address->s, key, 32) &&
((isV6 && address->IsV6 ()) || (!isV6 && address->IsV4 ()));
return address->IsSSU2 () && !memcmp (address->s, key, 32) && address->IsV6 () == isV6;
});
}
@@ -1099,8 +1009,8 @@ namespace data
return (address->IsSSU2 ()) && address->IsPeerTesting () &&
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU ();
});
}
}
bool RouterInfo::IsIntroducer (bool v4) const
{
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
@@ -1112,17 +1022,6 @@ namespace data
});
}
bool RouterInfo::IsSSU2Introducer (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->IsIntroducer () &&
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified ();
});
}
void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
{
for (auto& addr: *m_Addresses)
@@ -1143,28 +1042,20 @@ namespace data
for (const auto& addr: *m_Addresses)
{
uint8_t transports = 0;
switch (addr->transportStyle)
if (addr->transportStyle == eTransportNTCP)
{
case eTransportNTCP:
if (addr->IsV4 ()) transports |= eNTCP2V4;
if (addr->IsV6 ())
transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6);
if (addr->IsPublishedNTCP2 ())
m_ReachableTransports |= transports;
break;
case eTransportSSU:
if (addr->IsV4 ()) transports |= eSSUV4;
if (addr->IsV6 ()) transports |= eSSUV6;
if (addr->IsReachableSSU ())
m_ReachableTransports |= transports;
break;
case eTransportSSU2:
if (addr->IsV4 ()) transports |= eSSU2V4;
if (addr->IsV6 ()) transports |= eSSU2V6;
if (addr->IsReachableSSU ())
m_ReachableTransports |= transports;
break;
default: ;
if (addr->IsV4 ()) transports |= eNTCP2V4;
if (addr->IsV6 ())
transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6);
if (addr->IsPublishedNTCP2 ())
m_ReachableTransports |= transports;
}
else if (addr->transportStyle == eTransportSSU)
{
if (addr->IsV4 ()) transports |= eSSUV4;
if (addr->IsV6 ()) transports |= eSSUV6;
if (addr->IsReachableSSU ())
m_ReachableTransports |= transports;
}
m_SupportedTransports |= transports;
}
@@ -1334,7 +1225,6 @@ namespace data
if (address.published)
{
isPublished = true;
if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
}
else
@@ -1354,7 +1244,7 @@ namespace data
else
WriteString ("", s);
if (isPublished && !address.host.is_unspecified ())
if (isPublished)
{
WriteString ("host", properties);
properties << '=';
@@ -1457,7 +1347,7 @@ namespace data
properties << ';';
}
}
if ((isPublished || (address.ssu && !address.IsSSU2 ())) && address.port)
if (isPublished || (address.ssu && !address.IsSSU2 ()))
{
WriteString ("port", properties);
properties << '=';
@@ -1525,40 +1415,5 @@ namespace data
{
return std::make_shared<Buffer> ();
}
bool LocalRouterInfo::AddSSU2Introducer (const Introducer& introducer, bool v4)
{
for (auto& addr : GetAddresses ())
{
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
{
for (auto& intro: addr->ssu->introducers)
if (intro.iTag == introducer.iTag) return false; // already presented
addr->ssu->introducers.push_back (introducer);
SetReachableTransports (GetReachableTransports () | ((addr->IsV4 () ? eSSU2V4 : eSSU2V6)));
return true;
}
}
return false;
}
bool LocalRouterInfo::RemoveSSU2Introducer (const IdentHash& h, bool v4)
{
for (auto& addr: GetAddresses ())
{
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
{
for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it)
if (h == it->iKey)
{
addr->ssu->introducers.erase (it);
if (addr->ssu->introducers.empty ())
SetReachableTransports (GetReachableTransports () & ~(addr->IsV4 () ? eSSU2V4 : eSSU2V6));
return true;
}
}
}
return false;
}
}
}

View File

@@ -193,7 +193,6 @@ namespace data
std::shared_ptr<const Address> GetYggdrasilAddress () const;
std::shared_ptr<const Address> GetSSU2V4Address () const;
std::shared_ptr<const Address> GetSSU2V6Address () const;
std::shared_ptr<const Address> GetSSU2Address (bool v4) const;
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
@@ -235,7 +234,6 @@ namespace data
bool IsPeerTesting (bool v4) const;
bool IsSSU2PeerTesting (bool v4) const;
bool IsIntroducer (bool v4) const;
bool IsSSU2Introducer (bool v4) const;
uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps) { m_Caps = caps; };
@@ -275,8 +273,6 @@ namespace data
void SetBufferLen (size_t len) { m_BufferLen = len; };
void RefreshTimestamp ();
const Addresses& GetAddresses () const { return *m_Addresses; };
CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; };
void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; };
private:
@@ -319,9 +315,6 @@ namespace data
std::string GetProperty (const std::string& key) const;
void ClearProperties () override { m_Properties.clear (); };
bool AddSSU2Introducer (const Introducer& introducer, bool v4);
bool RemoveSSU2Introducer (const IdentHash& h, bool v4);
private:
void WriteToStream (std::ostream& s) const;

File diff suppressed because it is too large Load Diff

View File

@@ -17,24 +17,15 @@ namespace i2p
{
namespace transport
{
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds
const int SSU2_RESEND_CHECK_TIMEOUT = 400; // in milliseconds
const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 100; // in milliseconds
const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 10; // in milliseconds
const size_t SSU2_MAX_RESEND_PACKETS = 128; // packets to resend at the time
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
const size_t SSU2_MAX_NUM_INTRODUCERS = 3;
const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
const int SSU2_KEEP_ALIVE_INTERVAL = 30; // in seconds
const int SSU2_PROXY_CONNECT_RETRY_TIMEOUT = 30; // in seconds
class SSU2Server: private i2p::util::RunnableServiceWithWork
{
struct Packet
{
uint8_t buf[SSU2_MAX_PACKET_SIZE];
uint8_t buf[SSU2_MTU];
size_t len;
boost::asio::ip::udp::endpoint from;
};
@@ -57,23 +48,13 @@ namespace transport
void Start ();
void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); };
void SetLocalAddress (const boost::asio::ip::address& localAddress);
bool SetProxy (const std::string& address, uint16_t port);
bool UsesProxy () const { return m_IsThroughProxy; };
bool IsSupported (const boost::asio::ip::address& addr) const;
uint16_t GetPort (bool v4) const;
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
void AddSession (std::shared_ptr<SSU2Session> session);
void RemoveSession (uint64_t connID);
void AddSessionByRouterHash (std::shared_ptr<SSU2Session> session);
bool AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session);
void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep);
void AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session);
std::shared_ptr<SSU2Session> FindSession (const i2p::data::IdentHash& ident) const;
std::shared_ptr<SSU2Session> FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const;
std::shared_ptr<SSU2Session> GetRandomSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports,
const i2p::data::IdentHash& excluded) const;
void AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay);
void RemoveRelay (uint32_t tag);
std::shared_ptr<SSU2Session> FindRelaySession (uint32_t tag);
@@ -84,18 +65,12 @@ namespace transport
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 peerTest = false);
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);
std::pair<uint64_t, uint32_t> NewIncomingToken (const boost::asio::ip::udp::endpoint& ep);
void RescheduleIntroducersUpdateTimer ();
void RescheduleIntroducersUpdateTimerV6 ();
i2p::util::MemoryPool<SSU2SentPacket>& GetSentPacketsPool () { return m_SentPacketsPool; };
private:
@@ -110,55 +85,25 @@ namespace transport
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
void ScheduleResend (bool more);
void ScheduleResend ();
void HandleResendTimer (const boost::system::error_code& ecode);
void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session);
std::list<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
bool v4, const std::set<i2p::data::IdentHash>& excluded) const;
void UpdateIntroducers (bool v4);
void ScheduleIntroducersUpdateTimer ();
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4);
void ScheduleIntroducersUpdateTimerV6 ();
void ConnectThroughIntroducer (std::shared_ptr<const i2p::data::RouterInfo> router,
std::shared_ptr<const i2p::data::RouterInfo::Address> address);
void SendThroughProxy (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);
void ProcessNextPacketFromProxy (uint8_t * buf, size_t len);
void ConnectToProxy ();
void ReconnectToProxy ();
void HandshakeWithProxy ();
void ReadHandshakeWithProxyReply ();
void SendUDPAssociateRequest ();
void ReadUDPAssociateReply ();
void ReadUDPAssociateSocket (); // handle if closed by peer
private:
ReceiveService m_ReceiveService;
boost::asio::ip::udp::socket m_SocketV4, m_SocketV6;
boost::asio::ip::address m_AddressV4, m_AddressV6;
std::unordered_map<uint64_t, std::shared_ptr<SSU2Session> > m_Sessions;
std::unordered_map<i2p::data::IdentHash, std::shared_ptr<SSU2Session> > m_SessionsByRouterHash;
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
std::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to
i2p::util::MemoryPoolMt<Packet> m_PacketsPool;
i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool;
boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer,
m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6;
boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer;
std::shared_ptr<SSU2Session> m_LastSession;
bool m_IsPublished; // if we maintain introducers
bool m_IsSyncClockFromPeers;
// proxy
bool m_IsThroughProxy;
uint8_t m_UDPRequestHeader[SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE];
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
std::unique_ptr<boost::asio::ip::tcp::socket> m_UDPAssociateSocket;
std::unique_ptr<boost::asio::ip::udp::endpoint> m_ProxyRelayEndpoint;
std::unique_ptr<boost::asio::deadline_timer> m_ProxyConnectRetryTimer;
public:
// for HTTP/I2PControl

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,6 @@
#include <boost/asio.hpp>
#include "Crypto.h"
#include "RouterInfo.h"
#include "RouterContext.h"
#include "TransportSession.h"
namespace i2p
@@ -26,30 +25,15 @@ namespace transport
{
const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
const int SSU2_CLOCK_SKEW = 60; // in seconds
const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust
const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds
const int SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52*60; // for next token block, in seconds
const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds
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_MAX_PACKET_SIZE = 1500;
const size_t SSU2_MIN_PACKET_SIZE = 1280;
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in millseconds
const int SSU2_RESEND_INTERVAL = 300; // in milliseconds
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_MIN_WINDOW_SIZE = 16; // in packets
const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets
const size_t SSU2_MIN_RTO = 100; // in milliseconds
const size_t SSU2_MAX_RTO = 2500; // in milliseconds
const float SSU2_kAPPA = 1.8;
const size_t SSU2_MAX_OUTGOING_QUEUE_SIZE = 500; // in messages
const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send
const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64;
// flags
const uint8_t SSU2_FLAG_IMMEDIATE_ACK_REQUESTED = 0x01;
const size_t SSU2_MAX_WINDOW_SIZE = 128; // in packets
enum SSU2MessageType
{
@@ -92,20 +76,11 @@ namespace transport
enum SSU2SessionState
{
eSSU2SessionStateUnknown,
eSSU2SessionStateTokenReceived,
eSSU2SessionStateSessionRequestSent,
eSSU2SessionStateSessionRequestReceived,
eSSU2SessionStateSessionCreatedSent,
eSSU2SessionStateSessionCreatedReceived,
eSSU2SessionStateSessionConfirmedSent,
eSSU2SessionStateEstablished,
eSSU2SessionStateClosing,
eSSU2SessionStateTerminated,
eSSU2SessionStateFailed,
eSSU2SessionStateIntroduced,
eSSU2SessionStatePeerTest,
eSSU2SessionStatePeerTestReceived, // 5 before 4
eSSU2SessionStateTokenRequestReceived
eSSU2SessionStateEstablished,
eSSU2SessionStateTerminated,
eSSU2SessionStateFailed
};
enum SSU2PeerTestCode
@@ -123,49 +98,13 @@ namespace transport
eSSU2PeerTestCodeCharlieAliceIsBanned = 69,
eSSU2PeerTestCodeCharlieAliceIsUnknown = 70,
eSSU2PeerTestCodeUnspecified = 128
};
enum SSU2RelayResponseCode
{
eSSU2RelayResponseCodeAccept = 0,
eSSU2RelayResponseCodeBobRelayTagNotFound = 5,
eSSU2RelayResponseCodeCharlieUnsupportedAddress = 65,
eSSU2RelayResponseCodeCharlieSignatureFailure = 67,
eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70
};
enum SSU2TerminationReason
{
eSSU2TerminationReasonNormalClose = 0,
eSSU2TerminationReasonTerminationReceived = 1,
eSSU2TerminationReasonIdleTimeout = 2,
eSSU2TerminationReasonRouterShutdown = 3,
eSSU2TerminationReasonDataPhaseAEADFailure= 4,
eSSU2TerminationReasonIncompatibleOptions = 5,
eSSU2TerminationReasonTncompatibleSignatureType = 6,
eSSU2TerminationReasonClockSkew = 7,
eSSU2TerminationPaddingViolation = 8,
eSSU2TerminationReasonAEADFramingError = 9,
eSSU2TerminationReasonPayloadFormatError = 10,
eSSU2TerminationReasonSessionRequestError = 11,
eSSU2TerminationReasonSessionCreatedError = 12,
eSSU2TerminationReasonSessionConfirmedError = 13,
eSSU2TerminationReasonTimeout = 14,
eSSU2TerminationReasonRouterInfoSignatureVerificationFail = 15,
eSSU2TerminationReasonInvalidS = 16,
eSSU2TerminationReasonBanned = 17,
eSSU2TerminationReasonBadToken = 18,
eSSU2TerminationReasonConnectionLimits = 19,
eSSU2TerminationReasonIncompatibleVersion = 20,
eSSU2TerminationReasonWrongNetID = 21,
eSSU2TerminationReasonReplacedByNewSession = 22
};
};
struct SSU2IncompleteMessage
{
struct Fragment
{
uint8_t buf[SSU2_MAX_PACKET_SIZE];
uint8_t buf[SSU2_MTU];
size_t len;
bool isLast;
};
@@ -174,16 +113,6 @@ namespace transport
int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments;
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
};
struct SSU2SentPacket
{
uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = 0;
uint64_t sendTime; // in milliseconds
int numResends = 0;
};
// RouterInfo flags
@@ -206,14 +135,19 @@ namespace transport
} h;
};
struct HandshakePacket
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 headerX[48]; // part1 for SessionConfirmed
uint8_t payload[SSU2_MAX_PACKET_SIZE*2];
size_t payloadSize = 0;
uint64_t sendTime = 0; // in milliseconds
bool isSecondFragment = false; // for SessionConfirmed
uint8_t payload[SSU2_MAX_PAYLOAD_SIZE];
size_t payloadSize;
};
typedef std::function<void ()> OnEstablished;
@@ -226,24 +160,18 @@ namespace transport
void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; };
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; };
i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports () const { return m_RemoteTransports; };
std::shared_ptr<const i2p::data::RouterInfo::Address> GetAddress () const { return m_Address; };
void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; };
OnEstablished GetOnEstablished () const { return m_OnEstablished; };
void Connect ();
bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag);
void WaitForIntroduction ();
void SendPeerTest (); // Alice, Data message
void SendKeepAlive ();
void RequestTermination (SSU2TerminationReason reason);
void Terminate ();
void TerminateByTimeout ();
void CleanUp (uint64_t ts);
void FlushData ();
void Done () override;
void SendLocalRouterInfo (bool update) override;
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
uint32_t GetRelayTag () const override { return m_RelayTag; };
size_t Resend (uint64_t ts); // return number or resent packets
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; };
@@ -255,19 +183,14 @@ namespace transport
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, const boost::asio::ip::udp::endpoint& from);
void ProcessData (uint8_t * buf, size_t len);
private:
void Terminate ();
void Established ();
void ScheduleConnectTimer ();
void HandleConnectTimer (const boost::system::error_code& ecode);
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
bool SendQueue (); // returns true if ack block was sent
bool SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
void ResendHandshakePacket ();
void ConnectAfterIntroduction ();
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);
@@ -278,25 +201,18 @@ namespace transport
void KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba);
void SendTokenRequest ();
void SendRetry ();
uint32_t SendData (const uint8_t * buf, size_t len, uint8_t flags = 0); // returns packet num
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, uint64_t token);
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message
void SendPathResponse (const uint8_t * data, size_t len);
void SendPathChallenge ();
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 HandleDateTime (const uint8_t * buf, size_t len);
void HandleAck (const uint8_t * buf, size_t len);
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts);
void HandleAddress (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;
void AdjustMaxPayloadSize ();
RouterStatus GetRouterStatus () const;
void SetRouterStatus (RouterStatus status) 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
@@ -316,40 +232,33 @@ namespace transport
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, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4);
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
size_t CreateTerminationBlock (uint8_t * buf, size_t len);
private:
SSU2Server& m_Server;
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
std::unique_ptr<HandshakePacket> m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice
std::unique_ptr<HandshakePacket> m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed
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;
i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests
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<SSU2SentPacket> > m_SentPackets; // packetNum -> packet
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, m_RTT, m_RTO;
size_t m_WindowSize;
uint32_t m_RelayTag; // between Bob and Charlie
OnEstablished m_OnEstablished; // callback from Established
boost::asio::deadline_timer m_ConnectTimer;
SSU2TerminationReason m_TerminationReason;
size_t m_MaxPayloadSize;
std::unique_ptr<i2p::data::IdentHash> m_PathChallenge;
};
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)

View File

@@ -19,14 +19,17 @@
#include "I2NPProtocol.h"
#include "Identity.h"
#include "RouterInfo.h"
#include "TransportSession.h"
namespace i2p
{
namespace transport
{
const size_t SSU_MTU_V4 = 1484;
const size_t SSU_MTU_V6 = 1488;
const size_t IPV4_HEADER_SIZE = 20;
const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8;
const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440
const int RESEND_INTERVAL = 3; // in seconds

View File

@@ -41,6 +41,7 @@ namespace transport
i2p::context.GetRouterInfo ().GetSSUAddress (true);
if (address) m_IntroKey = address->i;
}
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
}
SSUSession::~SSUSession ()
@@ -719,8 +720,8 @@ namespace transport
if (i2p::context.GetStatus () == eRouterStatusTesting)
i2p::context.SetError (eRouterErrorSymmetricNAT);
}
else if (i2p::context.GetError () == eRouterErrorSymmetricNAT)
i2p::context.SetError (eRouterErrorNone);
else if (i2p::context.GetStatus () == eRouterStatusError && i2p::context.GetError () == eRouterErrorSymmetricNAT)
i2p::context.SetStatus (eRouterStatusTesting);
}
uint32_t nonce = bufbe32toh (buf);
buf += 4; // nonce

View File

@@ -103,6 +103,8 @@ namespace transport
void SendKeepAlive ();
uint32_t GetRelayTag () const { return m_RelayTag; };
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
void FlushData ();
void CleanUp (uint64_t ts);
@@ -165,6 +167,7 @@ namespace transport
i2p::crypto::AESKey m_SessionKey;
i2p::crypto::MACKey m_MacKey;
i2p::data::RouterInfo::IntroKey m_IntroKey;
uint32_t m_CreationTime; // seconds since epoch
SSUData m_Data;
bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only

View File

@@ -474,29 +474,6 @@ namespace stream
Close (); // check is all outgoing messages have been sent and we can send close
}
size_t Stream::Receive (uint8_t * buf, size_t len, int timeout)
{
if (!len) return 0;
size_t ret = 0;
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;
std::unique_lock<std::mutex> l(newDataReceivedMutex);
AsyncReceive (boost::asio::buffer (buf, len),
[&ret, &newDataReceived, &newDataReceivedMutex](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode == boost::asio::error::timed_out)
ret = 0;
else
ret = bytes_transferred;
std::unique_lock<std::mutex> l(newDataReceivedMutex);
newDataReceived.notify_all ();
},
timeout);
if (newDataReceived.wait_for (l, std::chrono::seconds (timeout)) == std::cv_status::timeout)
ret = 0;
return ret;
}
size_t Stream::Send (const uint8_t * buf, size_t len)
{
AsyncSend (buf, len, nullptr);
@@ -752,7 +729,7 @@ namespace stream
Terminate ();
break;
default:
LogPrint (eLogWarning, "Streaming: Unexpected stream status=", (int)m_Status, " for sSID=", m_SendStreamID);
LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status, "sSID=", m_SendStreamID);
};
}
@@ -878,7 +855,7 @@ namespace stream
for (const auto& it: packets)
{
auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage (
it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets (), it->IsSYN ()));
it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets ()));
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeTunnel,
@@ -1108,6 +1085,8 @@ namespace stream
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
m_PendingIncomingTimer (m_Owner->GetService ())
{
if (m_Gzip)
m_Deflator.reset (new i2p::data::GzipDeflator);
}
StreamingDestination::~StreamingDestination ()
@@ -1362,26 +1341,6 @@ namespace stream
acceptor (stream);
}
std::shared_ptr<Stream> StreamingDestination::AcceptStream (int timeout)
{
std::shared_ptr<i2p::stream::Stream> stream;
std::condition_variable streamAccept;
std::mutex streamAcceptMutex;
std::unique_lock<std::mutex> l(streamAcceptMutex);
AcceptOnce (
[&streamAccept, &streamAcceptMutex, &stream](std::shared_ptr<i2p::stream::Stream> s)
{
stream = s;
std::unique_lock<std::mutex> l(streamAcceptMutex);
streamAccept.notify_all ();
});
if (timeout)
streamAccept.wait_for (l, std::chrono::seconds (timeout));
else
streamAccept.wait (l);
return stream;
}
void StreamingDestination::HandlePendingIncomingTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
@@ -1406,7 +1365,7 @@ namespace stream
}
std::shared_ptr<I2NPMessage> StreamingDestination::CreateDataMessage (
const uint8_t * payload, size_t len, uint16_t toPort, bool checksum, bool gzip)
const uint8_t * payload, size_t len, uint16_t toPort, bool checksum)
{
size_t size;
auto msg = m_I2NPMsgsPool.AcquireShared ();
@@ -1414,8 +1373,8 @@ namespace stream
buf += 4; // reserve for lengthlength
msg->len += 4;
if (m_Gzip || gzip)
size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
if (m_Gzip && m_Deflator)
size = m_Deflator->Deflate (payload, len, buf, msg->maxLen - msg->len);
else
size = i2p::data::GzipNoCompression (payload, len, buf, msg->maxLen - msg->len);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -185,8 +185,7 @@ namespace stream
template<typename Buffer, typename ReceiveHandler>
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0);
size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); };
size_t Receive (uint8_t * buf, size_t len, int timeout);
void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); };
/** only call close from destination thread, use Stream::AsyncClose for other threads */
@@ -279,14 +278,13 @@ namespace stream
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
void AcceptOnce (const Acceptor& acceptor);
void AcceptOnceAcceptor (std::shared_ptr<Stream> stream, Acceptor acceptor, Acceptor prev);
std::shared_ptr<Stream> AcceptStream (int timeout = 0); // sync
std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_Owner; };
void SetOwner (std::shared_ptr<i2p::client::ClientDestination> owner) { m_Owner = owner; };
uint16_t GetLocalPort () const { return m_LocalPort; };
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort, bool checksum = true, bool gzip = false);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort, bool checksum = true);
Packet * NewPacket () { return m_PacketsPool.Acquire(); }
void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); }
@@ -317,7 +315,7 @@ namespace stream
public:
i2p::data::GzipInflator m_Inflator;
i2p::data::GzipDeflator m_Deflator;
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
// for HTTP only
const decltype(m_Streams)& GetStreams () const { return m_Streams; };
@@ -338,10 +336,11 @@ namespace stream
int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout;
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t));
int left = timeout - t;
s->m_ReceiveTimer.async_wait (
[s, buffer, handler, left](const boost::system::error_code & ec)
auto self = s->shared_from_this();
self->m_ReceiveTimer.async_wait (
[self, buffer, handler, left](const boost::system::error_code & ec)
{
s->HandleReceiveTimer(ec, buffer, handler, left);
self->HandleReceiveTimer(ec, buffer, handler, left);
});
}
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
* Copyright (c) 2013-2020, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -24,10 +24,6 @@ namespace i2p
{
namespace transport
{
const size_t IPV4_HEADER_SIZE = 20;
const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8;
class SignedData
{
public:
@@ -37,12 +33,6 @@ namespace transport
{
m_Stream << other.m_Stream.rdbuf ();
}
void Reset ()
{
m_Stream.str("");
}
void Insert (const uint8_t * buf, size_t len)
{
m_Stream.write ((char *)buf, len);
@@ -79,7 +69,6 @@ namespace transport
{
if (router)
m_RemoteIdentity = router->GetRouterIdentity ();
m_CreationTime = m_LastActivityTimestamp;
}
virtual ~TransportSession () {};
@@ -107,11 +96,7 @@ namespace transport
bool IsTerminationTimeoutExpired (uint64_t ts) const
{ return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); };
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
virtual uint32_t GetRelayTag () const { return 0; };
virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); };
virtual void SendLocalRouterInfo () { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
protected:
@@ -122,17 +107,7 @@ namespace transport
bool m_IsOutgoing;
int m_TerminationTimeout;
uint64_t m_LastActivityTimestamp;
uint32_t m_CreationTime; // seconds since epoch
};
// SOCKS5 proxy
const uint8_t SOCKS5_VER = 0x05;
const uint8_t SOCKS5_CMD_CONNECT = 0x01;
const uint8_t SOCKS5_CMD_UDP_ASSOCIATE = 0x03;
const uint8_t SOCKS5_ATYP_IPV4 = 0x01;
const uint8_t SOCKS5_ATYP_IPV6 = 0x04;
const size_t SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE = 10;
const size_t SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE = 22;
}
}

View File

@@ -167,8 +167,6 @@ namespace transport
m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service);
}
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
i2p::config::GetOption("nat", m_IsNAT);
m_X25519KeysPairSupplier.Start ();
m_IsRunning = true;
@@ -192,8 +190,6 @@ namespace transport
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port, proxyurl.user, proxyurl.pass);
i2p::context.SetStatus (eRouterStatusProxy);
if (ipv6)
i2p::context.SetStatusV6 (eRouterStatusProxy);
}
else
LogPrint(eLogError, "Transports: Unsupported NTCP2 proxy URL ", ntcp2proxy);
@@ -222,29 +218,10 @@ namespace transport
}
}
// create SSU2 server
if (enableSSU2)
{
m_SSU2Server = new SSU2Server ();
std::string ssu2proxy; i2p::config::GetOption("ssu2.proxy", ssu2proxy);
if (!ssu2proxy.empty())
{
if (proxyurl.parse (ssu2proxy) && proxyurl.schema == "socks")
{
if (m_SSU2Server->SetProxy (proxyurl.host, proxyurl.port))
{
i2p::context.SetStatus (eRouterStatusProxy);
if (ipv6)
i2p::context.SetStatusV6 (eRouterStatusProxy);
}
else
LogPrint(eLogError, "Transports: Can't set SSU2 proxy ", ssu2proxy);
}
else
LogPrint(eLogError, "Transports: Invalid SSU2 proxy URL ", ssu2proxy);
}
}
if (enableSSU2) m_SSU2Server = new SSU2Server ();
// bind to interfaces
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
if (ipv4)
{
std::string address; i2p::config::GetOption("address4", address);
@@ -256,22 +233,11 @@ namespace transport
{
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr);
}
}
if (enableSSU2)
{
uint16_t mtu; i2p::config::GetOption ("ssu2.mtu4", mtu);
if (mtu)
{
if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE;
if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE;
i2p::context.SetMTU (mtu, true);
}
}
}
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
if (ipv6)
{
std::string address; i2p::config::GetOption("address6", address);
@@ -283,20 +249,8 @@ namespace transport
{
if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr);
if (m_SSUServer) m_SSUServer->SetLocalAddress (addr);
if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr);
}
}
if (enableSSU2)
{
uint16_t mtu; i2p::config::GetOption ("ssu2.mtu6", mtu);
if (mtu)
{
if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE;
if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE;
i2p::context.SetMTU (mtu, false);
}
}
}
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
@@ -329,8 +283,8 @@ namespace transport
delete m_SSUServer;
m_SSUServer = nullptr;
}
if (m_SSUServer) DetectExternalIP ();
}
if (m_SSUServer || m_SSU2Server) DetectExternalIP ();
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
@@ -459,9 +413,9 @@ namespace transport
auto r = netdb.FindRouter (ident);
if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, {r, ts})).first;
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
i2p::util::GetSecondsSinceEpoch (), {} })).first;
}
connected = ConnectToPeer (ident, it->second);
}
@@ -496,81 +450,127 @@ namespace transport
peer.router = netdb.FindRouter (ident); // try to get new one from netdb
if (peer.router) // we have RI already
{
if (peer.priority.empty ())
SetPriority (peer);
while (peer.numAttempts < (int)peer.priority.size ())
if (peer.numAttempts < 2) // NTCP2, 0 - ipv6, 1- ipv4
{
auto tr = peer.priority[peer.numAttempts];
peer.numAttempts++;
switch (tr)
if (m_NTCP2Server) // we support NTCP2
{
case i2p::data::RouterInfo::eNTCP2V4:
case i2p::data::RouterInfo::eNTCP2V6:
std::shared_ptr<const RouterInfo::Address> address;
if (!peer.numAttempts) // NTCP2 ipv6
{
if (!m_NTCP2Server) continue;
std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eNTCP2V6) ?
peer.router->GetPublishedNTCP2V6Address () : peer.router->GetPublishedNTCP2V4Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
if (address)
if (context.GetRouterInfo ().IsNTCP2V6 () && peer.router->IsReachableBy (RouterInfo::eNTCP2V6))
{
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
if( m_NTCP2Server->UsingProxy())
m_NTCP2Server->ConnectWithProxy(s);
else
m_NTCP2Server->Connect (s);
return true;
address = peer.router->GetPublishedNTCP2V6Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
}
break;
peer.numAttempts++;
}
case i2p::data::RouterInfo::eSSU2V4:
case i2p::data::RouterInfo::eSSU2V6:
if (!address && peer.numAttempts == 1) // NTCP2 ipv4
{
if (!m_SSU2Server) continue;
std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eSSU2V6) ?
peer.router->GetSSU2V6Address () : peer.router->GetSSU2V4Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
if (address && address->IsReachableSSU ())
if (context.GetRouterInfo ().IsNTCP2 (true) && peer.router->IsReachableBy (RouterInfo::eNTCP2V4))
{
if (m_SSU2Server->CreateSession (peer.router, address))
return true;
address = peer.router->GetPublishedNTCP2V4Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
}
break;
peer.numAttempts++;
}
case i2p::data::RouterInfo::eSSUV4:
case i2p::data::RouterInfo::eSSUV6:
if (address)
{
if (!m_SSUServer) continue;
std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eSSUV6) ?
peer.router->GetSSUV6Address () : peer.router->GetSSUAddress (true);
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
if (address && address->IsReachableSSU ())
{
if (m_SSUServer->CreateSession (peer.router, address))
return true;
}
break;
}
case i2p::data::RouterInfo::eNTCP2V6Mesh:
{
if (!m_NTCP2Server) continue;
auto address = peer.router->GetYggdrasilAddress ();
if (address)
{
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
if( m_NTCP2Server->UsingProxy())
m_NTCP2Server->ConnectWithProxy(s);
else
m_NTCP2Server->Connect (s);
return true;
}
break;
return true;
}
}
else
peer.numAttempts = 2; // switch to SSU
}
if (peer.numAttempts == 2 || peer.numAttempts == 3) // SSU
{
if (m_SSUServer)
{
std::shared_ptr<const RouterInfo::Address> address;
if (peer.numAttempts == 2) // SSU ipv6
{
if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsReachableBy (RouterInfo::eSSUV6))
{
address = peer.router->GetSSUV6Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
}
peer.numAttempts++;
}
if (!address && peer.numAttempts == 3) // SSU ipv4
{
if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsReachableBy (RouterInfo::eSSUV4))
{
address = peer.router->GetSSUAddress (true);
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
}
peer.numAttempts++;
}
if (address && address->IsReachableSSU ())
{
if (m_SSUServer->CreateSession (peer.router, address))
return true;
}
}
else
peer.numAttempts += 2; // switch to Mesh
}
if (peer.numAttempts == 4) // Mesh
{
peer.numAttempts++;
if (m_NTCP2Server && context.GetRouterInfo ().IsMesh () && peer.router->IsMesh ())
{
auto address = peer.router->GetYggdrasilAddress ();
if (address)
{
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
m_NTCP2Server->Connect (s);
return true;
}
default:
LogPrint (eLogError, "Transports: Unknown transport ", (int)tr);
}
}
LogPrint (eLogInfo, "Transports: No compatible addresses available");
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");
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed
peer.Done ();
std::unique_lock<std::mutex> l(m_PeersMutex);
@@ -586,41 +586,6 @@ namespace transport
return true;
}
void Transports::SetPriority (Peer& peer) const
{
static const std::vector<i2p::data::RouterInfo::SupportedTransports>
ntcp2Priority =
{
i2p::data::RouterInfo::eNTCP2V6,
i2p::data::RouterInfo::eNTCP2V4,
i2p::data::RouterInfo::eSSU2V6,
i2p::data::RouterInfo::eSSU2V4,
i2p::data::RouterInfo::eNTCP2V6Mesh,
i2p::data::RouterInfo::eSSUV6,
i2p::data::RouterInfo::eSSUV4
},
ssu2Priority =
{
i2p::data::RouterInfo::eSSU2V6,
i2p::data::RouterInfo::eSSU2V4,
i2p::data::RouterInfo::eNTCP2V6,
i2p::data::RouterInfo::eNTCP2V4,
i2p::data::RouterInfo::eNTCP2V6Mesh,
i2p::data::RouterInfo::eSSUV6,
i2p::data::RouterInfo::eSSUV4
};
if (!peer.router) return;
auto compatibleTransports = context.GetRouterInfo ().GetCompatibleTransports (false) &
peer.router->GetCompatibleTransports (true);
peer.numAttempts = 0;
peer.priority.clear ();
bool ssu2 = rand () & 1;
const auto& priority = ssu2 ? ssu2Priority : ntcp2Priority;
for (auto transport: priority)
if (transport & compatibleTransports)
peer.priority.push_back (transport);
}
void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
{
m_Service->post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
@@ -654,111 +619,88 @@ namespace transport
i2p::context.SetStatus (eRouterStatusOK);
return;
}
if (m_SSUServer || m_SSU2Server)
if (m_SSUServer)
PeerTest ();
else
LogPrint (eLogWarning, "Transports: Can't detect external IP. SSU or SSU2 is not available");
LogPrint (eLogError, "Transports: Can't detect external IP. SSU is not available");
}
void Transports::PeerTest (bool ipv4, bool ipv6)
{
if (RoutesRestricted() || (!m_SSUServer && !m_SSU2Server)) return;
if (RoutesRestricted() || !m_SSUServer) return;
if (ipv4 && i2p::context.SupportsV4 ())
{
LogPrint (eLogInfo, "Transports: Started peer test IPv4");
std::set<i2p::data::IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
if (m_SSUServer)
bool statusChanged = false;
for (int i = 0; i < 5; i++)
{
bool statusChanged = false;
for (int i = 0; i < 5; i++)
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4
if (router)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4
if (router)
auto addr = router->GetSSUAddress (true); // ipv4
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
{
auto addr = router->GetSSUAddress (true); // ipv4
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
if (!statusChanged)
{
if (!statusChanged)
{
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, addr, true); // peer test v4
statusChanged = true;
i2p::context.SetStatus (eRouterStatusTesting); // first time only
}
excluded.insert (router->GetIdentHash ());
m_SSUServer->CreateSession (router, addr, true); // peer test v4
}
excluded.insert (router->GetIdentHash ());
}
if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
}
if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
// SSU2
if (m_SSU2Server && !m_SSU2Server->UsesProxy ())
if (m_SSU2Server)
{
excluded.clear ();
excluded.insert (i2p::context.GetIdentHash ());
int numTests = m_SSUServer ? 3 : 5;
for (int i = 0; i < numTests; i++)
{
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4
if (router)
{
if (i2p::context.GetStatus () != eRouterStatusTesting)
i2p::context.SetStatusSSU2 (eRouterStatusTesting);
m_SSU2Server->StartPeerTest (router, true);
excluded.insert (router->GetIdentHash ());
}
}
}
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4
if (router)
m_SSU2Server->StartPeerTest (router, true);
}
}
if (ipv6 && i2p::context.SupportsV6 ())
{
LogPrint (eLogInfo, "Transports: Started peer test IPv6");
std::set<i2p::data::IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
if (m_SSUServer)
bool statusChanged = false;
for (int i = 0; i < 5; i++)
{
bool statusChanged = false;
for (int i = 0; i < 5; i++)
auto router = i2p::data::netdb.GetRandomPeerTestRouter (false, excluded); // v6
if (router)
{
auto router = i2p::data::netdb.GetRandomPeerTestRouter (false, excluded); // v6
if (router)
auto addr = router->GetSSUV6Address ();
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
{
auto addr = router->GetSSUV6Address ();
if (addr && !i2p::util::net::IsInReservedRange(addr->host))
if (!statusChanged)
{
if (!statusChanged)
{
statusChanged = true;
i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only
}
m_SSUServer->CreateSession (router, addr, true); // peer test v6
statusChanged = true;
i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only
}
excluded.insert (router->GetIdentHash ());
m_SSUServer->CreateSession (router, addr, true); // peer test v6
}
excluded.insert (router->GetIdentHash ());
}
if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
}
if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
// SSU2
if (m_SSU2Server && !m_SSU2Server->UsesProxy ())
if (m_SSU2Server)
{
excluded.clear ();
excluded.insert (i2p::context.GetIdentHash ());
int numTests = m_SSUServer ? 3 : 5;
for (int i = 0; i < numTests; i++)
{
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6
if (router)
{
if (i2p::context.GetStatusV6 () != eRouterStatusTesting)
i2p::context.SetStatusV6SSU2 (eRouterStatusTesting);
m_SSU2Server->StartPeerTest (router, false);
excluded.insert (router->GetIdentHash ());
}
}
}
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6
if (router)
m_SSU2Server->StartPeerTest (router, false);
}
}
}
@@ -809,9 +751,8 @@ namespace transport
return;
}
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
auto ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.insert (std::make_pair (ident, Peer{ nullptr, ts }));
std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} }));
}
});
}
@@ -872,17 +813,7 @@ namespace transport
it = m_Peers.erase (it);
}
else
{
if (ts > it->second.nextRouterInfoUpdateTime)
{
auto session = it->second.sessions.front ();
if (session)
session->SendLocalRouterInfo (true);
it->second.nextRouterInfoUpdateTime = ts + PEER_ROUTER_INFO_UPDATE_INTERVAL +
rand () % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE;
}
++it;
}
}
UpdateBandwidth (); // TODO: use separate timer(s) for it
bool ipv4Testing = i2p::context.GetStatus () == eRouterStatusTesting;
@@ -1008,122 +939,5 @@ namespace transport
i2p::context.SetError (eRouterErrorOffline);
}
}
void InitAddressFromIface ()
{
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
// ifname -> address
std::string ifname; i2p::config::GetOption("ifname", ifname);
if (ipv4 && i2p::config::IsDefault ("address4"))
{
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
if (!ifname4.empty ())
i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname4, false).to_string ()); // v4
else if (!ifname.empty ())
i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname, false).to_string ()); // v4
}
if (ipv6 && i2p::config::IsDefault ("address6"))
{
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
if (!ifname6.empty ())
i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname6, true).to_string ()); // v6
else if (!ifname.empty ())
i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname, true).to_string ()); // v6
}
}
void InitTransports ()
{
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
uint16_t port; i2p::config::GetOption("port", port);
boost::asio::ip::address_v6 yggaddr;
if (ygg)
{
std::string yggaddress; i2p::config::GetOption ("meshnets.yggaddress", yggaddress);
if (!yggaddress.empty ())
{
yggaddr = boost::asio::ip::address_v6::from_string (yggaddress);
if (yggaddr.is_unspecified () || !i2p::util::net::IsYggdrasilAddress (yggaddr) ||
!i2p::util::net::IsLocalAddress (yggaddr))
{
LogPrint(eLogWarning, "Transports: Can't find Yggdrasil address ", yggaddress);
ygg = false;
}
}
else
{
yggaddr = i2p::util::net::GetYggdrasilAddress ();
if (yggaddr.is_unspecified ())
{
LogPrint(eLogWarning, "Transports: Yggdrasil is not running. Disabled");
ygg = false;
}
}
}
if (!i2p::config::IsDefault("port"))
{
LogPrint(eLogInfo, "Transports: Accepting incoming connections at port ", port);
i2p::context.UpdatePort (port);
}
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4);
i2p::context.SetSupportsMesh (ygg, yggaddr);
i2p::context.RemoveNTCPAddress (!ipv6); // TODO: remove later
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2)
{
bool published; i2p::config::GetOption("ntcp2.published", published);
if (published)
{
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
if (!ntcp2proxy.empty ()) published = false;
}
if (published)
{
uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port);
if (!ntcp2port) ntcp2port = port; // use standard port
i2p::context.PublishNTCP2Address (ntcp2port, true, ipv4, ipv6, false); // publish
if (ipv6)
{
std::string ipv6Addr; i2p::config::GetOption("ntcp2.addressv6", ipv6Addr);
auto addr = boost::asio::ip::address_v6::from_string (ipv6Addr);
if (!addr.is_unspecified () && addr != boost::asio::ip::address_v6::any ())
i2p::context.UpdateNTCP2V6Address (addr); // set ipv6 address if configured
}
}
else
i2p::context.PublishNTCP2Address (port, false, ipv4, ipv6, false); // unpublish
}
if (ygg)
{
i2p::context.PublishNTCP2Address (port, true, false, false, true);
i2p::context.UpdateNTCP2V6Address (yggaddr);
if (!ipv4 && !ipv6)
i2p::context.SetStatus (eRouterStatusMesh);
}
bool ssu; i2p::config::GetOption("ssu", ssu);
if (!ssu) i2p::context.RemoveSSUAddress (); // TODO: remove later
bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2);
if (ssu2 && i2p::config::IsDefault ("ssu2.enabled") && !ipv4 && !ipv6)
ssu2 = false; // don't enable ssu2 for yggdrasil only router
if (ssu2)
{
uint16_t ssu2port; i2p::config::GetOption("ssu2.port", ssu2port);
if (!ssu2port && port) ssu2port = ssu ? (port + 1) : port;
bool published; i2p::config::GetOption("ssu2.published", published);
if (published)
i2p::context.PublishSSU2Address (ssu2port, true, ipv4, ipv6); // publish
else
i2p::context.PublishSSU2Address (ssu2port, false, ipv4, ipv6); // unpublish
}
}
}
}

View File

@@ -62,22 +62,13 @@ namespace transport
};
typedef EphemeralKeysSupplier<i2p::crypto::X25519Keys> X25519KeysPairSupplier;
const int PEER_ROUTER_INFO_UPDATE_INTERVAL = 31*60; // in seconds
const int PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE = 7*60; // in seconds
struct Peer
{
int numAttempts;
std::shared_ptr<const i2p::data::RouterInfo> router;
std::list<std::shared_ptr<TransportSession> > sessions;
uint64_t creationTime, nextRouterInfoUpdateTime;
uint64_t creationTime;
std::vector<std::shared_ptr<i2p::I2NPMessage> > delayedMessages;
std::vector<i2p::data::RouterInfo::SupportedTransports> priority;
Peer (std::shared_ptr<const i2p::data::RouterInfo> r, uint64_t ts):
numAttempts (0), router (r), creationTime (ts),
nextRouterInfoUpdateTime (ts + PEER_ROUTER_INFO_UPDATE_INTERVAL)
{
}
void Done ()
{
@@ -86,7 +77,7 @@ namespace transport
}
};
const uint64_t SESSION_CREATION_TIMEOUT = 15; // in seconds
const size_t SESSION_CREATION_TIMEOUT = 15; // in seconds
const int PEER_TEST_INTERVAL = 71; // in minutes
const int MAX_NUM_DELAYED_MESSAGES = 150;
class Transports
@@ -100,7 +91,6 @@ namespace transport
void Stop ();
bool IsBoundSSU() const { return m_SSUServer != nullptr; }
bool IsBoundSSU2() const { return m_SSU2Server != nullptr; }
bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; }
bool IsOnline() const { return m_IsOnline; };
@@ -154,7 +144,6 @@ namespace transport
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
void SetPriority (Peer& peer) const;
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void HandlePeerTestTimer (const boost::system::error_code& ecode);
@@ -203,9 +192,6 @@ namespace transport
};
extern Transports transports;
void InitAddressFromIface ();
void InitTransports ();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
* Copyright (c) 2013-2020, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -60,7 +60,6 @@ namespace api
else
i2p::log::Logger().SendTo (i2p::fs::DataDirPath (i2p::fs::GetAppName () + ".log"));
i2p::log::Logger().Start ();
i2p::transport::InitTransports ();
LogPrint(eLogInfo, "API: Starting NetDB");
i2p::data::netdb.Start();
LogPrint(eLogInfo, "API: Starting Transports");

View File

@@ -12,7 +12,6 @@
#include "util.h"
#include "Log.h"
#include "I2PEndian.h"
#if not defined (__FreeBSD__)
#include <pthread.h>
@@ -22,9 +21,6 @@
#include <pthread_np.h>
#endif
#if defined(__APPLE__)
# include <AvailabilityMacros.h>
#endif
#ifdef _WIN32
#include <stdlib.h>
@@ -39,7 +35,7 @@
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
// inet_pton and inet_ntop have been in Windows since Vista, but XP doesn't have these functions!
// inet_pton exists Windows since Vista, but XP doesn't have that function!
// This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found
int inet_pton_xp (int af, const char *src, void *dst)
{
@@ -65,29 +61,6 @@ int inet_pton_xp (int af, const char *src, void *dst)
}
return 0;
}
const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size)
{
struct sockaddr_storage ss;
unsigned long s = size;
ZeroMemory(&ss, sizeof(ss));
ss.ss_family = af;
switch(af) {
case AF_INET:
((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
break;
case AF_INET6:
((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
break;
default:
return NULL;
}
/* cannot direclty use &size because of strict aliasing rules */
return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? dst : NULL;
}
#else /* !_WIN32 => UNIX */
#include <sys/types.h>
#ifdef ANDROID
@@ -146,15 +119,8 @@ namespace util
}
void SetThreadName (const char *name) {
#if defined(__APPLE__)
# if (!defined(MAC_OS_X_VERSION_10_6) || \
(MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || \
defined(__POWERPC__))
/* pthread_setname_np is not there on <10.6 and all PPC.
So do nothing. */
# else
#if defined(__APPLE__) && !defined(__powerpc__)
pthread_setname_np((char*)name);
# endif
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
pthread_set_name_np(pthread_self(), name);
#elif defined(__NetBSD__)
@@ -167,12 +133,27 @@ namespace util
namespace net
{
#ifdef _WIN32
bool IsWindowsXPorLater ()
{
static bool isRequested = false;
static bool isXP = false;
if (!isRequested)
{
// request
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
isXP = osvi.dwMajorVersion <= 5;
isRequested = true;
}
return isXP;
}
int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback)
{
typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size);
IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop");
if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found
ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
@@ -191,7 +172,7 @@ namespace net
if(dwRetVal != NO_ERROR)
{
LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed");
LogPrint(eLogError, "NetIface: GetMTU(): Enclosed GetAdaptersAddresses() call has failed");
FREE(pAddresses);
return fallback;
}
@@ -203,7 +184,7 @@ namespace net
pUnicast = pCurrAddresses->FirstUnicastAddress;
if(pUnicast == nullptr)
LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv4 address, this is not supported");
LogPrint(eLogError, "NetIface: GetMTU(): Not a unicast IPv4 address, this is not supported");
for(int i = 0; pUnicast != nullptr; ++i)
{
@@ -211,13 +192,8 @@ namespace net
sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr;
if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr)
{
char addr[INET_ADDRSTRLEN];
inetntop(AF_INET, &(((struct sockaddr_in *)localInterfaceAddress)->sin_addr), addr, INET_ADDRSTRLEN);
auto result = pCurrAddresses->Mtu;
auto result = pAddresses->Mtu;
FREE(pAddresses);
pAddresses = nullptr;
LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv4 address ", addr);
return result;
}
pUnicast = pUnicast->Next;
@@ -225,23 +201,19 @@ namespace net
pCurrAddresses = pCurrAddresses->Next;
}
LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv4 addresses found");
LogPrint(eLogError, "NetIface: GetMTU(): No usable unicast IPv4 addresses found");
FREE(pAddresses);
return fallback;
}
int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback)
{
typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size);
IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop");
if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found
ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
if(GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen)
== ERROR_BUFFER_OVERFLOW)
{
FREE(pAddresses);
@@ -252,23 +224,23 @@ namespace net
AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen
);
if (dwRetVal != NO_ERROR)
if(dwRetVal != NO_ERROR)
{
LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed");
LogPrint(eLogError, "NetIface: GetMTU(): Enclosed GetAdaptersAddresses() call has failed");
FREE(pAddresses);
return fallback;
}
bool found_address = false;
pCurrAddresses = pAddresses;
while (pCurrAddresses)
while(pCurrAddresses)
{
PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress;
pUnicast = pCurrAddresses->FirstUnicastAddress;
if (pUnicast == nullptr)
LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv6 address, this is not supported");
if(pUnicast == nullptr)
LogPrint(eLogError, "NetIface: GetMTU(): Not a unicast IPv6 address, this is not supported");
for (int i = 0; pUnicast != nullptr; ++i)
for(int i = 0; pUnicast != nullptr; ++i)
{
LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr;
sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr;
@@ -283,13 +255,9 @@ namespace net
if (found_address)
{
char addr[INET6_ADDRSTRLEN];
inetntop(AF_INET6, &(((struct sockaddr_in6 *)localInterfaceAddress)->sin6_addr), addr, INET6_ADDRSTRLEN);
auto result = pCurrAddresses->Mtu;
auto result = pAddresses->Mtu;
FREE(pAddresses);
pAddresses = nullptr;
LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv6 address ", addr);
return result;
}
pUnicast = pUnicast->Next;
@@ -298,7 +266,7 @@ namespace net
pCurrAddresses = pCurrAddresses->Next;
}
LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv6 addresses found");
LogPrint(eLogError, "NetIface: GetMTU(): No usable unicast IPv6 addresses found");
FREE(pAddresses);
return fallback;
}
@@ -330,7 +298,7 @@ namespace net
}
else
{
LogPrint(eLogError, "NetIface: GetMTU: Address family is not supported");
LogPrint(eLogError, "NetIface: GetMTU(): Address family is not supported");
return fallback;
}
}
@@ -455,27 +423,6 @@ namespace net
#endif
}
int GetMaxMTU (const boost::asio::ip::address_v6& localAddress)
{
uint32_t prefix = bufbe32toh (localAddress.to_bytes ().data ());
switch (prefix)
{
case 0x20010470:
case 0x260070ff:
// Hurricane Electric
return 1480;
break;
case 0x2a06a003:
case 0x2a06a004:
case 0x2a06a005:
// route48
return 1420;
break;
default: ;
}
return 1500;
}
static bool IsYggdrasilAddress (const uint8_t addr[16])
{
return addr[0] == 0x02 || addr[0] == 0x03;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -218,7 +218,6 @@ namespace util
namespace net
{
int GetMTU (const boost::asio::ip::address& localAddress);
int GetMaxMTU (const boost::asio::ip::address_v6& localAddress); // check tunnel broker for ipv6 address
const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6=false);
boost::asio::ip::address_v6 GetYggdrasilAddress ();
bool IsLocalAddress (const boost::asio::ip::address& addr);

View File

@@ -16,8 +16,8 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 44
#define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_MINOR 42
#define I2PD_VERSION_MICRO 1
#define I2PD_VERSION_PATCH 0
#ifdef GITVER
#define I2PD_VERSION GITVER
@@ -31,7 +31,7 @@
#define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 56
#define I2P_VERSION_MICRO 54
#define I2P_VERSION_PATCH 0
#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)

View File

@@ -313,14 +313,14 @@ namespace client
{
i2p::config::GetOption("addressbook.enabled", m_IsEnabled);
if (m_IsEnabled)
{
{
if (!m_Storage)
m_Storage = new AddressBookFilesystemStorage;
m_Storage->Init();
LoadHosts (); /* try storage, then hosts.txt, then download */
StartSubscriptions ();
StartLookups ();
}
}
}
void AddressBook::StartResolvers ()
@@ -397,19 +397,6 @@ namespace client
return nullptr;
}
bool AddressBook::RecordExists (const std::string& address, const std::string& jump)
{
auto addr = FindAddress(address);
if (!addr)
return false;
i2p::data::IdentityEx ident;
if (ident.FromBase64 (jump) && ident.GetIdentHash () == addr->identHash)
return true;
return false;
}
void AddressBook::InsertAddress (const std::string& address, const std::string& jump)
{
auto pos = jump.find(".b32.i2p");
@@ -580,7 +567,6 @@ namespace client
void AddressBook::LoadLocal ()
{
if (!m_Storage) return;
std::map<std::string, std::shared_ptr<Address>> localAddresses;
m_Storage->LoadLocal (localAddresses);
for (const auto& it: localAddresses)
@@ -833,22 +819,40 @@ namespace client
}
else
m_Ident = addr->identHash;
// save url parts for later use
std::string dest_host = url.host;
int dest_port = url.port ? url.port : 80;
// try to create stream to addressbook site
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (m_Ident, dest_port);
if (!stream)
/* this code block still needs some love */
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (m_Ident);
if (!leaseSet)
{
std::unique_lock<std::mutex> l(newDataReceivedMutex);
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (m_Ident,
[&newDataReceived, &leaseSet, &newDataReceivedMutex](std::shared_ptr<i2p::data::LeaseSet> ls)
{
leaseSet = ls;
std::unique_lock<std::mutex> l1(newDataReceivedMutex);
newDataReceived.notify_all ();
});
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
{
LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (m_Ident, false); // don't notify, because we know it already
return false;
}
}
if (!leaseSet) {
/* still no leaseset found */
LogPrint (eLogError, "Addressbook: LeaseSet for address ", url.host, " not found");
return false;
}
if (m_Etag.empty() && m_LastModified.empty())
{
}
if (m_Etag.empty() && m_LastModified.empty()) {
m_Book.GetEtag (m_Ident, m_Etag, m_LastModified);
LogPrint (eLogDebug, "Addressbook: Loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified);
}
// create http request & send it
/* save url parts for later use */
std::string dest_host = url.host;
int dest_port = url.port ? url.port : 80;
/* create http request & send it */
i2p::http::HTTPReq req;
req.AddHeader("Host", dest_host);
req.AddHeader("User-Agent", "Wget/1.11.4");
@@ -859,39 +863,44 @@ namespace client
req.AddHeader("If-None-Match", m_Etag);
if (!m_LastModified.empty())
req.AddHeader("If-Modified-Since", m_LastModified);
// convert url to relative
/* convert url to relative */
url.schema = "";
url.host = "";
req.uri = url.to_string();
req.version = "HTTP/1.1";
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port);
std::string request = req.to_string();
stream->Send ((const uint8_t *) request.data(), request.length());
// read response
/* read response */
std::string response;
uint8_t recv_buf[4096];
bool end = false;
int numAttempts = 0;
while (!end)
{
size_t received = stream->Receive (recv_buf, 4096, SUBSCRIPTION_REQUEST_TIMEOUT);
if (received)
{
response.append ((char *)recv_buf, received);
if (!stream->IsOpen ()) end = true;
}
else if (!stream->IsOpen ())
end = true;
else
stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096),
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (bytes_transferred)
response.append ((char *)recv_buf, bytes_transferred);
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
end = true;
newDataReceived.notify_all ();
},
SUBSCRIPTION_REQUEST_TIMEOUT);
std::unique_lock<std::mutex> l(newDataReceivedMutex);
// wait 1 more second
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT + 1)) == std::cv_status::timeout)
{
LogPrint (eLogError, "Addressbook: Subscriptions request timeout expired");
numAttempts++;
if (numAttempts > 5) end = true;
}
}
}
// process remaining buffer
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf)))
response.append ((char *)recv_buf, len);
// parse response
/* parse response */
i2p::http::HTTPRes res;
int res_head_len = res.parse(response);
if (res_head_len < 0)
@@ -904,7 +913,7 @@ namespace client
LogPrint(eLogError, "Addressbook: Incomplete http response from ", dest_host, ", interrupted by timeout");
return false;
}
// assert: res_head_len > 0
/* assert: res_head_len > 0 */
response.erase(0, res_head_len);
if (res.code == 304)
{
@@ -927,7 +936,7 @@ namespace client
LogPrint(eLogError, "Addressbook: Response size mismatch, expected: ", len, ", got: ", response.length(), "bytes");
return false;
}
// assert: res.code == 200
/* assert: res.code == 200 */
auto it = res.headers.find("ETag");
if (it != res.headers.end()) m_Etag = it->second;
it = res.headers.find("Last-Modified");

View File

@@ -90,8 +90,6 @@ namespace client
void InsertAddress (const std::string& address, const std::string& jump); // for jump links
void InsertFullAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
bool RecordExists (const std::string& address, const std::string& jump);
bool LoadHostsFromStream (std::istream& f, bool is_update);
void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified);
//This method returns the ".b32.i2p" address

View File

@@ -156,7 +156,7 @@ namespace client
{
if (stream)
{
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, m_Endpoint, m_IsQuiet);
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), m_Endpoint, m_IsQuiet);
AddHandler (conn);
conn->Connect ();
}
@@ -168,7 +168,7 @@ namespace client
m_LocalDestination (localDestination),
m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr),
m_Nickname(nickname), m_InHost(inhost), m_OutHost(outhost),
m_InPort(inport), m_OutPort(outport), m_Quiet(quiet), m_IsRunning(false)
m_InPort(inport), m_OutPort(outport), m_Quiet(quiet)
{
}
@@ -183,7 +183,6 @@ namespace client
{
if (m_OutboundTunnel) m_OutboundTunnel->Start ();
if (m_InboundTunnel) m_InboundTunnel->Start ();
m_IsRunning = true;
}
void BOBDestination::Stop ()
@@ -194,7 +193,6 @@ namespace client
void BOBDestination::StopTunnels ()
{
m_IsRunning = false;
if (m_OutboundTunnel)
{
m_OutboundTunnel->Stop ();
@@ -363,7 +361,7 @@ namespace client
const auto issetStr = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost
const auto issetNum = [&issetStr](const int p) { return issetStr(p == 0 ? "" : std::to_string(p)); }; // for inport, outport
const auto destExists = [](const BOBDestination * const dest) { return dest != nullptr; };
const auto destReady = [](const BOBDestination * const dest) { return dest->IsRunning(); };
const auto destReady = [](const BOBDestination * const dest) { return dest->GetLocalDestination()->IsReady(); };
const auto bool_str = [](const bool v) { return v ? "true" : "false"; }; // bool -> str
// tunnel info
@@ -481,43 +479,26 @@ namespace client
void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: setnick ", operand);
if(*operand)
{
auto dest = m_Owner.FindDestination (operand);
if (!dest)
{
m_Nickname = operand;
std::string msg ("Nickname set to ");
msg += m_Nickname;
SendReplyOK (msg.c_str ());
}
else
SendReplyError ("tunnel is active");
}
else
SendReplyError ("no nickname has been set");
m_Nickname = operand;
std::string msg ("Nickname set to ");
msg += m_Nickname;
SendReplyOK (msg.c_str ());
}
void BOBCommandSession::GetNickCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: getnick ", operand);
if(*operand)
m_CurrentDestination = m_Owner.FindDestination (operand);
if (m_CurrentDestination)
{
m_CurrentDestination = m_Owner.FindDestination (operand);
if (m_CurrentDestination)
{
m_Keys = m_CurrentDestination->GetKeys ();
m_IsActive = m_CurrentDestination->IsRunning ();
m_Nickname = operand;
}
if (m_Nickname == operand)
{
std::string msg ("Nickname set to ");
msg += m_Nickname;
SendReplyOK (msg.c_str ());
}
else
SendReplyError ("no nickname has been set");
m_Keys = m_CurrentDestination->GetKeys ();
m_Nickname = operand;
}
if (m_Nickname == operand)
{
std::string msg ("Nickname set to ");
msg += m_Nickname;
SendReplyOK (msg.c_str ());
}
else
SendReplyError ("no nickname has been set");
@@ -547,14 +528,14 @@ namespace client
}
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType, true);
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType);
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
}
void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: setkeys ", operand);
if (*operand && m_Keys.FromBase64 (operand))
if (m_Keys.FromBase64 (operand))
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
else
SendReplyError ("invalid keys");
@@ -581,55 +562,35 @@ namespace client
void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: outhost ", operand);
if (*operand)
{
m_OutHost = operand;
SendReplyOK ("outhost set");
}
else
SendReplyError ("empty outhost");
m_OutHost = operand;
SendReplyOK ("outhost set");
}
void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: outport ", operand);
if (*operand)
{
m_OutPort = std::stoi(operand);
if (m_OutPort >= 0)
SendReplyOK ("outbound port set");
else
SendReplyError ("port out of range");
}
m_OutPort = std::stoi(operand);
if (m_OutPort >= 0)
SendReplyOK ("outbound port set");
else
SendReplyError ("empty outport");
SendReplyError ("port out of range");
}
void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: inhost ", operand);
if (*operand)
{
m_InHost = operand;
SendReplyOK ("inhost set");
}
else
SendReplyError ("empty inhost");
m_InHost = operand;
SendReplyOK ("inhost set");
}
void BOBCommandSession::InportCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: inport ", operand);
if (*operand)
{
m_InPort = std::stoi(operand);
if (m_InPort >= 0)
SendReplyOK ("inbound port set");
else
SendReplyError ("port out of range");
}
m_InPort = std::stoi(operand);
if (m_InPort >= 0)
SendReplyOK ("inbound port set");
else
SendReplyError ("empty inport");
SendReplyError ("port out of range");
}
void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len)
@@ -652,62 +613,52 @@ namespace client
void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: lookup ", operand);
if (*operand)
auto addr = context.GetAddressBook ().GetAddress (operand);
if (!addr)
{
auto addr = context.GetAddressBook ().GetAddress (operand);
if (!addr)
SendReplyError ("Address Not found");
return;
}
auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination ();
if (addr->IsIdentHash ())
{
// we might have leaseset already
auto leaseSet = localDestination->FindLeaseSet (addr->identHash);
if (leaseSet)
{
SendReplyError ("Address Not found");
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
return;
}
auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination ();
if (addr->IsIdentHash ())
{
// we might have leaseset already
auto leaseSet = localDestination->FindLeaseSet (addr->identHash);
if (leaseSet)
{
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
return;
}
}
// trying to request
auto s = shared_from_this ();
auto requstCallback = [s](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
else
s->SendReplyError ("LeaseSet Not found");
};
if (addr->IsIdentHash ())
localDestination->RequestDestination (addr->identHash, requstCallback);
else
localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback);
}
// trying to request
auto s = shared_from_this ();
auto requstCallback = [s](std::shared_ptr<i2p::data::LeaseSet> ls)
{
if (ls)
s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
else
s->SendReplyError ("LeaseSet Not found");
};
if (addr->IsIdentHash ())
localDestination->RequestDestination (addr->identHash, requstCallback);
else
SendReplyError ("empty lookup address");
localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback);
}
void BOBCommandSession::LookupLocalCommandHandler (const char * operand, size_t len)
{
LogPrint (eLogDebug, "BOB: lookup local ", operand);
if (*operand)
auto addr = context.GetAddressBook ().GetAddress (operand);
if (!addr)
{
auto addr = context.GetAddressBook ().GetAddress (operand);
if (!addr)
{
SendReplyError ("Address Not found");
return;
}
auto ls = i2p::data::netdb.FindLeaseSet (addr->identHash);
if (ls)
SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
else
SendReplyError ("Local LeaseSet Not found");
SendReplyError ("Address Not found");
return;
}
auto ls = i2p::data::netdb.FindLeaseSet (addr->identHash);
if (ls)
SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
else
SendReplyError ("empty lookup address");
SendReplyError ("Local LeaseSet Not found");
}
void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)
@@ -767,11 +718,11 @@ namespace client
std::string statusLine;
// always prefer destination
auto dest = m_Owner.FindDestination(name);
if(dest)
auto ptr = m_Owner.FindDestination(name);
if(ptr != nullptr)
{
// tunnel destination exists
BuildStatusLine(false, dest, statusLine);
BuildStatusLine(false, ptr, statusLine);
SendReplyOK(statusLine.c_str());
}
else
@@ -791,7 +742,7 @@ namespace client
void BOBCommandSession::HelpCommandHandler (const char * operand, size_t len)
{
auto helpStrings = m_Owner.GetHelpStrings();
if(!*operand)
if(len == 0)
{
std::stringstream ss;
ss << "COMMANDS:";

View File

@@ -163,7 +163,6 @@ namespace client
int GetInPort() const { return m_InPort; }
int GetOutPort() const { return m_OutPort; }
bool GetQuiet() const { return m_Quiet; }
bool IsRunning() const { return m_IsRunning; }
const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); };
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDestination; };
@@ -177,7 +176,6 @@ namespace client
std::string m_InHost, m_OutHost;
int m_InPort, m_OutPort;
bool m_Quiet;
bool m_IsRunning;
};
class BOBCommandChannel;

View File

@@ -261,7 +261,7 @@ namespace client
static const std::string transient("transient");
if (!filename.compare (0, transient.length (), transient)) // starts with transient
{
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
return true;
}
@@ -288,7 +288,7 @@ namespace client
else
{
LogPrint (eLogError, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType);
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
size_t len = keys.GetFullLen ();
uint8_t * buf = new uint8_t[len];
@@ -328,7 +328,7 @@ namespace client
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType,
const std::map<std::string, std::string> * params)
{
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
auto localDestination = std::make_shared<RunnableClientDestination> (keys, isPublic, params);
AddLocalDestination (localDestination);
return localDestination;
@@ -339,7 +339,7 @@ namespace client
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType,
const std::map<std::string, std::string> * params)
{
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
auto localDestination = std::make_shared<ClientDestination> (service, keys, isPublic, params);
AddLocalDestination (localDestination);
return localDestination;
@@ -726,7 +726,6 @@ namespace client
std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, "");
bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true);
bool ssl = section.second.get(I2P_SERVER_TUNNEL_SSL, false);
// I2CP
std::map<std::string, std::string> options;
@@ -800,13 +799,11 @@ namespace client
if (!address.empty ())
serverTunnel->SetLocalAddress (address);
if (!isUniqueLocal)
if(!isUniqueLocal)
{
LogPrint(eLogInfo, "Clients: Disabling loopback address mapping");
serverTunnel->SetUniqueLocal(isUniqueLocal);
}
if (ssl)
serverTunnel->SetSSL (true);
if (accessList.length () > 0)
{
std::set<i2p::data::IdentHash> idents;
@@ -868,7 +865,7 @@ namespace client
std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL);
bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper);
if (httpAddresshelper)
i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book
i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
LogPrint(eLogInfo, "Clients: Starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
if (httpProxyKeys.length () > 0)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -18,7 +18,6 @@
#include "HTTPProxy.h"
#include "SOCKS.h"
#include "I2PTunnel.h"
#include "UDPTunnel.h"
#include "SAM.h"
#include "BOB.h"
#include "I2CP.h"
@@ -62,7 +61,7 @@ namespace client
const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword";
const char I2P_SERVER_TUNNEL_ADDRESS[] = "address";
const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal";
const char I2P_SERVER_TUNNEL_SSL[] = "ssl";
class ClientContext
{

View File

@@ -36,14 +36,12 @@ namespace proxy {
"reg.i2p",
"stats.i2p",
"identiguy.i2p",
"notbob.i2p"
};
static const std::map<std::string, std::string> jumpservices = {
{ "reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/" },
{ "identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname=" },
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" },
{ "notbob.i2p", "http://nytzrhrjjfsutowojvxi7hphesskpqqr65wpistz6wa7cpajhp7a.b32.i2p/cgi-bin/jump.cgi?q=" }
};
static const char *pageHead =
@@ -82,9 +80,8 @@ namespace proxy {
/* error helpers */
void GenericProxyError(const std::string& title, const std::string& description);
void GenericProxyInfo(const std::string& title, const std::string& description);
void HostNotFound(std::string& host);
void SendProxyError(std::string& content);
void SendRedirect(std::string& address);
void HostNotFound(std::string & host);
void SendProxyError(std::string & content);
void ForwardToUpstreamProxy();
void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec);
@@ -177,7 +174,7 @@ namespace proxy {
SendProxyError(content);
}
void HTTPReqHandler::HostNotFound(std::string& host) {
void HTTPReqHandler::HostNotFound(std::string & host) {
std::stringstream ss;
ss << "<h1>" << tr("Proxy error: Host not found") << "</h1>\r\n"
<< "<p>" << tr("Remote host not found in router's addressbook") << "</p>\r\n"
@@ -194,7 +191,7 @@ namespace proxy {
SendProxyError(content);
}
void HTTPReqHandler::SendProxyError(std::string& content)
void HTTPReqHandler::SendProxyError(std::string & content)
{
i2p::http::HTTPRes res;
res.code = 500;
@@ -210,17 +207,6 @@ namespace proxy {
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
}
void HTTPReqHandler::SendRedirect(std::string& address)
{
i2p::http::HTTPRes res;
res.code = 302;
res.add_header("Location", address);
res.add_header("Connection", "close");
std::string response = res.to_string();
boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(),
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
}
bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64, bool & confirm)
{
confirm = false;
@@ -251,7 +237,6 @@ namespace proxy {
req.RemoveHeader("Via");
req.RemoveHeader("From");
req.RemoveHeader("Forwarded");
req.RemoveHeader("DNT"); // Useless DoNotTrack flag
req.RemoveHeader("Accept", "Accept-Encoding"); // Accept*, but Accept-Encoding
/* drop proxy-disclosing headers */
req.RemoveHeader("X-Forwarded");
@@ -312,14 +297,7 @@ namespace proxy {
GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported"));
return true;
}
if (i2p::client::context.GetAddressBook ().RecordExists (m_RequestURL.host, jump))
{
std::string full_url = m_RequestURL.to_string();
SendRedirect(full_url);
return true;
}
else if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm)
if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm)
{
i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump);
LogPrint (eLogInfo, "HTTPProxy: Added address from addresshelper for ", m_RequestURL.host);
@@ -335,8 +313,7 @@ namespace proxy {
std::string full_url = m_RequestURL.to_string();
std::stringstream ss;
ss << tr("Host") << " " << m_RequestURL.host << " <font color=red>" << tr("already in router's addressbook") << "</font>. ";
ss << tr(/* tr: The "record" means addressbook's record. That message appears when domain was already added to addressbook, but helper link is opened for it. */ "Click here to update record:" );
ss << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper=");
ss << tr("Click here to update record:") << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper=");
ss << jump << "&update=true\">" << tr("Continue") << "</a>.";
GenericProxyInfo(tr("Addresshelper found"), ss.str());
return true; /* request processed */
@@ -445,8 +422,8 @@ namespace proxy {
void HTTPReqHandler::ForwardToUpstreamProxy()
{
LogPrint(eLogDebug, "HTTPProxy: Forwarded to upstream");
// build http request
/* build http request */
m_ClientRequestURL = m_RequestURL;
LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host);
m_ClientRequestURL.schema = "";
@@ -454,17 +431,17 @@ namespace proxy {
std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for?
m_ClientRequest.uri = m_ClientRequestURL.to_string();
/* update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections */
// update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections
if(m_ClientRequest.method != "CONNECT")
m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0");
m_ClientRequest.write(m_ClientRequestBuffer);
m_ClientRequestBuffer << m_recv_buf.substr(m_req_len);
/* assume http if empty schema */
// assume http if empty schema
if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http")
{
/* handle upstream http proxy */
// handle upstream http proxy
if (!m_ProxyURL.port) m_ProxyURL.port = 80;
if (m_ProxyURL.is_i2p())
{
@@ -472,9 +449,9 @@ namespace proxy {
auto auth = i2p::http::CreateBasicAuthorizationString (m_ProxyURL.user, m_ProxyURL.pass);
if (!auth.empty ())
{
/* remove existing authorization if any */
// remove existing authorization if any
m_ClientRequest.RemoveHeader("Proxy-");
/* add own http proxy authorization */
// add own http proxy authorization
m_ClientRequest.AddHeader("Proxy-Authorization", auth);
}
m_send_buf = m_ClientRequest.to_string();
@@ -493,7 +470,7 @@ namespace proxy {
}
else if (m_ProxyURL.schema == "socks")
{
/* handle upstream socks proxy */
// handle upstream socks proxy
if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
@@ -502,7 +479,7 @@ namespace proxy {
}
else
{
/* unknown type, complain */
// unknown type, complain
GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string());
}
}

View File

@@ -21,7 +21,7 @@ namespace client
{
/** set standard socket options */
static void I2PTunnelSetSocketOptions (std::shared_ptr<boost::asio::ip::tcp::socket> socket)
static void I2PTunnelSetSocketOptions(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
if (socket && socket->is_open())
{
@@ -46,13 +46,10 @@ namespace client
}
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, bool quiet,
std::shared_ptr<boost::asio::ssl::context> sslCtx):
I2PServiceHandler(owner), m_Stream (stream), m_RemoteEndpoint (target), m_IsQuiet (quiet)
std::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
m_RemoteEndpoint (target), m_IsQuiet (quiet)
{
m_Socket = std::make_shared<boost::asio::ip::tcp::socket> (owner->GetService ());
if (sslCtx)
m_SSL = std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > (*m_Socket, *sslCtx);
}
I2PTunnelConnection::~I2PTunnelConnection ()
@@ -72,7 +69,7 @@ namespace client
Receive ();
}
boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr)
static boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr)
{
boost::asio::ip::address_v4::bytes_type bytes;
const uint8_t * ident = addr;
@@ -83,26 +80,24 @@ namespace client
}
#ifdef __linux__
static void MapToLoopback(std::shared_ptr<boost::asio::ip::tcp::socket> sock, const i2p::data::IdentHash & addr)
static void MapToLoopback(const std::shared_ptr<boost::asio::ip::tcp::socket> & sock, const i2p::data::IdentHash & addr)
{
if (sock)
{
// bind to 127.x.x.x address
// where x.x.x are first three bytes from ident
auto ourIP = GetLoopbackAddressFor(addr);
boost::system::error_code ec;
sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec);
if (ec)
LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ());
}
// bind to 127.x.x.x address
// where x.x.x are first three bytes from ident
auto ourIP = GetLoopbackAddressFor(addr);
boost::system::error_code ec;
sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec);
if (ec)
LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ());
}
#endif
void I2PTunnelConnection::Connect (bool isUniqueLocal)
{
I2PTunnelSetSocketOptions(m_Socket);
if (m_Socket)
{
I2PTunnelSetSocketOptions (m_Socket);
#ifdef __linux__
if (isUniqueLocal && m_RemoteEndpoint.address ().is_v4 () &&
m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127)
@@ -136,7 +131,6 @@ namespace client
void I2PTunnelConnection::Terminate ()
{
if (Kill()) return;
if (m_SSL) m_SSL = nullptr;
if (m_Stream)
{
m_Stream->Close ();
@@ -151,17 +145,12 @@ namespace client
void I2PTunnelConnection::Receive ()
{
if (m_SSL)
m_SSL->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
else
m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (),
std::placeholders::_1, std::placeholders::_2));
}
void I2PTunnelConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{
if (ecode)
{
@@ -250,12 +239,8 @@ namespace client
void I2PTunnelConnection::Write (const uint8_t * buf, size_t len)
{
if (m_SSL)
boost::asio::async_write (*m_SSL, boost::asio::buffer (buf, len), boost::asio::transfer_all (),
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
else
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (),
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (),
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
}
void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
@@ -268,43 +253,20 @@ namespace client
else
{
LogPrint (eLogDebug, "I2PTunnel: Connected");
if (m_SSL)
m_SSL->async_handshake (boost::asio::ssl::stream_base::client,
std::bind (&I2PTunnelConnection::HandleHandshake, shared_from_this (), std::placeholders::_1));
if (m_IsQuiet)
StreamReceive ();
else
Established ();
}
}
void I2PTunnelConnection::HandleHandshake (const boost::system::error_code& ecode)
{
if (ecode)
{
LogPrint (eLogError, "I2PTunnel: Handshake error: ", ecode.message ());
Terminate ();
}
else
{
LogPrint (eLogDebug, "I2PTunnel: SSL connected");
Established ();
}
}
void I2PTunnelConnection::Established ()
{
if (m_IsQuiet)
StreamReceive ();
else
{
// send destination first like received from I2P
std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 ();
dest += "\n";
if(sizeof(m_StreamBuffer) >= dest.size()) {
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
{
// send destination first like received from I2P
std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 ();
dest += "\n";
if(sizeof(m_StreamBuffer) >= dest.size()) {
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
}
HandleStreamReceive (boost::system::error_code (), dest.size ());
}
HandleStreamReceive (boost::system::error_code (), dest.size ());
Receive ();
}
Receive ();
}
void I2PClientTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
@@ -359,24 +321,15 @@ namespace client
m_HeaderSent = true;
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
}
else if (m_OutHeader.tellp () < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE)
StreamReceive (); // read more header
else
{
LogPrint (eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE);
Terminate ();
}
}
}
I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, const std::string& host,
std::shared_ptr<boost::asio::ssl::context> sslCtx):
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_Host (host),
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& host):
I2PTunnelConnection (owner, stream, socket, target), m_Host (host),
m_HeaderSent (false), m_ResponseHeaderSent (false), m_From (stream->GetRemoteIdentity ())
{
if (sslCtx)
SSL_set_tlsext_host_name(GetSSL ()->native_handle(), host.c_str ());
}
void I2PServerTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
@@ -400,7 +353,7 @@ namespace client
// strip up some headers
static const std::vector<std::string> excluded // list of excluded headers
{
"Keep-Alive:", "X-I2P"
"Keep-Alive:", "X-I2P"
};
bool matched = false;
for (const auto& it: excluded)
@@ -422,8 +375,8 @@ namespace client
else
m_OutHeader << "Connection: close\r\n";
connection = true;
}
else // forward as is
}
else // forward as is
m_OutHeader << line << "\n";
}
}
@@ -451,13 +404,6 @@ namespace client
m_HeaderSent = true;
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
}
else if (m_OutHeader.tellp () < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE)
StreamReceive (); // read more header
else
{
LogPrint (eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE);
Terminate ();
}
}
}
@@ -514,9 +460,9 @@ namespace client
}
I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass,
std::shared_ptr<boost::asio::ssl::context> sslCtx):
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_From (stream->GetRemoteIdentity ()),
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass):
I2PTunnelConnection (owner, stream, socket, target), m_From (stream->GetRemoteIdentity ()),
m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass)
{
}
@@ -527,8 +473,7 @@ namespace client
if (m_NeedsWebIrc)
{
m_NeedsWebIrc = false;
m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ())
<< " " << GetSocket ()->local_endpoint ().address () << std::endl;
m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " " << GetSocket ()->local_endpoint ().address () << std::endl;
}
m_InPacket.clear ();
@@ -715,12 +660,6 @@ namespace client
void I2PServerTunnel::Stop ()
{
if (m_PortDestination)
m_PortDestination->ResetAcceptor ();
auto localDestination = GetLocalDestination ();
if (localDestination)
localDestination->StopAcceptingStreams ();
ClearHandlers ();
}
@@ -794,17 +733,6 @@ namespace client
LogPrint (eLogError, "I2PTunnel: Can't set local address ", localAddress);
}
void I2PServerTunnel::SetSSL (bool ssl)
{
if (ssl)
{
m_SSLCtx = std::make_shared<boost::asio::ssl::context> (boost::asio::ssl::context::sslv23);
m_SSLCtx->set_verify_mode(boost::asio::ssl::context::verify_none);
}
else
m_SSLCtx = nullptr;
}
void I2PServerTunnel::Accept ()
{
if (m_PortDestination)
@@ -845,7 +773,7 @@ namespace client
std::shared_ptr<I2PTunnelConnection> I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
return std::make_shared<I2PTunnelConnection> (this, stream, GetEndpoint (), true, m_SSLCtx);
return std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint ());
}
@@ -859,7 +787,8 @@ namespace client
std::shared_ptr<I2PTunnelConnection> I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream, GetEndpoint (), m_Host, GetSSLCtx ());
return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream,
std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), m_Host);
}
I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address,
@@ -872,7 +801,371 @@ namespace client
std::shared_ptr<I2PTunnelConnection> I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{
return std::make_shared<I2PTunnelConnectionIRC> (this, stream, GetEndpoint (), m_WebircPass, GetSSLCtx ());
return std::make_shared<I2PTunnelConnectionIRC> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), this->m_WebircPass);
}
void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if (!m_LastSession || m_LastSession->Identity.GetLL()[0] != from.GetIdentHash ().GetLL()[0] || fromPort != m_LastSession->RemotePort)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
m_LastSession = ObtainUDPSession(from, toPort, fromPort);
}
m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint);
m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch();
}
void I2PUDPServerTunnel::HandleRecvFromI2PRaw (uint16_t, uint16_t, const uint8_t * buf, size_t len)
{
if (m_LastSession)
{
m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint);
m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch();
}
}
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
auto itr = m_Sessions.begin();
while(itr != m_Sessions.end()) {
if(now - (*itr)->LastActivity >= delta )
itr = m_Sessions.erase(itr);
else
++itr;
}
}
void I2PUDPClientTunnel::ExpireStale(const uint64_t delta)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
std::vector<uint16_t> removePorts;
for (const auto & s : m_Sessions) {
if (now - s.second->second >= delta)
removePorts.push_back(s.first);
}
for(auto port : removePorts) {
m_Sessions.erase(port);
}
}
UDPSessionPtr I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort)
{
auto ih = from.GetIdentHash();
for (auto & s : m_Sessions )
{
if (s->Identity.GetLL()[0] == ih.GetLL()[0] && remotePort == s->RemotePort)
{
/** found existing session */
LogPrint(eLogDebug, "UDPServer: Found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32());
return s;
}
}
boost::asio::ip::address addr;
/** create new udp session */
if(m_IsUniqueLocal && m_LocalAddress.is_loopback())
{
auto ident = from.GetIdentHash();
addr = GetLoopbackAddressFor(ident);
}
else
addr = m_LocalAddress;
boost::asio::ip::udp::endpoint ep(addr, 0);
m_Sessions.push_back(std::make_shared<UDPSession>(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort));
auto & back = m_Sessions.back();
return back;
}
UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash * to,
uint16_t ourPort, uint16_t theirPort) :
m_Destination(localDestination->GetDatagramDestination()),
IPSocket(localDestination->GetService(), localEndpoint),
SendEndpoint(endpoint),
LastActivity(i2p::util::GetMillisecondsSinceEpoch()),
LocalPort(ourPort),
RemotePort(theirPort)
{
IPSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU ));
memcpy(Identity, to->data(), 32);
Receive();
}
void UDPSession::Receive()
{
LogPrint(eLogDebug, "UDPSession: Receive");
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));
}
void UDPSession::HandleReceived(const boost::system::error_code & ecode, std::size_t len)
{
if(!ecode)
{
LogPrint(eLogDebug, "UDPSession: Forward ", len, "B from ", FromEndpoint);
auto ts = i2p::util::GetMillisecondsSinceEpoch();
auto session = m_Destination->GetSession (Identity);
if (ts > LastActivity + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
m_Destination->SendDatagram(session, m_Buffer, len, LocalPort, RemotePort);
else
m_Destination->SendRawDatagram(session, m_Buffer, len, LocalPort, RemotePort);
size_t numPackets = 0;
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
{
boost::system::error_code ec;
size_t moreBytes = IPSocket.available(ec);
if (ec || !moreBytes) break;
len = IPSocket.receive_from (boost::asio::buffer (m_Buffer, I2P_UDP_MAX_MTU), FromEndpoint, 0, ec);
m_Destination->SendRawDatagram (session, m_Buffer, len, LocalPort, RemotePort);
numPackets++;
}
if (numPackets > 0)
LogPrint(eLogDebug, "UDPSession: Forward more ", numPackets, "packets B from ", FromEndpoint);
m_Destination->FlushSendQueue (session);
LastActivity = ts;
Receive();
}
else
LogPrint(eLogError, "UDPSession: ", ecode.message());
}
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) :
m_IsUniqueLocal (true), m_Name (name), m_LocalAddress (localAddress),
m_RemoteEndpoint (forwardTo), m_LocalDest (localDestination), m_Gzip (gzip)
{
}
I2PUDPServerTunnel::~I2PUDPServerTunnel ()
{
Stop ();
}
void I2PUDPServerTunnel::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));
}
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::lock_guard<std::mutex> lock (m_SessionsMutex);
for (UDPSessionPtr s: m_Sessions)
{
if (!s->m_Destination) continue;
auto info = s->m_Destination->GetInfoForRemote (s->Identity);
if (!info) continue;
auto sinfo = std::make_shared<DatagramSessionInfo> ();
sinfo->Name = m_Name;
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash> (m_LocalDest->GetIdentHash ().data ());
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash> (s->Identity.data ());
sinfo->CurrentIBGW = info->IBGW;
sinfo->CurrentOBEP = info->OBEP;
sessions.push_back (sinfo);
}
return sessions;
}
I2PUDPClientTunnel::I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest,
boost::asio::ip::udp::endpoint localEndpoint,
std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort, bool gzip) :
m_Name (name), m_RemoteDest (remoteDest), m_LocalDest (localDestination), m_LocalEndpoint (localEndpoint),
m_RemoteIdent (nullptr), m_ResolveThread (nullptr), m_LocalSocket (nullptr), RemotePort (remotePort),
m_LastPort (0), m_cancel_resolve (false), m_Gzip (gzip)
{
}
I2PUDPClientTunnel::~I2PUDPClientTunnel ()
{
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::_3, std::placeholders::_4,
std::placeholders::_5));
dgram->SetRawReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
m_LocalDest->Start ();
if (m_ResolveThread == nullptr)
m_ResolveThread = new std::thread (std::bind (&I2PUDPClientTunnel::TryResolving, this));
RecvFromLocal ();
}
void I2PUDPClientTunnel::Stop ()
{
auto dgram = m_LocalDest->GetDatagramDestination ();
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::RecvFromLocal ()
{
m_LocalSocket->async_receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU),
m_RecvEndpoint, std::bind (&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2));
}
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;
}
if (ec) {
LogPrint (eLogError, "UDP Client: Reading from socket error: ", ec.message (), ". Restarting listener...");
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
}
auto remotePort = m_RecvEndpoint.port ();
if (!m_LastPort || m_LastPort != remotePort)
{
auto itr = m_Sessions.find (remotePort);
if (itr != m_Sessions.end ())
m_LastSession = itr->second;
else
{
m_LastSession = std::make_shared<UDPConvo> (boost::asio::ip::udp::endpoint (m_RecvEndpoint), 0);
m_Sessions.emplace (remotePort, m_LastSession);
}
m_LastPort = remotePort;
}
// send off to remote i2p destination
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
LogPrint (eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteIdent->ToBase32 (), ":", RemotePort);
auto session = m_LocalDest->GetDatagramDestination ()->GetSession (*m_RemoteIdent);
if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
m_LocalDest->GetDatagramDestination ()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
else
m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
size_t numPackets = 0;
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
{
boost::system::error_code ec;
size_t moreBytes = m_LocalSocket->available (ec);
if (ec || !moreBytes) break;
transferred = m_LocalSocket->receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), m_RecvEndpoint, 0, ec);
remotePort = m_RecvEndpoint.port ();
// TODO: check remotePort
m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
numPackets++;
}
if (numPackets)
LogPrint (eLogDebug, "UDP Client: Sent ", numPackets, " more packets to ", m_RemoteIdent->ToBase32 ());
m_LocalDest->GetDatagramDestination ()->FlushSendQueue (session);
// mark convo as active
if (m_LastSession)
m_LastSession->second = ts;
RecvFromLocal ();
}
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions ()
{
// TODO: implement
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
return infos;
}
void I2PUDPClientTunnel::TryResolving ()
{
i2p::util::SetThreadName ("UDP Resolver");
LogPrint (eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
std::shared_ptr<const Address> addr;
while (!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve)
{
LogPrint (eLogWarning, "UDP Tunnel: Failed to lookup ", m_RemoteDest);
std::this_thread::sleep_for (std::chrono::seconds (1));
}
if (m_cancel_resolve)
{
LogPrint(eLogError, "UDP Tunnel: Lookup of ", m_RemoteDest, " was cancelled");
return;
}
if (!addr || !addr->IsIdentHash ())
{
LogPrint (eLogError, "UDP Tunnel: ", m_RemoteDest, " not found");
return;
}
m_RemoteIdent = new i2p::data::IdentHash;
*m_RemoteIdent = addr->identHash;
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)
{
if (m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
HandleRecvFromI2PRaw (fromPort, toPort, buf, len);
else
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)
{
auto itr = m_Sessions.find (toPort);
// found convo ?
if (itr != m_Sessions.end ())
{
// found convo
if (len > 0)
{
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);
// mark convo as active
itr->second->second = i2p::util::GetMillisecondsSinceEpoch ();
}
}
else
LogPrint (eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort);
}
}
}

View File

@@ -16,9 +16,9 @@
#include <memory>
#include <sstream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include "Identity.h"
#include "Destination.h"
#include "Datagram.h"
#include "Streaming.h"
#include "I2PService.h"
#include "AddressBook.h"
@@ -34,7 +34,6 @@ namespace client
const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64
const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64
const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address
const int I2P_TUNNEL_HTTP_MAX_HEADER_SIZE = 8192;
class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this<I2PTunnelConnection>
{
@@ -44,9 +43,8 @@ namespace client
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port = 0); // to I2P
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, bool quiet = true,
std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr); // from I2P
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
~I2PTunnelConnection ();
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
void Connect (bool isUniqueLocal = true);
@@ -57,27 +55,21 @@ namespace client
void Terminate ();
void Receive ();
void StreamReceive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
virtual void Write (const uint8_t * buf, size_t len); // can be overloaded
void HandleWrite (const boost::system::error_code& ecode);
virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded
std::shared_ptr<boost::asio::ip::tcp::socket> GetSocket () const { return m_Socket; };
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > GetSSL () const { return m_SSL; };
private:
void HandleConnect (const boost::system::error_code& ecode);
void HandleHandshake (const boost::system::error_code& ecode);
void Established ();
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleWrite (const boost::system::error_code& ecode);
void StreamReceive ();
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleConnect (const boost::system::error_code& ecode);
std::shared_ptr<const boost::asio::ip::tcp::socket> GetSocket () const { return m_Socket; };
private:
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > m_SSL;
std::shared_ptr<i2p::stream::Stream> m_Stream;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsQuiet; // don't send destination
@@ -107,8 +99,8 @@ namespace client
public:
I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, const std::string& host,
std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr);
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& host);
protected:
@@ -128,8 +120,8 @@ namespace client
public:
I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass,
std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr);
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass);
protected:
@@ -179,6 +171,162 @@ namespace client
std::unique_ptr<boost::asio::deadline_timer> m_KeepAliveTimer;
};
/** 2 minute timeout for udp sessions */
const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2;
const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds
/** max size for i2p udp */
const size_t I2P_UDP_MAX_MTU = 64*1024;
struct UDPSession
{
i2p::datagram::DatagramDestination * m_Destination;
boost::asio::ip::udp::socket IPSocket;
i2p::data::IdentHash Identity;
boost::asio::ip::udp::endpoint FromEndpoint;
boost::asio::ip::udp::endpoint SendEndpoint;
uint64_t LastActivity;
uint16_t LocalPort;
uint16_t RemotePort;
uint8_t m_Buffer[I2P_UDP_MAX_MTU];
UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash * ident,
uint16_t ourPort, uint16_t theirPort);
void HandleReceived(const boost::system::error_code & ecode, std::size_t len);
void Receive();
};
/** read only info about a datagram session */
struct DatagramSessionInfo
{
/** the name of this forward */
std::string Name;
/** ident hash of local destination */
std::shared_ptr<const i2p::data::IdentHash> LocalIdent;
/** ident hash of remote destination */
std::shared_ptr<const i2p::data::IdentHash> RemoteIdent;
/** ident hash of IBGW in use currently in this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> CurrentIBGW;
/** ident hash of OBEP in use for this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> CurrentOBEP;
/** i2p router's udp endpoint */
boost::asio::ip::udp::endpoint LocalEndpoint;
/** client's udp endpoint */
boost::asio::ip::udp::endpoint RemoteEndpoint;
/** how long has this converstation been idle in ms */
uint64_t idle;
};
typedef std::shared_ptr<UDPSession> UDPSessionPtr;
/** server side udp tunnel, many i2p inbound to 1 ip outbound */
class I2PUDPServerTunnel
{
public:
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);
~I2PUDPServerTunnel ();
/** expire stale udp conversations */
void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
void Start ();
void Stop ();
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; }
void SetUniqueLocal (bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; }
private:
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);
UDPSessionPtr ObtainUDPSession (const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
private:
bool m_IsUniqueLocal;
const std::string m_Name;
boost::asio::ip::address m_LocalAddress;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
std::mutex m_SessionsMutex;
std::vector<UDPSessionPtr> m_Sessions;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
UDPSessionPtr m_LastSession;
bool m_Gzip;
public:
bool isUpdated; // transient, used during reload only
};
class I2PUDPClientTunnel
{
public:
I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest,
boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort, bool gzip);
~I2PUDPClientTunnel ();
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; }
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:
typedef std::pair<boost::asio::ip::udp::endpoint, uint64_t> UDPConvo;
void RecvFromLocal ();
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 HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void TryResolving ();
private:
const std::string m_Name;
std::mutex m_SessionsMutex;
std::unordered_map<uint16_t, std::shared_ptr<UDPConvo> > m_Sessions; // maps i2p port -> local udp convo
const std::string m_RemoteDest;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
i2p::data::IdentHash * m_RemoteIdent;
std::thread * m_ResolveThread;
std::unique_ptr<boost::asio::ip::udp::socket> m_LocalSocket;
boost::asio::ip::udp::endpoint m_RecvEndpoint;
uint8_t m_RecvBuff[I2P_UDP_MAX_MTU];
uint16_t RemotePort, m_LastPort;
bool m_cancel_resolve;
bool m_Gzip;
std::shared_ptr<UDPConvo> m_LastSession;
public:
bool isUpdated; // transient, used during reload only
};
class I2PServerTunnel: public I2PService
{
public:
@@ -194,9 +342,6 @@ namespace client
void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; }
bool IsUniqueLocal () const { return m_IsUniqueLocal; }
void SetSSL (bool ssl);
std::shared_ptr<boost::asio::ssl::context> GetSSLCtx () const { return m_SSLCtx; };
void SetLocalAddress (const std::string& localAddress);
const std::string& GetAddress() const { return m_Address; }
@@ -225,7 +370,6 @@ namespace client
std::set<i2p::data::IdentHash> m_AccessList;
bool m_IsAccessList;
std::unique_ptr<boost::asio::ip::address> m_LocalAddress;
std::shared_ptr<boost::asio::ssl::context> m_SSLCtx;
};
class I2PServerTunnelHTTP: public I2PServerTunnel
@@ -261,8 +405,6 @@ namespace client
std::string m_WebircPass;
};
boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@@ -709,7 +709,7 @@ namespace client
LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ());
}
}
auto keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType, true);
auto keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType);
#ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
@@ -1478,21 +1478,14 @@ namespace client
auto session = FindSession (sessionID);
if (session)
{
auto localDest = session->GetLocalDestination ();
auto datagramDest = localDest ? localDest->GetDatagramDestination () : nullptr;
if (datagramDest)
{
i2p::data::IdentityEx dest;
dest.FromBase64 (destination);
if (session->Type == eSAMSessionTypeDatagram)
datagramDest->SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
else if (session->Type == eSAMSessionTypeRaw)
datagramDest->SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
else
LogPrint (eLogError, "SAM: Unexpected session type ", (int)session->Type, "for session ", sessionID);
}
else
LogPrint (eLogError, "SAM: Datagram destination is not set for session ", sessionID);
i2p::data::IdentityEx dest;
dest.FromBase64 (destination);
if (session->Type == eSAMSessionTypeDatagram)
session->GetLocalDestination ()->GetDatagramDestination ()->
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
else // raw
session->GetLocalDestination ()->GetDatagramDestination ()->
SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
}
else
LogPrint (eLogError, "SAM: Session ", sessionID, " not found");

View File

@@ -1,377 +0,0 @@
/*
* 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 "Log.h"
#include "util.h"
#include "ClientContext.h"
#include "I2PTunnel.h" // for GetLoopbackAddressFor
#include "UDPTunnel.h"
namespace i2p
{
namespace client
{
void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if (!m_LastSession || m_LastSession->Identity.GetLL()[0] != from.GetIdentHash ().GetLL()[0] || fromPort != m_LastSession->RemotePort)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
m_LastSession = ObtainUDPSession(from, toPort, fromPort);
}
m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint);
m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch();
}
void I2PUDPServerTunnel::HandleRecvFromI2PRaw (uint16_t, uint16_t, const uint8_t * buf, size_t len)
{
if (m_LastSession)
{
m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint);
m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch();
}
}
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
auto itr = m_Sessions.begin();
while(itr != m_Sessions.end()) {
if(now - (*itr)->LastActivity >= delta )
itr = m_Sessions.erase(itr);
else
++itr;
}
}
void I2PUDPClientTunnel::ExpireStale(const uint64_t delta)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
std::vector<uint16_t> removePorts;
for (const auto & s : m_Sessions) {
if (now - s.second->second >= delta)
removePorts.push_back(s.first);
}
for(auto port : removePorts) {
m_Sessions.erase(port);
}
}
UDPSessionPtr I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort)
{
auto ih = from.GetIdentHash();
for (auto & s : m_Sessions )
{
if (s->Identity.GetLL()[0] == ih.GetLL()[0] && remotePort == s->RemotePort)
{
/** found existing session */
LogPrint(eLogDebug, "UDPServer: Found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32());
return s;
}
}
boost::asio::ip::address addr;
/** create new udp session */
if(m_IsUniqueLocal && m_LocalAddress.is_loopback())
{
auto ident = from.GetIdentHash();
addr = GetLoopbackAddressFor(ident);
}
else
addr = m_LocalAddress;
boost::asio::ip::udp::endpoint ep(addr, 0);
m_Sessions.push_back(std::make_shared<UDPSession>(ep, m_LocalDest, m_RemoteEndpoint, ih, localPort, remotePort));
auto & back = m_Sessions.back();
return back;
}
UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
const boost::asio::ip::udp::endpoint& endpoint, const i2p::data::IdentHash& to,
uint16_t ourPort, uint16_t theirPort) :
m_Destination(localDestination->GetDatagramDestination()),
IPSocket(localDestination->GetService(), localEndpoint),
Identity (to), SendEndpoint(endpoint),
LastActivity(i2p::util::GetMillisecondsSinceEpoch()),
LocalPort(ourPort),
RemotePort(theirPort)
{
IPSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU ));
Receive();
}
void UDPSession::Receive()
{
LogPrint(eLogDebug, "UDPSession: Receive");
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));
}
void UDPSession::HandleReceived(const boost::system::error_code & ecode, std::size_t len)
{
if(!ecode)
{
LogPrint(eLogDebug, "UDPSession: Forward ", len, "B from ", FromEndpoint);
auto ts = i2p::util::GetMillisecondsSinceEpoch();
auto session = m_Destination->GetSession (Identity);
if (ts > LastActivity + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
m_Destination->SendDatagram(session, m_Buffer, len, LocalPort, RemotePort);
else
m_Destination->SendRawDatagram(session, m_Buffer, len, LocalPort, RemotePort);
size_t numPackets = 0;
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
{
boost::system::error_code ec;
size_t moreBytes = IPSocket.available(ec);
if (ec || !moreBytes) break;
len = IPSocket.receive_from (boost::asio::buffer (m_Buffer, I2P_UDP_MAX_MTU), FromEndpoint, 0, ec);
m_Destination->SendRawDatagram (session, m_Buffer, len, LocalPort, RemotePort);
numPackets++;
}
if (numPackets > 0)
LogPrint(eLogDebug, "UDPSession: Forward more ", numPackets, "packets B from ", FromEndpoint);
m_Destination->FlushSendQueue (session);
LastActivity = ts;
Receive();
}
else
LogPrint(eLogError, "UDPSession: ", ecode.message());
}
I2PUDPServerTunnel::I2PUDPServerTunnel (const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
const boost::asio::ip::address& localAddress, const boost::asio::ip::udp::endpoint& forwardTo, uint16_t port, bool gzip) :
m_IsUniqueLocal (true), m_Name (name), m_LocalAddress (localAddress),
m_RemoteEndpoint (forwardTo), m_LocalDest (localDestination), m_Gzip (gzip)
{
}
I2PUDPServerTunnel::~I2PUDPServerTunnel ()
{
Stop ();
}
void I2PUDPServerTunnel::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));
}
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::lock_guard<std::mutex> lock (m_SessionsMutex);
for (UDPSessionPtr s: m_Sessions)
{
if (!s->m_Destination) continue;
auto info = s->m_Destination->GetInfoForRemote (s->Identity);
if (!info) continue;
auto sinfo = std::make_shared<DatagramSessionInfo> ();
sinfo->Name = m_Name;
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash> (m_LocalDest->GetIdentHash ().data ());
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash> (s->Identity.data ());
sinfo->CurrentIBGW = info->IBGW;
sinfo->CurrentOBEP = info->OBEP;
sessions.push_back (sinfo);
}
return sessions;
}
I2PUDPClientTunnel::I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest,
const boost::asio::ip::udp::endpoint& localEndpoint,
std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort, bool gzip) :
m_Name (name), m_RemoteDest (remoteDest), m_LocalDest (localDestination), m_LocalEndpoint (localEndpoint),
m_ResolveThread (nullptr), m_LocalSocket (nullptr), RemotePort (remotePort),
m_LastPort (0), m_cancel_resolve (false), m_Gzip (gzip)
{
}
I2PUDPClientTunnel::~I2PUDPClientTunnel ()
{
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::_3, std::placeholders::_4,
std::placeholders::_5));
dgram->SetRawReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
m_LocalDest->Start ();
if (m_ResolveThread == nullptr)
m_ResolveThread = new std::thread (std::bind (&I2PUDPClientTunnel::TryResolving, this));
RecvFromLocal ();
}
void I2PUDPClientTunnel::Stop ()
{
auto dgram = m_LocalDest->GetDatagramDestination ();
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;
}
m_RemoteAddr = nullptr;
}
void I2PUDPClientTunnel::RecvFromLocal ()
{
m_LocalSocket->async_receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU),
m_RecvEndpoint, std::bind (&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2));
}
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;
}
if (ec) {
LogPrint (eLogError, "UDP Client: Reading from socket error: ", ec.message (), ". Restarting listener...");
RecvFromLocal (); // Restart listener and continue work
return;
}
if (!m_RemoteAddr || !m_RemoteAddr->IsIdentHash ()) // TODO: handle B33
{
LogPrint (eLogWarning, "UDP Client: Remote endpoint not resolved yet");
RecvFromLocal ();
return; // drop, remote not resolved
}
auto remotePort = m_RecvEndpoint.port ();
if (!m_LastPort || m_LastPort != remotePort)
{
auto itr = m_Sessions.find (remotePort);
if (itr != m_Sessions.end ())
m_LastSession = itr->second;
else
{
m_LastSession = std::make_shared<UDPConvo> (boost::asio::ip::udp::endpoint (m_RecvEndpoint), 0);
m_Sessions.emplace (remotePort, m_LastSession);
}
m_LastPort = remotePort;
}
// send off to remote i2p destination
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
LogPrint (eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteAddr->identHash.ToBase32 (), ":", RemotePort);
auto session = m_LocalDest->GetDatagramDestination ()->GetSession (m_RemoteAddr->identHash);
if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
m_LocalDest->GetDatagramDestination ()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
else
m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
size_t numPackets = 0;
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
{
boost::system::error_code ec;
size_t moreBytes = m_LocalSocket->available (ec);
if (ec || !moreBytes) break;
transferred = m_LocalSocket->receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), m_RecvEndpoint, 0, ec);
remotePort = m_RecvEndpoint.port ();
// TODO: check remotePort
m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
numPackets++;
}
if (numPackets)
LogPrint (eLogDebug, "UDP Client: Sent ", numPackets, " more packets to ", m_RemoteAddr->identHash.ToBase32 ());
m_LocalDest->GetDatagramDestination ()->FlushSendQueue (session);
// mark convo as active
if (m_LastSession)
m_LastSession->second = ts;
RecvFromLocal ();
}
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions ()
{
// TODO: implement
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
return infos;
}
void I2PUDPClientTunnel::TryResolving ()
{
i2p::util::SetThreadName ("UDP Resolver");
LogPrint (eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
while (!(m_RemoteAddr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve)
{
LogPrint (eLogWarning, "UDP Tunnel: Failed to lookup ", m_RemoteDest);
std::this_thread::sleep_for (std::chrono::seconds (1));
}
if (m_cancel_resolve)
{
LogPrint(eLogError, "UDP Tunnel: Lookup of ", m_RemoteDest, " was cancelled");
return;
}
if (!m_RemoteAddr)
{
LogPrint (eLogError, "UDP Tunnel: ", m_RemoteDest, " not found");
return;
}
LogPrint(eLogInfo, "UDP Tunnel: Resolved ", m_RemoteDest, " to ", m_RemoteAddr->identHash.ToBase32 ());
}
void I2PUDPClientTunnel::HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if (m_RemoteAddr && from.GetIdentHash() == m_RemoteAddr->identHash)
HandleRecvFromI2PRaw (fromPort, toPort, buf, len);
else
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)
{
auto itr = m_Sessions.find (toPort);
// found convo ?
if (itr != m_Sessions.end ())
{
// found convo
if (len > 0)
{
LogPrint (eLogDebug, "UDP Client: Got ", len, "B from ", m_RemoteAddr ? m_RemoteAddr->identHash.ToBase32 () : "");
m_LocalSocket->send_to (boost::asio::buffer (buf, len), itr->second->first);
// mark convo as active
itr->second->second = i2p::util::GetMillisecondsSinceEpoch ();
}
}
else
LogPrint (eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort);
}
}
}

View File

@@ -1,187 +0,0 @@
/*
* 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 UDPTUNNEL_H__
#define UDPTUNNEL_H__
#include <inttypes.h>
#include <string>
#include <memory>
#include <thread>
#include <vector>
#include <unordered_map>
#include <boost/asio.hpp>
#include "Identity.h"
#include "Destination.h"
#include "Datagram.h"
#include "AddressBook.h"
namespace i2p
{
namespace client
{
/** 2 minute timeout for udp sessions */
const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2;
const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds
/** max size for i2p udp */
const size_t I2P_UDP_MAX_MTU = 64*1024;
struct UDPSession
{
i2p::datagram::DatagramDestination * m_Destination;
boost::asio::ip::udp::socket IPSocket;
i2p::data::IdentHash Identity;
boost::asio::ip::udp::endpoint FromEndpoint;
boost::asio::ip::udp::endpoint SendEndpoint;
uint64_t LastActivity;
uint16_t LocalPort;
uint16_t RemotePort;
uint8_t m_Buffer[I2P_UDP_MAX_MTU];
UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
const boost::asio::ip::udp::endpoint& remote, const i2p::data::IdentHash& ident,
uint16_t ourPort, uint16_t theirPort);
void HandleReceived(const boost::system::error_code & ecode, std::size_t len);
void Receive();
};
/** read only info about a datagram session */
struct DatagramSessionInfo
{
/** the name of this forward */
std::string Name;
/** ident hash of local destination */
std::shared_ptr<const i2p::data::IdentHash> LocalIdent;
/** ident hash of remote destination */
std::shared_ptr<const i2p::data::IdentHash> RemoteIdent;
/** ident hash of IBGW in use currently in this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> CurrentIBGW;
/** ident hash of OBEP in use for this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> CurrentOBEP;
/** i2p router's udp endpoint */
boost::asio::ip::udp::endpoint LocalEndpoint;
/** client's udp endpoint */
boost::asio::ip::udp::endpoint RemoteEndpoint;
/** how long has this converstation been idle in ms */
uint64_t idle;
};
typedef std::shared_ptr<UDPSession> UDPSessionPtr;
/** server side udp tunnel, many i2p inbound to 1 ip outbound */
class I2PUDPServerTunnel
{
public:
I2PUDPServerTunnel (const std::string & name,
std::shared_ptr<i2p::client::ClientDestination> localDestination,
const boost::asio::ip::address& localAddress,
const boost::asio::ip::udp::endpoint& forwardTo, uint16_t port, bool gzip);
~I2PUDPServerTunnel ();
/** expire stale udp conversations */
void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
void Start ();
void Stop ();
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; }
void SetUniqueLocal (bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; }
private:
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);
UDPSessionPtr ObtainUDPSession (const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
private:
bool m_IsUniqueLocal;
const std::string m_Name;
boost::asio::ip::address m_LocalAddress;
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
std::mutex m_SessionsMutex;
std::vector<UDPSessionPtr> m_Sessions;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
UDPSessionPtr m_LastSession;
bool m_Gzip;
public:
bool isUpdated; // transient, used during reload only
};
class I2PUDPClientTunnel
{
public:
I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest,
const boost::asio::ip::udp::endpoint& localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort, bool gzip);
~I2PUDPClientTunnel ();
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; }
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:
typedef std::pair<boost::asio::ip::udp::endpoint, uint64_t> UDPConvo;
void RecvFromLocal ();
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 HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
void TryResolving ();
private:
const std::string m_Name;
std::mutex m_SessionsMutex;
std::unordered_map<uint16_t, std::shared_ptr<UDPConvo> > m_Sessions; // maps i2p port -> local udp convo
const std::string m_RemoteDest;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
std::shared_ptr<const Address> m_RemoteAddr;
std::thread * m_ResolveThread;
std::unique_ptr<boost::asio::ip::udp::socket> m_LocalSocket;
boost::asio::ip::udp::endpoint m_RecvEndpoint;
uint8_t m_RecvBuff[I2P_UDP_MAX_MTU];
uint16_t RemotePort, m_LastPort;
bool m_cancel_resolve;
bool m_Gzip;
std::shared_ptr<UDPConvo> m_LastSession;
public:
bool isUpdated; // transient, used during reload only
};
}
}
#endif

View File

@@ -27,14 +27,16 @@
#include "RouterContext.h"
#include "ClientContext.h"
#include "HTTPServer.h"
#include "Daemon.h"
#include "util.h"
#include "ECIESX25519AEADRatchetSession.h"
#include "I18N.h"
#ifdef WIN32_APP
#include "Win32App.h"
#endif
//#ifdef WIN32_APP
//#include "Win32App.h"
//#endif
// Inja template engine
#include "inja/inja.hpp"
// For image, style and info
#include "version.h"
@@ -58,7 +60,7 @@ namespace http {
static void GetStyles (std::stringstream& s)
{
if (externalCSS.length() != 0)
s << "<style>\r\n" << externalCSS << "</style>\r\n";
s << externalCSS;
else
s << internalCSS;
}
@@ -80,7 +82,7 @@ namespace http {
const char HTTP_COMMAND_SHUTDOWN_CANCEL[] = "shutdown_cancel";
const char HTTP_COMMAND_SHUTDOWN_NOW[] = "terminate";
const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
const char HTTP_COMMAND_RELOAD_TUNNELS_CONFIG[] = "reload_tunnels_config";
const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config";
const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel";
const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit";
@@ -182,9 +184,11 @@ namespace http {
" <meta charset=\"UTF-8\">\r\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n"
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
" <title>" << tr(/* tr: Webconsole page title */ "Purple I2P Webconsole") << "</title>\r\n";
" <title>Purple I2P Webconsole</title>\r\n"
" <style>";
GetStyles(s);
s <<
"\r\n</style>\r\n"
"</head>\r\n"
"<body>\r\n"
"<div class=\"header\">" << tr("<b>i2pd</b> webconsole") << "</div>\r\n"
@@ -222,7 +226,7 @@ namespace http {
s << "<b>" << tr("ERROR") << ":</b>&nbsp;" << string << "<br>\r\n";
}
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, RouterError error)
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status)
{
switch (status)
{
@@ -232,24 +236,25 @@ namespace http {
case eRouterStatusUnknown: s << tr("Unknown"); break;
case eRouterStatusProxy: s << tr("Proxy"); break;
case eRouterStatusMesh: s << tr("Mesh"); break;
default: s << tr("Unknown");
}
if (error != eRouterErrorNone)
{
s << "<br>";
switch (error)
case eRouterStatusError:
{
case eRouterErrorClockSkew:
s << " - " << tr("Clock skew");
s << tr("Error");
switch (i2p::context.GetError ())
{
case eRouterErrorClockSkew:
s << " - " << tr("Clock skew");
break;
case eRouterErrorOffline:
s << " - " << tr("Offline");
break;
case eRouterErrorSymmetricNAT:
s << " - " << tr("Symmetric NAT");
break;
default: ;
}
break;
case eRouterErrorOffline:
s << " - " << tr("Offline");
break;
case eRouterErrorSymmetricNAT:
s << " - " << tr("Symmetric NAT");
break;
default: ;
}
default: s << tr("Unknown");
}
}
@@ -259,31 +264,30 @@ namespace http {
ShowUptime(s, i2p::context.GetUptime ());
s << "<br>\r\n";
s << "<b>" << tr("Network status") << ":</b> ";
ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetError ());
ShowNetworkStatus (s, i2p::context.GetStatus ());
s << "<br>\r\n";
if (i2p::context.SupportsV6 ())
{
s << "<b>" << tr("Network status v6") << ":</b> ";
ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetErrorV6 ());
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
s << "<br>\r\n";
}
// TODO: rewrite timer access
#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> ";
ShowUptime(s, remains);
s << "<br>\r\n";
}
#elif defined(WIN32_APP)
/*#elif defined(WIN32_APP)
if (i2p::win32::g_GracefulShutdownEndtime != 0) {
uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000;
s << "<b>" << tr("Stopping in") << ":</b> ";
ShowUptime(s, remains);
s << "<br>\r\n";
}
}*/
#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("Received") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
@@ -530,21 +534,19 @@ namespace http {
ShowLeaseSetDestination (s, dest, token);
// Print table with streams information
s << "<table>\r\n<caption>"
<< tr("Streams")
<< "</caption>\r\n<thead>\r\n<tr>"
<< "<th style=\"width:25px;\">StreamID</th>"
<< "<th style=\"width:5px;\" \\>" // Stream closing button column
<< "<th class=\"streamdest\">Destination</th>"
<< "<th>Sent</th>"
<< "<th>Received</th>"
<< "<th>Out</th>"
<< "<th>In</th>"
<< "<th>Buf</th>"
<< "<th>RTT</th>"
<< "<th>Window</th>"
<< "<th>Status</th>"
<< "</tr>\r\n</thead>\r\n<tbody class=\"tableitem\">\r\n";
s << "<table>\r\n<caption>" << tr("Streams") << "</caption>\r\n<thead>\r\n<tr>";
s << "<th style=\"width:25px;\">StreamID</th>";
s << "<th style=\"width:5px;\" \\>"; // Stream closing button column
s << "<th class=\"streamdest\">Destination</th>";
s << "<th>Sent</th>";
s << "<th>Received</th>";
s << "<th>Out</th>";
s << "<th>In</th>";
s << "<th>Buf</th>";
s << "<th>RTT</th>";
s << "<th>Window</th>";
s << "<th>Status</th>";
s << "</tr>\r\n</thead>\r\n<tbody class=\"tableitem\">\r\n";
for (const auto& it: dest->GetAllStreams ())
{
@@ -698,7 +700,8 @@ namespace http {
s << "<b>" << tr("Router commands") << "</b><br>\r\n<br>\r\n<div class=\"commands\">\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a><br>\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RELOAD_TUNNELS_CONFIG << "&token=" << token << "\">" << tr("Reload tunnels configuration") << "</a><br>\r\n";
// s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
if (i2p::context.AcceptsTunnels ())
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">" << tr("Decline transit tunnels") << "</a><br>\r\n";
@@ -710,11 +713,12 @@ namespace http {
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
else
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)
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
*/
#endif
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">" << tr("Force shutdown") << "</a><br><br>\r\n";
@@ -739,25 +743,17 @@ namespace http {
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n";
// get current used language
std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage();
s << "<b>"
<< tr("Change language")
<< "</b><br>\r\n"
<< "<form method=\"get\" action=\"" << webroot << "\">\r\n"
<< " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_SETLANGUAGE << "\">\r\n"
<< " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n"
<< " <select name=\"lang\" id=\"lang\">\r\n";
std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language
s << "<b>" << tr("Change language") << "</b><br>\r\n";
s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_SETLANGUAGE << "\">\r\n";
s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n";
s << " <select name=\"lang\" id=\"lang\">\r\n";
for (const auto& it: i2p::i18n::languages)
s << " <option value=\"" << it.first << "\"" << ((it.first.compare(currLang) == 0) ? " selected" : "") << ">" << it.second.LocaleName << "</option>\r\n";
s << " </select>\r\n"
<< " <button type=\"submit\">"
<< tr("Change")
<< "</button>\r\n"
<< "</form>\r\n<br>\r\n";
s << " </select>\r\n";
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n";
}
@@ -791,30 +787,25 @@ namespace http {
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
for (const auto& it: sessions )
{
auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second && it.second->IsEstablished () && endpoint.address ().is_v4 ())
if (it.second && it.second->IsEstablished () && !it.second->GetRemoteEndpoint ().address ().is_v6 ())
{
tmp_s << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< endpoint.address ().to_string () << ":" << endpoint.port ();
<< it.second->GetRemoteEndpoint ().address ().to_string ();
if (!it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ())
tmp_s << " [itag:" << it.second->GetRelayTag () << "]";
tmp_s << "</div>\r\n" << std::endl;
cnt++;
}
if (it.second && it.second->IsEstablished () && endpoint.address ().is_v6 ())
if (it.second && it.second->IsEstablished () && it.second->GetRemoteEndpoint ().address ().is_v6 ())
{
tmp_s6 << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< "[" << endpoint.address ().to_string () << "]:" << endpoint.port ();
<< "[" << it.second->GetRemoteEndpoint ().address ().to_string () << "]";
if (!it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ())
tmp_s6 << " [itag:" << it.second->GetRelayTag () << "]";
tmp_s6 << "</div>\r\n" << std::endl;
cnt6++;
}
@@ -1032,8 +1023,8 @@ namespace http {
}
}
HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
m_Socket (socket), m_BufferLen (0), expected_host(hostname)
HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket, HTTPServer& server):
m_Socket (socket), m_Server (server), m_BufferLen (0), expected_host(hostname)
{
/* cache options */
i2p::config::GetOption("http.auth", needAuth);
@@ -1245,7 +1236,7 @@ namespace http {
std::string cmd = params["cmd"];
if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
i2p::transport::transports.PeerTest ();
else if (cmd == HTTP_COMMAND_RELOAD_TUNNELS_CONFIG)
else if (cmd == HTTP_COMMAND_RELOAD_CONFIG)
i2p::client::context.ReloadConfig ();
else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT)
i2p::context.SetAcceptsTunnels (true);
@@ -1254,28 +1245,33 @@ namespace http {
else if (cmd == HTTP_COMMAND_SHUTDOWN_START)
{
i2p::context.SetAcceptsTunnels (false);
// TODO: rewrite timer access
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
Daemon.gracefulShutdownInterval = 10*60;
#elif defined(WIN32_APP)
if (m_DaemonGracefulTimer) m_DaemonGracefulTimer = 10 * 60;
/*#elif defined(WIN32_APP)
i2p::win32::GracefulShutdown ();
*/
#endif
}
else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL)
{
i2p::context.SetAcceptsTunnels (true);
// TODO: rewrite timer access
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
Daemon.gracefulShutdownInterval = 0;
#elif defined(WIN32_APP)
if (m_DaemonGracefulTimer) m_DaemonGracefulTimer = 0;
/*#elif defined(WIN32_APP)
i2p::win32::StopGracefulShutdown ();
*/
#endif
}
else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW)
{
#ifndef WIN32_APP
Daemon.running = false;
#else
i2p::win32::StopWin32App ();
#endif
// TODO: rewrite stop command access
//#ifndef WIN32_APP
m_Server.GetDaemonStop();
//#else
// i2p::win32::StopWin32App ();
//#endif
}
else if (cmd == HTTP_COMMAND_LOGLEVEL)
{
@@ -1509,7 +1505,7 @@ namespace http {
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 ();
}
} // http

View File

@@ -25,11 +25,13 @@ namespace http
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
class HTTPServer;
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
{
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 ();
private:
@@ -48,6 +50,7 @@ namespace http
private:
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
HTTPServer& m_Server;
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
size_t m_BufferLen;
std::string m_SendBuffer;
@@ -69,6 +72,13 @@ namespace http
void Start ();
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:
void Run ();
@@ -85,6 +95,11 @@ namespace http
boost::asio::io_service::work m_Work;
boost::asio::ip::tcp::acceptor m_Acceptor;
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

View File

@@ -33,7 +33,6 @@ namespace http
// bundled style sheet
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"
"@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"
@@ -85,12 +84,127 @@ namespace http
" 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"
" border-radius: 5px; position: relative; height: 36px; display: -webkit-inline-box; margin-top: 10px; }\r\n"
"}\r\n"
"</style>\r\n";
"}\r\n";
// for external style sheet
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
} // i2p

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

Some files were not shown because too many files have changed in this diff Show More