diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 00000000..1c01d373 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,63 @@ +name: Build containers + +on: [push] + +jobs: + docker: + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to GitHub Container registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - 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: linux/amd64,linux/386,linux/arm64,linux/arm/v7 + push: true + tags: | + purplei2p/i2pd:latest + ghcr.io/purplei2p/i2pd:latest + + - name: Set env + if: ${{ startsWith(github.ref, 'refs/tags/') }} + run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV + + - name: Build and push release container + if: ${{ startsWith(github.ref, 'refs/tags/') }} + uses: docker/build-push-action@v2 + with: + 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:release-${{ env.RELEASE_VERSION }} + ghcr.io/purplei2p/i2pd:latest + ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }} diff --git a/.gitignore b/.gitignore index 2506fd93..bafe2e18 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,13 @@ netDb /i2pd /libi2pd.a /libi2pdclient.a +/libi2pdlang.a /libi2pd.so /libi2pdclient.so +/libi2pdlang.so +/libi2pd.dll +/libi2pdclient.dll +/libi2pdlang.dll *.exe diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index af47f458..00000000 --- a/.travis.yml +++ /dev/null @@ -1,54 +0,0 @@ -language: cpp -cache: - apt: true -os: -- linux -#- osx -dist: xenial -sudo: required -compiler: -- g++ -- clang++ -env: - global: - - MAKEFLAGS="-j 2" - matrix: - - BUILD_TYPE=make UPNP=ON MAKE_UPNP=yes - - BUILD_TYPE=make UPNP=OFF MAKE_UPNP=no - - BUILD_TYPE=cmake UPNP=ON MAKE_UPNP=yes - - BUILD_TYPE=cmake UPNP=OFF MAKE_UPNP=no -matrix: - exclude: - - os: osx - env: BUILD_TYPE=cmake UPNP=ON MAKE_UPNP=yes - - os: osx - env: BUILD_TYPE=cmake UPNP=OFF MAKE_UPNP=no - - os: linux - compiler: clang++ - env: BUILD_TYPE=make UPNP=ON MAKE_UPNP=yes - - os: linux - compiler: clang++ - env: BUILD_TYPE=make UPNP=OFF MAKE_UPNP=no -addons: - apt: - packages: - - build-essential - - cmake - - g++ - - clang - - libboost-chrono-dev - - libboost-date-time-dev - - libboost-filesystem-dev - - libboost-program-options-dev - - libboost-system-dev - - libboost-thread-dev - - libminiupnpc-dev - - libssl-dev -before_install: -- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi -- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libressl miniupnpc ; fi -- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated boost || brew upgrade boost ; fi -script: -- if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_TYPE" == "cmake" ]]; then cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_UPNP=${UPNP} && make ; fi -- if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_TYPE" == "make" ]]; then make USE_UPNP=${MAKE_UPNP} ; fi -- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then make HOMEBREW=1 USE_UPNP=${MAKE_UPNP} ; fi diff --git a/ChangeLog b/ChangeLog index 9a79440e..2128f5d2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,49 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog -## [2.38.0] - 2021-03-17 +## [2.39.0] - 2021-08-23 +### Added +- Short tunnel build messages +- Localization. To: Russian, Ukrainian, Turkmen, Uzbek and Afrikaans +- Custom CSS styles for webconsole +- Avoid slow tunnels with more than 250 ms per hop +- Process DELAY_REQUESTED streaming option +- "certsdir" options for certificates location +- Keep own RouterInfo in NetBb +- Pick ECIES routers only for tunnels on non-x64 +- NTP sync through ipv6 +- Allow ipv6 addresses for UDP server tunnels +### Changed +- Rekey of all routers to ECIES +- Better distribution for random tunnel's peer selection +- Yggdrasil reseed for v0.4, added two more +- Encryption type 0,4 by default for server tunnels +- Handle i2cp.dontPublishLeaseSet param for all destinations +- reg.i2p for subscriptions +- LeaseSet type 3 by default +- Don't allocate payload buffer for every single ECIESx25519 message +- Prefer public ipv6 instead rfc4941 +- Optimal padding for one-time ECIESx25519 message +- Don't send datetime block for one-time ECIESx25519 message with one-time key +- Router with expired introducer is still valid +- Don't disable floodfill if still reachable by ipv6 +- Set minimal version for floodfill to 0.9.38 +- Eliminate extra lookups for sequential fragments on tunnel endpoint +- Consistent path for explicit peers +- Always create new tunnel from exploratory pool +- Don't try to connect to a router not reachable from us +- Mark additional ipv6 addresses/nets as reserved (#1679) +### Fixed +- Zero-hop tunnels +- Crash upon SAM session termination +- Build with boost < 1.55.0 +- Address type for NTCP2 acceptors +- Check of ipv4/ipv6 address +- Request router to send to if not in NetDb +- Count outbound traffic for zero-hop tunnels +- URLdecode domain for registration string generator in webconsole + +## [2.38.0] - 2021-05-17 ### Added - Publish ipv6 introducers - Bind ipv6 or yggdrasil NTCP2 acceptor to specified address diff --git a/Makefile b/Makefile index 40e72918..d7765af7 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,37 @@ SYS := $(shell $(CXX) -dumpmachine) -SHLIB := libi2pd.so + +ifneq (, $(findstring darwin, $(SYS))) + SHARED_SUFFIX = dylib +else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS))) + SHARED_SUFFIX = dll +else + SHARED_SUFFIX = so +endif + +SHLIB := libi2pd.$(SHARED_SUFFIX) ARLIB := libi2pd.a -SHLIB_CLIENT := libi2pdclient.so +SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX) +ARLIB_LANG := libi2pdlang.a +SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX) ARLIB_CLIENT := libi2pdclient.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 LANG_SRC_DIR := i18n DAEMON_SRC_DIR := daemon # import source files lists include filelist.mk -USE_AESNI := yes -USE_STATIC := no -USE_MESHNET := no -USE_UPNP := no -DEBUG := yes +USE_AESNI := $(or $(USE_AESNI),yes) +USE_STATIC := $(or $(USE_STATIC),no) +USE_MESHNET := $(or $(USE_MESHNET),no) +USE_UPNP := $(or $(USE_UPNP),no) +DEBUG := $(or $(DEBUG),yes) ifeq ($(DEBUG),yes) CXX_DEBUG = -g @@ -56,21 +70,25 @@ 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)) DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) -DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) +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) -all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD) +## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary +all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD) mk_obj_dir: - @mkdir -p obj - @mkdir -p obj/Win32 @mkdir -p obj/$(LIB_SRC_DIR) @mkdir -p obj/$(LIB_CLIENT_SRC_DIR) @mkdir -p obj/$(LANG_SRC_DIR) @mkdir -p obj/$(DAEMON_SRC_DIR) + @mkdir -p obj/$(WRAP_SRC_DIR) + @mkdir -p obj/Win32 -api: mk_obj_dir $(SHLIB) $(ARLIB) -client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT) -api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) +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) ## 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. @@ -79,23 +97,33 @@ api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) ## -std=c++11. If you want to remove this variable please do so in a way that allows setting ## custom FLAGS to work at build-time. -obj/%.o: %.cpp +obj/%.o: %.cpp | mk_obj_dir $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -c -o $@ $< # '-' is 'ignore if missing' on first run -include $(DEPS) -$(I2PD): $(LANG_OBJS) $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) +$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS) -$(SHLIB): $(LIB_OBJS) +$(SHLIB): $(LIB_OBJS) $(SHLIB_LANG) +ifneq ($(USE_STATIC),yes) + $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB_LANG) +endif + +$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS) $(SHLIB) $(SHLIB_LANG) +ifneq ($(USE_STATIC),yes) + $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG) +endif + +$(SHLIB_WRAP): $(WRAP_LIB_OBJS) ifneq ($(USE_STATIC),yes) $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) endif -$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS) +$(SHLIB_LANG): $(LANG_OBJS) ifneq ($(USE_STATIC),yes) - $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) + $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) endif $(ARLIB): $(LIB_OBJS) @@ -104,12 +132,18 @@ $(ARLIB): $(LIB_OBJS) $(ARLIB_CLIENT): $(LIB_CLIENT_OBJS) $(AR) -r $@ $^ +$(ARLIB_WRAP): $(WRAP_LIB_OBJS) + $(AR) -r $@ $^ + +$(ARLIB_LANG): $(LANG_OBJS) + $(AR) -r $@ $^ + clean: $(RM) -r obj $(RM) -r docs/generated - $(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) + $(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WRAP) $(ARLIB_WRAP) -strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB) +strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG) strip $^ LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl) @@ -133,6 +167,7 @@ doxygen: .PHONY: api .PHONY: api_client .PHONY: client +.PHONY: lang .PHONY: mk_obj_dir .PHONY: install .PHONY: strip diff --git a/Makefile.mingw b/Makefile.mingw index ce1966a1..e31d895e 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -3,9 +3,9 @@ USE_WIN32_APP := yes WINDRES = windres -CXXFLAGS := $(CXX_DEBUG) -D_MT -DWIN32_LEAN_AND_MEAN -fPIC -msse +CXXFLAGS := $(CXX_DEBUG) -DWIN32_LEAN_AND_MEAN -fPIC -msse INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32 -LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc +LDFLAGS := ${LD_DEBUG} -static # detect proper flag for c++11 support by compilers CXXVER := $(shell $(CXX) -dumpversion) @@ -61,5 +61,5 @@ ifeq ($(USE_ASLR),yes) LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols endif -obj/%.o : %.rc +obj/%.o : %.rc | mk_obj_dir $(WINDRES) -i $< -o $@ diff --git a/README.md b/README.md index 92a2f46f..6f2d23ec 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![License](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE) [![Packaging status](https://repology.org/badge/tiny-repos/i2pd.svg)](https://repology.org/project/i2pd/versions) [![Docker Pulls](https://img.shields.io/docker/pulls/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd) +[![Crowdin](https://badges.crowdin.net/i2pd/localized.svg)](https://crowdin.com/project/i2pd) *note: i2pd for Android can be found in [i2pd-android](https://github.com/PurpleI2P/i2pd-android) repository and with Qt GUI in [i2pd-qt](https://github.com/PurpleI2P/i2pd-qt) repository* @@ -73,7 +74,7 @@ Build instructions: * Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc. * Windows - [![Build on Windows](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml) * Mac OS X - [![Build on OSX](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml) -* Docker image - [![Build Status](https://img.shields.io/docker/cloud/build/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd/builds/) +* Docker image - [![Build Status](https://img.shields.io/docker/cloud/build/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd/builds/) [![Build containers](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml) * Snap - [![i2pd](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) [![i2pd](https://snapcraft.io/i2pd/trending.svg?name=0)](https://snapcraft.io/i2pd) * FreeBSD - [![Build on FreeBSD](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml) * Android - [![Android CI](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml/badge.svg)](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml) @@ -85,6 +86,16 @@ Using i2pd See [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/run/) and [example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/contrib/i2pd.conf). +Localization +------------ + +You can help us with translation i2pd to your language using Crowdin platform! +Translation project can be found [here](https://crowdin.com/project/i2pd). + +New languages can be requested on project's [discussion page](https://crowdin.com/project/i2pd/discussions). + +Current status: [![Crowdin](https://badges.crowdin.net/i2pd/localized.svg)](https://crowdin.com/project/i2pd) + Donations --------- diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 89d85494..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,57 +0,0 @@ -version: 2.38.0.{build} -pull_requests: - do_not_increment_build_number: true -branches: - only: - - openssl -skip_tags: true -os: Visual Studio 2015 -shallow_clone: true -clone_depth: 1 - -# avoid building 32-bit if 64-bit failed already -matrix: - fast_finish: true - -environment: - APPVEYOR_SAVE_CACHE_ON_ERROR: true - MSYS2_PATH_TYPE: inherit - CHERE_INVOKING: enabled_from_arguments - matrix: - - MSYSTEM: MINGW64 - - MSYSTEM: MINGW32 - -cache: - - c:\msys64\var\cache\pacman\pkg\ - -install: -# install new signing keyring -- c:\msys64\usr\bin\bash -lc "curl -O https://mirror.selfnet.de/msys2/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" -- c:\msys64\usr\bin\bash -lc "curl -O https://mirror.selfnet.de/msys2/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig" -- c:\msys64\usr\bin\bash -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig" -- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -U msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz" -# remove packages which can break build -- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc mingw-w64-{i686,x86_64}-gcc-ada mingw-w64-{i686,x86_64}-gcc-objc" -# update runtime -- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu" -# Kill bash before next try -- taskkill /T /F /IM bash.exe /IM gpg.exe /IM gpg-agent.exe | exit /B 0 -# update packages and install required -- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu $MINGW_PACKAGE_PREFIX-boost $MINGW_PACKAGE_PREFIX-miniupnpc" - -build_script: -- c:\msys64\usr\bin\bash -lc "make USE_UPNP=yes DEBUG=no -j3" -# prepare archive for uploading -- set "FILELIST=i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates contrib/tunnels.d" -- echo This is development build, use it carefully! For running in portable mode, move all files from contrib directory here. > README.txt -- 7z a -tzip -mx9 -mmt i2pd-%APPVEYOR_BUILD_VERSION%-%APPVEYOR_REPO_COMMIT:~0,7%-mingw-win%MSYSTEM:~-2%.zip %FILELIST% - -after_build: -- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Sc" - -test: off - -deploy: off - -artifacts: -- path: i2pd-*.zip diff --git a/build/.gitignore b/build/.gitignore index b595141b..872332c5 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -3,6 +3,7 @@ /i2pd /libi2pd.a /libi2pdclient.a +/libi2pdlang.a /cmake_install.cmake /CMakeCache.txt /CPackConfig.cmake @@ -11,4 +12,4 @@ /arch.c # windows build script i2pd*.zip -build*.log \ No newline at end of file +build*.log diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index d1b51f23..2cd598f4 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -27,6 +27,9 @@ option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules") set(CMAKE_SOURCE_DIR "..") +#Handle paths nicely +include(GNUInstallDirs) + # architecture include(TargetArch) target_architecture(ARCHITECTURE) @@ -48,8 +51,8 @@ set_target_properties(libi2pd PROPERTIES PREFIX "") if(WITH_LIBRARY) install(TARGETS libi2pd EXPORT libi2pd - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Libraries) # TODO Make libi2pd available to 3rd party projects via CMake as imported target # FIXME This pulls stdafx @@ -63,12 +66,22 @@ set_target_properties(libi2pdclient PROPERTIES PREFIX "") if(WITH_LIBRARY) install(TARGETS libi2pdclient EXPORT libi2pdclient - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + 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 "") + +if(WITH_LIBRARY) + install(TARGETS libi2pdlang + EXPORT libi2pdlang + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT Libraries) +endif() set(DAEMON_SRC "${DAEMON_SRC_DIR}/Daemon.cpp" @@ -198,10 +211,11 @@ if(WITH_PCH) ) target_compile_options(libi2pd PRIVATE -include libi2pd/stdafx.h) target_compile_options(libi2pdclient PRIVATE -include libi2pd/stdafx.h) + target_compile_options(libi2pdlang PRIVATE -include libi2pd/stdafx.h) target_link_libraries(libi2pd stdafx) endif() -target_link_libraries(libi2pdclient libi2pd) +target_link_libraries(libi2pdclient libi2pd libi2pdlang) find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED) if(NOT DEFINED Boost_INCLUDE_DIRS) @@ -261,11 +275,8 @@ message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}") message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}") message(STATUS "---------------------------------------") -#Handle paths nicely -include(GNUInstallDirs) - if(WITH_BINARY) - add_executable("${PROJECT_NAME}" ${LANG_SRC} ${DAEMON_SRC}) + add_executable("${PROJECT_NAME}" ${DAEMON_SRC}) if(WITH_STATIC) set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static") @@ -295,7 +306,7 @@ if(WITH_BINARY) endif() target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY}) - target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${MINGW_EXTRA} ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES}) + target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${MINGW_EXTRA} ${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}") diff --git a/build/docker/README.md b/build/docker/README.md deleted file mode 100644 index 877a9ce5..00000000 --- a/build/docker/README.md +++ /dev/null @@ -1,34 +0,0 @@ -Howto build & run -================== - -**Build** - -Assuming you're in the root directory of the anoncoin source code. - -$ `cd build/docker` -$ `docker -t meeh/i2pd:latest .` - -**Run** - -To run either the local build, or if not found - fetched prebuild from hub.docker.io, run the following command. - -$ `docker run --name anonnode -v /path/to/i2pd/datadir/on/host:/var/lib/i2pd -p 7070:7070 -p 4444:4444 -p 4447:4447 -p 7656:7656 -p 2827:2827 -p 7654:7654 -p 7650:7650 -d meeh/i2pd` - -All the ports ( -p HOSTPORT:DOCKERPORT ) is optional. However the command above enable all features (Webconsole, HTTP Proxy, BOB, SAM, i2cp, etc) - -The volume ( -v HOSTDIR:DOCKERDIR ) is also optional, but if you don't use it, your config, routerid and private keys will die along with the container. - -**Options** - -Options are set via docker environment variables. This can be set at run with -e parameters. - -* **ENABLE_IPV6** - Enable IPv6 support. Any value can be used - it triggers as long as it's not empty. -* **LOGLEVEL** - Set the loglevel. -* **ENABLE_AUTH** - Enable auth for the webconsole. Username and password needs to be set manually in i2pd.conf cause security reasons. - -**Logging** - -Logging happens to STDOUT as the best practise with docker containers, since infrastructure systems like kubernetes with ELK integration can automatically forward the log to say, kibana or greylog without manual setup. :) - - - diff --git a/build/docker/old-ubuntu-based/Dockerfile b/build/docker/old-ubuntu-based/Dockerfile deleted file mode 100644 index 3faddf2e..00000000 --- a/build/docker/old-ubuntu-based/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM ubuntu - -RUN apt-get update && apt-get install -y libboost-dev libboost-filesystem-dev \ - libboost-program-options-dev libboost-date-time-dev \ - libssl-dev git build-essential - -RUN git clone https://github.com/PurpleI2P/i2pd.git -WORKDIR /i2pd -RUN make - -CMD ./i2pd diff --git a/build/fig.yml b/build/fig.yml deleted file mode 100644 index 372ef350..00000000 --- a/build/fig.yml +++ /dev/null @@ -1,2 +0,0 @@ -i2pd: - build: . diff --git a/contrib/certificates/reseed/orignal_at_mail.i2p.crt b/contrib/certificates/reseed/orignal_at_mail.i2p.crt new file mode 100644 index 00000000..799b601b --- /dev/null +++ b/contrib/certificates/reseed/orignal_at_mail.i2p.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIEbNbRPjANBgkqhkiG9w0BAQ0FADBwMQswCQYDVQQGEwJY +WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEZMBcGA1UEAwwQb3JpZ25hbEBtYWls +LmkycDAeFw0yMTA3MDYyMjExMDFaFw0zMTA3MDQyMjExMDFaMHAxCzAJBgNVBAYT +AlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxHjAcBgNVBAoMFUkyUCBBbm9u +eW1vdXMgTmV0d29yazEMMAoGA1UECwwDSTJQMRkwFwYDVQQDDBBvcmlnbmFsQG1h +aWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvNJz2KGuAkHP +tGFobfLvpybtxB50fkcEsTc9opmiy7wBKK9rSI01VS616IhABkWKZVfK2A9NqpGv +v/CyhTKoaeSNeXY7+zORUWgWK/zA9fA4GRZFqlW8j4tbompDwcLYNqRBCsn1C0OY +YA5JhXPBixMcnXl8N8x4sXhQ4l9R3+QrydhUHRvgDc8dOxRyIX7zuQAyf8tmA2Xo +xZLdvDcCJdLBIbFwxhIceIhgcOwaOx7oRkZDZdYcLJd3zjyPbu8JtOM2ZkwH7r+0 +ro5PktuDp2LAS6SII5yYNcwcrvPZGPqhLdifIw1BrdTIb/rIkQZ5iXOOdyPmT7e8 +IwAJcPFlfvrS4Vbi9oDqyx3aDUBoubgmFnO1TirL56ck83R/ubcKtdnyzAn5dp+f +ZNYW6/foSBpDDOCViylbFAR5H0HJEbBns7PZx6mGEEI4tUAJdNYl7Ly7Df60a9Rz +cD/gz08U9UwFXYKoT6roEjToADGAzb5MI4cVlAb2AmQaMNXNe04HcDL1bU50mkNU +amqPv8nxf72fBQCEmZz2G57T6QiYTtcCwiWS1QdWsuaOtCo9zO0MKcjzSdUxuxEc +dXhjQdNegsgg/Xk7bJ8lKOsACqMpFftdPmuyeZU2t+3RPuBpV/0j2qUfg/y6kb0z +CxAOYmlcL4kqw4VT+5V/EeZLIG0h9I0CAwEAAaMhMB8wHQYDVR0OBBYEFD/wJObg +CCDuhMJCVWTSTj+B3rsUMA0GCSqGSIb3DQEBDQUAA4ICAQC0PjsTSPWlGbLNeeI8 +F0B5xAwXYJzZ7/LRxh8u42HDUqVIDjqkuls1l3v9D7htty2Gr3Ws2dcvcOr2KcOy +mEWg+jdP/N3vt9IkZeVS4YQoPgq6orn7lVkk00bcKb24f7ZnoQnnVV0/m42Y5P4j +LLh+8MBxsez9azXyZbDVEkgsMUAkdVO6KNz6scqz7wb8egV2GAMAp7cwChC6lanK +gv9ZyJhG/HdTv6VyuMZhJy6rX4geM97tm1iHu1VLsQcIzBKAdEvWJv8ofMeiyINe +hqAP9NYaeowKi975NOrmf+XZwxd0niApIohV684RCVUfL8H7HSPbdXhBJ/WslyDP +cTGhA2BLqEXZBn/nLQknlnl0SZTQxG2n4fEgD1E5YS/aoBrig/uXtWm2Zdf8U3mM ++bNXhbi9s7LneN2ye8LlNJBSRklNn/bNo8OmzLII1RQwf1+vaHT96lASbTVepMZ/ +Y9VcC8fAmho/zfQEKueLEB03K+gr2dGD+1crmMtUBjWJ9vPjtooZArtkDbh+kVYA +cx4N4NXULRwxVWZe5wTQOqcZ3qSS1ClMwaziwychGaj8xRAirHMZnlPOZO1UK4+5 +8F4RMJktyZjNgSLP76XPS4rJK5fobuPqFeA4OpDFn/5+/XeQFF6i6wntx1tzztzH +zc+BrVZOdcYPqu9iLXyRQ9JwwA== +-----END CERTIFICATE----- diff --git a/contrib/certificates/router/orignal_at_mail.i2p.crt b/contrib/certificates/router/orignal_at_mail.i2p.crt deleted file mode 100644 index c1229f3b..00000000 --- a/contrib/certificates/router/orignal_at_mail.i2p.crt +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFVDCCAzwCCQC2r1XWYtqtAzANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJY -WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMRMwEQYDVQQKDApQdXJwbGUgSTJQ -MQ0wCwYDVQQLDARJMlBEMR8wHQYJKoZIhvcNAQkBFhBvcmlnbmFsQG1haWwuaTJw -MB4XDTE1MDIyMjEzNTgxOFoXDTI1MDIxOTEzNTgxOFowbDELMAkGA1UEBhMCWFgx -CzAJBgNVBAgMAlhYMQswCQYDVQQHDAJYWDETMBEGA1UECgwKUHVycGxlIEkyUDEN -MAsGA1UECwwESTJQRDEfMB0GCSqGSIb3DQEJARYQb3JpZ25hbEBtYWlsLmkycDCC -AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALp3D/gdvFjrMm+IE8tHZCWE -hQ6Pp0CCgCGDBC3WQFLqR98bqVPl4UwRG/MKY/LY7Woai06JNmGcpfw0LMoNnHxT -bvKtDRe/8kQdhdLHhgIkWKSbMvTAl7uUdV6FzsPgDR0x7scoFVWEhkF0wfmzGF2V -yr/WCBQejFPu69z03m5tRQ8Xjp2txWV45RawUmFu50bgbZvLCSLfTkIvxmfJzgPN -pJ3sPa/g7TBZl2uEiAu4uaEKvTuuzStOWCGgFaHYFVlTfFXTvmhFMqHfaidtzrlu -H35WGrmIWTDl6uGPC5QkSppvkj73rDj5aEyPzWMz5DN3YeECoVSchN+OJJCM6m7+ -rLFYXghVEp2h+T9O1GBRfcHlQ2E3CrWWvxhmK8dfteJmd501dyNX2paeuIg/aPFO -54/8m2r11uyF29hgY8VWLdXtqvwhKuK36PCzofEwDp9QQX8GRsEV4pZTrn4bDhGo -kb9BF7TZTqtL3uyiRmIyBXrNNiYlA1Xm4fyKRtxl0mrPaUXdgdnCt3KxOAJ8WM2B -7L/kk9U8C/nexHbMxIZfTap49XcUg5dxSO9kOBosIOcCUms8sAzBPDV2tWAByhYF -jI/Tutbd3F0+fvcmTcIFOlGbOxKgO2SfwXjv/44g/3LMK6IAMFB9UOc8KhnnJP0f -uAHvMXn1ahRs4pM1VizLAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAIOxdaXT+wfu -nv/+1hy5T4TlRMNNsuj79ROcy6Mp+JwMG50HjTc0qTlXh8C7nHybDJn4v7DA+Nyn -RxT0J5I+Gqn+Na9TaC9mLeX/lwe8/KomyhBWxjrsyWj1V6v/cLO924S2rtcfzMDm -l3SFh9YHM1KF/R9N1XYBwtMzr3bupWDnE1yycYp1F4sMLr5SMzMQ0svQpQEM2/y5 -kly8+eUzryhm+ag9x1686uEG5gxhQ1eHQoZEaClHUOsV+28+d5If7cqcYx9Hf5Tt -CiVjJQzdxBF+6GeiJtKxnLtevqlkbyIJt6Cm9/7YIy/ovRGF2AKSYN6oCwmZQ6i1 -8nRnFq5zE7O94m+GXconWZxy0wVqA6472HThMi7S+Tk/eLYen2ilGY+KCb9a0FH5 -5MOuWSoJZ8/HfW2VeQmL8EjhWm5F2ybg28wgXK4BOGR3jQi03Fsc+AFidnWxSKo0 -aiJoPgOsfyu8/fnCcAi07kSmjzUKIWskApgcpGQLNXHFK9mtg7+VA8esRnfLlKtP -tJf+nNAPY1sqHfGBzh7WWGWal5RGHF5nEm3ta3oiFF5sMKCJ6C87zVwFkEcRytGC -xOGmiG1O1RPrO5NG7rZUaQ4y1OKl2Y1H+nGONzZ3mvoAOvxEq6JtUnU2kZscpPlk -fpeOSDoGBYJGbIpzDreBDhxaZrwGq36k ------END CERTIFICATE----- diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index dc9f5501..71af141e 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -25,7 +25,8 @@ RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \ # 1. install deps, clone and build. # 2. strip binaries. # 3. Purge all dependencies and other unrelated packages, including build directory. -RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl miniupnpc-dev git \ +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 \ diff --git a/contrib/i18n/English.po b/contrib/i18n/English.po index 1de2ddaa..25378f82 100644 --- a/contrib/i18n/English.po +++ b/contrib/i18n/English.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: i2pd\n" "Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n" -"POT-Creation-Date: 2021-06-15 17:40\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,559 +18,567 @@ msgstr "" "X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n" "X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n" -#: daemon/HTTPServer.cpp:85 -msgid "Disabled" -msgstr "" - -#: daemon/HTTPServer.cpp:86 -msgid "Enabled" -msgstr "" - -#: daemon/HTTPServer.cpp:141 +#: daemon/HTTPServer.cpp:177 msgid "day" msgid_plural "days" msgstr[0] "" msgstr[1] "" -#: daemon/HTTPServer.cpp:145 + +#: daemon/HTTPServer.cpp:181 msgid "hour" msgid_plural "hours" msgstr[0] "" msgstr[1] "" -#: daemon/HTTPServer.cpp:149 + +#: daemon/HTTPServer.cpp:185 msgid "minute" msgid_plural "minutes" msgstr[0] "" msgstr[1] "" -#: daemon/HTTPServer.cpp:152 +#: daemon/HTTPServer.cpp:188 msgid "second" msgid_plural "seconds" msgstr[0] "" msgstr[1] "" -#: daemon/HTTPServer.cpp:160 daemon/HTTPServer.cpp:188 +#. tr: Kibibit +#: daemon/HTTPServer.cpp:196 daemon/HTTPServer.cpp:224 msgid "KiB" msgstr "" -#: daemon/HTTPServer.cpp:162 +#. tr: Mebibit +#: daemon/HTTPServer.cpp:198 msgid "MiB" msgstr "" -#: daemon/HTTPServer.cpp:164 +#. tr: Gibibit +#: daemon/HTTPServer.cpp:200 msgid "GiB" msgstr "" -#: daemon/HTTPServer.cpp:181 +#: daemon/HTTPServer.cpp:217 msgid "building" msgstr "" -#: daemon/HTTPServer.cpp:182 +#: daemon/HTTPServer.cpp:218 msgid "failed" msgstr "" -#: daemon/HTTPServer.cpp:183 +#: daemon/HTTPServer.cpp:219 msgid "expiring" msgstr "" -#: daemon/HTTPServer.cpp:184 +#: daemon/HTTPServer.cpp:220 msgid "established" msgstr "" -#: daemon/HTTPServer.cpp:185 +#: daemon/HTTPServer.cpp:221 msgid "unknown" msgstr "" -#: daemon/HTTPServer.cpp:187 +#: daemon/HTTPServer.cpp:223 msgid "exploratory" msgstr "" -#: daemon/HTTPServer.cpp:223 +#: daemon/HTTPServer.cpp:259 msgid "i2pd webconsole" msgstr "" -#: daemon/HTTPServer.cpp:226 +#: daemon/HTTPServer.cpp:262 msgid "Main page" msgstr "" -#: daemon/HTTPServer.cpp:227 daemon/HTTPServer.cpp:683 +#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:725 msgid "Router commands" msgstr "" -#: daemon/HTTPServer.cpp:228 -msgid "Local destinations" -msgstr "" - -#: daemon/HTTPServer.cpp:230 daemon/HTTPServer.cpp:382 -#: daemon/HTTPServer.cpp:463 daemon/HTTPServer.cpp:469 -#: daemon/HTTPServer.cpp:599 daemon/HTTPServer.cpp:642 -#: daemon/HTTPServer.cpp:646 -msgid "LeaseSets" -msgstr "" - -#: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:652 -msgid "Tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:727 -#: daemon/HTTPServer.cpp:743 -msgid "Transit tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:234 daemon/HTTPServer.cpp:792 -msgid "Transports" -msgstr "" - -#: daemon/HTTPServer.cpp:235 -msgid "I2P tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:237 daemon/HTTPServer.cpp:854 -#: daemon/HTTPServer.cpp:864 -msgid "SAM sessions" -msgstr "" - -#: daemon/HTTPServer.cpp:253 daemon/HTTPServer.cpp:1254 -#: daemon/HTTPServer.cpp:1257 daemon/HTTPServer.cpp:1260 -#: daemon/HTTPServer.cpp:1274 daemon/HTTPServer.cpp:1319 -#: daemon/HTTPServer.cpp:1322 daemon/HTTPServer.cpp:1325 -msgid "ERROR" -msgstr "" - -#: daemon/HTTPServer.cpp:260 -msgid "OK" -msgstr "" - -#: daemon/HTTPServer.cpp:261 -msgid "Testing" -msgstr "" - -#: daemon/HTTPServer.cpp:262 -msgid "Firewalled" -msgstr "" - -#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:284 -#: daemon/HTTPServer.cpp:370 -msgid "Unknown" -msgstr "" - -#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:394 -#: daemon/HTTPServer.cpp:395 daemon/HTTPServer.cpp:922 -#: daemon/HTTPServer.cpp:931 -msgid "Proxy" -msgstr "" - -#: daemon/HTTPServer.cpp:265 -msgid "Mesh" -msgstr "" - -#: daemon/HTTPServer.cpp:268 -msgid "Error" -msgstr "" - -#: daemon/HTTPServer.cpp:272 -msgid "Clock skew" -msgstr "" - -#: daemon/HTTPServer.cpp:275 -msgid "Offline" -msgstr "" - -#: daemon/HTTPServer.cpp:278 -msgid "Symmetric NAT" -msgstr "" - -#: daemon/HTTPServer.cpp:290 -msgid "Uptime" -msgstr "" - -#: daemon/HTTPServer.cpp:293 -msgid "Network status" -msgstr "" - -#: daemon/HTTPServer.cpp:298 -msgid "Network status v6" -msgstr "" - -#: daemon/HTTPServer.cpp:304 daemon/HTTPServer.cpp:311 -msgid "Stopping in" -msgstr "" - -#: daemon/HTTPServer.cpp:318 -msgid "Family" -msgstr "" - -#: daemon/HTTPServer.cpp:319 -msgid "Tunnel creation success rate" -msgstr "" - -#: daemon/HTTPServer.cpp:320 -msgid "Received" -msgstr "" - -#: daemon/HTTPServer.cpp:322 daemon/HTTPServer.cpp:325 -#: daemon/HTTPServer.cpp:328 -msgid "KiB/s" -msgstr "" - -#: daemon/HTTPServer.cpp:323 -msgid "Sent" -msgstr "" - -#: daemon/HTTPServer.cpp:326 -msgid "Transit" -msgstr "" - -#: daemon/HTTPServer.cpp:329 -msgid "Data path" -msgstr "" - -#: daemon/HTTPServer.cpp:332 -msgid "Hidden content. Press on text to see." -msgstr "" - -#: daemon/HTTPServer.cpp:335 -msgid "Router Ident" -msgstr "" - -#: daemon/HTTPServer.cpp:337 -msgid "Router Family" -msgstr "" - -#: daemon/HTTPServer.cpp:338 -msgid "Router Caps" -msgstr "" - -#: daemon/HTTPServer.cpp:339 -msgid "Version" -msgstr "" - -#: daemon/HTTPServer.cpp:340 -msgid "Our external address" -msgstr "" - -#: daemon/HTTPServer.cpp:348 -msgid "supported" -msgstr "" - -#: daemon/HTTPServer.cpp:380 -msgid "Routers" -msgstr "" - -#: daemon/HTTPServer.cpp:381 -msgid "Floodfills" -msgstr "" - -#: daemon/HTTPServer.cpp:388 daemon/HTTPServer.cpp:908 -msgid "Client Tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:389 -msgid "Transit Tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:393 -msgid "Services" -msgstr "" - -#: daemon/HTTPServer.cpp:407 daemon/HTTPServer.cpp:419 +#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:448 +#: daemon/HTTPServer.cpp:460 msgid "Local Destinations" msgstr "" -#: daemon/HTTPServer.cpp:442 +#: 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:268 daemon/HTTPServer.cpp:694 +msgid "Tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:269 daemon/HTTPServer.cpp:425 +#: daemon/HTTPServer.cpp:787 daemon/HTTPServer.cpp:803 +msgid "Transit Tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:270 daemon/HTTPServer.cpp:852 +msgid "Transports" +msgstr "" + +#: daemon/HTTPServer.cpp:271 +msgid "I2P tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:914 +#: daemon/HTTPServer.cpp:924 +msgid "SAM sessions" +msgstr "" + +#: 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:296 +msgid "OK" +msgstr "" + +#: daemon/HTTPServer.cpp:297 +msgid "Testing" +msgstr "" + +#: daemon/HTTPServer.cpp:298 +msgid "Firewalled" +msgstr "" + +#: daemon/HTTPServer.cpp:299 daemon/HTTPServer.cpp:320 +#: daemon/HTTPServer.cpp:406 +msgid "Unknown" +msgstr "" + +#: 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:301 +msgid "Mesh" +msgstr "" + +#: daemon/HTTPServer.cpp:304 +msgid "Error" +msgstr "" + +#: daemon/HTTPServer.cpp:308 +msgid "Clock skew" +msgstr "" + +#: daemon/HTTPServer.cpp:311 +msgid "Offline" +msgstr "" + +#: daemon/HTTPServer.cpp:314 +msgid "Symmetric NAT" +msgstr "" + +#: daemon/HTTPServer.cpp:326 +msgid "Uptime" +msgstr "" + +#: daemon/HTTPServer.cpp:329 +msgid "Network status" +msgstr "" + +#: daemon/HTTPServer.cpp:334 +msgid "Network status v6" +msgstr "" + +#: daemon/HTTPServer.cpp:340 daemon/HTTPServer.cpp:347 +msgid "Stopping in" +msgstr "" + +#: daemon/HTTPServer.cpp:354 +msgid "Family" +msgstr "" + +#: daemon/HTTPServer.cpp:355 +msgid "Tunnel creation success rate" +msgstr "" + +#: daemon/HTTPServer.cpp:356 +msgid "Received" +msgstr "" + +#. tr: Kibibit/s +#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:361 +#: daemon/HTTPServer.cpp:364 +msgid "KiB/s" +msgstr "" + +#: daemon/HTTPServer.cpp:359 +msgid "Sent" +msgstr "" + +#: daemon/HTTPServer.cpp:362 +msgid "Transit" +msgstr "" + +#: daemon/HTTPServer.cpp:365 +msgid "Data path" +msgstr "" + +#: daemon/HTTPServer.cpp:368 +msgid "Hidden content. Press on text to see." +msgstr "" + +#: daemon/HTTPServer.cpp:371 +msgid "Router Ident" +msgstr "" + +#: daemon/HTTPServer.cpp:373 +msgid "Router Family" +msgstr "" + +#: daemon/HTTPServer.cpp:374 +msgid "Router Caps" +msgstr "" + +#: daemon/HTTPServer.cpp:375 +msgid "Version" +msgstr "" + +#: daemon/HTTPServer.cpp:376 +msgid "Our external address" +msgstr "" + +#: daemon/HTTPServer.cpp:384 +msgid "supported" +msgstr "" + +#: daemon/HTTPServer.cpp:416 +msgid "Routers" +msgstr "" + +#: daemon/HTTPServer.cpp:417 +msgid "Floodfills" +msgstr "" + +#: daemon/HTTPServer.cpp:424 daemon/HTTPServer.cpp:968 +msgid "Client Tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:434 +msgid "Services" +msgstr "" + +#: 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: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:483 msgid "Encrypted B33 address" msgstr "" -#: daemon/HTTPServer.cpp:451 +#: daemon/HTTPServer.cpp:492 msgid "Address registration line" msgstr "" -#: daemon/HTTPServer.cpp:456 +#: daemon/HTTPServer.cpp:497 msgid "Domain" msgstr "" -#: daemon/HTTPServer.cpp:457 +#: daemon/HTTPServer.cpp:498 msgid "Generate" msgstr "" -#: daemon/HTTPServer.cpp:458 +#: daemon/HTTPServer.cpp:499 msgid "" "Note: result string can be used only for registering 2LD domains " "(example.i2p). For registering subdomains please use i2pd-tools." msgstr "" -#: daemon/HTTPServer.cpp:464 +#: daemon/HTTPServer.cpp:505 msgid "Address" msgstr "" -#: daemon/HTTPServer.cpp:464 +#: daemon/HTTPServer.cpp:505 msgid "Type" msgstr "" -#: daemon/HTTPServer.cpp:464 +#: daemon/HTTPServer.cpp:505 msgid "EncType" msgstr "" -#: daemon/HTTPServer.cpp:474 daemon/HTTPServer.cpp:657 +#: daemon/HTTPServer.cpp:515 daemon/HTTPServer.cpp:699 msgid "Inbound tunnels" msgstr "" -#: daemon/HTTPServer.cpp:479 daemon/HTTPServer.cpp:489 -#: daemon/HTTPServer.cpp:662 daemon/HTTPServer.cpp:672 -#: Means milliseconds +#. tr: Milliseconds +#: daemon/HTTPServer.cpp:520 daemon/HTTPServer.cpp:530 +#: daemon/HTTPServer.cpp:704 daemon/HTTPServer.cpp:714 msgid "ms" msgstr "" -#: daemon/HTTPServer.cpp:484 daemon/HTTPServer.cpp:667 +#: daemon/HTTPServer.cpp:525 daemon/HTTPServer.cpp:709 msgid "Outbound tunnels" msgstr "" -#: daemon/HTTPServer.cpp:496 +#: daemon/HTTPServer.cpp:537 msgid "Tags" msgstr "" -#: daemon/HTTPServer.cpp:496 +#: daemon/HTTPServer.cpp:537 msgid "Incoming" msgstr "" -#: daemon/HTTPServer.cpp:503 daemon/HTTPServer.cpp:506 +#: daemon/HTTPServer.cpp:544 daemon/HTTPServer.cpp:547 msgid "Outgoing" msgstr "" -#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:520 +#: daemon/HTTPServer.cpp:545 daemon/HTTPServer.cpp:561 msgid "Destination" msgstr "" -#: daemon/HTTPServer.cpp:504 +#: daemon/HTTPServer.cpp:545 msgid "Amount" msgstr "" -#: daemon/HTTPServer.cpp:511 +#: daemon/HTTPServer.cpp:552 msgid "Incoming Tags" msgstr "" -#: daemon/HTTPServer.cpp:519 daemon/HTTPServer.cpp:522 +#: daemon/HTTPServer.cpp:560 daemon/HTTPServer.cpp:563 msgid "Tags sessions" msgstr "" -#: daemon/HTTPServer.cpp:520 +#: daemon/HTTPServer.cpp:561 msgid "Status" msgstr "" -#: daemon/HTTPServer.cpp:529 daemon/HTTPServer.cpp:584 +#: daemon/HTTPServer.cpp:570 daemon/HTTPServer.cpp:626 msgid "Local Destination" msgstr "" -#: daemon/HTTPServer.cpp:538 daemon/HTTPServer.cpp:887 +#: daemon/HTTPServer.cpp:580 daemon/HTTPServer.cpp:947 msgid "Streams" msgstr "" -#: daemon/HTTPServer.cpp:560 +#: daemon/HTTPServer.cpp:602 msgid "Close stream" msgstr "" -#: daemon/HTTPServer.cpp:589 +#: daemon/HTTPServer.cpp:631 msgid "I2CP session not found" msgstr "" -#: daemon/HTTPServer.cpp:592 +#: daemon/HTTPServer.cpp:634 msgid "I2CP is not enabled" msgstr "" -#: daemon/HTTPServer.cpp:618 +#: daemon/HTTPServer.cpp:660 msgid "Invalid" msgstr "" -#: daemon/HTTPServer.cpp:621 +#: daemon/HTTPServer.cpp:663 msgid "Store type" msgstr "" -#: daemon/HTTPServer.cpp:622 +#: daemon/HTTPServer.cpp:664 msgid "Expires" msgstr "" -#: daemon/HTTPServer.cpp:627 +#: daemon/HTTPServer.cpp:669 msgid "Non Expired Leases" msgstr "" -#: daemon/HTTPServer.cpp:630 +#: daemon/HTTPServer.cpp:672 msgid "Gateway" msgstr "" -#: daemon/HTTPServer.cpp:631 +#: daemon/HTTPServer.cpp:673 msgid "TunnelID" msgstr "" -#: daemon/HTTPServer.cpp:632 +#: daemon/HTTPServer.cpp:674 msgid "EndDate" msgstr "" -#: daemon/HTTPServer.cpp:642 +#: daemon/HTTPServer.cpp:684 msgid "not floodfill" msgstr "" -#: daemon/HTTPServer.cpp:653 +#: daemon/HTTPServer.cpp:695 msgid "Queue size" msgstr "" -#: daemon/HTTPServer.cpp:684 +#: daemon/HTTPServer.cpp:726 msgid "Run peer test" msgstr "" -#: daemon/HTTPServer.cpp:687 +#: daemon/HTTPServer.cpp:731 msgid "Decline transit tunnels" msgstr "" -#: daemon/HTTPServer.cpp:689 +#: daemon/HTTPServer.cpp:733 msgid "Accept transit tunnels" msgstr "" -#: daemon/HTTPServer.cpp:692 daemon/HTTPServer.cpp:697 +#: daemon/HTTPServer.cpp:737 daemon/HTTPServer.cpp:742 msgid "Cancel graceful shutdown" msgstr "" -#: daemon/HTTPServer.cpp:694 daemon/HTTPServer.cpp:699 +#: daemon/HTTPServer.cpp:739 daemon/HTTPServer.cpp:744 msgid "Start graceful shutdown" msgstr "" -#: daemon/HTTPServer.cpp:701 +#: daemon/HTTPServer.cpp:747 msgid "Force shutdown" msgstr "" -#: daemon/HTTPServer.cpp:704 +#: daemon/HTTPServer.cpp:748 +msgid "Reload external CSS styles" +msgstr "" + +#: daemon/HTTPServer.cpp:751 msgid "" "Note: any action done here are not persistent and not changes your " "config files." msgstr "" -#: daemon/HTTPServer.cpp:706 +#: daemon/HTTPServer.cpp:753 msgid "Logging level" msgstr "" -#: daemon/HTTPServer.cpp:714 +#: daemon/HTTPServer.cpp:761 msgid "Transit tunnels limit" msgstr "" -#: daemon/HTTPServer.cpp:719 +#: daemon/HTTPServer.cpp:766 daemon/HTTPServer.cpp:778 msgid "Change" msgstr "" -#: daemon/HTTPServer.cpp:743 +#: daemon/HTTPServer.cpp:770 +msgid "Change language" +msgstr "" + +#: daemon/HTTPServer.cpp:803 msgid "no transit tunnels currently built" msgstr "" -#: daemon/HTTPServer.cpp:848 daemon/HTTPServer.cpp:871 +#: daemon/HTTPServer.cpp:908 daemon/HTTPServer.cpp:931 msgid "SAM disabled" msgstr "" -#: daemon/HTTPServer.cpp:864 +#: daemon/HTTPServer.cpp:924 msgid "no sessions currently running" msgstr "" -#: daemon/HTTPServer.cpp:877 +#: daemon/HTTPServer.cpp:937 msgid "SAM session not found" msgstr "" -#: daemon/HTTPServer.cpp:882 +#: daemon/HTTPServer.cpp:942 msgid "SAM Session" msgstr "" -#: daemon/HTTPServer.cpp:939 +#: daemon/HTTPServer.cpp:999 msgid "Server Tunnels" msgstr "" -#: daemon/HTTPServer.cpp:955 +#: daemon/HTTPServer.cpp:1015 msgid "Client Forwards" msgstr "" -#: daemon/HTTPServer.cpp:969 +#: daemon/HTTPServer.cpp:1029 msgid "Server Forwards" msgstr "" -#: daemon/HTTPServer.cpp:1175 +#: daemon/HTTPServer.cpp:1227 msgid "Unknown page" msgstr "" -#: daemon/HTTPServer.cpp:1194 +#: daemon/HTTPServer.cpp:1246 msgid "Invalid token" msgstr "" -#: daemon/HTTPServer.cpp:1252 daemon/HTTPServer.cpp:1309 -#: daemon/HTTPServer.cpp:1337 +#: daemon/HTTPServer.cpp:1304 daemon/HTTPServer.cpp:1361 +#: daemon/HTTPServer.cpp:1401 msgid "SUCCESS" msgstr "" -#: daemon/HTTPServer.cpp:1252 +#: daemon/HTTPServer.cpp:1304 msgid "Stream closed" msgstr "" -#: daemon/HTTPServer.cpp:1254 +#: daemon/HTTPServer.cpp:1306 msgid "Stream not found or already was closed" msgstr "" -#: daemon/HTTPServer.cpp:1257 +#: daemon/HTTPServer.cpp:1309 msgid "Destination not found" msgstr "" -#: daemon/HTTPServer.cpp:1260 +#: daemon/HTTPServer.cpp:1312 msgid "StreamID can't be null" msgstr "" -#: daemon/HTTPServer.cpp:1262 daemon/HTTPServer.cpp:1327 +#: daemon/HTTPServer.cpp:1314 daemon/HTTPServer.cpp:1379 msgid "Return to destination page" msgstr "" -#: daemon/HTTPServer.cpp:1263 daemon/HTTPServer.cpp:1276 -msgid "You will be redirected back in 5 seconds" +#: daemon/HTTPServer.cpp:1315 daemon/HTTPServer.cpp:1328 +#: daemon/HTTPServer.cpp:1403 +msgid "You will be redirected in 5 seconds" msgstr "" -#: daemon/HTTPServer.cpp:1274 +#: daemon/HTTPServer.cpp:1326 msgid "Transit tunnels count must not exceed 65535" msgstr "" -#: daemon/HTTPServer.cpp:1275 daemon/HTTPServer.cpp:1338 +#: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1402 msgid "Back to commands list" msgstr "" -#: daemon/HTTPServer.cpp:1311 +#: daemon/HTTPServer.cpp:1363 msgid "Register at reg.i2p" msgstr "" -#: daemon/HTTPServer.cpp:1312 +#: daemon/HTTPServer.cpp:1364 msgid "Description" msgstr "" -#: daemon/HTTPServer.cpp:1312 +#: daemon/HTTPServer.cpp:1364 msgid "A bit information about service on domain" msgstr "" -#: daemon/HTTPServer.cpp:1313 +#: daemon/HTTPServer.cpp:1365 msgid "Submit" msgstr "" -#: daemon/HTTPServer.cpp:1319 +#: daemon/HTTPServer.cpp:1371 msgid "Domain can't end with .b32.i2p" msgstr "" -#: daemon/HTTPServer.cpp:1322 +#: daemon/HTTPServer.cpp:1374 msgid "Domain must end with .i2p" msgstr "" -#: daemon/HTTPServer.cpp:1325 +#: daemon/HTTPServer.cpp:1377 msgid "Such destination is not found" msgstr "" -#: daemon/HTTPServer.cpp:1333 +#: daemon/HTTPServer.cpp:1397 msgid "Unknown command" msgstr "" -#: daemon/HTTPServer.cpp:1337 +#: daemon/HTTPServer.cpp:1401 msgid "Command accepted" msgstr "" -#: daemon/HTTPServer.cpp:1339 -msgid "You will be redirected in 5 seconds" -msgstr "" - #: libi2pd_client/HTTPProxy.cpp:157 msgid "Proxy error" msgstr "" @@ -592,7 +600,7 @@ msgid "You may try to find this host on jump services below" msgstr "" #: libi2pd_client/HTTPProxy.cpp:273 libi2pd_client/HTTPProxy.cpp:288 -#: libi2pd_client/HTTPProxy.cpp:365 +#: libi2pd_client/HTTPProxy.cpp:322 libi2pd_client/HTTPProxy.cpp:365 msgid "Invalid request" msgstr "" @@ -613,16 +621,12 @@ msgstr "" msgid "added to router's addressbook from helper" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:307 -msgid "Click" +#: libi2pd_client/HTTPProxy.cpp:298 +msgid "Click here to proceed:" msgstr "" #: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308 -msgid "here" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:298 -msgid "to proceed" +msgid "Continue" msgstr "" #: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309 @@ -633,12 +637,8 @@ msgstr "" msgid "already in router's addressbook" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:308 -msgid "to update record" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:322 -msgid "Invalid Request" +#: libi2pd_client/HTTPProxy.cpp:307 +msgid "Click here to update record:" msgstr "" #: libi2pd_client/HTTPProxy.cpp:322 diff --git a/contrib/i18n/README.md b/contrib/i18n/README.md new file mode 100644 index 00000000..04779473 --- /dev/null +++ b/contrib/i18n/README.md @@ -0,0 +1,29 @@ +`xgettext` command for extracting translation +--- + +``` +xgettext --omit-header -ctr: -ktr -ktr:1,2 daemon/HTTPServer.cpp libi2pd_client/HTTPProxy.cpp +``` + +Regex for transforming gettext translations to our format: +--- + +``` +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 +``` + +``` +in: msgid\ \"(.*)\"\nmsgstr\ \"(.*)\"\n +out: {"$1", "$2"},\n +``` + +``` +in: ^#[:.](.*)$\n +out: +``` + +``` +in: \n\n +out: \n +``` diff --git a/contrib/i18n/regex.txt b/contrib/i18n/regex.txt deleted file mode 100644 index 768a4b01..00000000 --- a/contrib/i18n/regex.txt +++ /dev/null @@ -1,7 +0,0 @@ -Regex for transforming gettext translations to our format - -msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\nmsgstr\[1\]\ \"(.*)\"\n(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)? -#{"$2", {"$3", "$4", "$6", "$8", "$10"}},\n - -msgid\ \"(.*)\"\nmsgstr\ \"(.*)\"\n -{"$1", "$2"},\n diff --git a/contrib/i2pd.conf b/contrib/i2pd.conf index f3cea2b5..dbf74002 100644 --- a/contrib/i2pd.conf +++ b/contrib/i2pd.conf @@ -15,6 +15,10 @@ ## Default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d # tunnelsdir = /var/lib/i2pd/tunnels.d +## Path to certificates used for verifying .su3, families +## Default: ~/.i2pd/certificates or /var/lib/i2pd/certificates +# certsdir = /var/lib/i2pd/certificates + ## Where to write pidfile (default: i2pd.pid, not used in Windows) # pidfile = /run/i2pd.pid @@ -104,7 +108,7 @@ port = 7070 # user = i2pd # pass = changeme ## Select webconsole language -## Currently supported english (default), russian, turkmen and ukrainian languages +## Currently supported english (default), afrikaans, russian, turkmen, ukrainian and uzbek languages # lang = english [httpproxy] diff --git a/contrib/rpm/i2pd-git.spec b/contrib/rpm/i2pd-git.spec index 1061fc8f..b1354173 100644 --- a/contrib/rpm/i2pd-git.spec +++ b/contrib/rpm/i2pd-git.spec @@ -1,7 +1,7 @@ %define git_hash %(git rev-parse HEAD | cut -c -7) Name: i2pd-git -Version: 2.38.0 +Version: 2.39.0 Release: git%{git_hash}%{?dist} Summary: I2P router written in C++ Conflicts: i2pd @@ -56,9 +56,14 @@ cd build %endif %endif + +%if 0%{?fedora} >= 35 +pushd redhat-linux-build +%else %if 0%{?fedora} >= 33 pushd %{_target_platform} %endif +%endif %if 0%{?mageia} > 7 pushd build @@ -77,9 +82,13 @@ popd %install pushd build +%if 0%{?fedora} >= 35 +pushd redhat-linux-build +%else %if 0%{?fedora} >= 33 pushd %{_target_platform} %endif +%endif %if 0%{?mageia} pushd build @@ -137,6 +146,13 @@ getent passwd i2pd >/dev/null || \ %changelog +* Mon Aug 24 2021 r4sas - 2.39.0-2 +- changed if statements to cover fedora 35 + +* Mon Aug 23 2021 orignal - 2.39.0 +- update to 2.39.0 +- fixed build on fedora 36 + * Mon May 17 2021 orignal - 2.38.0 - update to 2.38.0 diff --git a/contrib/rpm/i2pd.spec b/contrib/rpm/i2pd.spec index a2994f28..ef8e32f2 100644 --- a/contrib/rpm/i2pd.spec +++ b/contrib/rpm/i2pd.spec @@ -1,6 +1,6 @@ Name: i2pd -Version: 2.38.0 -Release: 1%{?dist} +Version: 2.39.0 +Release: 2%{?dist} Summary: I2P router written in C++ Conflicts: i2pd-git @@ -54,9 +54,13 @@ cd build %endif %endif +%if 0%{?fedora} >= 35 +pushd redhat-linux-build +%else %if 0%{?fedora} >= 33 pushd %{_target_platform} %endif +%endif %if 0%{?mageia} > 7 pushd build @@ -75,9 +79,13 @@ popd %install pushd build +%if 0%{?fedora} >= 35 +pushd redhat-linux-build +%else %if 0%{?fedora} >= 33 pushd %{_target_platform} %endif +%endif %if 0%{?mageia} pushd build @@ -135,6 +143,13 @@ getent passwd i2pd >/dev/null || \ %changelog +* Mon Aug 24 2021 r4sas - 2.39.0-2 +- changed if statements to cover fedora 35 + +* Mon Aug 23 2021 orignal - 2.39.0 +- update to 2.39.0 +- fixed build on fedora 36 + * Mon May 17 2021 orignal - 2.38.0 - update to 2.38.0 diff --git a/contrib/tunnels.conf b/contrib/tunnels.conf index 3358ffc4..55723c43 100644 --- a/contrib/tunnels.conf +++ b/contrib/tunnels.conf @@ -1,16 +1,16 @@ -[IRC-IRC2P] +[IRC-ILITA] type = client address = 127.0.0.1 port = 6668 -destination = irc.postman.i2p +destination = irc.ilita.i2p destinationport = 6667 keys = irc-keys.dat -#[IRC-ILITA] +#[IRC-IRC2P] #type = client #address = 127.0.0.1 #port = 6669 -#destination = irc.ilita.i2p +#destination = irc.postman.i2p #destinationport = 6667 #keys = irc-keys.dat diff --git a/contrib/webconsole/style.css b/contrib/webconsole/style.css new file mode 100644 index 00000000..047839a6 --- /dev/null +++ b/contrib/webconsole/style.css @@ -0,0 +1,243 @@ +body { + font: 100%/1.5em sans-serif; + margin: 0; + padding: 1.5em; + background: #FAFAFA; + color: #103456; +} + +a, .slide label { + text-decoration: none; + color: #894C84; +} + +a:hover, .slide label:hover { + color: #FAFAFA; + background: #894C84; +} + +a.button { + -webkit-appearance: button; + -moz-appearance: button; + appearance: button; + text-decoration: none; + padding: 0 5px; + border: 1px solid #894C84; +} + +.header { + font-size: 2.5em; + text-align: center; + margin: 1em 0; + color: #894C84; +} + +.wrapper { + margin: 0 auto; + padding: 1em; + max-width: 64em; +} + +.menu { + display: block; + float: left; + overflow: hidden; + max-width: 12em; + white-space: nowrap; + text-overflow: ellipsis; +} + +.listitem { + display: block; + font-family: monospace; + font-size: 1.2em; + white-space: nowrap; +} + +.tableitem { + font-family: monospace; + font-size: 1.2em; + white-space: nowrap; +} + +.content { + float: left; + font-size: 1em; + margin-left: 4em; + max-width: 48em; + overflow: auto; +} + +.tunnel.established { + color: #56B734; +} + +.tunnel.expiring { + color: #D3AE3F; +} + +.tunnel.failed { + color: #D33F3F; +} + +.tunnel.building { + color: #434343; +} + +caption { + font-size: 1.5em; + text-align: center; + color: #894C84; +} + +table { + display: table; + border-collapse: collapse; + text-align: center; +} + +table.extaddr { + text-align: left; +} + +table.services { + width: 100%; +} + +textarea { + word-break: break-all; +} + +.streamdest { + width: 120px; + max-width: 240px; + overflow: hidden; + text-overflow: ellipsis; +} + +.slide div.slidecontent, .slide [type="checkbox"] { + display: none; +} + +.slide [type="checkbox"]:checked ~ div.slidecontent { + display: block; + margin-top: 0; + padding: 0; +} + +.disabled { + color: #D33F3F; +} + +.enabled { + color: #56B734; +} + +@media screen and (max-width: 1150px) { /* adaptive style */ + .wrapper { + max-width: 58em; + } + + .menu { + max-width: 10em; + } + + .content { + margin-left: 2em; + max-width: 42em; + } +} + +@media screen and (max-width: 980px) { + body { + padding: 1.5em 0 0 0; + } + + .menu { + width: 100%; + max-width: unset; + display: block; + float: none; + position: unset; + font-size: 16px; + text-align: center; + } + + .menu a, .commands a { + display: inline-block; + padding: 4px; + } + + .content { + float: none; + margin-left: unset; + margin-top: 16px; + max-width: 100%; + width: 100%; + text-align: center; + } + + a, .slide label { + /* margin-right: 10px; */ + display: block; + /* font-size: 18px; */ + } + + .header { + margin: unset; + font-size: 1.5em; + } + + small { + display: block + } + + a.button { + -webkit-appearance: button; + -moz-appearance: button; + appearance: button; + text-decoration: none; + margin-top: 10px; + padding: 6px; + border: 1px solid #894c84; + width: -webkit-fill-available; + } + + input, select { + width: 35%; + text-align: center; + padding: 5px; + border: 2px solid #ccc; + -webkit-border-radius: 5px; + border-radius: 5px; + font-size: 18px; + } + + table.extaddr { + margin: auto; + text-align: unset; + } + + textarea { + width: -webkit-fill-available; + height: auto; + padding:5px; + border:2px solid #ccc; + -webkit-border-radius: 5px; + border-radius: 5px; + font-size: 12px; + } + + button[type=submit] { + padding: 5px 15px; + background: #ccc; + border: 0 none; + cursor: pointer; + -webkit-border-radius: 5px; + border-radius: 5px; + position: relative; + height: 36px; + display: -webkit-inline-box; + margin-top: 10px; + } +} \ No newline at end of file diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index f7f2ef2f..445c4dfd 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -94,6 +94,11 @@ namespace util i2p::config::GetOption("daemon", isDaemon); + std::string certsdir; i2p::config::GetOption("certsdir", certsdir); + i2p::fs::SetCertsDir(certsdir); + + certsdir = i2p::fs::GetCertsDir(); + std::string logs = ""; i2p::config::GetOption("log", logs); std::string logfile = ""; i2p::config::GetOption("logfile", logfile); std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel); @@ -132,6 +137,7 @@ namespace util LogPrint(eLogNone, "i2pd v", VERSION, " starting"); LogPrint(eLogDebug, "FS: main config file: ", config); LogPrint(eLogDebug, "FS: data directory: ", datadir); + LogPrint(eLogDebug, "FS: certificates directory: ", certsdir); bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); bool aesni; i2p::config::GetOption("cpuext.aesni", aesni); diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 7eb296c4..1e002b1b 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -36,73 +36,31 @@ #include "Win32App.h" #endif -// For image and info +// For image, style and info #include "version.h" +#include "HTTPServerResources.h" namespace i2p { namespace http { - const std::string itoopieFavicon = - "data:image/png;base64," - "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx" - "jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0" - "d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB" - "vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn" - "cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP" - "6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps" - "/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X" - "+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/" - "z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/" - "P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am" - "+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM" - "EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1" - "oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ" - "JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ" - "RU5ErkJggg=="; + static void LoadExtCSS () + { + std::stringstream s; + std::string styleFile = i2p::fs::DataDirPath ("webconsole/style.css"); + if (i2p::fs::Exists(styleFile)) { + std::ifstream f(styleFile, std::ifstream::binary); + s << f.rdbuf(); + externalCSS = s.str(); + } else if (externalCSS.length() != 0) { // clean up external style if file was removed + externalCSS = ""; + } + } static void GetStyles (std::stringstream& s) { - s << "\r\n"; + if (externalCSS.length() != 0) + s << "\r\n"; + else + s << internalCSS; } const char HTTP_PAGE_TUNNELS[] = "tunnels"; @@ -127,11 +85,20 @@ namespace http { const char HTTP_COMMAND_KILLSTREAM[] = "closestream"; const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit"; const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string"; + const char HTTP_COMMAND_SETLANGUAGE[] = "setlanguage"; + const char HTTP_COMMAND_RELOAD_CSS[] = "reload_css"; const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; const char HTTP_PARAM_ADDRESS[] = "address"; - static std::string ConvertTime (uint64_t time); - std::map HTTPConnection::m_Tokens; + static std::string ConvertTime (uint64_t time) + { + lldiv_t divTime = lldiv(time, 1000); + time_t t = divTime.quot; + struct tm *tm = localtime(&t); + char date[128]; + snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03lld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem); + return date; + } static void ShowUptime (std::stringstream& s, int seconds) { @@ -157,11 +124,11 @@ namespace http { s << std::fixed << std::setprecision(2); auto numKBytes = (double) bytes / 1024; if (numKBytes < 1024) - s << numKBytes << " " << tr("KiB"); + s << numKBytes << " " << tr(/* tr: Kibibit */ "KiB"); else if (numKBytes < 1024 * 1024) - s << numKBytes / 1024 << " " << tr("MiB"); + s << numKBytes / 1024 << " " << tr(/* tr: Mebibit */ "MiB"); else - s << numKBytes / 1024 / 1024 << " " << tr("GiB"); + s << numKBytes / 1024 / 1024 << " " << tr(/* tr: Gibibit */ "GiB"); } static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes) @@ -185,7 +152,7 @@ namespace http { else stateText = tr("unknown"); s << " " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << ", "; - s << " " << (int) (bytes / 1024) << " " << tr("KiB") << "\r\n"; + s << " " << (int) (bytes / 1024) << " " << tr(/* tr: Kibibit */ "KiB") << "\r\n"; } static void SetLogLevel (const std::string& level) @@ -204,9 +171,9 @@ namespace http { std::string webroot; i2p::config::GetOption("http.webroot", webroot); // Page language - std::string lang, langCode; i2p::config::GetOption("http.lang", lang); - if (lang == "russian") langCode = "ru"; - else langCode = "en"; + std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language + auto it = i2p::i18n::languages.find(currLang); + std::string langCode = it->second.ShortCode; s << "\r\n" @@ -223,18 +190,18 @@ namespace http { "
" << tr("i2pd webconsole") << "
\r\n" "
\r\n" "\r\n" "
"; @@ -319,13 +286,13 @@ namespace http { s << "" << tr("Tunnel creation success rate") << ": " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%
\r\n"; s << "" << tr("Received") << ": "; ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ()); - s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr("KiB/s") << ")
\r\n"; + s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")
\r\n"; s << "" << tr("Sent") << ": "; ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ()); - s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr("KiB/s") << ")
\r\n"; + s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")
\r\n"; s << "" << tr("Transit") << ": "; ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); - s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr("KiB/s") << ")
\r\n"; + s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")
\r\n"; s << "" << tr("Data path") << ": " << i2p::fs::GetUTF8DataDir() << "
\r\n"; s << "
"; if((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) { @@ -389,14 +356,19 @@ namespace http { s << "" << tr("Transit Tunnels") << ": " << std::to_string(transitTunnelCount) << "
\r\n
\r\n"; if(outputFormat==OutputFormatEnum::forWebConsole) { - bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); + bool httpproxy = i2p::client::context.GetHttpProxy () ? true : false; + bool socksproxy = i2p::client::context.GetSocksProxy () ? true : false; + bool bob = i2p::client::context.GetBOBCommandChannel () ? true : false; + bool sam = i2p::client::context.GetSAMBridge () ? true : false; + bool i2cp = i2p::client::context.GetI2CPServer () ? true : false; + bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; s << "
" << tr("Services") << "
" << "HTTP " << tr("Proxy") << "
" << "SOCKS " << tr("Proxy") << "
" << "BOB" << "
" << "SAM" << "
" << "I2CP" << "
" << "I2PControl" << "
" << "HTTP " << tr("Proxy") << "" << (httpproxy ? tr("Enabled") : tr("Disabled")) << "
" << "SOCKS " << tr("Proxy") << "" << (socksproxy ? tr("Enabled") : tr("Disabled")) << "
" << "BOB" << "" << (bob ? tr("Enabled") : tr("Disabled")) << "
" << "SAM" << "" << (sam ? tr("Enabled") : tr("Disabled")) << "
" << "I2CP" << "" << (i2cp ? tr("Enabled") : tr("Disabled")) << "
" << "I2PControl" << "" << (i2pcontrol ? tr("Enabled") : tr("Disabled")) << "
\r\n"; } } @@ -476,7 +448,7 @@ namespace http { s << "
"; it->Print(s); if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; + s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )"; ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); s << "
\r\n"; } @@ -486,7 +458,7 @@ namespace http { s << "
"; it->Print(s); if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; + s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ()); s << "
\r\n"; } @@ -534,7 +506,8 @@ namespace http { if (dest) { ShowLeaseSetDestination (s, dest, token); - // show streams + + // Print table with streams information s << "\r\n\r\n\r\n"; s << ""; s << "
" << tr("Streams") << "
StreamID"; // Stream closing button column @@ -679,26 +652,31 @@ namespace http { static void ShowCommands (std::stringstream& s, uint32_t token) { std::string webroot; i2p::config::GetOption("http.webroot", webroot); - /* commands */ + s << "" << tr("Router commands") << "
\r\n
\r\n
\r\n"; - s << " " << tr("Run peer test") << "\r\n"; - //s << " Reload config
\r\n"; + s << " " << tr("Run peer test") << "
\r\n"; + + // s << " Reload config
\r\n"; + if (i2p::context.AcceptsTunnels ()) - s << " " << tr("Decline transit tunnels") << "\r\n"; + s << " " << tr("Decline transit tunnels") << "
\r\n"; else - s << " " << tr("Accept transit tunnels") << "\r\n"; + s << " " << tr("Accept transit tunnels") << "
\r\n"; + #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) if (Daemon.gracefulShutdownInterval) - s << " " << tr("Cancel graceful shutdown") << "\r\n"; + s << " " << tr("Cancel graceful shutdown") << "
\r\n"; else - s << " " << tr("Start graceful shutdown") << "\r\n"; + s << " " << tr("Start graceful shutdown") << "
\r\n"; #elif defined(WIN32_APP) if (i2p::util::DaemonWin32::Instance().isGraceful) - s << " " << tr("Cancel graceful shutdown") << "\r\n"; + s << " " << tr("Cancel graceful shutdown") << "
\r\n"; else - s << " " << tr("Start graceful shutdown") << "\r\n"; + s << " " << tr("Start graceful shutdown") << "
\r\n"; #endif - s << " " << tr("Force shutdown") << "\r\n"; + + s << " " << tr("Force shutdown") << "

\r\n"; + s << " " << tr("Reload external CSS styles") << "\r\n"; s << "
"; s << "
\r\n" << tr("Note: any action done here are not persistent and not changes your config files.") << "\r\n
\r\n"; @@ -718,13 +696,26 @@ namespace http { s << " \r\n"; s << " \r\n"; s << "\r\n
\r\n"; + + std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language + s << "" << tr("Change language") << "
\r\n"; + s << "
\r\n"; + s << " \r\n"; + s << " \r\n"; + s << " \r\n"; + s << " \r\n"; + s << "
\r\n
\r\n"; + } void ShowTransitTunnels (std::stringstream& s) { if(i2p::tunnel::tunnels.CountTransitTunnels()) { - s << "" << tr("Transit tunnels") << ":
\r\n
\r\n"; + s << "" << tr("Transit Tunnels") << ":
\r\n
\r\n"; for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) { s << "
\r\n"; @@ -740,7 +731,7 @@ namespace http { } else { - s << "" << tr("Transit tunnels") << ": " << tr("no transit tunnels currently built") << ".
\r\n"; + s << "" << tr("Transit Tunnels") << ": " << tr("no transit tunnels currently built") << ".
\r\n"; } } @@ -979,16 +970,6 @@ namespace http { } } - std::string ConvertTime (uint64_t time) - { - lldiv_t divTime = lldiv(time, 1000); - time_t t = divTime.quot; - struct tm *tm = localtime(&t); - char date[128]; - snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03lld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem); - return date; - } - HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr socket): m_Socket (socket), m_BufferLen (0), expected_host(hostname) { @@ -1115,6 +1096,8 @@ namespace http { SendReply (res, content); } + std::map HTTPConnection::m_Tokens; + uint32_t HTTPConnection::CreateToken () { uint32_t token; @@ -1260,7 +1243,7 @@ namespace http { s << "" << tr("ERROR") << ": " << tr("StreamID can't be null") << "
\r\n
\r\n"; s << "" << tr("Return to destination page") << "
\r\n"; - s << "

" << tr("You will be redirected back in 5 seconds") << ""; + s << "

" << tr("You will be redirected in 5 seconds") << ""; redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32; res.add_header("Refresh", redirect.c_str()); return; @@ -1273,7 +1256,7 @@ namespace http { else { s << "" << tr("ERROR") << ": " << tr("Transit tunnels count must not exceed 65535") << "\r\n
\r\n
\r\n"; s << "" << tr("Back to commands list") << "\r\n
\r\n"; - s << "

" << tr("You will be redirected back in 5 seconds") << ""; + s << "

" << tr("You will be redirected in 5 seconds") << ""; res.add_header("Refresh", redirect.c_str()); return; } @@ -1281,7 +1264,7 @@ namespace http { else if (cmd == HTTP_COMMAND_GET_REG_STRING) { std::string b32 = params["b32"]; - std::string name = params["name"]; + std::string name = i2p::http::UrlDecode(params["name"]); i2p::data::IdentHash ident; ident.FromBase32 (b32); @@ -1327,6 +1310,18 @@ namespace http { s << "" << tr("Return to destination page") << "\r\n"; return; } + else if (cmd == HTTP_COMMAND_SETLANGUAGE) + { + std::string lang = params["lang"]; + std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); + + if (currLang.compare(lang) != 0) + i2p::i18n::SetLanguage(lang); + } + else if (cmd == HTTP_COMMAND_RELOAD_CSS) + { + LoadExtCSS(); + } else { res.code = 400; @@ -1389,6 +1384,8 @@ namespace http { m_Thread.reset (new std::thread (std::bind (&HTTPServer::Run, this))); m_Acceptor.listen (); Accept (); + + LoadExtCSS(); } void HTTPServer::Stop () diff --git a/daemon/HTTPServerResources.h b/daemon/HTTPServerResources.h new file mode 100644 index 00000000..876948e8 --- /dev/null +++ b/daemon/HTTPServerResources.h @@ -0,0 +1,89 @@ +/* +* Copyright (c) 2013-2021, 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 HTTP_SERVER_RESOURCES_H__ +#define HTTP_SERVER_RESOURCES_H__ + +namespace i2p +{ +namespace http +{ + const std::string itoopieFavicon = + "data:image/png;base64," + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx" + "jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0" + "d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB" + "vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn" + "cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP" + "6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps" + "/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X" + "+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/" + "z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/" + "P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am" + "+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM" + "EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1" + "oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ" + "JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ" + "RU5ErkJggg=="; + + // bundled style sheet + const std::string internalCSS = + "\r\n"; + + // for external style sheet + std::string externalCSS; + +} // http +} // i2p + +#endif /* HTTP_SERVER_RESOURCES_H__ */ diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index 12602c99..73dde618 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -406,7 +406,7 @@ namespace client void I2PControlService::UptimeHandler (std::ostringstream& results) { - InsertParam (results, "i2p.router.uptime", (int)i2p::context.GetUptime ()*1000); + InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL)); } void I2PControlService::VersionHandler (std::ostringstream& results) diff --git a/debian/changelog b/debian/changelog index 777b6216..c675f263 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +i2pd (2.39.0-1) unstable; urgency=medium + + * updated to version 2.39.0/0.9.51 + + -- orignal Mon, 23 Aug 2021 16:00:00 +0000 + i2pd (2.38.0-1) unstable; urgency=medium * updated to version 2.38.0/0.9.50 diff --git a/debian/patches/02-upnp.patch b/debian/patches/02-upnp.patch index 7b9bb317..75ea2bfa 100644 --- a/debian/patches/02-upnp.patch +++ b/debian/patches/02-upnp.patch @@ -6,12 +6,12 @@ Last-Update: 2021-01-16 --- i2pd.orig/Makefile +++ i2pd/Makefile -@@ -15,7 +15,7 @@ include filelist.mk - USE_AESNI := yes - USE_STATIC := no - USE_MESHNET := no --USE_UPNP := no -+USE_UPNP := yes - DEBUG := yes +@@ -21,7 +21,7 @@ include filelist.mk + USE_AESNI := $(or $(USE_AESNI),yes) + USE_STATIC := $(or $(USE_STATIC),no) + USE_MESHNET := $(or $(USE_MESHNET),no) +-USE_UPNP := $(or $(USE_UPNP),no) ++USE_UPNP := $(or $(USE_UPNP),yes) + DEBUG := $(or $(DEBUG),yes) ifeq ($(DEBUG),yes) diff --git a/filelist.mk b/filelist.mk index e2a5d40e..d8f503e6 100644 --- a/filelist.mk +++ b/filelist.mk @@ -21,4 +21,6 @@ LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_SRC_DIR)/*.cpp) LANG_SRC = $(wildcard $(LANG_SRC_DIR)/*.cpp) +WRAP_LIB_SRC = $(wildcard $(WRAP_SRC_DIR)/*.cpp) + DAEMON_SRC = $(wildcard $(DAEMON_SRC_DIR)/*.cpp) diff --git a/i18n/Afrikaans.cpp b/i18n/Afrikaans.cpp index 098c1105..5860facf 100644 --- a/i18n/Afrikaans.cpp +++ b/i18n/Afrikaans.cpp @@ -13,14 +13,16 @@ #include "I18N.h" // Afrikaans localization file -// This is an example translation file without strings in it. namespace i2p { namespace i18n { -namespace afrikaans // language +namespace afrikaans // language namespace { + // language name in lowercase + static std::string language = "afrikaans"; + // See for language plural forms here: // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html static int plural (int n) { @@ -29,12 +31,9 @@ namespace afrikaans // language static std::map strings { - {"Disabled", "Gedeaktiveer"}, - {"Enabled", "Geaktiveer"}, {"failed", "Het misluk"}, {"unknown", "onbekend"}, {"Tunnels", "Tonnels"}, - {"Transit tunnels", "Deurgang tonnels"}, {"I2P tunnels", "I2P tonnels"}, {"SAM sessions", "SAM sessies"}, {"OK", "LEKKER"}, @@ -52,6 +51,14 @@ namespace afrikaans // language {"Hidden content. Press on text to see.", "Hidden content. Druk om te sien."}, {"Router Ident", "Router Ident"}, {"Router Family", "Router Familie"}, + {"Enabled", "Geaktiveer"}, + {"Disabled", "Gedeaktiveer"}, + {"Change", "Verander"}, + {"Change language", "Verander taal"}, + {"Description", "Beskrywing"}, + {"Submit", "Stuur"}, + {"Proxy error", "Proxy-fout"}, + {"Host", "Gasheer"}, {"", ""}, }; @@ -66,7 +73,7 @@ namespace afrikaans // language std::shared_ptr GetLocale() { - return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/i18n/English.cpp b/i18n/English.cpp index 6015f8e1..2670e984 100644 --- a/i18n/English.cpp +++ b/i18n/English.cpp @@ -19,8 +19,11 @@ namespace i2p { namespace i18n { -namespace english // language +namespace english // language namespace { + // language name in lowercase + static std::string language = "english"; + // See for language plural forms here: // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html static int plural (int n) { @@ -39,7 +42,7 @@ namespace english // language std::shared_ptr GetLocale() { - return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/i18n/I18N.h b/i18n/I18N.h index 272f65e8..5024fb56 100644 --- a/i18n/I18N.h +++ b/i18n/I18N.h @@ -9,7 +9,7 @@ #ifndef __I18N_H__ #define __I18N_H__ -#include "RouterContext.h" +#include "ClientContext.h" namespace i2p { @@ -17,26 +17,21 @@ namespace i18n { inline void SetLanguage(const std::string &lang) { - if (!lang.compare("afrikaans")) - i2p::context.SetLanguage (i2p::i18n::afrikaans::GetLocale()); - else if (!lang.compare("russian")) - i2p::context.SetLanguage (i2p::i18n::russian::GetLocale()); - else if (!lang.compare("turkmen")) - i2p::context.SetLanguage (i2p::i18n::turkmen::GetLocale()); - else if (!lang.compare("ukrainian")) - i2p::context.SetLanguage (i2p::i18n::ukrainian::GetLocale()); - else // fallback - i2p::context.SetLanguage (i2p::i18n::english::GetLocale()); + const auto it = i2p::i18n::languages.find(lang); + if (it == i2p::i18n::languages.end()) // fallback + i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale()); + else + i2p::client::context.SetLanguage (it->second.LocaleFunc()); } inline std::string translate (const std::string& arg) { - return i2p::context.GetLanguage ()->GetString (arg); + return i2p::client::context.GetLanguage ()->GetString (arg); } inline std::string translate (const std::string& arg, const std::string& arg2, const int& n) { - return i2p::context.GetLanguage ()->GetPlural (arg, arg2, n); + return i2p::client::context.GetLanguage ()->GetPlural (arg, arg2, n); } } // i18n } // i2p diff --git a/i18n/I18N_langs.h b/i18n/I18N_langs.h index 181a4793..559b17be 100644 --- a/i18n/I18N_langs.h +++ b/i18n/I18N_langs.h @@ -17,10 +17,17 @@ namespace i18n { public: Locale ( + const std::string& language, const std::map& strings, const std::map>& plurals, std::function formula - ): m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { }; + ): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { }; + + // Get activated language name for webconsole + std::string GetLanguage() const + { + return m_Language; + } std::string GetString (const std::string& arg) const { @@ -50,17 +57,39 @@ namespace i18n } private: + const std::string m_Language; const std::map m_Strings; const std::map> m_Plurals; std::function m_Formula; }; + struct langData + { + std::string LocaleName; // localized name + std::string ShortCode; // short language code, like "en" + std::function (void)> LocaleFunc; + }; + // Add localization here with language name as namespace namespace afrikaans { std::shared_ptr GetLocale (); } namespace english { std::shared_ptr GetLocale (); } namespace russian { std::shared_ptr GetLocale (); } namespace turkmen { std::shared_ptr GetLocale (); } namespace ukrainian { std::shared_ptr GetLocale (); } + namespace uzbek { std::shared_ptr GetLocale (); } + + /** + * That map contains international language name lower-case and name in it's language + */ + static std::map languages + { + { "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} }, + { "english", {"English", "en", i2p::i18n::english::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} }, + }; } // i18n } // i2p diff --git a/i18n/Russian.cpp b/i18n/Russian.cpp index 7df82d54..f6a19c7c 100644 --- a/i18n/Russian.cpp +++ b/i18n/Russian.cpp @@ -18,8 +18,11 @@ namespace i2p { namespace i18n { -namespace russian // language +namespace russian // language namespace { + // language name in lowercase + static std::string language = "russian"; + // See for language plural forms here: // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html static int plural (int n) { @@ -28,23 +31,148 @@ namespace russian // language static std::map strings { - // HTTP Proxy + {"KiB", "КиБ"}, + {"MiB", "МиБ"}, + {"GiB", "ГиБ"}, + {"building", "строится"}, + {"failed", "неудачный"}, + {"expiring", "истекает"}, + {"established", "работает"}, + {"unknown", "неизвестно"}, + {"exploratory", "исследовательский"}, + {"i2pd webconsole", "Веб-консоль i2pd"}, + {"Main page", "Главная"}, + {"Router commands", "Команды роутера"}, + {"Local Destinations", "Локальные назначения"}, + {"LeaseSets", "Лизсеты"}, + {"Tunnels", "Туннели"}, + {"Transit Tunnels", "Транзитные туннели"}, + {"Transports", "Транспорты"}, + {"I2P tunnels", "I2P туннели"}, + {"SAM sessions", "SAM сессии"}, + {"ERROR", "ОШИБКА"}, + {"OK", "OK"}, + {"Testing", "Тестирование"}, + {"Firewalled", "Заблокировано извне"}, + {"Unknown", "Неизвестно"}, + {"Proxy", "Прокси"}, + {"Mesh", "MESH-сеть"}, + {"Error", "Ошибка"}, + {"Clock skew", "Не точное время"}, + {"Offline", "Оффлайн"}, + {"Symmetric NAT", "Симметричный NAT"}, + {"Uptime", "В сети"}, + {"Network status", "Сетевой статус"}, + {"Network status v6", "Сетевой статус v6"}, + {"Stopping in", "Остановка через"}, + {"Family", "Семейство"}, + {"Tunnel creation success rate", "Успешно построенных туннелей"}, + {"Received", "Получено"}, + {"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", "Сгенерировать"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Примечание: полученная строка может быть использована только для регистрации доменов второго уровня (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", "Не истекшие Lease-ы"}, + {"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 стили"}, + {"Note: any action done here are not persistent and not changes your config files.", "Примечание: любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."}, + {"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", "Вы можете попробовать найти адрес на джамп сервисах ниже"}, + {"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", "addresshelper не поддерживается"}, - {"Host", "Адрес"}, + {"Host", "Узел"}, {"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"}, - {"already in router's addressbook", "уже в адресной книге роутера"}, - {"Click", "Нажмите"}, - {"here", "здесь"}, - {"to proceed", "чтобы продолжить"}, - {"to update record", "чтобы обновить запись"}, + {"Click here to proceed:", "Нажмите здесь, чтобы продолжить:"}, + {"Continue", "Продолжить"}, {"Addresshelper found", "Найден addresshelper"}, + {"already in router's addressbook", "уже в адресной книге роутера"}, + {"Click here to update record:", "Нажмите здесь, чтобы обновить запись:"}, {"invalid request uri", "некорректный URI запроса"}, {"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"}, {"Outproxy failure", "Ошибка внешнего прокси"}, @@ -63,169 +191,13 @@ namespace russian // language {"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.", - "Не удалось установить соединение к запрошенному адресу, возможно он не в сети. Попробуйте повторить запрос позже."}, - - // Webconsole // - // cssStyles - {"Disabled", "Выключено"}, - {"Enabled", "Включено"}, - // ShowTraffic - {"KiB", "КиБ"}, - {"MiB", "МиБ"}, - {"GiB", "ГиБ"}, - // ShowTunnelDetails - {"building", "строится"}, - {"failed", "неудачный"}, - {"expiring", "истекает"}, - {"established", "работает"}, - {"exploratory", "исследовательский"}, - {"unknown", "неизвестно"}, - {"i2pd webconsole", "Веб-консоль i2pd"}, - // ShowPageHead - {"Main page", "Главная"}, - {"Router commands", "Команды роутера"}, - {"Local destinations", "Локальные назнач."}, - {"LeaseSets", "Лизсеты"}, - {"Tunnels", "Туннели"}, - {"Transit tunnels", "Транзит. туннели"}, - {"Transports", "Транспорты"}, - {"I2P tunnels", "I2P туннели"}, - {"SAM sessions", "SAM сессии"}, - // Network Status - {"OK", "OK"}, - {"Testing", "Тестирование"}, - {"Firewalled", "Заблокировано извне"}, - {"Unknown", "Неизвестно"}, - {"Proxy", "Прокси"}, - {"Mesh", "MESH-сеть"}, - {"Error", "Ошибка"}, - {"Clock skew", "Не точное время"}, - {"Offline", "Оффлайн"}, - {"Symmetric NAT", "Симметричный NAT"}, - // Status - {"Uptime", "В сети"}, - {"Network status", "Сетевой статус"}, - {"Network status v6", "Сетевой статус v6"}, - {"Stopping in", "Остановка через"}, - {"Family", "Семейство"}, - {"Tunnel creation success rate", "Успешно построенных туннелей"}, - {"Received", "Получено"}, - {"Sent", "Отправлено"}, - {"Transit", "Транзит"}, - {"KiB/s", "КиБ/с"}, - {"Data path", "Путь к данным"}, - {"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."}, - {"Router Ident", "Идентификатор роутера"}, - {"Router Family", "Семейство роутера"}, - {"Router Caps", "Флаги роутера"}, - {"Version", "Версия"}, - {"Our external address", "Наш внешний адрес"}, - {"supported", "поддерживается"}, - {"Routers", "Роутеры"}, - {"Floodfills", "Флудфилы"}, - {"LeaseSets", "Лизсеты"}, - {"Client Tunnels", "Клиентские туннели"}, - {"Transit Tunnels", "Транзитные туннели"}, - {"Services", "Сервисы"}, - // ShowLocalDestinations - {"Local Destinations", "Локальные назначения"}, - // ShowLeaseSetDestination - {"Encrypted B33 address", "Шифрованные B33 адреса"}, - {"Address registration line", "Строка регистрации адреса"}, - {"Domain", "Домен"}, - {"Generate", "Сгенерировать"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", - "Примечание: полученная строка может быть использована только для регистрации доменов второго уровня. Для регистрации поддоменов используйте i2pd-tools."}, - {"Address", "Адрес"}, - {"Type", "Тип"}, - {"EncType", "ТипШифр"}, - {"Inbound tunnels", "Входящие туннели"}, - {"Outbound tunnels", "Исходящие туннели"}, - {"ms", "мс"}, // milliseconds - {"Tags", "Теги"}, - {"Incoming", "Входящие"}, - {"Outgoing", "Исходящие"}, - {"Destination", "Назначение"}, - {"Amount", "Количество"}, - {"Incoming Tags", "Входящие Теги"}, - {"Tags sessions", "Сессии Тегов"}, - {"Status", "Статус"}, - // ShowLocalDestination - {"Local Destination", "Локальное назначение"}, - {"Streams", "Стримы"}, - {"Close stream", "Закрыть стрим"}, - // ShowI2CPLocalDestination - {"I2CP session not found", "I2CP сессия не найдена"}, - {"I2CP is not enabled", "I2CP не включен"}, - // ShowLeasesSets - {"Invalid", "Некорректный"}, - {"Store type", "Тип хранилища"}, - {"Expires", "Истекает"}, - {"Non Expired Leases", "Не истекшие Lease-ы"}, - {"Gateway", "Шлюз"}, - {"TunnelID", "ID туннеля"}, - {"EndDate", "Заканчивается"}, - {"not floodfill", "не флудфил"}, - // ShowTunnels - {"Queue size", "Размер очереди"}, - // ShowCommands - {"Run peer test", "Запустить тестирование"}, - {"Decline transit tunnels", "Отклонять транзитные туннели"}, - {"Accept transit tunnels", "Принимать транзитные туннели"}, - {"Cancel graceful shutdown", "Отменить плавную остановку"}, - {"Start graceful shutdown", "Запустить плавную остановку"}, - {"Force shutdown", "Принудительная остановка"}, - {"Note: any action done here are not persistent and not changes your config files.", - "Примечание: любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."}, - {"Logging level", "Уровень логирования"}, - {"Transit tunnels limit", "Лимит транзитных туннелей"}, - {"Change", "Изменить"}, - // ShowTransitTunnels - {"no transit tunnels currently built", "нет построенных транзитных туннелей"}, - // ShowSAMSessions/ShowSAMSession - {"SAM disabled", "SAM выключен"}, - {"SAM session not found", "SAM сессия не найдена"}, - {"no sessions currently running", "нет запущенных сессий"}, - {"SAM Session", "SAM сессия"}, - // ShowI2PTunnels - {"Server Tunnels", "Серверные туннели"}, - {"Client Forwards", "Клиентские перенаправления"}, - {"Server Forwards", "Серверные перенаправления"}, - // HandlePage - {"Unknown page", "Неизвестная страница"}, - // HandleCommand, ShowError - {"Invalid token", "Неверный токен"}, - {"SUCCESS", "УСПЕШНО"}, - {"ERROR", "ОШИБКА"}, - {"Unknown command", "Неизвестная команда"}, - {"Command accepted", "Команда принята"}, - {"Back to commands list", "Вернуться к списку команд"}, - {"You will be redirected in 5 seconds", "Вы будете переадресованы через 5 секунд"}, - // HTTP_COMMAND_KILLSTREAM - {"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 back in 5 seconds", "Вы будете переадресованы назад через 5 секунд"}, - // HTTP_COMMAND_LIMITTRANSIT - {"Transit tunnels count must not exceed 65535", "Число транзитных туннелей не должно превышать 65535"}, - // HTTP_COMMAND_GET_REG_STRING - {"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", "Такая точка назначения не найдена"}, + {"Host is down", "Узел недоступен"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Не удалось установить соединение к запрошенному узлу, возможно он не в сети. Попробуйте повторить запрос позже."}, {"", ""}, }; static std::map> plurals { - // ShowUptime {"days", {"день", "дня", "дней"}}, {"hours", {"час", "часа", "часов"}}, {"minutes", {"минуту", "минуты", "минут"}}, @@ -235,7 +207,7 @@ namespace russian // language std::shared_ptr GetLocale() { - return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/i18n/Turkmen.cpp b/i18n/Turkmen.cpp index 0edc9a6e..1d4f5ee1 100644 --- a/i18n/Turkmen.cpp +++ b/i18n/Turkmen.cpp @@ -18,8 +18,11 @@ namespace i2p { namespace i18n { -namespace turkmen // language +namespace turkmen // language namespace { + // language name in lowercase + static std::string language = "turkmen"; + // See for language plural forms here: // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html static int plural (int n) { @@ -28,7 +31,133 @@ namespace turkmen // language static std::map strings { - // HTTP Proxy + {"KiB", "KiB"}, + {"MiB", "MiB"}, + {"GiB", "GiB"}, + {"building", "bina"}, + {"failed", "şowsuz"}, + {"expiring", "möhleti gutarýar"}, + {"established", "işleýär"}, + {"unknown", "näbelli"}, + {"exploratory", "gözleg"}, + {"i2pd webconsole", "Web konsoly i2pd"}, + {"Main page", "Esasy sahypa"}, + {"Router commands", "Marşrutizator buýruklary"}, + {"Local Destinations", "Ýerli ýerler"}, + {"LeaseSets", "Lizset"}, + {"Tunnels", "Tuneller"}, + {"Transit Tunnels", "Tranzit Tunelleri"}, + {"Transports", "Daşamak"}, + {"I2P tunnels", "I2P tuneller"}, + {"SAM sessions", "SAM Sessiýasy"}, + {"ERROR", "Ýalňyşlyk"}, + {"OK", "OK"}, + {"Testing", "Synag etmek"}, + {"Firewalled", "Daşynda petiklendi"}, + {"Unknown", "Näbelli"}, + {"Proxy", "Proksi"}, + {"Mesh", "MESH-tor"}, + {"Error", "Ýalňyşlyk"}, + {"Clock skew", "Takyk wagt däl"}, + {"Offline", "Awtonom"}, + {"Symmetric NAT", "Simmetriklik NAT"}, + {"Uptime", "Onlaýn onlaýn sözlügi"}, + {"Network status", "Tor ýagdaýy"}, + {"Network status v6", "Tor ýagdaýy v6"}, + {"Stopping in", "Soň duruň"}, + {"Family", "Maşgala"}, + {"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"}, + {"Received", "Alnan"}, + {"KiB/s", "KiB/s"}, + {"Sent", "Ýerleşdirildi"}, + {"Transit", "Tranzit"}, + {"Data path", "Maglumat ýoly"}, + {"Hidden content. Press on text to see.", "Gizlin mazmun. Görkezmek üçin tekste basyň."}, + {"Router Ident", "Marşrutly kesgitleýji"}, + {"Router Family", "Marşrutler maşgalasy"}, + {"Router Caps", "Baýdaklar marşruteri"}, + {"Version", "Wersiýasy"}, + {"Our external address", "Daşarky salgymyz"}, + {"supported", "goldanýar"}, + {"Routers", "Marşrutizatorlar"}, + {"Floodfills", "Fludfillar"}, + {"Client Tunnels", "Müşderi tunelleri"}, + {"Services", "Hyzmatlar"}, + {"Enabled", "Goşuldy"}, + {"Disabled", "Öçürildi"}, + {"Encrypted B33 address", "Şifrlenen B33 salgylar"}, + {"Address registration line", "Hasaba alyş salgysy"}, + {"Domain", "Domen"}, + {"Generate", "Öndürmek"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Bellik: Alnan setir diňe ikinji derejeli domenleri bellige almak üçin ulanylyp bilner (example.i2p). Subýutmalary hasaba almak üçin i2pd ulanyň-tools."}, + {"Address", "Salgysy"}, + {"Type", "Görnüş"}, + {"EncType", "Şifrlemek görnüşi"}, + {"Inbound tunnels", "Gelýän tuneller"}, + {"ms", "ms"}, + {"Outbound tunnels", "Çykýan tuneller"}, + {"Tags", "Bellikler"}, + {"Incoming", "Gelýän"}, + {"Outgoing", "Çykýan"}, + {"Destination", "Maksat"}, + {"Amount", "Sany"}, + {"Incoming Tags", "Gelýän bellikler"}, + {"Tags sessions", "Sapaklar bellikler"}, + {"Status", "Ýagdaýy"}, + {"Local Destination", "Ýerli maksat"}, + {"Streams", "Strimlary"}, + {"Close stream", "Yap strim"}, + {"I2CP session not found", "I2CP Sessiýa tapylmady"}, + {"I2CP is not enabled", "I2CP goşulmaýar"}, + {"Invalid", "Nädogry"}, + {"Store type", "Ammar görnüşi"}, + {"Expires", "Möhleti gutarýar"}, + {"Non Expired Leases", "Möhleti gutarmady Lizsetlary"}, + {"Gateway", "Derweze"}, + {"TunnelID", "Tuneliň ID"}, + {"EndDate", "Gutarýar"}, + {"not floodfill", "fludfil däl"}, + {"Queue size", "Nobatyň ululygy"}, + {"Run peer test", "Synag başlaň"}, + {"Decline transit tunnels", "Tranzit tunellerini ret ediň"}, + {"Accept transit tunnels", "Tranzit tunellerini alyň"}, + {"Cancel graceful shutdown", "Tekiz durmagy ýatyryň"}, + {"Start graceful shutdown", "Tekiz durmak"}, + {"Force shutdown", "Mejbury duralga"}, + {"Reload external CSS styles", "Daşarky CSS stillerini täzeden ýükläň"}, + {"Note: any action done here are not persistent and not changes your config files.", "Bellik: Bu ýerde öndürilen islendik çäre hemişelik däl we konfigurasiýa faýllaryňyzy üýtgetmeýär."}, + {"Logging level", "Giriş derejesi"}, + {"Transit tunnels limit", "Tranzit tunelleriniň çägi"}, + {"Change", "Üýtgetmek"}, + {"Change language", "Dil üýtgetmek"}, + {"no transit tunnels currently built", "gurlan tranzit tunelleri ýok"}, + {"SAM disabled", "SAM öçürilen"}, + {"no sessions currently running", "başlamagyň sessiýalary ýok"}, + {"SAM session not found", "SAM Sessiýa tapylmady"}, + {"SAM Session", "SAM Sessiýa"}, + {"Server Tunnels", "Serwer tunelleri"}, + {"Client Forwards", "Müşderi gönükdirýär"}, + {"Server Forwards", "Serweriň täzeden düzlüleri"}, + {"Unknown page", "Näbelli sahypa"}, + {"Invalid token", "Nädogry token"}, + {"SUCCESS", "Üstünlikli"}, + {"Stream closed", "Strim ýapyk"}, + {"Stream not found or already was closed", "Strim tapylmady ýa-da eýýäm ýapyldy"}, + {"Destination not found", "Niýetlenen ýeri tapylmady"}, + {"StreamID can't be null", "StreamID boş bolup bilmez"}, + {"Return to destination page", "Barmaly nokadynyň nokadyna gaýdyp geliň"}, + {"You will be redirected in 5 seconds", "5 sekuntdan soň täzeden ugrukdyrylarsyňyz"}, + {"Transit tunnels count must not exceed 65535", "Tranzit tagtalaryň sany 65535-den geçmeli däldir"}, + {"Back to commands list", "Topar sanawyna dolan"}, + {"Register at reg.i2p", "Reg.i2P-de hasaba duruň"}, + {"Description", "Beýany"}, + {"A bit information about service on domain", "Domendäki hyzmat barada käbir maglumatlar"}, + {"Submit", "Iber"}, + {"Domain can't end with .b32.i2p", "Domain .b32.i2p bilen gutaryp bilmez"}, + {"Domain must end with .i2p", "Domeni .i2p bilen gutarmaly"}, + {"Such destination is not found", "Bu barmaly ýer tapylmady"}, + {"Unknown command", "Näbelli topar"}, + {"Command accepted", "Topar kabul edilýär"}, {"Proxy error", "Proksi ýalňyşlygy"}, {"Proxy info", "Proksi maglumat"}, {"Proxy error: Host not found", "Proksi ýalňyşlygy: Host tapylmady"}, @@ -39,12 +168,11 @@ namespace turkmen // language {"addresshelper is not supported", "Salgylandyryjy goldanok"}, {"Host", "Adres"}, {"added to router's addressbook from helper", "marşruteriň adresini kömekçiden goşdy"}, - {"already in router's addressbook", "marşruteriň adres kitaby"}, - {"Click", "Basyň"}, - {"here", "bu ýerde"}, - {"to proceed", "dowam etmek"}, - {"to update record", "recordazgyny täzelemek üçin"}, + {"Click here to proceed:", "Dowam etmek bu ýerde basyň:"}, + {"Continue", "Dowam et"}, {"Addresshelper found", "Forgelper tapyldy"}, + {"already in router's addressbook", "marşruteriň adres kitaby"}, + {"Click here to update record:", "Recordazgyny täzelemek üçin bu ýerde basyň:"}, {"invalid request uri", "nädogry haýyş URI"}, {"Can't detect destination host from request", "Haýyşdan barmaly ýerini tapyp bilemok"}, {"Outproxy failure", "Daşarky proksi ýalňyşlyk"}, @@ -64,161 +192,7 @@ namespace turkmen // language {"http out proxy not implemented", "daşarky HTTP proksi serwerini goldamak amala aşyrylmaýar"}, {"cannot connect to upstream http proxy", "ýokary akym HTTP proksi serwerine birigip bilmedi"}, {"Host is down", "Salgy elýeterli däl"}, - {"Can't create connection to requested host, it may be down. Please try again later.", - "Talap edilýän salgyda birikmäni gurup bilmedim, onlaýn bolup bilmez. Soňra haýyşy soň gaýtalamaga synanyşyň."}, - - // Webconsole // - // cssStyles - {"Disabled", "Öçürildi"}, - {"Enabled", "Goşuldy"}, - // ShowTraffic - {"KiB", "KiB"}, - {"MiB", "MiB"}, - {"GiB", "GiB"}, - // ShowTunnelDetails - {"building", "bina"}, - {"failed", "şowsuz"}, - {"expiring", "möhleti gutarýar"}, - {"established", "işleýär"}, - {"exploratory", "gözleg"}, - {"unknown", "näbelli"}, - {"i2pd webconsole", "Web konsoly i2pd"}, - // ShowPageHead - {"Main page", "Esasy sahypa"}, - {"Router commands", "Marşrutizator buýruklary"}, - {"Local destinations", "Ýerli ýerler"}, - {"LeaseSets", "Lizset"}, - {"Tunnels", "Tuneller"}, - {"Transit tunnels", "Tranzit tunels"}, - {"Transports", "Daşamak"}, - {"I2P tunnels", "I2P tuneller"}, - {"SAM sessions", "SAM Sessiýasy"}, - // Network Status - {"OK", "OK"}, - {"Testing", "Synag etmek"}, - {"Firewalled", "Daşynda petiklendi"}, - {"Unknown", "Näbelli"}, - {"Proxy", "Proksi"}, - {"Mesh", "MESH-tor"}, - {"Error", "Ýalňyşlyk"}, - {"Clock skew", "Takyk wagt däl"}, - {"Offline", "Awtonom"}, - {"Symmetric NAT", "Simmetriklik NAT"}, - // Status - {"Uptime", "Onlaýn onlaýn sözlügi"}, - {"Network status", "Tor ýagdaýy"}, - {"Network status v6", "Tor ýagdaýy v6"}, - {"Stopping in", "Soň duruň"}, - {"Family", "Maşgala"}, - {"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"}, - {"Received", "Alnan"}, - {"Sent", "Ýerleşdirildi"}, - {"Transit", "Tranzit"}, - {"KiB/s", "KiB/s"}, - {"Data path", "Maglumat ýoly"}, - {"Hidden content. Press on text to see.", "Gizlin mazmun. Görkezmek üçin tekste basyň."}, - {"Router Ident", "Marşrutly kesgitleýji"}, - {"Router Family", "Marşrutler maşgalasy"}, - {"Router Caps", "Baýdaklar marşruteri"}, - {"Version", "Wersiýasy"}, - {"Our external address", "Daşarky salgymyz"}, - {"supported", "goldanýar"}, - {"Routers", "Marşrutizatorlar"}, - {"Floodfills", "Fludfillar"}, - {"Client Tunnels", "Müşderi tunelleri"}, - {"Transit Tunnels", "Tranzit Tunelleri"}, - {"Services", "Hyzmatlar"}, - // ShowLocalDestinations - {"Local Destinations", "Ýerli ýerler"}, - // ShowLeaseSetDestination - {"Encrypted B33 address", "Şifrlenen B33 salgylar"}, - {"Address registration line", "Hasaba alyş salgysy"}, - {"Domain", "Domen"}, - {"Generate", "Öndürmek"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", - "Bellik: Alnan setir diňe ikinji derejeli domenleri bellige almak üçin ulanylyp bilner. Subýutmalary hasaba almak üçin i2pd ulanyň-tools."}, - {"Address", "Salgysy"}, - {"Type", "Görnüş"}, - {"EncType", "Şifrlemek görnüşi"}, - {"Inbound tunnels", "Gelýän tuneller"}, - {"Outbound tunnels", "Çykýan tuneller"}, - {"ms", "ms"}, // milliseconds - {"Tags", "Bellikler"}, - {"Incoming", "Gelýän"}, - {"Outgoing", "Çykýan"}, - {"Destination", "Maksat"}, - {"Amount", "Sany"}, - {"Incoming Tags", "Gelýän bellikler"}, - {"Tags sessions", "Sapaklar bellikler"}, - {"Status", "Ýagdaýy"}, - // ShowLocalDestination - {"Local Destination", "Ýerli maksat"}, - {"Streams", "Strimlary"}, - {"Close stream", "Yap strim"}, - // ShowI2CPLocalDestination - {"I2CP session not found", "I2CP Sessiýa tapylmady"}, - {"I2CP is not enabled", "I2CP goşulmaýar"}, - // ShowLeasesSets - {"Invalid", "Nädogry"}, - {"Store type", "Ammar görnüşi"}, - {"Expires", "Möhleti gutarýar"}, - {"Non Expired Leases", "Möhleti gutarmady Lizsetlary"}, - {"Gateway", "Derweze"}, - {"TunnelID", "Tuneliň ID"}, - {"EndDate", "Gutarýar"}, - {"not floodfill", "fludfil däl"}, - // ShowTunnels - {"Queue size", "Nobatyň ululygy"}, - // ShowCommands - {"Run peer test", "Synag başlaň"}, - {"Decline transit tunnels", "Tranzit tunellerini ret ediň"}, - {"Accept transit tunnels", "Tranzit tunellerini alyň"}, - {"Cancel graceful shutdown", "Tekiz durmagy ýatyryň"}, - {"Start graceful shutdown", "Tekiz durmak"}, - {"Force shutdown", "Mejbury duralga"}, - {"Note: any action done here are not persistent and not changes your config files.", - "Bellik: Bu ýerde öndürilen islendik çäre hemişelik däl we konfigurasiýa faýllaryňyzy üýtgetmeýär."}, - {"Logging level", "Giriş derejesi"}, - {"Transit tunnels limit", "Tranzit tunelleriniň çägi"}, - {"Change", "Üýtgetmek"}, - // ShowTransitTunnels - {"no transit tunnels currently built", "gurlan tranzit tunelleri ýok"}, - // ShowSAMSessions/ShowSAMSession - {"SAM disabled", "SAM öçürilen"}, - {"SAM session not found", "SAM Sessiýa tapylmady"}, - {"no sessions currently running", "başlamagyň sessiýalary ýok"}, - {"SAM Session", "SAM Sessiýa"}, - // ShowI2PTunnels - {"Server Tunnels", "Serwer tunelleri"}, - {"Client Forwards", "Müşderi gönükdirýär"}, - {"Server Forwards", "Serweriň täzeden düzlüleri"}, - // HandlePage - {"Unknown page", "Näbelli sahypa"}, - // HandleCommand, ShowError - {"Invalid token", "Nädogry token"}, - {"SUCCESS", "Üstünlikli"}, - {"ERROR", "Ýalňyşlyk"}, - {"Unknown command", "Näbelli topar"}, - {"Command accepted", "Topar kabul edilýär"}, - {"Back to commands list", "Topar sanawyna dolan"}, - {"You will be redirected in 5 seconds", "5 sekuntdan soň täzeden ugrukdyrylarsyňyz"}, - // HTTP_COMMAND_KILLSTREAM - {"Stream closed", "Strim ýapyk"}, - {"Stream not found or already was closed", "Strim tapylmady ýa-da eýýäm ýapyldy"}, - {"Destination not found", "Niýetlenen ýeri tapylmady"}, - {"StreamID can't be null", "StreamID boş bolup bilmez"}, - {"Return to destination page", "Barmaly nokadynyň nokadyna gaýdyp geliň"}, - {"You will be redirected back in 5 seconds", "5 sekuntda yzyna iberiler"}, - // HTTP_COMMAND_LIMITTRANSIT - {"Transit tunnels count must not exceed 65535", "Tranzit tagtalaryň sany 65535-den geçmeli däldir"}, - // HTTP_COMMAND_GET_REG_STRING - {"Register at reg.i2p", "Reg.i2P-de hasaba duruň"}, - {"Description", "Beýany"}, - {"A bit information about service on domain", "Domendäki hyzmat barada käbir maglumatlar"}, - {"Submit", "Iber"}, - {"Domain can't end with .b32.i2p", "Domain .b32.i2p bilen gutaryp bilmez"}, - {"Domain must end with .i2p", "Domeni .i2p bilen gutarmaly"}, - {"Such destination is not found", "Bu barmaly ýer tapylmady"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Talap edilýän salgyda birikmäni gurup bilmedim, onlaýn bolup bilmez. Soňra haýyşy soň gaýtalamaga synanyşyň."}, {"", ""}, }; @@ -234,7 +208,7 @@ namespace turkmen // language std::shared_ptr GetLocale() { - return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/i18n/Ukrainian.cpp b/i18n/Ukrainian.cpp index 11878cde..abbe8f81 100644 --- a/i18n/Ukrainian.cpp +++ b/i18n/Ukrainian.cpp @@ -18,8 +18,11 @@ namespace i2p { namespace i18n { -namespace ukrainian // language +namespace ukrainian // language namespace { + // language name in lowercase + static std::string language = "ukrainian"; + // See for language plural forms here: // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html static int plural (int n) { @@ -28,23 +31,148 @@ namespace ukrainian // language static std::map strings { - // HTTP Proxy + {"KiB", "КіБ"}, + {"MiB", "МіБ"}, + {"GiB", "ГіБ"}, + {"building", "будується"}, + {"failed", "невдалий"}, + {"expiring", "завершується"}, + {"established", "працює"}, + {"unknown", "невідомо"}, + {"exploratory", "дослідницький"}, + {"i2pd webconsole", "Веб-консоль i2pd"}, + {"Main page", "Головна"}, + {"Router commands", "Команди маршрутизатора"}, + {"Local Destinations", "Локальні Призначення"}, + {"LeaseSets", "Лізсети"}, + {"Tunnels", "Тунелі"}, + {"Transit Tunnels", "Транзитні Тунелі"}, + {"Transports", "Транспорти"}, + {"I2P tunnels", "I2P тунелі"}, + {"SAM sessions", "SAM сесії"}, + {"ERROR", "ПОМИЛКА"}, + {"OK", "OK"}, + {"Testing", "Тестування"}, + {"Firewalled", "Заблоковано ззовні"}, + {"Unknown", "Невідомо"}, + {"Proxy", "Проксі"}, + {"Mesh", "MESH-мережа"}, + {"Error", "Помилка"}, + {"Clock skew", "Неточний час"}, + {"Offline", "Офлайн"}, + {"Symmetric NAT", "Симетричний NAT"}, + {"Uptime", "У мережі"}, + {"Network status", "Мережевий статус"}, + {"Network status v6", "Мережевий статус v6"}, + {"Stopping in", "Зупинка через"}, + {"Family", "Сімейство"}, + {"Tunnel creation success rate", "Успішно побудованих тунелів"}, + {"Received", "Отримано"}, + {"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", "Згенерувати"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Примітка: отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня (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", "Не завершені Lease-и"}, + {"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"}, + {"Note: any action done here are not persistent and not changes your config files.", "Примітка: будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."}, + {"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", "Ідентифікатор потоку не може бути порожнім"}, + {"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", "Віддалена адреса не знайдена в адресній книзі роутера"}, + {"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", "addresshelper не підтримується"}, {"Host", "Адреса"}, - {"added to router's addressbook from helper", "доданий в адресну книгу роутера через хелпер"}, - {"already in router's addressbook", "вже в адресній книзі роутера"}, - {"Click", "Натисніть"}, - {"here", "тут"}, - {"to proceed", "щоб продовжити"}, - {"to update record", "щоб оновити запис"}, + {"added to router's addressbook from helper", "доданий в адресну книгу маршрутизатора через хелпер"}, + {"Click here to proceed:", "Натисніть тут щоб продовжити:"}, + {"Continue", "Продовжити"}, {"Addresshelper found", "Знайдено addresshelper"}, + {"already in router's addressbook", "вже в адресній книзі маршрутизатора"}, + {"Click here to update record:", "Натисніть тут щоб оновити запис:"}, {"invalid request uri", "некоректний URI запиту"}, {"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"}, {"Outproxy failure", "Помилка зовнішнього проксі"}, @@ -64,169 +192,14 @@ namespace ukrainian // language {"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.", - "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."}, - - // Webconsole // - // cssStyles - {"Disabled", "Вимкнуто"}, - {"Enabled", "Увімкнуто"}, - // ShowTraffic - {"KiB", "КіБ"}, - {"MiB", "МіБ"}, - {"GiB", "ГіБ"}, - // ShowTunnelDetails - {"building", "будується"}, - {"failed", "невдалий"}, - {"expiring", "завершується"}, - {"established", "працює"}, - {"exploratory", "дослідницький"}, - {"unknown", "невідомо"}, - {"i2pd webconsole", "Веб-консоль i2pd"}, - // ShowPageHead - {"Main page", "Головна"}, - {"Router commands", "Команди роутера"}, - {"Local destinations", "Локальні призначення"}, - {"LeaseSets", "Лізсети"}, - {"Tunnels", "Тунелі"}, - {"Transit tunnels", "Транзитні тунелі"}, - {"Transports", "Транспорти"}, - {"I2P tunnels", "I2P тунелі"}, - {"SAM sessions", "SAM сесії"}, - // Network Status - {"OK", "OK"}, - {"Testing", "Тестування"}, - {"Firewalled", "Заблоковано ззовні"}, - {"Unknown", "Невідомо"}, - {"Proxy", "Проксі"}, - {"Mesh", "MESH-мережа"}, - {"Error", "Помилка"}, - {"Clock skew", "Неточний час"}, - {"Offline", "Офлайн"}, - {"Symmetric NAT", "Симетричний NAT"}, - // Status - {"Uptime", "В мережі"}, - {"Network status", "Мережевий статус"}, - {"Network status v6", "Мережевий статус v6"}, - {"Stopping in", "Зупинка через"}, - {"Family", "Сімейство"}, - {"Tunnel creation success rate", "Успішно побудованих тунелів"}, - {"Received", "Отримано"}, - {"Sent", "Відправлено"}, - {"Transit", "Транзит"}, - {"KiB/s", "КіБ/с"}, - {"Data path", "Шлях до даних"}, - {"Hidden content. Press on text to see.", "Прихований вміст. Натисніть на текст щоб відобразити."}, - {"Router Ident", "Ідентифікатор Роутера"}, - {"Router Family", "Сімейство Роутера"}, - {"Router Caps", "Прапорці Роутера"}, - {"Version", "Версія"}, - {"Our external address", "Наша зовнішня адреса"}, - {"supported", "підтримується"}, - {"Routers", "Роутери"}, - {"Floodfills", "Флудфіли"}, - {"Client Tunnels", "Клієнтські Тунелі"}, - {"Transit Tunnels", "Транзитні Тунелі"}, - {"Services", "Сервіси"}, - // ShowLocalDestinations - {"Local Destinations", "Локальні Призначення"}, - // ShowLeaseSetDestination - {"Encrypted B33 address", "Шифровані B33 адреси"}, - {"Address registration line", "Рядок реєстрації адреси"}, - {"Domain", "Домен"}, - {"Generate", "Згенерувати"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", - "Примітка: отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня. Для реєстрації піддоменів використовуйте i2pd-tools."}, - {"Address", "Адреса"}, - {"Type", "Тип"}, - {"EncType", "ТипШифр"}, - {"Inbound tunnels", "Вхідні тунелі"}, - {"Outbound tunnels", "Вихідні тунелі"}, - {"ms", "мс"}, // milliseconds - {"Tags", "Теги"}, - {"Incoming", "Вхідні"}, - {"Outgoing", "Вихідні"}, - {"Destination", "Призначення"}, - {"Amount", "Кількість"}, - {"Incoming Tags", "Вхідні Теги"}, - {"Tags sessions", "Сесії тегів"}, - {"Status", "Статус"}, - // ShowLocalDestination - {"Local Destination", "Локальні Призначення"}, - {"Streams", "Потоки"}, - {"Close stream", "Закрити потік"}, - // ShowI2CPLocalDestination - {"I2CP session not found", "I2CP сесія не знайдена"}, - {"I2CP is not enabled", "I2CP не увікнуто"}, - // ShowLeasesSets - {"Invalid", "Некоректний"}, - {"Store type", "Тип сховища"}, - {"Expires", "Завершується"}, - {"Non Expired Leases", "Не завершені Lease-и"}, - {"Gateway", "Шлюз"}, - {"TunnelID", "ID тунеля"}, - {"EndDate", "Закінчується"}, - {"not floodfill", "не флудфіл"}, - // ShowTunnels - {"Queue size", "Розмір черги"}, - // ShowCommands - {"Run peer test", "Запустити тестування"}, - {"Decline transit tunnels", "Відхиляти транзитні тунелі"}, - {"Accept transit tunnels", "Ухвалювати транзитні тунелі"}, - {"Cancel graceful shutdown", "Скасувати плавну зупинку"}, - {"Start graceful shutdown", "Запустити плавну зупинку"}, - {"Force shutdown", "Примусова зупинка"}, - {"Note: any action done here are not persistent and not changes your config files.", - "Примітка: будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."}, - {"Logging level", "Рівень логування"}, - {"Transit tunnels limit", "Обмеження транзитних тунелів"}, - {"Change", "Змінити"}, - // ShowTransitTunnels - {"no transit tunnels currently built", "немає побудованих транзитних тунелів"}, - // ShowSAMSessions/ShowSAMSession - {"SAM disabled", "SAM вимкнуто"}, - {"SAM session not found", "SAM сесія не знайдена"}, - {"no sessions currently running", "немає запущених сесій"}, - {"SAM Session", "SAM сесія"}, - // ShowI2PTunnels - {"Server Tunnels", "Серверні Тунелі"}, - {"Client Forwards", "Клієнтські Переспрямування"}, - {"Server Forwards", "Серверні Переспрямування"}, - // HandlePage - {"Unknown page", "Невідома сторінка"}, - // HandleCommand, ShowError - {"Invalid token", "Невірний токен"}, - {"SUCCESS", "УСПІШНО"}, - {"ERROR", "ПОМИЛКА"}, - {"Unknown command", "Невідома команда"}, - {"Command accepted", "Команда прийнята"}, - {"Back to commands list", "Повернутися до списку команд"}, - {"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"}, - // HTTP_COMMAND_KILLSTREAM - {"Stream closed", "Потік зачинений"}, - {"Stream not found or already was closed", "Потік не знайдений або вже зачинений"}, - {"Destination not found", "Точка призначення не знайдена"}, - {"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"}, - {"Return to destination page", "Повернутися на сторінку точки призначення"}, - {"You will be redirected back in 5 seconds", "Ви будете переадресовані назад через 5 секунд"}, - // HTTP_COMMAND_LIMITTRANSIT - {"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"}, - // HTTP_COMMAND_GET_REG_STRING - {"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", "Така точка призначення не знайдена"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."}, {"", ""}, }; static std::map> plurals { - // ShowUptime - {"days", {"день", "дня", "днів"}}, - {"hours", {"годину", "години", "годин"}}, + {"days", {"день", "дня", "днів"}}, + {"hours", {"годину", "години", "годин"}}, {"minutes", {"хвилину", "хвилини", "хвилин"}}, {"seconds", {"секунду", "секунди", "секунд"}}, {"", {"", "", ""}}, @@ -234,7 +207,7 @@ namespace ukrainian // language std::shared_ptr GetLocale() { - return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/i18n/Uzbek.cpp b/i18n/Uzbek.cpp new file mode 100644 index 00000000..72734b58 --- /dev/null +++ b/i18n/Uzbek.cpp @@ -0,0 +1,206 @@ +/* +* Copyright (c) 2021, 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 +#include +#include +#include +#include "I18N.h" + +// Ukrainian localization file + +namespace i2p +{ +namespace i18n +{ +namespace uzbek // language namespace +{ + // language name in lowercase + static std::string language = "uzbek"; + + // 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 strings + { + {"KiB", "KiB"}, + {"MiB", "MiB"}, + {"GiB", "GiB"}, + {"building", "qurilish"}, + {"failed", "muvaffaqiyatsiz"}, + {"expiring", "muddati tugaydi"}, + {"established", "aloqa o'rnatildi"}, + {"unknown", "noma'lum"}, + {"exploratory", "tadqiqiy"}, + {"i2pd webconsole", "i2pd veb -konsoli"}, + {"Main page", "Asosiy sahifa"}, + {"Router commands", "Router buyruqlari"}, + {"LeaseSets", "LeaseSets"}, + {"Tunnels", "Tunnellar"}, + {"Transit Tunnels", "Tranzit Tunellar"}, + {"Transports", "Transportlar"}, + {"I2P tunnels", "I2P tunnellar"}, + {"SAM sessions", "SAM sessiyalari"}, + {"ERROR", "XATO"}, + {"OK", "OK"}, + {"Testing", "Testlash"}, + {"Firewalled", "Xavfsizlik devori bilan himoyalangan"}, + {"Unknown", "Notanish"}, + {"Proxy", "Proksi"}, + {"Mesh", "Mesh To'r"}, + {"Error", "Xato"}, + {"Clock skew", "Aniq vaqt emas"}, + {"Offline", "Oflayn"}, + {"Symmetric NAT", "Simmetrik NAT"}, + {"Uptime", "Ish vaqti"}, + {"Network status", "Tarmoq holati"}, + {"Network status v6", "Tarmoq holati v6"}, + {"Stopping in", "Ichida to'xtatish"}, + {"Family", "Oila"}, + {"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"}, + {"Received", "Qabul qilindi"}, + {"KiB/s", "KiB/s"}, + {"Sent", "Yuborilgan"}, + {"Transit", "Tranzit"}, + {"Data path", "Ma'lumotlar yo'li"}, + {"Hidden content. Press on text to see.", "Yashirin tarkib. Ko'rish uchun matn ustida bosing."}, + {"Router Ident", "Router identifikatori"}, + {"Router Family", "Router Oila"}, + {"Router Caps", "Router bayroqlari"}, + {"Version", "Versiya"}, + {"Our external address", "Bizning tashqi manzilimiz"}, + {"supported", "qo'llab -quvvatlanadi"}, + {"Routers", "Routerlar"}, + {"Floodfills", "Floodfills"}, + {"Client Tunnels", "Mijoz tunellari"}, + {"Services", "Xizmatlar"}, + {"Enabled", "Yoqilgan"}, + {"Disabled", "O'chirilgan"}, + {"Encrypted B33 address", "Shifrlangan B33 manzil"}, + {"Address registration line", "Manzilni ro'yxatga olish liniyasi"}, + {"Domain", "Domen"}, + {"Generate", "Varatish"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Eslatma: natija satridan faqat 2LD domenlarini ro'yxatdan o'tkazish uchun foydalanish mumkin (example.i2p). Subdomenlarni ro'yxatdan o'tkazish uchun i2pd-tools dan foydalaning."}, + {"Address", "Manzil"}, + {"Type", "Turi"}, + {"EncType", "ShifrlashTuri"}, + {"Inbound tunnels", "Kirish tunnellari"}, + {"ms", "ms"}, + {"Outbound tunnels", "Chiquvchi tunnellar"}, + {"Tags", "Teglar"}, + {"Incoming", "Kiruvchi"}, + {"Outgoing", "Chiquvchi"}, + {"Destination", "Manzilgoh"}, + {"Amount", "Yig'indi"}, + {"Incoming Tags", "Kiruvchi teglar"}, + {"Tags sessions", "Teglar sessiyalari"}, + {"Status", "Holat"}, + {"Streams", "Strim"}, + {"Close stream", "Strimni o'chirish"}, + {"I2CP session not found", "I2CP sessiyasi topilmadi"}, + {"I2CP is not enabled", "I2CP yoqilmagan"}, + {"Invalid", "Noto'g'ri"}, + {"Store type", "Saqlash turi"}, + {"Expires", "Muddati tugaydi"}, + {"Non Expired Leases", "Muddati O'tmagan Leases"}, + {"Gateway", "Kirish yo'li"}, + {"TunnelID", "TunnelID"}, + {"EndDate", "Tugash Sanasi"}, + {"not floodfill", "floodfill emas"}, + {"Queue size", "Navbat hajmi"}, + {"Run peer test", "Sinovni boshlang"}, + {"Decline transit tunnels", "Tranzit tunnellarni rad etish"}, + {"Accept transit tunnels", "Tranzit tunnellarni qabul qilish"}, + {"Cancel graceful shutdown", "Yumshoq to'xtashni bekor qiling"}, + {"Start graceful shutdown", "Yumshoq to'xtashni boshlang"}, + {"Force shutdown", "Bizning tashqi manzilimiz"}, + {"Reload external CSS styles", "Tashqi CSS uslublarini qayta yuklang"}, + {"Note: any action done here are not persistent and not changes your config files.", "Eslatma: bu erda qilingan har qanday harakat doimiy emas va konfiguratsiya fayllarini o'zgartirmaydi."}, + {"Transit tunnels limit", "Tranzit tunellar chegarasi"}, + {"Change", "O'zgartirish"}, + {"Change language", "Tilni o'zgartirish"}, + {"no transit tunnels currently built", "qurilgan tranzit tunnellari yo'q"}, + {"SAM disabled", "SAM o'chirilgan"}, + {"no sessions currently running", "hech qanday ishlaydigan sessiyalar yo'q"}, + {"SAM session not found", "SAM sessiyasi topilmadi"}, + {"SAM Session", "SAM sessiyasi"}, + {"Server Tunnels", "Server Tunellari"}, + {"Client Forwards", "Mijozlarni Yo'naltirish"}, + {"Server Forwards", "Serverni Yo'naltirish"}, + {"Unknown page", "Noma'lum sahifa"}, + {"Invalid token", "Noto‘g‘ri belgi"}, + {"SUCCESS", "Muvaffaqiyat"}, + {"Stream closed", "Strim yopiq"}, + {"Stream not found or already was closed", "Strim topilmadi yoki allaqachon yopilgan"}, + {"Destination not found", "Yo'nalish topilmadi"}, + {"StreamID can't be null", "StreamID bo'sh bo'lishi mumkin emas"}, + {"Return to destination page", "Belgilangan sahifaga qaytish"}, + {"You will be redirected in 5 seconds", "Siz 5 soniyada qayta yo'naltirilasiz"}, + {"Transit tunnels count must not exceed 65535", "Tranzit tunnellar soni 65535 dan oshmasligi kerak"}, + {"Back to commands list", "Buyruqlar ro'yxatiga qaytish"}, + {"Register at reg.i2p", "Reg.i2p-da ro'yxatdan o'ting"}, + {"Description", "Tavsif"}, + {"A bit information about service on domain", "Domen xizmatlari haqida bir oz ma'lumot"}, + {"Submit", "Yuborish"}, + {"Domain can't end with .b32.i2p", "Domen .b32.i2p bilan tugashi mumkin emas"}, + {"Domain must end with .i2p", "Domen .i2p bilan tugashi kerak"}, + {"Such destination is not found", "Bunday yo'nalish topilmadi"}, + {"Unknown command", "Noma'lum buyruq"}, + {"Command accepted", "Buyruq qabul qilindi"}, + {"Proxy error", "Proksi xatosi"}, + {"Proxy info", "Proksi ma'lumotlari"}, + {"Proxy error: Host not found", "Proksi xatosi: Xost topilmadi"}, + {"Remote host not found in router's addressbook", "Masofaviy xost yo'riqnoma manzillar kitobida topilmadi"}, + {"Invalid request", "Noto‘g‘ri so‘rov"}, + {"Proxy unable to parse your request", "Proksi sizning so'rovingizni tahlil qila olmaydi"}, + {"addresshelper is not supported", "addresshelper qo'llab -quvvatlanmaydi"}, + {"Host", "Xost"}, + {"Addresshelper found", "Addresshelper topildi"}, + {"invalid request uri", "noto'g'ri URI so'rovi"}, + {"Can't detect destination host from request", "So‘rov orqali manzil xostini aniqlab bo'lmayapti"}, + {"Outproxy failure", "Tashqi proksi muvaffaqiyatsizligi"}, + {"bad outproxy settings", "noto'g'ri tashqi proksi -server sozlamalari"}, + {"not inside I2P network, but outproxy is not enabled", "I2P tarmog'ida emas, lekin tashqi proksi yoqilmagan"}, + {"unknown outproxy url", "noma'lum outproxy url"}, + {"cannot resolve upstream proxy", "yuqoridagi proksi -serverni aniqlab olib bolmaydi"}, + {"hostname too long", "xost nomi juda uzun"}, + {"cannot connect to upstream socks proxy", "yuqori soks proksi -serveriga ulanib bo'lmaydi"}, + {"Cannot negotiate with socks proxy", "Soks proksi bilan muzokara olib bo'lmaydi"}, + {"CONNECT error", "CONNECT xatosi"}, + {"Failed to Connect", "Ulanmadi"}, + {"socks proxy error", "soks proksi xatosi"}, + {"failed to send request to upstream", "yuqori http proksi-serveriga ulanib bo'lmadi"}, + {"No Reply From socks proxy", "Soks-proksidan javob yo'q"}, + {"cannot connect", "ulab bo'lmaydi"}, + {"http out proxy not implemented", "tashqi HTTP proksi -serverni qo'llab -quvvatlash amalga oshirilmagan"}, + {"cannot connect to upstream http proxy", "yuqori http proksi-serveriga ulanib bo'lmadi"}, + {"Host is down", "Xost ishlamayapti"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Talab qilingan xost bilan aloqa o'rnatilmadi, u ishlamay qolishi mumkin. Iltimos keyinroq qayta urinib ko'ring."}, + {"", ""}, + }; + + static std::map> plurals + { + {"days", {"kun", "kunlar"}}, + {"hours", {"soat", "soat"}}, + {"minutes", {"daqiqa", "daqiqalar"}}, + {"seconds", {"soniya", "soniyalar"}}, + {"", {"", ""}}, + }; + + std::shared_ptr GetLocale() + { + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); + } + +} // language +} // i18n +} // i2p diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index a80778a1..a8516e00 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -37,6 +37,7 @@ namespace config { ("conf", value()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)") ("tunconf", value()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)") ("tunnelsdir", value()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d") + ("certsdir", value()->default_value(""), "Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates") ("pidfile", value()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)") ("log", value()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)") ("logfile", value()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)") @@ -212,7 +213,9 @@ namespace config { "https://i2p.novg.net/" ), "Reseed URLs, separated by comma") ("reseed.yggurls", value()->default_value( - "http://[324:9de3:fea4:f6ac::ace]:7070/" + "http://[324:71e:281a:9ed3::ace]:7070/," + "http://[301:65b9:c7cd:9a36::1]:18801/," + "http://[320:8936:ec1a:31f1::216]/" ), "Reseed URLs through the Yggdrasil, separated by comma") ; @@ -284,7 +287,7 @@ namespace config { options_description meshnets("Meshnet transports options"); meshnets.add_options() - ("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (deafult: false)") + ("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)") ("meshnets.yggaddress", value()->default_value(""), "Yggdrasil address to publish") ; diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 14ef83ae..427bbbd6 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -398,8 +398,9 @@ namespace crypto } // ElGamal - void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) + void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted) { + BN_CTX * ctx = BN_CTX_new (); BN_CTX_start (ctx); // everything, but a, because a might come from table BIGNUM * k = BN_CTX_get (ctx); @@ -435,37 +436,32 @@ namespace crypto BN_bin2bn (m, 255, b); BN_mod_mul (b, b1, b, elgp, ctx); // copy a and b - if (zeroPadding) - { - encrypted[0] = 0; - bn2buf (a, encrypted + 1, 256); - encrypted[257] = 0; - bn2buf (b, encrypted + 258, 256); - } - else - { - bn2buf (a, encrypted, 256); - bn2buf (b, encrypted + 256, 256); - } + encrypted[0] = 0; + bn2buf (a, encrypted + 1, 256); + encrypted[257] = 0; + bn2buf (b, encrypted + 258, 256); + BN_free (a); BN_CTX_end (ctx); + BN_CTX_free (ctx); } - bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, - uint8_t * data, BN_CTX * ctx, bool zeroPadding) + bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data) { + BN_CTX * ctx = BN_CTX_new (); BN_CTX_start (ctx); BIGNUM * x = BN_CTX_get (ctx), * a = BN_CTX_get (ctx), * b = BN_CTX_get (ctx); BN_bin2bn (key, 256, x); BN_sub (x, elgp, x); BN_sub_word (x, 1); // x = elgp - x- 1 - BN_bin2bn (zeroPadding ? encrypted + 1 : encrypted, 256, a); - BN_bin2bn (zeroPadding ? encrypted + 258 : encrypted + 256, 256, b); + BN_bin2bn (encrypted + 1, 256, a); + BN_bin2bn (encrypted + 258, 256, b); // m = b*(a^x mod p) mod p BN_mod_exp (x, a, x, elgp, ctx); BN_mod_mul (b, b, x, elgp, ctx); uint8_t m[255]; bn2buf (b, m, 255); BN_CTX_end (ctx); + BN_CTX_free (ctx); uint8_t hash[32]; SHA256 (m + 33, 222, hash); if (memcmp (m + 1, hash, 32)) @@ -499,8 +495,9 @@ namespace crypto } // ECIES - void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) + void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted) { + BN_CTX * ctx = BN_CTX_new (); BN_CTX_start (ctx); BIGNUM * q = BN_CTX_get (ctx); EC_GROUP_get_order(curve, q, ctx); @@ -512,19 +509,10 @@ namespace crypto EC_POINT_mul (curve, p, k, nullptr, nullptr, ctx); BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx); EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr); - if (zeroPadding) - { - encrypted[0] = 0; - bn2buf (x, encrypted + 1, len); - bn2buf (y, encrypted + 1 + len, len); - RAND_bytes (encrypted + 1 + 2*len, 256 - 2*len); - } - else - { - bn2buf (x, encrypted, len); - bn2buf (y, encrypted + len, len); - RAND_bytes (encrypted + 2*len, 256 - 2*len); - } + encrypted[0] = 0; + bn2buf (x, encrypted + 1, len); + bn2buf (y, encrypted + 1 + len, len); + RAND_bytes (encrypted + 1 + 2*len, 256 - 2*len); // encryption key and iv EC_POINT_mul (curve, p, nullptr, key, k, ctx); EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr); @@ -541,36 +529,25 @@ namespace crypto CBCEncryption encryption; encryption.SetKey (shared); encryption.SetIV (iv); - if (zeroPadding) - { - encrypted[257] = 0; - encryption.Encrypt (m, 256, encrypted + 258); - } - else - encryption.Encrypt (m, 256, encrypted + 256); + encrypted[257] = 0; + encryption.Encrypt (m, 256, encrypted + 258); EC_POINT_free (p); BN_CTX_end (ctx); + BN_CTX_free (ctx); } - bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) + bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data) { bool ret = true; + BN_CTX * ctx = BN_CTX_new (); BN_CTX_start (ctx); BIGNUM * q = BN_CTX_get (ctx); EC_GROUP_get_order(curve, q, ctx); int len = BN_num_bytes (q); // point for shared secret BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx); - if (zeroPadding) - { - BN_bin2bn (encrypted + 1, len, x); - BN_bin2bn (encrypted + 1 + len, len, y); - } - else - { - BN_bin2bn (encrypted, len, x); - BN_bin2bn (encrypted + len, len, y); - } + BN_bin2bn (encrypted + 1, len, x); + BN_bin2bn (encrypted + 1 + len, len, y); auto p = EC_POINT_new (curve); if (EC_POINT_set_affine_coordinates_GFp (curve, p, x, y, nullptr)) { @@ -587,10 +564,7 @@ namespace crypto CBCDecryption decryption; decryption.SetKey (shared); decryption.SetIV (iv); - if (zeroPadding) - decryption.Decrypt (encrypted + 258, 256, m); - else - decryption.Decrypt (encrypted + 256, 256, m); + decryption.Decrypt (encrypted + 258, 256, m); // verify and copy uint8_t hash[32]; SHA256 (m + 33, 222, hash); @@ -610,6 +584,7 @@ namespace crypto EC_POINT_free (p); BN_CTX_end (ctx); + BN_CTX_free (ctx); return ret; } diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index a6c64c70..5f42b527 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -108,13 +108,13 @@ namespace crypto }; // ElGamal - void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding = false); - bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding = false); + void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted + bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub); // ECIES - void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding = false); // 222 bytes data, 514 bytes encrypted with zeropadding, 512 without - bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding = false); + void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted); // 222 bytes data, 514 bytes encrypted + bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data); // 514 bytes encrypted, 222 data void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub); // HMAC diff --git a/libi2pd/CryptoKey.cpp b/libi2pd/CryptoKey.cpp index ad93d386..ad986129 100644 --- a/libi2pd/CryptoKey.cpp +++ b/libi2pd/CryptoKey.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -20,10 +20,9 @@ namespace crypto memcpy (m_PublicKey, pub, 256); } - void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) + void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted) { - if (!ctx) return; - ElGamalEncrypt (m_PublicKey, data, encrypted, ctx, zeroPadding); + ElGamalEncrypt (m_PublicKey, data, encrypted); } ElGamalDecryptor::ElGamalDecryptor (const uint8_t * priv) @@ -31,10 +30,9 @@ namespace crypto memcpy (m_PrivateKey, priv, 256); } - bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) + bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data) { - if (!ctx) return false; - return ElGamalDecrypt (m_PrivateKey, encrypted, data, ctx, zeroPadding); + return ElGamalDecrypt (m_PrivateKey, encrypted, data); } ECIESP256Encryptor::ECIESP256Encryptor (const uint8_t * pub) @@ -54,10 +52,10 @@ namespace crypto if (m_PublicKey) EC_POINT_free (m_PublicKey); } - void ECIESP256Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) + void ECIESP256Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted) { if (m_Curve && m_PublicKey) - ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted, ctx, zeroPadding); + ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted); } ECIESP256Decryptor::ECIESP256Decryptor (const uint8_t * priv) @@ -72,10 +70,10 @@ namespace crypto if (m_PrivateKey) BN_free (m_PrivateKey); } - bool ECIESP256Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) + bool ECIESP256Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data) { if (m_Curve && m_PrivateKey) - return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data, ctx, zeroPadding); + return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data); return false; } @@ -114,10 +112,10 @@ namespace crypto if (m_PublicKey) EC_POINT_free (m_PublicKey); } - void ECIESGOSTR3410Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) + void ECIESGOSTR3410Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted) { if (m_PublicKey) - ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted, ctx, zeroPadding); + ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted); } ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor (const uint8_t * priv) @@ -130,10 +128,10 @@ namespace crypto if (m_PrivateKey) BN_free (m_PrivateKey); } - bool ECIESGOSTR3410Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) + bool ECIESGOSTR3410Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data) { if (m_PrivateKey) - return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data, ctx, zeroPadding); + return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data); return false; } @@ -161,7 +159,7 @@ namespace crypto memcpy (m_PublicKey, pub, 32); } - void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool) + void ECIESX25519AEADRatchetEncryptor::Encrypt (const uint8_t *, uint8_t * pub) { memcpy (pub, m_PublicKey, 32); } @@ -171,7 +169,7 @@ namespace crypto m_StaticKeys.SetPrivateKey (priv, calculatePublic); } - bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding) + bool ECIESX25519AEADRatchetDecryptor::Decrypt (const uint8_t * epub, uint8_t * sharedSecret) { return m_StaticKeys.Agree (epub, sharedSecret); } diff --git a/libi2pd/CryptoKey.h b/libi2pd/CryptoKey.h index fb69558f..0ac0bdd5 100644 --- a/libi2pd/CryptoKey.h +++ b/libi2pd/CryptoKey.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -21,7 +21,7 @@ namespace crypto public: virtual ~CryptoKeyEncryptor () {}; - virtual void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) = 0; // 222 bytes data, 512/514 bytes encrypted + virtual void Encrypt (const uint8_t * data, uint8_t * encrypted) = 0; }; class CryptoKeyDecryptor @@ -29,7 +29,7 @@ namespace crypto public: virtual ~CryptoKeyDecryptor () {}; - virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) = 0; // 512/514 bytes encrypted, 222 bytes data + virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data) = 0; virtual size_t GetPublicKeyLen () const = 0; // we need it to set key in LS2 }; @@ -39,7 +39,7 @@ namespace crypto public: ElGamalEncryptor (const uint8_t * pub); - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); + void Encrypt (const uint8_t * data, uint8_t * encrypted) override; // 222 bytes data, 514 bytes encrypted private: @@ -51,8 +51,8 @@ namespace crypto public: ElGamalDecryptor (const uint8_t * priv); - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); - size_t GetPublicKeyLen () const { return 256; }; + bool Decrypt (const uint8_t * encrypted, uint8_t * data) override; // 514 bytes encrypted, 222 bytes data + size_t GetPublicKeyLen () const override { return 256; }; private: @@ -67,7 +67,7 @@ namespace crypto ECIESP256Encryptor (const uint8_t * pub); ~ECIESP256Encryptor (); - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); + void Encrypt (const uint8_t * data, uint8_t * encrypted) override; private: @@ -82,8 +82,8 @@ namespace crypto ECIESP256Decryptor (const uint8_t * priv); ~ECIESP256Decryptor (); - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); - size_t GetPublicKeyLen () const { return 64; }; + bool Decrypt (const uint8_t * encrypted, uint8_t * data) override; + size_t GetPublicKeyLen () const override { return 64; }; private: @@ -101,7 +101,7 @@ namespace crypto ECIESGOSTR3410Encryptor (const uint8_t * pub); ~ECIESGOSTR3410Encryptor (); - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); + void Encrypt (const uint8_t * data, uint8_t * encrypted) override; private: @@ -115,8 +115,8 @@ namespace crypto ECIESGOSTR3410Decryptor (const uint8_t * priv); ~ECIESGOSTR3410Decryptor (); - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); - size_t GetPublicKeyLen () const { return 64; }; + bool Decrypt (const uint8_t * encrypted, uint8_t * data) override; + size_t GetPublicKeyLen () const override { return 64; }; private: @@ -133,7 +133,7 @@ namespace crypto ECIESX25519AEADRatchetEncryptor (const uint8_t * pub); ~ECIESX25519AEADRatchetEncryptor () {}; - void Encrypt (const uint8_t *, uint8_t * pub, BN_CTX *, bool); + void Encrypt (const uint8_t *, uint8_t * pub) override; // copies m_PublicKey to pub private: @@ -147,9 +147,9 @@ namespace crypto ECIESX25519AEADRatchetDecryptor (const uint8_t * priv, bool calculatePublic = false); ~ECIESX25519AEADRatchetDecryptor () {}; - bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret, BN_CTX * ctx, bool zeroPadding); + bool Decrypt (const uint8_t * epub, uint8_t * sharedSecret) override; // agree with static and return in sharedSecret (32 bytes) - size_t GetPublicKeyLen () const { return 32; }; + size_t GetPublicKeyLen () const override { return 32; }; const uint8_t * GetPubicKey () const { return m_StaticKeys.GetPublicKey (); }; private: diff --git a/libi2pd/Datagram.cpp b/libi2pd/Datagram.cpp index d000a9e0..627c0481 100644 --- a/libi2pd/Datagram.cpp +++ b/libi2pd/Datagram.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -21,6 +21,9 @@ namespace datagram DatagramDestination::DatagramDestination (std::shared_ptr owner, bool gzip): m_Owner (owner), m_Receiver (nullptr), m_RawReceiver (nullptr), m_Gzip (gzip) { + if (m_Gzip) + m_Deflator.reset (new i2p::data::GzipDeflator); + auto identityLen = m_Owner->GetIdentity ()->GetFullLen (); m_From.resize (identityLen); m_Owner->GetIdentity ()->ToBuffer (m_From.data (), identityLen); @@ -152,11 +155,16 @@ namespace datagram const std::vector >& payloads, uint16_t fromPort, uint16_t toPort, bool isRaw, bool checksum) { + size_t size; auto msg = m_I2NPMsgsPool.AcquireShared (); uint8_t * buf = msg->GetPayload (); buf += 4; // reserve for length - size_t size = m_Gzip ? m_Deflator.Deflate (payloads, buf, msg->maxLen - msg->len) : - i2p::data::GzipNoCompression (payloads, buf, msg->maxLen - msg->len); + + if (m_Gzip && m_Deflator) + size = m_Deflator->Deflate (payloads, buf, msg->maxLen - msg->len); + else + size = i2p::data::GzipNoCompression (payloads, buf, msg->maxLen - msg->len); + if (size) { htobe32buf (msg->GetPayload (), size); // length diff --git a/libi2pd/Datagram.h b/libi2pd/Datagram.h index 5dd6c8b6..564eb10b 100644 --- a/libi2pd/Datagram.h +++ b/libi2pd/Datagram.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -164,7 +164,7 @@ namespace datagram std::map m_ReceiversByPorts; i2p::data::GzipInflator m_Inflator; - i2p::data::GzipDeflator m_Deflator; + std::unique_ptr m_Deflator; std::vector m_From, m_Signature; i2p::util::MemoryPool > m_I2NPMsgsPool; }; diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 7bcfe111..54137664 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -345,10 +345,11 @@ namespace client void LeaseSetDestination::HandleI2NPMessage (const uint8_t * buf, size_t len) { I2NPMessageType typeID = (I2NPMessageType)(buf[I2NP_HEADER_TYPEID_OFFSET]); - LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE); + uint32_t msgID = bufbe32toh (buf + I2NP_HEADER_MSGID_OFFSET); + LeaseSetDestination::HandleCloveI2NPMessage (typeID, buf + I2NP_HEADER_SIZE, GetI2NPMessageLength(buf, len) - I2NP_HEADER_SIZE, msgID); } - bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) + bool LeaseSetDestination::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) { switch (typeID) { @@ -365,6 +366,9 @@ namespace client case eI2NPDatabaseSearchReply: HandleDatabaseSearchReplyMessage (payload, len); break; + case eI2NPShortTunnelBuildReply: // might come as garlic encrypted + i2p::HandleI2NPMessage (CreateI2NPMessage (typeID, payload, len, msgID)); + break; default: LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID); return false; @@ -1092,6 +1096,35 @@ namespace client return nullptr; } + void ClientDestination::SendPing (const i2p::data::IdentHash& to) + { + if (m_StreamingDestination) + { + auto leaseSet = FindLeaseSet (to); + if (leaseSet) + m_StreamingDestination->SendPing (leaseSet); + else + { + auto s = m_StreamingDestination; + RequestDestination (to, + [s](std::shared_ptr ls) + { + if (ls) s->SendPing (ls); + }); + } + } + } + + void ClientDestination::SendPing (std::shared_ptr to) + { + auto s = m_StreamingDestination; + RequestDestinationWithEncryptedLeaseSet (to, + [s](std::shared_ptr ls) + { + if (ls) s->SendPing (ls); + }); + } + std::shared_ptr ClientDestination::GetStreamingDestination (int port) const { if (port) @@ -1241,13 +1274,13 @@ namespace client if (m_DatagramDestination) m_DatagramDestination->CleanUp (); } - bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const + bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const { if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) if (m_ECIESx25519EncryptionKey && m_ECIESx25519EncryptionKey->decryptor) - return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data, ctx, true); + return m_ECIESx25519EncryptionKey->decryptor->Decrypt (encrypted, data); if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor) - return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data, ctx, true); + return m_StandardEncryptionKey->decryptor->Decrypt (encrypted, data); else LogPrint (eLogError, "Destinations: decryptor is not set"); return false; diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 6695796d..54df8706 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -139,12 +139,13 @@ namespace client void SetLeaseSetUpdated (); bool IsPublic () const { return m_IsPublic; }; + void SetPublic (bool pub) { m_IsPublic = pub; }; protected: // implements GarlicDestination void HandleI2NPMessage (const uint8_t * buf, size_t len); - bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len); + bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID); void SetLeaseSet (std::shared_ptr newLeaseSet); int GetLeaseSetType () const { return m_LeaseSetType; }; @@ -242,6 +243,8 @@ namespace client void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0); void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr dest, int port = 0); std::shared_ptr CreateStream (std::shared_ptr remote, int port = 0); + void SendPing (const i2p::data::IdentHash& to); + void SendPing (std::shared_ptr to); void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor); void StopAcceptingStreams (); bool IsAcceptingStreams () const; @@ -254,7 +257,7 @@ namespace client i2p::datagram::DatagramDestination * CreateDatagramDestination (bool gzip = true); // implements LocalDestination - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; + bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const; std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const; const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp index 55e962d8..c9671a7e 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.cpp +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -31,16 +31,16 @@ namespace garlic uint8_t keydata[64]; i2p::crypto::HKDF (rootKey, k, 32, "KDFDHRatchetStep", keydata); // keydata = HKDF(rootKey, k, "KDFDHRatchetStep", 64) memcpy (m_NextRootKey, keydata, 32); // nextRootKey = keydata[0:31] - i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_KeyData.buf); + i2p::crypto::HKDF (keydata + 32, nullptr, 0, "TagAndKeyGenKeys", m_SessionTagKeyData); // [sessTag_ck, symmKey_ck] = HKDF(keydata[32:63], ZEROLEN, "TagAndKeyGenKeys", 64) - memcpy (m_SymmKeyCK, m_KeyData.buf + 32, 32); + memcpy (m_SymmKeyCK, (const uint8_t *)m_SessionTagKeyData + 32, 32); m_NextSymmKeyIndex = 0; } void RatchetTagSet::NextSessionTagRatchet () { - i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), nullptr, 0, "STInitialization", m_KeyData.buf); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64) - memcpy (m_SessTagConstant, m_KeyData.GetSessTagConstant (), 32); + i2p::crypto::HKDF (m_SessionTagKeyData, nullptr, 0, "STInitialization", m_SessionTagKeyData); // [sessTag_ck, sesstag_constant] = HKDF(sessTag_ck, ZEROLEN, "STInitialization", 64) + memcpy (m_SessTagConstant, (const uint8_t *)m_SessionTagKeyData + 32, 32); // SESSTAG_CONSTANT = keydata[32:63] m_NextIndex = 0; } @@ -52,8 +52,8 @@ namespace garlic LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty"); return 0; } - i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64) - return m_KeyData.GetTag (); + i2p::crypto::HKDF (m_SessionTagKeyData, m_SessTagConstant, 32, "SessionTagKeyGen", m_SessionTagKeyData); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64) + return m_SessionTagKeyData.GetLL ()[4]; // tag = keydata[32:39] } void RatchetTagSet::GetSymmKey (int index, uint8_t * key) @@ -239,7 +239,7 @@ namespace garlic MixHash (m_Aepk, 32); // h = SHA256(h || aepk) uint8_t sharedSecret[32]; - if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) + if (!GetOwner ()->Decrypt (m_Aepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) { LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key"); return false; @@ -263,7 +263,7 @@ namespace garlic { // static key, fs is apk memcpy (m_RemoteStaticKey, fs, 32); - if (!GetOwner ()->Decrypt (fs, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, apk) + if (!GetOwner ()->Decrypt (fs, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, apk) { LogPrint (eLogWarning, "Garlic: Incorrect Alice static key"); return false; @@ -492,7 +492,7 @@ namespace garlic // KDF2 if (isStatic) { - GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk) + GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk) MixKey (sharedSecret); } else @@ -518,36 +518,7 @@ namespace garlic } return true; } - - bool ECIESX25519AEADRatchetSession::NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) - { - // we are Alice, router's bpk is m_RemoteStaticKey - i2p::crypto::InitNoiseNState (GetNoiseState (), m_RemoteStaticKey); - size_t offset = 0; - m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); - memcpy (out + offset, m_EphemeralKeys->GetPublicKey (), 32); - MixHash (out + offset, 32); // h = SHA256(h || aepk) - offset += 32; - uint8_t sharedSecret[32]; - if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // x25519(aesk, bpk) - { - LogPrint (eLogWarning, "Garlic: Incorrect Bob static key"); - return false; - } - MixKey (sharedSecret); - uint8_t nonce[12]; - CreateNonce (0, nonce); - // encrypt payload - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt - { - LogPrint (eLogWarning, "Garlic: Payload for router AEAD encryption failed"); - return false; - } - - m_State = eSessionStateNewSessionSent; - return true; - } - + bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) { // we are Bob @@ -668,7 +639,7 @@ namespace garlic return false; } MixKey (sharedSecret); - GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk) + GetOwner ()->Decrypt (bepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk) MixKey (sharedSecret); uint8_t nonce[12]; @@ -824,8 +795,9 @@ namespace garlic std::shared_ptr ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr msg) { - auto payload = CreatePayload (msg, m_State != eSessionStateEstablished); - size_t len = payload.size (); + uint8_t * payload = GetOwner ()->GetPayloadBuffer (); + if (!payload) return nullptr; + size_t len = CreatePayload (msg, m_State != eSessionStateEstablished, payload); if (!len) return nullptr; auto m = NewI2NPMessage (len + 100); // 96 + 4 m->Align (12); // in order to get buf aligned to 16 (12 + 4) @@ -834,35 +806,30 @@ namespace garlic switch (m_State) { case eSessionStateEstablished: - if (!NewExistingSessionMessage (payload.data (), payload.size (), buf, m->maxLen)) + if (!NewExistingSessionMessage (payload, len, buf, m->maxLen)) return nullptr; len += 24; break; case eSessionStateNew: - if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen)) + if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen)) return nullptr; len += 96; break; case eSessionStateNewSessionReceived: - if (!NewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen)) + if (!NewSessionReplyMessage (payload, len, buf, m->maxLen)) return nullptr; len += 72; break; case eSessionStateNewSessionReplySent: - if (!NextNewSessionReplyMessage (payload.data (), payload.size (), buf, m->maxLen)) + if (!NextNewSessionReplyMessage (payload, len, buf, m->maxLen)) return nullptr; len += 72; break; case eSessionStateOneTime: - if (!NewOutgoingSessionMessage (payload.data (), payload.size (), buf, m->maxLen, false)) + if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false)) return nullptr; len += 96; break; - case eSessionStateForRouter: - if (!NewOutgoingMessageForRouter (payload.data (), payload.size (), buf, m->maxLen)) - return nullptr; - len += 48; - break; default: return nullptr; } @@ -873,13 +840,13 @@ namespace garlic return m; } - std::shared_ptr ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr msg, bool isForRouter) + std::shared_ptr ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr msg) { - m_State = isForRouter ? eSessionStateForRouter : eSessionStateOneTime; + m_State = eSessionStateOneTime; return WrapSingleMessage (msg); } - std::vector ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr msg, bool first) + size_t ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr msg, bool first, uint8_t * payload) { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); size_t payloadLen = 0; @@ -941,89 +908,93 @@ namespace garlic payloadLen += paddingSize + 3; } } - std::vector v(payloadLen); if (payloadLen) - { + { + if (payloadLen > I2NP_MAX_MESSAGE_SIZE) + { + LogPrint (eLogError, "Garlic: payload length ", payloadLen, " is too long"); + return 0; + } m_LastSentTimestamp = ts; size_t offset = 0; // DateTime if (first) { - v[offset] = eECIESx25519BlkDateTime; offset++; - htobe16buf (v.data () + offset, 4); offset += 2; - htobe32buf (v.data () + offset, ts/1000); offset += 4; // in seconds + payload[offset] = eECIESx25519BlkDateTime; offset++; + htobe16buf (payload + offset, 4); offset += 2; + htobe32buf (payload + offset, ts/1000); offset += 4; // in seconds } // LeaseSet if (leaseSet) { - offset += CreateLeaseSetClove (leaseSet, ts, v.data () + offset, payloadLen - offset); + offset += CreateLeaseSetClove (leaseSet, ts, payload + offset, payloadLen - offset); if (!first) { // ack request - v[offset] = eECIESx25519BlkAckRequest; offset++; - htobe16buf (v.data () + offset, 1); offset += 2; - v[offset] = 0; offset++; // flags + payload[offset] = eECIESx25519BlkAckRequest; offset++; + htobe16buf (payload + offset, 1); offset += 2; + payload[offset] = 0; offset++; // flags } } // msg if (msg) - offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset); + offset += CreateGarlicClove (msg, payload + offset, payloadLen - offset); // ack if (m_AckRequests.size () > 0) { - v[offset] = eECIESx25519BlkAck; offset++; - htobe16buf (v.data () + offset, m_AckRequests.size () * 4); offset += 2; + payload[offset] = eECIESx25519BlkAck; offset++; + htobe16buf (payload + offset, m_AckRequests.size () * 4); offset += 2; for (auto& it: m_AckRequests) { - htobe16buf (v.data () + offset, it.first); offset += 2; - htobe16buf (v.data () + offset, it.second); offset += 2; + htobe16buf (payload + offset, it.first); offset += 2; + htobe16buf (payload + offset, it.second); offset += 2; } m_AckRequests.clear (); } // next keys if (m_SendReverseKey) { - v[offset] = eECIESx25519BlkNextKey; offset++; - htobe16buf (v.data () + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2; - v[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG; + payload[offset] = eECIESx25519BlkNextKey; offset++; + htobe16buf (payload + offset, m_NextReceiveRatchet->newKey ? 35 : 3); offset += 2; + payload[offset] = ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG; int keyID = m_NextReceiveRatchet->keyID - 1; if (m_NextReceiveRatchet->newKey) { - v[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG; + payload[offset] |= ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG; keyID++; } offset++; // flag - htobe16buf (v.data () + offset, keyID); offset += 2; // keyid + htobe16buf (payload + offset, keyID); offset += 2; // keyid if (m_NextReceiveRatchet->newKey) { - memcpy (v.data () + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32); + memcpy (payload + offset, m_NextReceiveRatchet->key->GetPublicKey (), 32); offset += 32; // public key } m_SendReverseKey = false; } if (m_SendForwardKey) { - v[offset] = eECIESx25519BlkNextKey; offset++; - htobe16buf (v.data () + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2; - v[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; - if (!m_NextSendRatchet->keyID) v[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only + payload[offset] = eECIESx25519BlkNextKey; offset++; + htobe16buf (payload + offset, m_NextSendRatchet->newKey ? 35 : 3); offset += 2; + payload[offset] = m_NextSendRatchet->newKey ? ECIESX25519_NEXT_KEY_KEY_PRESENT_FLAG : ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; + if (!m_NextSendRatchet->keyID) payload[offset] |= ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG; // for first key only offset++; // flag - htobe16buf (v.data () + offset, m_NextSendRatchet->keyID); offset += 2; // keyid + htobe16buf (payload + offset, m_NextSendRatchet->keyID); offset += 2; // keyid if (m_NextSendRatchet->newKey) { - memcpy (v.data () + offset, m_NextSendRatchet->key->GetPublicKey (), 32); + memcpy (payload + offset, m_NextSendRatchet->key->GetPublicKey (), 32); offset += 32; // public key } } // padding if (paddingSize) { - v[offset] = eECIESx25519BlkPadding; offset++; - htobe16buf (v.data () + offset, paddingSize); offset += 2; - memset (v.data () + offset, 0, paddingSize); offset += paddingSize; + payload[offset] = eECIESx25519BlkPadding; offset++; + htobe16buf (payload + offset, paddingSize); offset += 2; + memset (payload + offset, 0, paddingSize); offset += paddingSize; } } - return v; + return payloadLen; } size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len) @@ -1113,7 +1084,7 @@ namespace garlic // we are Bob m_CurrentNoiseState.MixHash (buf, 32); uint8_t sharedSecret[32]; - if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) + if (!GetOwner ()->Decrypt (buf, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) { LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key"); return false; @@ -1132,40 +1103,108 @@ namespace garlic HandlePayload (payload.data (), len - 16, nullptr, 0); return true; } + + static size_t CreateGarlicPayload (std::shared_ptr msg, uint8_t * payload, + bool datetime, size_t optimalSize) + { + size_t len = 0; + if (datetime) + { + // DateTime + payload[0] = eECIESx25519BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); + len = 7; + } + // I2NP + payload += len; + uint16_t cloveSize = msg->GetPayloadLength () + 10; + payload[0] = eECIESx25519BlkGalicClove; // clove type + htobe16buf (payload + 1, cloveSize); // size + payload += 3; + payload[0] = 0; // flag and delivery instructions + payload[1] = msg->GetTypeID (); // I2NP msg type + htobe32buf (payload + 2, msg->GetMsgID ()); // msgID + htobe32buf (payload + 6, msg->GetExpiration () / 1000); // expiration in seconds + memcpy (payload + 10, msg->GetPayload (), msg->GetPayloadLength ()); + len += cloveSize + 3; + payload += cloveSize; + // padding + int delta = (int)optimalSize - (int)len; + if (delta < 0 || delta > 3) // don't create padding if we are close to optimal size + { + uint8_t paddingSize = rand () & 0x0F; // 0 - 15 + if (delta > 3) + { + delta -= 3; + if (paddingSize > delta) paddingSize %= delta; + } + payload[0] = eECIESx25519BlkPadding; + htobe16buf (payload + 1, paddingSize); + if (paddingSize) memset (payload + 3, 0, paddingSize); + len += paddingSize + 3; + } + return len; + } - std::shared_ptr WrapECIESX25519AEADRatchetMessage (std::shared_ptr msg, const uint8_t * key, uint64_t tag) + std::shared_ptr WrapECIESX25519Message (std::shared_ptr msg, const uint8_t * key, uint64_t tag) { auto m = NewI2NPMessage (); m->Align (12); // in order to get buf aligned to 16 (12 + 4) uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length - uint8_t nonce[12]; - memset (nonce, 0, 12); // n = 0 size_t offset = 0; memcpy (buf + offset, &tag, 8); offset += 8; auto payload = buf + offset; - uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; - size_t len = cloveSize + 3; - payload[0] = eECIESx25519BlkGalicClove; // clove type - htobe16buf (payload + 1, cloveSize); // size - payload += 3; - *payload = 0; payload++; // flag and delivery instructions - *payload = msg->GetTypeID (); // I2NP msg type - htobe32buf (payload + 1, msg->GetMsgID ()); // msgID - htobe32buf (payload + 5, msg->GetExpiration () / 1000); // expiration in seconds - memcpy (payload + 9, msg->GetPayload (), msg->GetPayloadLength ()); - - if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len, buf, 8, key, nonce, buf + offset, len + 16, true)) // encrypt + size_t len = CreateGarlicPayload (msg, payload, false, 956); // 1003 - 8 tag - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 3 local tunnel delivery + uint8_t nonce[12]; + memset (nonce, 0, 12); // n = 0 + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, buf, 8, key, nonce, payload, len + 16, true)) // encrypt { LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); return nullptr; } offset += len + 16; - htobe32buf (m->GetPayload (), offset); m->len += offset + 4; m->FillI2NPMessageHeader (eI2NPGarlic); return m; } + std::shared_ptr WrapECIESX25519MessageForRouter (std::shared_ptr msg, const uint8_t * routerPublicKey) + { + // Noise_N, we are Alice, routerPublicKey is Bob's + i2p::crypto::NoiseSymmetricState noiseState; + i2p::crypto::InitNoiseNState (noiseState, routerPublicKey); + auto m = NewI2NPMessage (); + m->Align (12); // in order to get buf aligned to 16 (12 + 4) + uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length + size_t offset = 0; + auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); + memcpy (buf + offset, ephemeralKeys->GetPublicKey (), 32); + noiseState.MixHash (buf + offset, 32); // h = SHA256(h || aepk) + offset += 32; + uint8_t sharedSecret[32]; + if (!ephemeralKeys->Agree (routerPublicKey, sharedSecret)) // x25519(aesk, bpk) + { + LogPrint (eLogWarning, "Garlic: Incorrect Bob static key"); + return nullptr; + } + noiseState.MixKey (sharedSecret); + auto payload = buf + offset; + size_t len = CreateGarlicPayload (msg, payload, true, 900); // 1003 - 32 eph key - 16 Poly1305 hash - 16 I2NP header - 4 garlic length - 35 router tunnel delivery + uint8_t nonce[12]; + memset (nonce, 0, 12); + // encrypt payload + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, noiseState.m_H, 32, noiseState.m_CK + 32, nonce, payload, len + 16, true)) // encrypt + { + LogPrint (eLogWarning, "Garlic: Payload for router AEAD encryption failed"); + return nullptr; + } + offset += len + 16; + htobe32buf (m->GetPayload (), offset); + m->len += offset + 4; + m->FillI2NPMessageHeader (eI2NPGarlic); + return m; + } } } diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h index cd7b9b40..5fd2c929 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.h +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -1,5 +1,6 @@ + /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -59,16 +60,7 @@ namespace garlic private: - union - { - uint64_t ll[8]; - uint8_t buf[64]; - - const uint8_t * GetSessTagCK () const { return buf; }; // sessTag_chainKey = keydata[0:31] - const uint8_t * GetSessTagConstant () const { return buf + 32; }; // SESSTAG_CONSTANT = keydata[32:63] - uint64_t GetTag () const { return ll[4]; }; // tag = keydata[32:39] - - } m_KeyData; + i2p::data::Tag<64> m_SessionTagKeyData; uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32]; int m_NextIndex, m_NextSymmKeyIndex; std::unordered_map > m_ItermediateSymmKeys; @@ -147,8 +139,7 @@ namespace garlic eSessionStateNewSessionSent, eSessionStateNewSessionReplySent, eSessionStateEstablished, - eSessionStateOneTime, - eSessionStateForRouter + eSessionStateOneTime }; struct DHRatchet @@ -166,7 +157,7 @@ namespace garlic bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr receiveTagset, int index = 0); std::shared_ptr WrapSingleMessage (std::shared_ptr msg); - std::shared_ptr WrapOneTimeMessage (std::shared_ptr msg, bool isForRouter = false); + std::shared_ptr WrapOneTimeMessage (std::shared_ptr msg); const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } @@ -207,9 +198,8 @@ namespace garlic bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); - bool NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); - - std::vector CreatePayload (std::shared_ptr msg, bool first); + + size_t CreatePayload (std::shared_ptr msg, bool first, uint8_t * payload); size_t CreateGarlicClove (std::shared_ptr msg, uint8_t * buf, size_t len); size_t CreateLeaseSetClove (std::shared_ptr ls, uint64_t ts, uint8_t * buf, size_t len); @@ -256,8 +246,10 @@ namespace garlic i2p::crypto::NoiseSymmetricState m_CurrentNoiseState; }; - std::shared_ptr WrapECIESX25519AEADRatchetMessage (std::shared_ptr msg, const uint8_t * key, uint64_t tag); + std::shared_ptr WrapECIESX25519Message (std::shared_ptr msg, const uint8_t * key, uint64_t tag); + std::shared_ptr WrapECIESX25519MessageForRouter (std::shared_ptr msg, const uint8_t * routerPublicKey); } } #endif + diff --git a/libi2pd/FS.cpp b/libi2pd/FS.cpp index 6ac302b0..f6653e55 100644 --- a/libi2pd/FS.cpp +++ b/libi2pd/FS.cpp @@ -24,6 +24,7 @@ namespace i2p { namespace fs { std::string appName = "i2pd"; std::string dataDir = ""; + std::string certsDir = ""; #ifdef _WIN32 std::string dirSep = "\\"; #else @@ -42,6 +43,10 @@ namespace fs { return dataDir; } + const std::string & GetCertsDir () { + return certsDir; + } + const std::string GetUTF8DataDir () { #ifdef _WIN32 boost::filesystem::wpath path (dataDir); @@ -126,6 +131,21 @@ namespace fs { #endif } + void SetCertsDir(const std::string & cmdline_certsdir) { + if (cmdline_certsdir != "") + { + if (cmdline_certsdir[cmdline_certsdir.length()-1] == '/') + certsDir = cmdline_certsdir.substr(0, cmdline_certsdir.size()-1); // strip trailing slash + else + certsDir = cmdline_certsdir; + } + else + { + certsDir = i2p::fs::DataDirPath("certificates"); + } + return; + } + bool Init() { if (!boost::filesystem::exists(dataDir)) boost::filesystem::create_directory(dataDir); diff --git a/libi2pd/FS.h b/libi2pd/FS.h index f07ee35c..d51aa955 100644 --- a/libi2pd/FS.h +++ b/libi2pd/FS.h @@ -75,6 +75,9 @@ namespace fs { /** @brief Returns datadir path */ const std::string & GetDataDir(); + /** @brief Returns certsdir path */ + const std::string & GetCertsDir(); + /** @brief Returns datadir path in UTF-8 encoding */ const std::string GetUTF8DataDir(); @@ -90,7 +93,20 @@ namespace fs { * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/ * Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/ */ - void DetectDataDir(const std::string & cmdline_datadir, bool isService = false); + void DetectDataDir(const std::string & cmdline_datadir, bool isService = false); + + /** + * @brief Set certsdir either from cmdline option or using autodetection + * @param cmdline_param Value of cmdline parameter --certsdir= + * + * Examples of autodetected paths: + * + * Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\certificates + * Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\certificates + * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/certificates + * Unix: /var/lib/i2pd/certificates (system=1) >> ~/.i2pd/ or /tmp/i2pd/certificates + */ + void SetCertsDir(const std::string & cmdline_certsdir); /** * @brief Create subdirectories inside datadir diff --git a/libi2pd/Family.cpp b/libi2pd/Family.cpp index fbb7b9ee..a6f0e2ee 100644 --- a/libi2pd/Family.cpp +++ b/libi2pd/Family.cpp @@ -13,6 +13,7 @@ #include "FS.h" #include "Log.h" #include "Family.h" +#include "Config.h" namespace i2p { @@ -98,7 +99,8 @@ namespace data void Families::LoadCertificates () { - std::string certDir = i2p::fs::DataDirPath("certificates", "family"); + std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "family"; + std::vector files; int numCertificates = 0; diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 2bf89556..ea30a249 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -164,9 +164,7 @@ namespace garlic RAND_bytes (elGamal.preIV, 32); // Pre-IV uint8_t iv[32]; // IV is first 16 bytes SHA256(elGamal.preIV, 32, iv); - BN_CTX * ctx = BN_CTX_new (); - m_Destination->Encrypt ((uint8_t *)&elGamal, buf, ctx); - BN_CTX_free (ctx); + m_Destination->Encrypt ((uint8_t *)&elGamal, buf); m_Encryption.SetIV (iv); buf += 514; len += 514; @@ -433,14 +431,14 @@ namespace garlic } GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default - m_NumRatchetInboundTags (0) // 0 means standard + m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0) // 0 means standard { - m_Ctx = BN_CTX_new (); } GarlicDestination::~GarlicDestination () { - BN_CTX_free (m_Ctx); + if (m_PayloadBuffer) + delete[] m_PayloadBuffer; } void GarlicDestination::CleanUp () @@ -471,8 +469,13 @@ namespace garlic { uint64_t t; memcpy (&t, tag, 8); + AddECIESx25519Key (key, t); + } + + void GarlicDestination::AddECIESx25519Key (const uint8_t * key, uint64_t tag) + { auto tagset = std::make_shared(this, key); - m_ECIESx25519Tags.emplace (t, ECIESX25519AEADRatchetIndexTagset{0, tagset}); + m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{0, tagset}); } bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) @@ -494,22 +497,9 @@ namespace garlic buf += 4; // length bool found = false; - uint64_t tag; if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) - { // try ECIESx25519 tag - memcpy (&tag, buf, 8); - auto it1 = m_ECIESx25519Tags.find (tag); - if (it1 != m_ECIESx25519Tags.end ()) - { - found = true; - if (it1->second.tagset->HandleNextMessage (buf, length, it1->second.index)) - m_LastTagset = it1->second.tagset; - else - LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message"); - m_ECIESx25519Tags.erase (it1); - } - } + found = HandleECIESx25519TagMessage (buf, length); if (!found) { auto it = !mod ? m_Tags.find (SessionTag(buf)) : m_Tags.end (); // AES block is multiple of 16 @@ -537,7 +527,7 @@ namespace garlic // try ElGamal/AES first if leading block is 514 ElGamalBlock elGamal; if (mod == 2 && length >= 514 && SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) && - Decrypt (buf, (uint8_t *)&elGamal, m_Ctx, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)) + Decrypt (buf, (uint8_t *)&elGamal, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL)) { auto decryption = std::make_shared(elGamal.sessionKey); uint8_t iv[32]; // IV is first 16 bytes @@ -555,6 +545,7 @@ namespace garlic // try to gererate more tags for last tagset if (m_LastTagset && (m_LastTagset->GetNextIndex () - m_LastTagset->GetTrimBehind () < 3*ECIESX25519_MAX_NUM_GENERATED_TAGS)) { + uint64_t missingTag; memcpy (&missingTag, buf, 8); auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS); LogPrint (eLogWarning, "Garlic: trying to generate more ECIES-X25519-AEAD-Ratchet tags"); for (int i = 0; i < maxTags; i++) @@ -565,10 +556,10 @@ namespace garlic LogPrint (eLogError, "Garlic: can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset"); break; } - if (nextTag == tag) + if (nextTag == missingTag) { LogPrint (eLogDebug, "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated"); - if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[tag].index)) + if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[nextTag].index)) found = true; break; } @@ -585,6 +576,23 @@ namespace garlic } } + bool GarlicDestination::HandleECIESx25519TagMessage (uint8_t * buf, size_t len) + { + uint64_t tag; + memcpy (&tag, buf, 8); + auto it = m_ECIESx25519Tags.find (tag); + if (it != m_ECIESx25519Tags.end ()) + { + if (it->second.tagset->HandleNextMessage (buf, len, it->second.index)) + m_LastTagset = it->second.tagset; + else + LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message"); + m_ECIESx25519Tags.erase (it); + return true; + } + return false; + } + void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, std::shared_ptr from) { @@ -749,11 +757,7 @@ namespace garlic std::shared_ptr msg) { if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - { - auto session = std::make_shared(this, false); - session->SetRemoteStaticKey (router->GetIdentity ()->GetEncryptionPublicKey ()); - return session->WrapOneTimeMessage (msg, true); - } + return WrapECIESX25519MessageForRouter (msg, router->GetIdentity ()->GetEncryptionPublicKey ()); else { auto session = GetRoutingSession (router, false); @@ -769,7 +773,7 @@ namespace garlic { ECIESX25519AEADRatchetSessionPtr session; uint8_t staticKey[32]; - destination->Encrypt (nullptr, staticKey, nullptr); // we are supposed to get static key + destination->Encrypt (nullptr, staticKey); // we are supposed to get static key auto it = m_ECIESx25519Sessions.find (staticKey); if (it != m_ECIESx25519Sessions.end ()) { @@ -1036,10 +1040,11 @@ namespace garlic { LogPrint (eLogDebug, "Garlic: type local"); I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid - buf += (4 + 4); // msgID + expiration + int32_t msgID = bufbe32toh (buf); buf += 4; // msgID + buf += 4; // expiration ptrdiff_t offset = buf - buf1; if (offset <= (int)len) - HandleCloveI2NPMessage (typeID, buf, len - offset); + HandleCloveI2NPMessage (typeID, buf, len - offset, msgID); else LogPrint (eLogError, "Garlic: clove is too long"); break; @@ -1058,13 +1063,14 @@ namespace garlic } uint32_t gwTunnel = bufbe32toh (buf); buf += 4; I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid - buf += (4 + 4); // msgID + expiration + uint32_t msgID = bufbe32toh (buf); buf += 4; // msgID + buf += 4; // expiration offset += 13; if (GetTunnelPool ()) { auto tunnel = GetTunnelPool ()->GetNextOutboundTunnel (); if (tunnel) - tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset)); + tunnel->SendTunnelDataMsg (gwHash, gwTunnel, CreateI2NPMessage (typeID, buf, len - offset, msgID)); else LogPrint (eLogWarning, "Garlic: No outbound tunnels available for garlic clove"); } @@ -1115,5 +1121,12 @@ namespace garlic m_ECIESx25519Sessions.erase (it); } } + + uint8_t * GarlicDestination::GetPayloadBuffer () + { + if (!m_PayloadBuffer) + m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE]; + return m_PayloadBuffer; + } } } diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index 4288e74b..0d7b8461 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -243,14 +243,15 @@ namespace garlic std::shared_ptr msg); void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag - void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag + void AddECIESx25519Key (const uint8_t * key, uint64_t tag); // one tag virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID); uint64_t AddECIESx25519SessionNextTag (ReceiveRatchetTagSetPtr tagset); void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session); void RemoveECIESx25519Session (const uint8_t * staticKey); void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len); - + uint8_t * GetPayloadBuffer (); + virtual void ProcessGarlicMessage (std::shared_ptr msg); virtual void ProcessDeliveryStatusMessage (std::shared_ptr msg); virtual void SetLeaseSetUpdated (); @@ -260,8 +261,10 @@ namespace garlic protected: + void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag + bool HandleECIESx25519TagMessage (uint8_t * buf, size_t len); // return true if found virtual void HandleI2NPMessage (const uint8_t * buf, size_t len) = 0; // called from clove only - virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) = 0; + virtual bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) = 0; void HandleGarlicMessage (std::shared_ptr msg); void HandleDeliveryStatusMessage (uint32_t msgID); @@ -276,12 +279,12 @@ namespace garlic private: - BN_CTX * m_Ctx; // incoming // outgoing sessions int m_NumTags; std::mutex m_SessionsMutex; std::unordered_map m_Sessions; std::unordered_map, ECIESX25519AEADRatchetSessionPtr> m_ECIESx25519Sessions; // static key -> session + uint8_t * m_PayloadBuffer; // for ECIESX25519AEADRatchet // incoming int m_NumRatchetInboundTags; std::unordered_map, std::hash > > m_Tags; diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index 7c4d84c3..29d68b3c 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -36,10 +36,21 @@ namespace i2p return std::make_shared >(); } - std::shared_ptr NewI2NPTunnelMessage () + std::shared_ptr NewI2NPTunnelMessage (bool endpoint) { - auto msg = new I2NPMessageBuffer(); // reserved for alignment and NTCP 16 + 6 + 12 - msg->Align (12); + I2NPMessage * msg = nullptr; + if (endpoint) + { + // should fit two tunnel message + tunnel gateway header, enough for one garlic encrypted streaming packet + msg = new I2NPMessageBuffer<2*i2p::tunnel::TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28>(); // reserved for alignment and NTCP 16 + 6 + 6 + msg->Align (6); + msg->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header + } + else + { + msg = new I2NPMessageBuffer(); // reserved for alignment and NTCP 16 + 6 + 12 + msg->Align (12); + } return std::shared_ptr(msg); } @@ -211,12 +222,12 @@ namespace i2p { memcpy (buf + 33, replyTag, 8); // 8 bytes tag buf += 41; - } - else - { + } + else + { memcpy (buf + 33, replyTag, 32); // 32 bytes tag buf += 65; - } + } m->len += (buf - m->GetPayload ()); m->FillI2NPMessageHeader (eI2NPDatabaseLookup); @@ -251,6 +262,12 @@ namespace i2p if (!router) // we send own RouterInfo router = context.GetSharedRouterInfo (); + if (!router->GetBuffer ()) + { + LogPrint (eLogError, "I2NP: Invalid RouterInfo buffer for DatabaseStore"); + return nullptr; + } + auto m = NewI2NPShortMessage (); uint8_t * payload = m->GetPayload (); @@ -268,12 +285,12 @@ namespace i2p buf += 32; // reply tunnel gateway } else - { + { memset (buf, 0, 4); // zero tunnelID means direct reply buf += 4; memcpy (buf, context.GetIdentHash (), 32); buf += 32; - } + } } uint8_t * sizePtr = buf; @@ -286,7 +303,7 @@ namespace i2p { i2p::data::GzipDeflator deflator; size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len); - } + } if (size) { htobe16buf (sizePtr, size); // size @@ -364,7 +381,7 @@ namespace i2p return g_MaxNumTransitTunnels; } - bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) + static bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) { for (int i = 0; i < num; i++) { @@ -374,78 +391,50 @@ namespace i2p LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours"); if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) return false; uint8_t retCode = 0; - bool isECIES = i2p::context.IsECIES (); // replace record to reply if (i2p::context.AcceptsTunnels () && i2p::tunnel::tunnels.GetTransitTunnels ().size () <= g_MaxNumTransitTunnels && !i2p::transport::transports.IsBandwidthExceeded () && !i2p::transport::transports.IsTransitBandwidthExceeded ()) { - auto transitTunnel = isECIES ? - i2p::tunnel::CreateTransitTunnel ( + auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) : - i2p::tunnel::CreateTransitTunnel ( - bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), - clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, - clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, - clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, - clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); } else retCode = 30; // always reject with bandwidth reason (30) - if (isECIES) - { - memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options - record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; - } - else - { - record[BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; - SHA256 (record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1, // + 1 byte of ret - record + BUILD_RESPONSE_RECORD_HASH_OFFSET); - } + memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options + record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; // encrypt reply i2p::crypto::CBCEncryption encryption; for (int j = 0; j < num; j++) { uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; - if (isECIES) - { - if (j == i) + if (j == i) + { + uint8_t nonce[12]; + memset (nonce, 0, 12); + auto& noiseState = i2p::context.GetCurrentNoiseState (); + if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, + noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt { - uint8_t nonce[12]; - memset (nonce, 0, 12); - auto& noiseState = i2p::context.GetCurrentNoiseState (); - if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt - { - LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed"); - return false; - } + LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed"); + return false; } - else - { - encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); - encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); - encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); - } } else { - encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); - encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); + encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); + encryption.SetIV (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); - } + } } return true; } @@ -453,7 +442,7 @@ namespace i2p return false; } - void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) + static void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) { int num = buf[0]; LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records"); @@ -482,84 +471,38 @@ namespace i2p } else { - if (i2p::context.IsECIES ()) + uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + if (HandleBuildRequestRecords (num, buf + 1, clearText)) { - uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (HandleBuildRequestRecords (num, buf + 1, clearText)) + if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel { - if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel - { - // so we send it to reply tunnel - transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - eI2NPVariableTunnelBuildReply, buf, len, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - else - transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + // so we send it to reply tunnel + transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + eI2NPVariableTunnelBuildReply, buf, len, + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); } + else + transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, + bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); } - else - { - uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (HandleBuildRequestRecords (num, buf + 1, clearText)) - { - if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel - { - // so we send it to reply tunnel - transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - eI2NPVariableTunnelBuildReply, buf, len, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - else - transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - } } } - void HandleTunnelBuildMsg (uint8_t * buf, size_t len) + static void HandleTunnelBuildMsg (uint8_t * buf, size_t len) { - if (i2p::context.IsECIES ()) - { - LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router"); - return; - } - if (len < NUM_TUNNEL_BUILD_RECORDS*TUNNEL_BUILD_RECORD_SIZE) - { - LogPrint (eLogError, "I2NP: TunnelBuild message is too short ", len); - return; - } - uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText)) - { - if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outbound tunnel - { - // so we send it to reply tunnel - transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - eI2NPTunnelBuildReply, buf, len, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - else - transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateI2NPMessage (eI2NPTunnelBuild, buf, len, - bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } + LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router"); } - void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) + static void HandleTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len, bool isShort) { int num = buf[0]; - LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID); - if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1) + LogPrint (eLogDebug, "I2NP: TunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID); + size_t recordSize = isShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; + if (len < num*recordSize + 1) { - LogPrint (eLogError, "I2NP: VaribleTunnelBuildReply message of ", num, " records is too short ", len); + LogPrint (eLogError, "I2NP: TunnelBuildReply message of ", num, " records is too short ", len); return; } @@ -583,13 +526,8 @@ namespace i2p LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found"); } - void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) + static void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) { - if (!i2p::context.IsECIES ()) - { - LogPrint (eLogWarning, "I2NP: ShortTunnelBuild can be handled by ECIES router only"); - return; - } int num = buf[0]; LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records"); if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) @@ -597,7 +535,24 @@ namespace i2p LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len); return; } - // TODO: check replyMsgID + auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (replyMsgID); + if (tunnel) + { + // endpoint of inbound tunnel + LogPrint (eLogDebug, "I2NP: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); + if (tunnel->HandleTunnelBuildResponse (buf, len)) + { + LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); + tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); + i2p::tunnel::tunnels.AddInboundTunnel (tunnel); + } + else + { + LogPrint (eLogInfo, "I2NP: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); + tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); + } + return; + } const uint8_t * record = buf + 1; for (int i = 0; i < num; i++) { @@ -606,92 +561,113 @@ namespace i2p LogPrint (eLogDebug, "I2NP: Short request record ", i, " is ours"); uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) - { + { LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i); return; } - auto& noiseState = i2p::context.GetCurrentNoiseState (); - uint8_t layerKeys[64]; // (layer key, iv key) - i2p::crypto::HKDF (noiseState.m_CK + 32, nullptr, 0, "LayerAndIVKeys", layerKeys); // TODO: correct domain - auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( - bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), - clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, - bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - layerKeys, layerKeys + 32, - clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, - clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); - i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); - if (clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) + if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES { - // we are endpoint, create OutboundTunnelBuildReply - auto otbrm = NewI2NPShortMessage (); - auto payload = otbrm->GetPayload (); - payload[0] = num; // num - payload[1] = i; // slot - payload +=2; - // reply - htobe16buf (payload, 3); payload += 2; // length, TODO - memset (payload, 0, 3); payload += 3; // ClearText: no options, and zero ret code. TODO - // ShortBuildReplyRecords. Exclude ours - uint8_t * records = buf + 1; - if (i > 0) - { - memcpy (payload, records, i*SHORT_TUNNEL_BUILD_RECORD_SIZE); - payload += i*SHORT_TUNNEL_BUILD_RECORD_SIZE; - records += i*SHORT_TUNNEL_BUILD_RECORD_SIZE; - } - if (i < num-1) - { - memcpy (payload, records, (num-1-i)*SHORT_TUNNEL_BUILD_RECORD_SIZE); - payload += (num-1-i)*SHORT_TUNNEL_BUILD_RECORD_SIZE; - } - otbrm->len += (payload - otbrm->GetPayload ()); - otbrm->FillI2NPMessageHeader (eI2NPOutboundTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); - uint8_t replyKeys[64]; // (reply key, tag) - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "ReplyKeyAndTag", replyKeys); // TODO: correct domain - uint64_t tag; - memcpy (&tag, replyKeys + 32, 8); - // send garlic to reply tunnel - transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - i2p::garlic::WrapECIESX25519AEADRatchetMessage (otbrm, replyKeys, tag))); + LogPrint (eLogWarning, "I2NP: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record"); + return; + } + auto& noiseState = i2p::context.GetCurrentNoiseState (); + uint8_t replyKey[32], layerKey[32], ivKey[32]; + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK); + memcpy (replyKey, noiseState.m_CK + 32, 32); + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK); + memcpy (layerKey, noiseState.m_CK + 32, 32); + bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; + if (isEndpoint) + { + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK); + memcpy (ivKey, noiseState.m_CK + 32, 32); } else - { - // we are participant, encrypt reply - uint8_t nonce[12]; - memset (nonce, 0, 12); - uint8_t * reply = buf + 1; - for (int j = 0; j < num; j++) - { - nonce[4] = j; // nonce is record # - if (j == i) + memcpy (ivKey, noiseState.m_CK , 32); + + // check if we accept this tunnel + uint8_t retCode = 0; + if (!i2p::context.AcceptsTunnels () || + i2p::tunnel::tunnels.GetTransitTunnels ().size () > g_MaxNumTransitTunnels || + i2p::transport::transports.IsBandwidthExceeded () || + i2p::transport::transports.IsTransitBandwidthExceeded ()) + retCode = 30; + if (!retCode) + { + // create new transit tunnel + auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( + bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), + clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, + bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + layerKey, ivKey, + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); + i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); + } + + // encrypt reply + uint8_t nonce[12]; + memset (nonce, 0, 12); + uint8_t * reply = buf + 1; + for (int j = 0; j < num; j++) + { + nonce[4] = j; // nonce is record # + if (j == i) + { + memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options + reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode; + if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, + noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt { - // TODO: fill reply - if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState.m_H, 32, noiseState.m_CK, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt - { - LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed"); - return; - } + LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed"); + return; } - else - i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, noiseState.m_CK, nonce, reply); - reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; } - transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, + else + i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); + reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; + } + // send reply + if (isEndpoint) + { + auto replyMsg = NewI2NPShortMessage (); + replyMsg->Concat (buf, len); + replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); + if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (), + clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local? + { + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK); + uint64_t tag; + memcpy (&tag, noiseState.m_CK, 8); + // we send it to reply tunnel + transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag))); + } + else + { + // IBGW is local + uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET); + auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID); + if (tunnel) + tunnel->SendTunnelDataMsg (replyMsg); + else + LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); + } + } + else + transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } return; - } + } record += SHORT_TUNNEL_BUILD_RECORD_SIZE; - } - } - + } + } + std::shared_ptr CreateTunnelDataMsg (const uint8_t * buf) { - auto msg = NewI2NPTunnelMessage (); + auto msg = NewI2NPTunnelMessage (false); msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE); msg->FillI2NPMessageHeader (eI2NPTunnelData); return msg; @@ -699,7 +675,7 @@ namespace i2p std::shared_ptr CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload) { - auto msg = NewI2NPTunnelMessage (); + auto msg = NewI2NPTunnelMessage (false); htobe32buf (msg->GetPayload (), tunnelID); msg->len += 4; // tunnelID msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4); @@ -707,9 +683,9 @@ namespace i2p return msg; } - std::shared_ptr CreateEmptyTunnelDataMsg () + std::shared_ptr CreateEmptyTunnelDataMsg (bool endpoint) { - auto msg = NewI2NPTunnelMessage (); + auto msg = NewI2NPTunnelMessage (endpoint); msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; return msg; } @@ -803,12 +779,15 @@ namespace i2p case eI2NPVariableTunnelBuild: HandleVariableTunnelBuildMsg (msgID, buf, size); break; - case eI2NPVariableTunnelBuildReply: - HandleVariableTunnelBuildReplyMsg (msgID, buf, size); - break; case eI2NPShortTunnelBuild: HandleShortTunnelBuildMsg (msgID, buf, size); - break; + break; + case eI2NPVariableTunnelBuildReply: + HandleTunnelBuildReplyMsg (msgID, buf, size, false); + break; + case eI2NPShortTunnelBuildReply: + HandleTunnelBuildReplyMsg (msgID, buf, size, true); + break; case eI2NPTunnelBuild: HandleTunnelBuildMsg (buf, size); break; @@ -865,7 +844,8 @@ namespace i2p case eI2NPVariableTunnelBuildReply: case eI2NPTunnelBuild: case eI2NPTunnelBuildReply: - case eI2NPShortTunnelBuild: + case eI2NPShortTunnelBuild: + case eI2NPShortTunnelBuildReply: // forward to tunnel thread i2p::tunnel::tunnels.PostTunnelData (msg); break; diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index ee143734..f0777ac2 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -55,33 +55,12 @@ namespace i2p // TunnelBuild const size_t TUNNEL_BUILD_RECORD_SIZE = 528; - const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 236; + const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 218; - //BuildRequestRecordClearText - const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0; - const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4; - const size_t BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET + 32; - const size_t BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4; - const size_t BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32; - const size_t BUILD_REQUEST_RECORD_IV_KEY_OFFSET = BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32; - const size_t BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32; - const size_t BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32; - const size_t BUILD_REQUEST_RECORD_FLAG_OFFSET = BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16; - const size_t BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = BUILD_REQUEST_RECORD_FLAG_OFFSET + 1; - const size_t BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4; - const size_t BUILD_REQUEST_RECORD_PADDING_OFFSET = BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4; - const size_t BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 222; - // BuildRequestRecordEncrypted const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0; const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16; - // BuildResponseRecord - const size_t BUILD_RESPONSE_RECORD_HASH_OFFSET = 0; - const size_t BUILD_RESPONSE_RECORD_PADDING_OFFSET = 32; - const size_t BUILD_RESPONSE_RECORD_PADDING_SIZE = 495; - const size_t BUILD_RESPONSE_RECORD_RET_OFFSET = BUILD_RESPONSE_RECORD_PADDING_OFFSET + BUILD_RESPONSE_RECORD_PADDING_SIZE; - // ECIES BuildRequestRecordClearText const size_t ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0; const size_t ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4; @@ -113,7 +92,12 @@ namespace i2p const size_t SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET = SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE + 1; const size_t SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET = SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4; const size_t SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET = SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4; - const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 172; + const size_t SHORT_REQUEST_RECORD_PADDING_OFFSET = SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4; + const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 154; + + // ShortResponseRecord + const size_t SHORT_RESPONSE_RECORD_OPTIONS_OFFSET = 0; + const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201; enum I2NPMessageType { @@ -131,7 +115,7 @@ namespace i2p eI2NPVariableTunnelBuild = 23, eI2NPVariableTunnelBuildReply = 24, eI2NPShortTunnelBuild = 25, - eI2NPOutboundTunnelBuildReply = 26 + eI2NPShortTunnelBuildReply = 26 }; const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80; @@ -278,7 +262,7 @@ namespace tunnel std::shared_ptr NewI2NPMessage (); std::shared_ptr NewI2NPShortMessage (); - std::shared_ptr NewI2NPTunnelMessage (); + std::shared_ptr NewI2NPTunnelMessage (bool endpoint); std::shared_ptr NewI2NPMessage (size_t len); std::shared_ptr CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); @@ -299,15 +283,9 @@ namespace tunnel std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr leaseSet, uint32_t replyToken = 0, std::shared_ptr replyTunnel = nullptr); bool IsRouterInfoMsg (std::shared_ptr msg); - bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText); - void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); - void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); - void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); - void HandleTunnelBuildMsg (uint8_t * buf, size_t len); - std::shared_ptr CreateTunnelDataMsg (const uint8_t * buf); std::shared_ptr CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload); - std::shared_ptr CreateEmptyTunnelDataMsg (); + std::shared_ptr CreateEmptyTunnelDataMsg (bool endpoint); std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len); std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, diff --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index 9dfaa1fc..aa03bfb7 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -42,7 +42,7 @@ namespace data } IdentityEx::IdentityEx (): - m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + m_ExtendedLen (0) { } @@ -119,11 +119,15 @@ namespace data m_StandardIdentity.certificate[0] = CERTIFICATE_TYPE_KEY; htobe16buf (m_StandardIdentity.certificate + 1, m_ExtendedLen); // fill extended buffer - m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; htobe16buf (m_ExtendedBuffer, type); htobe16buf (m_ExtendedBuffer + 2, cryptoType); if (excessLen && excessBuf) { + if (excessLen > MAX_EXTENDED_BUFFER_SIZE - 4) + { + LogPrint (eLogError, "Identity: Unexpected excessive signing key len ", excessLen); + excessLen = MAX_EXTENDED_BUFFER_SIZE - 4; + } memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen); delete[] excessBuf; } @@ -136,7 +140,6 @@ namespace data memset (m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate)); m_IdentHash = m_StandardIdentity.Hash (); m_ExtendedLen = 0; - m_ExtendedBuffer = nullptr; } CreateVerifier (); } @@ -154,26 +157,25 @@ namespace data } IdentityEx::IdentityEx (const uint8_t * buf, size_t len): - m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + m_ExtendedLen (0) { FromBuffer (buf, len); } IdentityEx::IdentityEx (const IdentityEx& other): - m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + m_ExtendedLen (0) { *this = other; } IdentityEx::IdentityEx (const Identity& standard): - m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + m_ExtendedLen (0) { *this = standard; } IdentityEx::~IdentityEx () { - delete[] m_ExtendedBuffer; delete m_Verifier; } @@ -182,15 +184,12 @@ namespace data memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE); m_IdentHash = other.m_IdentHash; - delete[] m_ExtendedBuffer; m_ExtendedLen = other.m_ExtendedLen; if (m_ExtendedLen > 0) { - m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; + if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE; memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen); } - else - m_ExtendedBuffer = nullptr; delete m_Verifier; m_Verifier = nullptr; @@ -203,8 +202,6 @@ namespace data m_StandardIdentity = standard; m_IdentHash = m_StandardIdentity.Hash (); - delete[] m_ExtendedBuffer; - m_ExtendedBuffer = nullptr; m_ExtendedLen = 0; delete m_Verifier; @@ -222,15 +219,12 @@ namespace data } memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE); - if(m_ExtendedBuffer) delete[] m_ExtendedBuffer; - m_ExtendedBuffer = nullptr; - m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1); if (m_ExtendedLen) { if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len) { - m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; + if (m_ExtendedLen > MAX_EXTENDED_BUFFER_SIZE) m_ExtendedLen = MAX_EXTENDED_BUFFER_SIZE; memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen); } else @@ -241,10 +235,7 @@ namespace data } } else - { m_ExtendedLen = 0; - m_ExtendedBuffer = nullptr; - } SHA256(buf, GetFullLen (), m_IdentHash); delete m_Verifier; @@ -258,7 +249,7 @@ namespace data const size_t fullLen = GetFullLen(); if (fullLen > len) return 0; // buffer is too small and may overflow somewhere else memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE); - if (m_ExtendedLen > 0 && m_ExtendedBuffer) + if (m_ExtendedLen > 0) memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen); return fullLen; } diff --git a/libi2pd/Identity.h b/libi2pd/Identity.h index e9cf63ed..f96d5f41 100644 --- a/libi2pd/Identity.h +++ b/libi2pd/Identity.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -84,8 +84,9 @@ namespace data typedef uint16_t SigningKeyType; typedef uint16_t CryptoKeyType; + const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521 class IdentityEx - { + { public: IdentityEx (); @@ -137,7 +138,7 @@ namespace data mutable i2p::crypto::Verifier * m_Verifier = nullptr; mutable std::mutex m_VerifierMutex; size_t m_ExtendedLen; - uint8_t * m_ExtendedBuffer; + uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE]; }; class PrivateKeys // for eepsites @@ -222,7 +223,7 @@ namespace data virtual ~RoutingDestination () {}; virtual std::shared_ptr GetIdentity () const = 0; - virtual void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const = 0; // encrypt data for + virtual void Encrypt (const uint8_t * data, uint8_t * encrypted) const = 0; // encrypt data for virtual bool IsDestination () const = 0; // for garlic const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; @@ -234,7 +235,7 @@ namespace data public: virtual ~LocalDestination() {}; - virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0; + virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, CryptoKeyType preferredCrypto = CRYPTO_KEY_TYPE_ELGAMAL) const = 0; virtual std::shared_ptr GetIdentity () const = 0; const IdentHash& GetIdentHash () const { return GetIdentity ()->GetIdentHash (); }; diff --git a/libi2pd/LeaseSet.cpp b/libi2pd/LeaseSet.cpp index a0b14385..f6734b10 100644 --- a/libi2pd/LeaseSet.cpp +++ b/libi2pd/LeaseSet.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -254,12 +254,12 @@ namespace data return ts > m_ExpirationTime; } - void LeaseSet::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const + void LeaseSet::Encrypt (const uint8_t * data, uint8_t * encrypted) const { if (!m_EncryptionKey) return; auto encryptor = m_Identity->CreateEncryptor (m_EncryptionKey); if (encryptor) - encryptor->Encrypt (data, encrypted, ctx, true); + encryptor->Encrypt (data, encrypted); } void LeaseSet::SetBuffer (const uint8_t * buf, size_t len) @@ -658,11 +658,11 @@ namespace data return offset - 1; } - void LeaseSet2::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const + void LeaseSet2::Encrypt (const uint8_t * data, uint8_t * encrypted) const { auto encryptor = m_Encryptor; // TODO: atomic if (encryptor) - encryptor->Encrypt (data, encrypted, ctx, true); + encryptor->Encrypt (data, encrypted); } uint64_t LeaseSet2::ExtractExpirationTimestamp (const uint8_t * buf, size_t len) const @@ -912,6 +912,11 @@ namespace data uint8_t blindedPriv[64], blindedPub[128]; // 64 and 128 max size_t publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub); std::unique_ptr blindedSigner (i2p::data::PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv)); + if (!blindedSigner) + { + LogPrint (eLogError, "LeaseSet2: Can't create blinded signer for signature type ", blindedKey.GetSigType ()); + return; + } auto offset = 1; htobe16buf (m_Buffer + offset, blindedKey.GetBlindedSigType ()); offset += 2; // Blinded Public Key Sig Type memcpy (m_Buffer + offset, blindedPub, publicKeyLen); offset += publicKeyLen; // Blinded Public Key diff --git a/libi2pd/LeaseSet.h b/libi2pd/LeaseSet.h index cd6535df..8d501cb1 100644 --- a/libi2pd/LeaseSet.h +++ b/libi2pd/LeaseSet.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -93,7 +93,7 @@ namespace data // implements RoutingDestination std::shared_ptr GetIdentity () const { return m_Identity; }; - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; + void Encrypt (const uint8_t * data, uint8_t * encrypted) const; bool IsDestination () const { return true; }; protected: @@ -156,7 +156,7 @@ namespace data bool IsNewer (const uint8_t * buf, size_t len) const; // implements RoutingDestination - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; + void Encrypt (const uint8_t * data, uint8_t * encrypted) const; CryptoKeyType GetEncryptionType () const { return m_EncryptionType; }; private: diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index cb200b42..05d68203 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -32,14 +32,12 @@ namespace i2p namespace transport { NTCP2Establisher::NTCP2Establisher (): - m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr) + m_SessionConfirmedBuffer (nullptr) { } NTCP2Establisher::~NTCP2Establisher () { - delete[] m_SessionRequestBuffer; - delete[] m_SessionCreatedBuffer; delete[] m_SessionConfirmedBuffer; } @@ -112,9 +110,8 @@ namespace transport void NTCP2Establisher::CreateSessionRequestMessage () { // create buffer and fill padding - auto paddingLength = rand () % (287 - 64); // message length doesn't exceed 287 bytes + auto paddingLength = rand () % (NTCP2_SESSION_REQUEST_MAX_SIZE - 64); // message length doesn't exceed 287 bytes m_SessionRequestBufferLen = paddingLength + 64; - m_SessionRequestBuffer = new uint8_t[m_SessionRequestBufferLen]; RAND_bytes (m_SessionRequestBuffer + 64, paddingLength); // encrypt X i2p::crypto::CBCEncryption encryption; @@ -152,9 +149,8 @@ namespace transport void NTCP2Establisher::CreateSessionCreatedMessage () { - auto paddingLen = rand () % (287 - 64); + auto paddingLen = rand () % (NTCP2_SESSION_CREATED_MAX_SIZE - 64); m_SessionCreatedBufferLen = paddingLen + 64; - m_SessionCreatedBuffer = new uint8_t[m_SessionCreatedBufferLen]; RAND_bytes (m_SessionCreatedBuffer + 64, paddingLen); // encrypt Y i2p::crypto::CBCEncryption encryption; @@ -332,7 +328,8 @@ namespace transport m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr), #endif m_NextReceivedLen (0), m_NextReceivedBuffer (nullptr), m_NextSendBuffer (nullptr), - m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), m_IsSending (false) + m_NextReceivedBufferSize (0), m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), + m_IsSending (false), m_IsReceiving (false), m_NextPaddingSize (16) { if (in_RemoteRouter) // Alice { @@ -404,7 +401,30 @@ namespace transport htole64buf (nonce + 4, seqn); } + void NTCP2Session::CreateNextReceivedBuffer (size_t size) + { + if (m_NextReceivedBuffer) + { + if (size <= m_NextReceivedBufferSize) + return; // buffer is good, do nothing + else + delete[] m_NextReceivedBuffer; + } + m_NextReceivedBuffer = new uint8_t[size]; + m_NextReceivedBufferSize = size; + } + void NTCP2Session::DeleteNextReceiveBuffer (uint64_t ts) + { + if (m_NextReceivedBuffer && !m_IsReceiving && + ts > m_LastActivityTimestamp + NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT) + { + delete[] m_NextReceivedBuffer; + m_NextReceivedBuffer = nullptr; + m_NextReceivedBufferSize = 0; + } + } + void NTCP2Session::KeyDerivationFunctionDataPhase () { uint8_t k[64]; @@ -439,7 +459,6 @@ namespace transport } else { - m_Establisher->m_SessionCreatedBuffer = new uint8_t[287]; // TODO: determine actual max size // we receive first 64 bytes (32 Y, and 32 ChaCha/Poly frame) first boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer, 64), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionCreatedReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -462,7 +481,7 @@ namespace transport { if (paddingLen > 0) { - if (paddingLen <= 287 - 64) // session request is 287 bytes max + if (paddingLen <= NTCP2_SESSION_REQUEST_MAX_SIZE - 64) // session request is 287 bytes max { boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer + 64, paddingLen), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionRequestPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -515,7 +534,7 @@ namespace transport { if (paddingLen > 0) { - if (paddingLen <= 287 - 64) // session created is 287 bytes max + if (paddingLen <= NTCP2_SESSION_CREATED_MAX_SIZE - 64) // session created is 287 bytes max { boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionCreatedBuffer + 64, paddingLen), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionCreatedPaddingReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -718,7 +737,6 @@ namespace transport void NTCP2Session::ServerLogin () { m_Establisher->CreateEphemeralKey (); - m_Establisher->m_SessionRequestBuffer = new uint8_t[287]; // 287 bytes max for now boost::asio::async_read (m_Socket, boost::asio::buffer(m_Establisher->m_SessionRequestBuffer, 64), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleSessionRequestReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -758,8 +776,7 @@ namespace transport LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen); if (m_NextReceivedLen >= 16) { - if (m_NextReceivedBuffer) delete[] m_NextReceivedBuffer; - m_NextReceivedBuffer = new uint8_t[m_NextReceivedLen]; + CreateNextReceivedBuffer (m_NextReceivedLen); boost::system::error_code ec; size_t moreBytes = m_Socket.available(ec); if (!ec && moreBytes >= m_NextReceivedLen) @@ -786,6 +803,7 @@ namespace transport const int one = 1; setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one)); #endif + m_IsReceiving = true; boost::asio::async_read (m_Socket, boost::asio::buffer(m_NextReceivedBuffer, m_NextReceivedLen), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -809,7 +827,7 @@ namespace transport { LogPrint (eLogDebug, "NTCP2: received message decrypted"); ProcessNextFrame (m_NextReceivedBuffer, m_NextReceivedLen-16); - delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = nullptr; // we don't need received buffer anymore + m_IsReceiving = false; ReceiveLength (); } else @@ -1058,7 +1076,15 @@ namespace transport size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100; if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3; if (paddingSize > len) paddingSize = len; - if (paddingSize) paddingSize = rand () % paddingSize; + if (paddingSize) + { + if (m_NextPaddingSize >= 16) + { + RAND_bytes ((uint8_t *)m_PaddingSizes, sizeof (m_PaddingSizes)); + m_NextPaddingSize = 0; + } + paddingSize = m_PaddingSizes[m_NextPaddingSize++] % paddingSize; + } buf[0] = eNTCP2BlkPadding; // blk htobe16buf (buf + 1, paddingSize); // size memset (buf + 3, 0, paddingSize); @@ -1439,6 +1465,8 @@ namespace transport LogPrint (eLogDebug, "NTCP2: No activity for ", session->GetTerminationTimeout (), " seconds"); session->TerminateByTimeout (); // it doesn't change m_NTCP2Session right a way } + else + it.second->DeleteNextReceiveBuffer (ts); // pending for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();) { diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index 01ee3e34..f9f3e751 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -28,18 +28,21 @@ namespace transport { const size_t NTCP2_UNENCRYPTED_FRAME_MAX_SIZE = 65519; + const size_t NTCP2_SESSION_REQUEST_MAX_SIZE = 287; + const size_t NTCP2_SESSION_CREATED_MAX_SIZE = 287; const int NTCP2_MAX_PADDING_RATIO = 6; // in % const int NTCP2_CONNECT_TIMEOUT = 5; // 5 seconds const int NTCP2_ESTABLISH_TIMEOUT = 10; // 10 seconds const int NTCP2_TERMINATION_TIMEOUT = 120; // 2 minutes const int NTCP2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds + const int NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT = 3; // 3 seconds const int NTCP2_ROUTERINFO_RESEND_INTERVAL = 25*60; // 25 minuntes in seconds const int NTCP2_ROUTERINFO_RESEND_INTERVAL_THRESHOLD = 25*60; // 25 minuntes const int NTCP2_CLOCK_SKEW = 60; // in seconds const int NTCP2_MAX_OUTGOING_QUEUE_SIZE = 500; // how many messages we can queue up - + enum NTCP2BlockType { eNTCP2BlkDateTime = 0, @@ -115,7 +118,8 @@ namespace transport i2p::data::IdentHash m_RemoteIdentHash; uint16_t m3p2Len; - uint8_t * m_SessionRequestBuffer, * m_SessionCreatedBuffer, * m_SessionConfirmedBuffer; + uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE], + m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE], * m_SessionConfirmedBuffer; size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; }; @@ -132,7 +136,8 @@ namespace transport void TerminateByTimeout (); void Done (); void Close () { m_Socket.close (); }; // for accept - + void DeleteNextReceiveBuffer (uint64_t ts); + boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; }; @@ -151,6 +156,7 @@ namespace transport void Established (); void CreateNonce (uint64_t seqn, uint8_t * nonce); + void CreateNextReceivedBuffer (size_t size); void KeyDerivationFunctionDataPhase (); void SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey); @@ -206,6 +212,7 @@ namespace transport #endif uint16_t m_NextReceivedLen; uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer; + size_t m_NextReceivedBufferSize; union { uint8_t buf[8]; @@ -215,9 +222,12 @@ namespace transport i2p::I2NPMessagesHandler m_Handler; - bool m_IsSending; + bool m_IsSending, m_IsReceiving; std::list > m_SendQueue; uint64_t m_NextRouterInfoResendTime; // seconds since epoch + + uint16_t m_PaddingSizes[16]; + int m_NextPaddingSize; }; class NTCP2Server: private i2p::util::RunnableServiceWithWork diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index a1d97cba..8d0025ec 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -55,10 +55,24 @@ namespace data Load (); uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold); - if (m_RouterInfos.size () < threshold) // reseed if # of router less than threshold + if (m_RouterInfos.size () < threshold || m_Floodfills.size () < NETDB_MIN_FLOODFILLS) // reseed if # of router less than threshold or too few floodfiils + { Reseed (); + } else if (!GetRandomRouter (i2p::context.GetSharedRouterInfo (), false)) Reseed (); // we don't have a router we can connect to. Trying to reseed + + auto it = m_RouterInfos.find (i2p::context.GetIdentHash ()); + if (it != m_RouterInfos.end ()) + { + // remove own router + m_RouterInfos.erase (it); + m_Floodfills.remove (it->second); + } + // insert own router + m_RouterInfos.emplace (i2p::context.GetIdentHash (), i2p::context.GetSharedRouterInfo ()); + if (i2p::context.IsFloodfill ()) + m_Floodfills.push_back (i2p::context.GetSharedRouterInfo ()); i2p::config::GetOption("persist.profiles", m_PersistProfiles); @@ -162,10 +176,18 @@ namespace data bool publish = false; if (m_PublishReplyToken) { + // next publishing attempt if (ts - lastPublish >= NETDB_PUBLISH_CONFIRMATION_TIMEOUT) publish = true; } else if (i2p::context.GetLastUpdateTime () > lastPublish || - ts - lastPublish >= NETDB_PUBLISH_INTERVAL) publish = true; + ts - lastPublish >= NETDB_PUBLISH_INTERVAL) + { + // new publish + m_PublishExcluded.clear (); + if (i2p::context.IsFloodfill ()) + m_PublishExcluded.insert (i2p::context.GetIdentHash ()); // do publish to ourselves + publish = true; + } if (publish) // update timestamp and publish { i2p::context.UpdateTimestamp (ts); @@ -453,14 +475,15 @@ namespace data bool NetDb::LoadRouterInfo (const std::string & path) { auto r = std::make_shared(path); - if (r->GetRouterIdentity () && !r->IsUnreachable () && - (r->IsReachable () || !r->IsSSU (false) || m_LastLoad < r->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)) // 1 hour - { + if (r->GetRouterIdentity () && !r->IsUnreachable () && r->HasValidAddresses ()) + { r->DeleteBuffer (); r->ClearProperties (); // properties are not used for regular routers - m_RouterInfos[r->GetIdentHash ()] = r; - if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable - m_Floodfills.push_back (r); + if (m_RouterInfos.emplace (r->GetIdentHash (), r).second) + { + if (r->IsFloodfill () && r->IsEligibleFloodfill ()) + m_Floodfills.push_back (r); + } } else { @@ -556,8 +579,9 @@ namespace data void NetDb::SaveUpdated () { - int updatedCount = 0, deletedCount = 0; + int updatedCount = 0, deletedCount = 0, deletedFloodfillsCount = 0; auto total = m_RouterInfos.size (); + auto totalFloodfills = m_Floodfills.size (); uint64_t expirationTimeout = NETDB_MAX_EXPIRATION_TIMEOUT*1000LL; uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); auto uptime = i2p::context.GetUptime (); @@ -567,8 +591,10 @@ namespace data expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL : NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total; + auto own = i2p::context.GetSharedRouterInfo (); for (auto& it: m_RouterInfos) { + if (it.second == own) continue; // skip own std::string ident = it.second->GetIdentHashBase64(); std::string path = m_Storage.Path(ident); if (it.second->IsUpdated ()) @@ -580,8 +606,9 @@ namespace data updatedCount++; continue; } - // make router reachable back if too few routers - if (it.second->IsUnreachable () && total - deletedCount < NETDB_MIN_ROUTERS) + // make router reachable back if too few routers or floodfills + if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || + (it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) it.second->SetUnreachable (false); // find & mark expired routers if (!it.second->IsReachable () && it.second->IsSSU (false)) @@ -595,6 +622,7 @@ namespace data if (it.second->IsUnreachable ()) { + if (it.second->IsFloodfill ()) deletedFloodfillsCount++; // delete RI file m_Storage.Remove(ident); deletedCount++; @@ -935,7 +963,8 @@ namespace data if (router) { LogPrint (eLogDebug, "NetDb: requested RouterInfo ", key, " found"); - router->LoadBuffer (); + if (!router->GetBuffer ()) + router->LoadBuffer (m_Storage.Path (router->GetIdentHashBase64 ())); if (router->GetBuffer ()) replyMsg = CreateDatabaseStoreMsg (router); } @@ -988,7 +1017,7 @@ namespace data { uint64_t tag; memcpy (&tag, excluded + 33, 8); - replyMsg = i2p::garlic::WrapECIESX25519AEADRatchetMessage (replyMsg, sessionKey, tag); + replyMsg = i2p::garlic::WrapECIESX25519Message (replyMsg, sessionKey, tag); } else { @@ -1145,7 +1174,8 @@ namespace data { return !router->IsHidden () && router != compatibleWith && (reverse ? compatibleWith->IsReachableFrom (*router) : - router->IsReachableFrom (*compatibleWith)); + router->IsReachableFrom (*compatibleWith)) && + router->IsECIES (); }); } @@ -1154,8 +1184,8 @@ namespace data return GetRandomRouter ( [v4, &excluded](std::shared_ptr router)->bool { - return !router->IsHidden () && router->IsPeerTesting (v4) && - !excluded.count (router->GetIdentHash ()); + return !router->IsHidden () && router->IsECIES () && + router->IsPeerTesting (v4) && !excluded.count (router->GetIdentHash ()); }); } @@ -1164,7 +1194,7 @@ namespace data return GetRandomRouter ( [](std::shared_ptr router)->bool { - return !router->IsHidden () && router->IsSSUV6 (); + return !router->IsHidden () && router->IsECIES () && router->IsSSUV6 (); }); } @@ -1173,8 +1203,8 @@ namespace data return GetRandomRouter ( [v4, &excluded](std::shared_ptr router)->bool { - return router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ()) && - !router->IsHidden () && !router->IsFloodfill (); // floodfills don't send relay tag + return !router->IsHidden () && router->IsECIES () && !router->IsFloodfill () && // floodfills don't send relay tag + router->IsIntroducer (v4) && !excluded.count (router->GetIdentHash ()); }); } @@ -1186,8 +1216,9 @@ namespace data return !router->IsHidden () && router != compatibleWith && (reverse ? compatibleWith->IsReachableFrom (*router) : router->IsReachableFrom (*compatibleWith)) && - (router->GetCaps () & RouterInfo::eHighBandwidth) && - router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION; + (router->GetCaps () & RouterInfo::eHighBandwidth) && + router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION && + router->IsECIES (); }); } @@ -1196,23 +1227,55 @@ namespace data { if (m_RouterInfos.empty()) return 0; - uint32_t ind = rand () % m_RouterInfos.size (); - for (int j = 0; j < 2; j++) + uint16_t inds[3]; + RAND_bytes ((uint8_t *)inds, sizeof (inds)); + std::unique_lock l(m_RouterInfosMutex); + inds[0] %= m_RouterInfos.size (); + auto it = m_RouterInfos.begin (); + std::advance (it, inds[0]); + // try random router + if (it != m_RouterInfos.end () && !it->second->IsUnreachable () && filter (it->second)) + return it->second; + // try some routers around + auto it1 = m_RouterInfos.begin (); + if (inds[0]) { - uint32_t i = 0; - std::unique_lock l(m_RouterInfosMutex); - for (const auto& it: m_RouterInfos) - { - if (i >= ind) - { - if (!it.second->IsUnreachable () && filter (it.second)) - return it.second; - } - else - i++; - } - // we couldn't find anything, try second pass - ind = 0; + // before + inds[1] %= inds[0]; + std::advance (it1, (inds[1] + inds[0])/2); + } + else + it1 = it; + auto it2 = it; + if (inds[0] < m_RouterInfos.size () - 1) + { + // after + inds[2] %= (m_RouterInfos.size () - 1 - inds[0]); inds[2] /= 2; + std::advance (it2, inds[2]); + } + // it1 - from, it2 - to + it = it1; + while (it != it2 && it != m_RouterInfos.end ()) + { + if (!it->second->IsUnreachable () && filter (it->second)) + return it->second; + it++; + } + // still not found, try from the begining + it = m_RouterInfos.begin (); + while (it != it1 && it != m_RouterInfos.end ()) + { + if (!it->second->IsUnreachable () && filter (it->second)) + return it->second; + it++; + } + // still not found, try to the begining + it = it2; + while (it != m_RouterInfos.end ()) + { + if (!it->second->IsUnreachable () && filter (it->second)) + return it->second; + it++; } return nullptr; // seems we have too few routers } diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index 364cae4b..097f92e7 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -36,6 +36,7 @@ namespace i2p namespace data { const int NETDB_MIN_ROUTERS = 90; + const int NETDB_MIN_FLOODFILLS = 5; const int NETDB_FLOODFILL_EXPIRATION_TIMEOUT = 60 * 60; // 1 hour, in seconds const int NETDB_INTRODUCEE_EXPIRATION_TIMEOUT = 65 * 60; const int NETDB_MIN_EXPIRATION_TIMEOUT = 90 * 60; // 1.5 hours @@ -44,7 +45,8 @@ namespace data const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15; const int NETDB_MIN_HIGHBANDWIDTH_VERSION = MAKE_VERSION_NUMBER(0, 9, 36); // 0.9.36 - const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 28); // 0.9.28 + const int NETDB_MIN_FLOODFILL_VERSION = MAKE_VERSION_NUMBER(0, 9, 38); // 0.9.38 + const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51 /** function for visiting a leaseset stored in a floodfill */ typedef std::function)> LeaseSetVisitor; diff --git a/libi2pd/Reseed.cpp b/libi2pd/Reseed.cpp index 41111ecc..aec683d4 100644 --- a/libi2pd/Reseed.cpp +++ b/libi2pd/Reseed.cpp @@ -497,7 +497,8 @@ namespace data void Reseeder::LoadCertificates () { - std::string certDir = i2p::fs::DataDirPath("certificates", "reseed"); + std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "reseed"; + std::vector files; int numCertificates = 0; diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 50dd6c59..3710092b 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -43,11 +43,8 @@ namespace i2p m_Decryptor = m_Keys.CreateDecryptor (nullptr); m_TunnelDecryptor = m_Keys.CreateDecryptor (nullptr); UpdateRouterInfo (); - if (IsECIES ()) - { - i2p::crypto::InitNoiseNState (m_InitialNoiseState, GetIdentity ()->GetEncryptionPublicKey ()); - m_ECIESSession = std::make_shared(m_InitialNoiseState); - } + i2p::crypto::InitNoiseNState (m_InitialNoiseState, GetIdentity ()->GetEncryptionPublicKey ()); + m_ECIESSession = std::make_shared(m_InitialNoiseState); } void RouterContext::CreateNewRouter () @@ -470,7 +467,8 @@ namespace i2p uint8_t caps = m_RouterInfo.GetCaps (); caps &= ~i2p::data::RouterInfo::eReachable; caps |= i2p::data::RouterInfo::eUnreachable; - caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill + if (v6 || !SupportsV6 ()) + caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill m_RouterInfo.SetCaps (caps); } uint16_t port = 0; @@ -489,6 +487,7 @@ namespace i2p if (ntcp2) PublishNTCP2Address (port, false, v4, v6, false); // update + m_RouterInfo.UpdateSupportedTransports (); UpdateRouterInfo (); } @@ -528,6 +527,7 @@ namespace i2p } } // update + m_RouterInfo.UpdateSupportedTransports (); UpdateRouterInfo (); } @@ -566,16 +566,21 @@ namespace i2p { bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published); - if (ntcp2 && ntcp2Published) + if (ntcp2) { - std::string ntcp2Host; - if (!i2p::config::IsDefault ("ntcp2.addressv6")) - i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host); + if (ntcp2Published) + { + std::string ntcp2Host; + if (!i2p::config::IsDefault ("ntcp2.addressv6")) + i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host); + else + ntcp2Host = "::1"; + uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port); + if (!ntcp2Port) ntcp2Port = port; + m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port); + } else - ntcp2Host = "::1"; - uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port); - if (!ntcp2Port) ntcp2Port = port; - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port); + m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV6); } } m_RouterInfo.EnableV6 (); @@ -630,7 +635,7 @@ namespace i2p m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (host), ntcp2Port); } else - m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv); + m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV4); } } m_RouterInfo.EnableV4 (); @@ -813,9 +818,9 @@ namespace i2p i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len))); } - bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len) + bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) { - auto msg = CreateI2NPMessage (typeID, payload, len); + auto msg = CreateI2NPMessage (typeID, payload, len, msgID); if (!msg) return false; i2p::HandleI2NPMessage (msg); return true; @@ -825,23 +830,22 @@ namespace i2p void RouterContext::ProcessGarlicMessage (std::shared_ptr msg) { std::unique_lock l(m_GarlicMutex); - if (IsECIES ()) + uint8_t * buf = msg->GetPayload (); + uint32_t len = bufbe32toh (buf); + if (len > msg->GetLength ()) { - uint8_t * buf = msg->GetPayload (); - uint32_t len = bufbe32toh (buf); - if (len > msg->GetLength ()) - { - LogPrint (eLogWarning, "Router: garlic message length ", len, " exceeds I2NP message length ", msg->GetLength ()); - return; - } - buf += 4; + LogPrint (eLogWarning, "Router: garlic message length ", len, " exceeds I2NP message length ", msg->GetLength ()); + return; + } + buf += 4; + if (!HandleECIESx25519TagMessage (buf, len)) // try tag first + { + // then Noise_N one-time decryption if (m_ECIESSession) m_ECIESSession->HandleNextMessage (buf, len); else LogPrint (eLogError, "Router: Session is not set for ECIES router"); - } - else - i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg); + } } void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr msg) @@ -866,23 +870,14 @@ namespace i2p return std::chrono::duration_cast (std::chrono::steady_clock::now() - m_StartupTime).count (); } - bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const + bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const { - return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false; + return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data) : false; } bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data) { - if (IsECIES ()) - return DecryptECIESTunnelBuildRecord (encrypted, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE); - else - { - if (!m_TunnelDecryptor) return false; - BN_CTX * ctx = BN_CTX_new (); - bool success = m_TunnelDecryptor->Decrypt (encrypted, data, ctx, false); - BN_CTX_free (ctx); - return success; - } + return DecryptECIESTunnelBuildRecord (encrypted, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE); } bool RouterContext::DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize) @@ -891,7 +886,7 @@ namespace i2p m_CurrentNoiseState = m_InitialNoiseState; m_CurrentNoiseState.MixHash (encrypted, 32); // h = SHA256(h || sepk) uint8_t sharedSecret[32]; - if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, nullptr, false)) + if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret)) { LogPrint (eLogWarning, "Router: Incorrect ephemeral public key"); return false; @@ -912,13 +907,7 @@ namespace i2p bool RouterContext::DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data) { - if (IsECIES ()) - return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE); - else - { - LogPrint (eLogWarning, "Router: Can't decrypt short request record on non-ECIES router"); - return false; - } + return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE); } i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index acb33795..647c1a7f 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, 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 "Identity.h" #include "RouterInfo.h" #include "Garlic.h" -#include "I18N_langs.h" namespace i2p { @@ -68,11 +67,11 @@ namespace garlic void Init (); const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; - i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; }; - std::shared_ptr GetSharedRouterInfo () const + i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; }; + std::shared_ptr GetSharedRouterInfo () { - return std::shared_ptr (&m_RouterInfo, - [](const i2p::data::RouterInfo *) {}); + return std::shared_ptr (&m_RouterInfo, + [](i2p::data::RouterInfo *) {}); } std::shared_ptr GetSharedDestination () { @@ -124,7 +123,6 @@ namespace garlic void SetSupportsV6 (bool supportsV6); void SetSupportsV4 (bool supportsV4); void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host); - bool IsECIES () const { return GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove @@ -134,7 +132,7 @@ namespace garlic // implements LocalDestination std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; + bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const; void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); }; void SetLeaseSetUpdated () {}; @@ -146,15 +144,11 @@ namespace garlic void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatusMessage (std::shared_ptr msg); - // i18n - std::shared_ptr GetLanguage () { return m_Language; }; - void SetLanguage (const std::shared_ptr language) { m_Language = language; }; - protected: // implements GarlicDestination void HandleI2NPMessage (const uint8_t * buf, size_t len); - bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len); + bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID); private: @@ -186,9 +180,6 @@ namespace garlic std::unique_ptr m_StaticKeys; // for ECIESx25519 i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState; - - // i18n - std::shared_ptr m_Language; }; extern RouterContext context; diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index dcef9463..cb5a3db7 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -35,12 +35,12 @@ namespace data } RouterInfo::RouterInfo (const std::string& fullPath): - m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false), - m_SupportedTransports (0), m_ReachableTransports (0), m_Caps (0), m_Version (0) + m_IsUpdated (false), m_IsUnreachable (false), m_SupportedTransports (0), + m_ReachableTransports (0), m_Caps (0), m_Version (0) { m_Addresses = boost::make_shared(); // create empty list m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; - ReadFromFile (); + ReadFromFile (fullPath); } RouterInfo::RouterInfo (const uint8_t * buf, int len): @@ -113,16 +113,16 @@ namespace data m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); } - bool RouterInfo::LoadFile () + bool RouterInfo::LoadFile (const std::string& fullPath) { - std::ifstream s(m_FullPath, std::ifstream::binary); + std::ifstream s(fullPath, std::ifstream::binary); if (s.is_open ()) { s.seekg (0,std::ios::end); m_BufferLen = s.tellg (); if (m_BufferLen < 40 || m_BufferLen > MAX_RI_BUFFER_SIZE) { - LogPrint(eLogError, "RouterInfo: File", m_FullPath, " is malformed"); + LogPrint(eLogError, "RouterInfo: File", fullPath, " is malformed"); return false; } s.seekg(0, std::ios::beg); @@ -131,15 +131,15 @@ namespace data } else { - LogPrint (eLogError, "RouterInfo: Can't open file ", m_FullPath); + LogPrint (eLogError, "RouterInfo: Can't open file ", fullPath); return false; } return true; } - void RouterInfo::ReadFromFile () + void RouterInfo::ReadFromFile (const std::string& fullPath) { - if (LoadFile ()) + if (LoadFile (fullPath)) ReadFromBuffer (false); else m_IsUnreachable = true; @@ -187,13 +187,15 @@ namespace data void RouterInfo::ReadFromStream (std::istream& s) { + if (!s) return; m_Caps = 0; s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); m_Timestamp = be64toh (m_Timestamp); // read addresses auto addresses = boost::make_shared(); uint8_t numAddresses; - s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return; + s.read ((char *)&numAddresses, sizeof (numAddresses)); + addresses->reserve (numAddresses); for (int i = 0; i < numAddresses; i++) { uint8_t supportedTransports = 0; @@ -281,7 +283,11 @@ namespace data if (s) continue; else return; } if (index >= address->ssu->introducers.size ()) + { + if (address->ssu->introducers.empty ()) // first time + address->ssu->introducers.reserve (3); address->ssu->introducers.resize (index + 1); + } Introducer& introducer = address->ssu->introducers.at (index); if (!strcmp (key, "ihost")) { @@ -343,7 +349,8 @@ namespace data int numValid = 0; for (auto& it: address->ssu->introducers) { - if ((!it.iExp || ts <= it.iExp) && it.iPort > 0 && + if (!it.iExp) it.iExp = m_Timestamp/1000 + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT; + if (ts <= it.iExp && it.iPort > 0 && ((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ()))) numValid++; else @@ -747,11 +754,11 @@ namespace data return bufbe64toh (buf + size) > m_Timestamp; } - const uint8_t * RouterInfo::LoadBuffer () + const uint8_t * RouterInfo::LoadBuffer (const std::string& fullPath) { if (!m_Buffer) { - if (LoadFile ()) + if (LoadFile (fullPath)) LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file"); } return m_Buffer; @@ -782,8 +789,8 @@ namespace data bool RouterInfo::SaveToFile (const std::string& fullPath) { - m_FullPath = fullPath; - if (!m_Buffer) { + if (!m_Buffer) + { LogPrint (eLogError, "RouterInfo: Can't save, m_Buffer == NULL"); return false; } @@ -840,21 +847,33 @@ namespace data for (const auto& it: *m_Addresses) // don't insert same address twice if (*it == *addr) return; m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4; + m_ReachableTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4; m_Addresses->push_back(std::move(addr)); } - void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host, int port) + void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, + const boost::asio::ip::address& host, int port, uint8_t caps) { auto addr = std::make_shared

(); addr->host = host; addr->port = port; addr->transportStyle = eTransportNTCP; - addr->caps = 0; + addr->caps = caps; addr->date = 0; addr->ntcp2.reset (new NTCP2Ext ()); if (port) addr->published = true; memcpy (addr->ntcp2->staticKey, staticKey, 32); memcpy (addr->ntcp2->iv, iv, 16); + if (addr->IsV4 ()) + { + m_SupportedTransports |= eNTCP2V4; + if (addr->published) m_ReachableTransports |= eNTCP2V4; + } + if (addr->IsV6 ()) + { + m_SupportedTransports |= eNTCP2V6; + if (addr->published) m_ReachableTransports |= eNTCP2V6; + } m_Addresses->push_back(std::move(addr)); } @@ -971,10 +990,10 @@ namespace data { if (!IsV6 ()) { - m_SupportedTransports |= eSSUV6 | eNTCP2V6; uint8_t addressCaps = AddressCaps::eV6; if (IsV4 ()) addressCaps |= AddressCaps::eV4; SetUnreachableAddressesTransportCaps (addressCaps); + UpdateSupportedTransports (); } } @@ -982,10 +1001,10 @@ namespace data { if (!IsV4 ()) { - m_SupportedTransports |= eSSUV4 | eNTCP2V4; uint8_t addressCaps = AddressCaps::eV4; if (IsV6 ()) addressCaps |= AddressCaps::eV6; SetUnreachableAddressesTransportCaps (addressCaps); + UpdateSupportedTransports (); } } @@ -994,7 +1013,6 @@ namespace data { if (IsV6 ()) { - m_SupportedTransports &= ~(eSSUV6 | eNTCP2V6); for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) { auto addr = *it; @@ -1011,6 +1029,7 @@ namespace data else ++it; } + UpdateSupportedTransports (); } } @@ -1018,7 +1037,6 @@ namespace data { if (IsV4 ()) { - m_SupportedTransports &= ~(eSSUV4 | eNTCP2V4); for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) { auto addr = *it; @@ -1035,13 +1053,17 @@ namespace data else ++it; } + UpdateSupportedTransports (); } } void RouterInfo::EnableMesh () { if (!IsMesh ()) + { m_SupportedTransports |= eNTCP2V6Mesh; + m_ReachableTransports |= eNTCP2V6Mesh; + } } void RouterInfo::DisableMesh () @@ -1049,6 +1071,7 @@ namespace data if (IsMesh ()) { m_SupportedTransports &= ~eNTCP2V6Mesh; + m_ReachableTransports &= ~eNTCP2V6Mesh; for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) { auto addr = *it; @@ -1138,18 +1161,17 @@ namespace data return m_Profile; } - void RouterInfo::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const + void RouterInfo::Encrypt (const uint8_t * data, uint8_t * encrypted) const { auto encryptor = m_RouterIdentity->CreateEncryptor (nullptr); if (encryptor) - encryptor->Encrypt (data, encrypted, ctx, true); + encryptor->Encrypt (data, encrypted); } bool RouterInfo::IsEligibleFloodfill () const { - // floodfill must be reachable somehow, >= 0.9.28 and not DSA - return (IsReachable () || (m_SupportedTransports & eSSUV4)) && - m_Version >= NETDB_MIN_FLOODFILL_VERSION && + // floodfill must be reachable by ipv4, >= 0.9.38 and not DSA + return IsReachableBy (eNTCP2V4 | eSSUV4) && m_Version >= NETDB_MIN_FLOODFILL_VERSION && GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1; } @@ -1175,28 +1197,6 @@ namespace data }); } - bool RouterInfo::IsReachableFrom (const RouterInfo& other) const - { - auto commonTransports = m_SupportedTransports & other.m_SupportedTransports; - if (!commonTransports) return false; - if (commonTransports & eNTCP2V6Mesh) return true; - return (bool)GetAddress ( - [commonTransports](std::shared_ptr address)->bool - { - if (address->IsPublishedNTCP2 ()) - { - if ((commonTransports & eNTCP2V4) && address->IsV4 ()) return true; - if ((commonTransports & eNTCP2V6) && address->IsV6 ()) return true; - } - else if (address->IsReachableSSU ()) - { - if ((commonTransports & eSSUV4) && address->IsV4 ()) return true; - if ((commonTransports & eSSUV6) && address->IsV6 ()) return true; - } - return false; - }); - } - void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) { for (auto& addr: *m_Addresses) diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index bea83fda..b2b883dc 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -67,7 +66,8 @@ namespace data eSSUV6 = 0x08, eNTCP2V6Mesh = 0x10 }; - + typedef uint8_t CompatibleTransports; + enum Caps { eFloodfill = 0x01, @@ -156,7 +156,7 @@ namespace data bool IsV4 () const { return (caps & AddressCaps::eV4) || (host.is_v4 () && !host.is_unspecified ()); }; bool IsV6 () const { return (caps & AddressCaps::eV6) || (host.is_v6 () && !host.is_unspecified ()); }; }; - typedef std::list > Addresses; + typedef std::vector > Addresses; RouterInfo (); RouterInfo (const std::string& fullPath); @@ -179,7 +179,8 @@ namespace data std::shared_ptr GetYggdrasilAddress () 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, const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0); + void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, + const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0, uint8_t caps = 0); bool AddIntroducer (const Introducer& introducer); bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only @@ -190,6 +191,7 @@ namespace data void UpdateSupportedTransports (); bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; }; bool IsReachable () const { return m_Caps & Caps::eReachable; }; + bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; bool IsSSU (bool v4only = true) const; bool IsSSUV6 () const; bool IsNTCP2 (bool v4only = true) const; @@ -204,8 +206,10 @@ namespace data void EnableMesh (); void DisableMesh (); bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; - bool IsReachableFrom (const RouterInfo& other) const; - bool HasValidAddresses () const { return m_SupportedTransports; }; + bool IsReachableFrom (const RouterInfo& other) const { return m_ReachableTransports & other.m_SupportedTransports; }; + bool IsReachableBy (CompatibleTransports transports) const { return m_ReachableTransports & transports; }; + CompatibleTransports GetCompatibleTransports (bool incoming) const { return incoming ? m_ReachableTransports : m_SupportedTransports; }; + bool HasValidAddresses () const { return m_SupportedTransports; }; bool IsHidden () const { return m_Caps & eHidden; }; bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; }; bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; }; @@ -221,7 +225,7 @@ namespace data bool IsUnreachable () const { return m_IsUnreachable; }; const uint8_t * GetBuffer () const { return m_Buffer; }; - const uint8_t * LoadBuffer (); // load if necessary + const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary int GetBufferLen () const { return m_BufferLen; }; void CreateBuffer (const PrivateKeys& privateKeys); @@ -241,14 +245,14 @@ namespace data // implements RoutingDestination std::shared_ptr GetIdentity () const { return m_RouterIdentity; }; - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const; + void Encrypt (const uint8_t * data, uint8_t * encrypted) const; bool IsDestination () const { return false; }; private: - bool LoadFile (); - void ReadFromFile (); + bool LoadFile (const std::string& fullPath); + void ReadFromFile (const std::string& fullPath); void ReadFromStream (std::istream& s); void ReadFromBuffer (bool verifySignature); void WriteToStream (std::ostream& s) const; @@ -262,7 +266,7 @@ namespace data private: - std::string m_FullPath, m_Family; + std::string m_Family; std::shared_ptr m_RouterIdentity; uint8_t * m_Buffer; size_t m_BufferLen; @@ -270,7 +274,8 @@ namespace data boost::shared_ptr m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 std::map m_Properties; bool m_IsUpdated, m_IsUnreachable; - uint8_t m_SupportedTransports, m_ReachableTransports, m_Caps; + CompatibleTransports m_SupportedTransports, m_ReachableTransports; + uint8_t m_Caps; int m_Version; mutable std::shared_ptr m_Profile; }; diff --git a/libi2pd/SSU.cpp b/libi2pd/SSU.cpp index f3da611c..be263370 100644 --- a/libi2pd/SSU.cpp +++ b/libi2pd/SSU.cpp @@ -218,7 +218,7 @@ namespace transport void SSUServer::AddRelay (uint32_t tag, std::shared_ptr relay) { - m_Relays[tag] = relay; + m_Relays.emplace (tag, relay); } void SSUServer::RemoveRelay (uint32_t tag) @@ -255,14 +255,14 @@ namespace transport void SSUServer::Receive () { - SSUPacket * packet = new SSUPacket (); + SSUPacket * packet = m_PacketsPool.AcquireMt (); m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet)); } void SSUServer::ReceiveV6 () { - SSUPacket * packet = new SSUPacket (); + SSUPacket * packet = m_PacketsPool.AcquireMt (); m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet)); } @@ -293,7 +293,7 @@ namespace transport { while (moreBytes && packets.size () < 25) { - packet = new SSUPacket (); + packet = m_PacketsPool.AcquireMt (); packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from, 0, ec); if (!ec) { @@ -304,7 +304,7 @@ namespace transport else { LogPrint (eLogError, "SSU: receive_from error: code ", ec.value(), ": ", ec.message ()); - delete packet; + m_PacketsPool.ReleaseMt (packet); break; } } @@ -315,7 +315,7 @@ namespace transport } else { - delete packet; + m_PacketsPool.ReleaseMt (packet); if (ecode != boost::asio::error::operation_aborted) { LogPrint (eLogError, "SSU: receive error: code ", ecode.value(), ": ", ecode.message ()); @@ -352,7 +352,7 @@ namespace transport { while (moreBytes && packets.size () < 25) { - packet = new SSUPacket (); + packet = m_PacketsPool.AcquireMt (); packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, 0, ec); if (!ec) { @@ -363,7 +363,7 @@ namespace transport else { LogPrint (eLogError, "SSU: v6 receive_from error: code ", ec.value(), ": ", ec.message ()); - delete packet; + m_PacketsPool.ReleaseMt (packet);; break; } } @@ -374,7 +374,7 @@ namespace transport } else { - delete packet; + m_PacketsPool.ReleaseMt (packet); if (ecode != boost::asio::error::operation_aborted) { LogPrint (eLogError, "SSU: v6 receive error: code ", ecode.value(), ": ", ecode.message ()); @@ -421,8 +421,8 @@ namespace transport if (session) session->FlushData (); session = nullptr; } - delete packet; } + m_PacketsPool.ReleaseMt (packets); if (session) session->FlushData (); } @@ -919,13 +919,19 @@ namespace transport } if (numDeleted > 0) LogPrint (eLogDebug, "SSU: ", numDeleted, " peer tests have been expired"); + // some cleaups. TODO: use separate timer + m_FragmentsPool.CleanUp (); + m_IncompleteMessagesPool.CleanUp (); + m_SentMessagesPool.CleanUp (); + SchedulePeerTestsCleanupTimer (); } } void SSUServer::ScheduleTermination () { - m_TerminationTimer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT)); + uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5; + m_TerminationTimer.expires_from_now (boost::posix_time::seconds(timeout)); m_TerminationTimer.async_wait (std::bind (&SSUServer::HandleTerminationTimer, this, std::placeholders::_1)); } @@ -947,13 +953,16 @@ namespace transport session->Failed (); }); } + else + it.second->CleanUp (ts); ScheduleTermination (); } } void SSUServer::ScheduleTerminationV6 () { - m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT)); + uint64_t timeout = SSU_TERMINATION_CHECK_TIMEOUT + (rand () % SSU_TERMINATION_CHECK_TIMEOUT)/5; + m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(timeout)); m_TerminationTimerV6.async_wait (std::bind (&SSUServer::HandleTerminationTimerV6, this, std::placeholders::_1)); } @@ -975,6 +984,8 @@ namespace transport session->Failed (); }); } + else + it.second->CleanUp (ts); ScheduleTerminationV6 (); } } diff --git a/libi2pd/SSU.h b/libi2pd/SSU.h index aad3a384..b97cacb4 100644 --- a/libi2pd/SSU.h +++ b/libi2pd/SSU.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -18,6 +18,7 @@ #include #include #include "Crypto.h" +#include "util.h" #include "I2PEndian.h" #include "Identity.h" #include "RouterInfo.h" @@ -63,6 +64,10 @@ namespace transport void DeleteAllSessions (); boost::asio::io_service& GetService () { return m_Service; }; + i2p::util::MemoryPool& GetFragmentsPool () { return m_FragmentsPool; }; + i2p::util::MemoryPool& GetIncompleteMessagesPool () { return m_IncompleteMessagesPool; }; + i2p::util::MemoryPool& GetSentMessagesPool () { return m_SentMessagesPool; }; + uint16_t GetPort () const { return m_Endpoint.port (); }; void SetLocalAddress (const boost::asio::ip::address& localAddress); @@ -136,6 +141,11 @@ namespace transport std::map > m_Relays; // we are introducer std::map m_PeerTests; // nonce -> creation time in milliseconds + i2p::util::MemoryPool m_FragmentsPool; + i2p::util::MemoryPool m_IncompleteMessagesPool; + i2p::util::MemoryPool m_SentMessagesPool; + i2p::util::MemoryPoolMt m_PacketsPool; + public: // for HTTP only const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; diff --git a/libi2pd/SSUData.cpp b/libi2pd/SSUData.cpp index 5458cc97..ccc89b41 100644 --- a/libi2pd/SSUData.cpp +++ b/libi2pd/SSUData.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -33,7 +33,6 @@ namespace transport SSUData::SSUData (SSUSession& session): m_Session (session), m_ResendTimer (session.GetService ()), - m_IncompleteMessagesCleanupTimer (session.GetService ()), m_MaxPacketSize (session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE), m_PacketSize (m_MaxPacketSize), m_LastMessageReceivedTime (0) { @@ -45,13 +44,11 @@ namespace transport void SSUData::Start () { - ScheduleIncompleteMessagesCleanup (); } void SSUData::Stop () { m_ResendTimer.cancel (); - m_IncompleteMessagesCleanupTimer.cancel (); m_IncompleteMessages.clear (); m_SentMessages.clear (); m_ReceivedMessages.clear (); @@ -140,7 +137,7 @@ namespace transport if (bitfield & mask) { if (fragment < numSentFragments) - it->second->fragments[fragment].reset (nullptr); + it->second->fragments[fragment] = nullptr; } fragment++; mask <<= 1; @@ -182,9 +179,9 @@ namespace transport auto msg = NewI2NPShortMessage (); msg->len -= I2NP_SHORT_HEADER_SIZE; it = m_IncompleteMessages.insert (std::make_pair (msgID, - std::unique_ptr(new IncompleteMessage (msg)))).first; + m_Session.GetServer ().GetIncompleteMessagesPool ().AcquireShared (msg))).first; } - std::unique_ptr& incompleteMessage = it->second; + auto& incompleteMessage = it->second; // mark fragment as received if (fragmentNum < 64) incompleteMessage->receivedFragmentsBits |= (0x01 << fragmentNum); @@ -224,8 +221,8 @@ namespace transport { // missing fragment LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID); - auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast); - if (incompleteMessage->savedFragments.insert (std::unique_ptr(savedFragment)).second) + auto savedFragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (fragmentNum, buf, fragmentSize, isLast); + if (incompleteMessage->savedFragments.insert (savedFragment).second) incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); else LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved"); @@ -246,8 +243,8 @@ namespace transport { if (!m_ReceivedMessages.count (msgID)) { - m_ReceivedMessages.insert (msgID); m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch (); + m_ReceivedMessages.emplace (msgID, m_LastMessageReceivedTime); if (!msg->IsExpired ()) { m_Handler.PutNextMessage (msg); @@ -313,8 +310,8 @@ namespace transport if (m_SentMessages.empty ()) // schedule resend at first message only ScheduleResend (); - auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr(new SentMessage))); - std::unique_ptr& sentMessage = ret.first->second; + auto ret = m_SentMessages.emplace (msgID, m_Session.GetServer ().GetSentMessagesPool ().AcquireShared ()); + auto& sentMessage = ret.first->second; if (ret.second) { sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL; @@ -328,7 +325,7 @@ namespace transport uint32_t fragmentNum = 0; while (len > 0 && fragmentNum <= 127) { - Fragment * fragment = new Fragment; + auto fragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (); fragment->fragmentNum = fragmentNum; uint8_t * payload = fragment->buf + sizeof (SSUHeader); *payload = DATA_FLAG_WANT_REPLY; // for compatibility @@ -358,7 +355,7 @@ namespace transport size += padding; } fragment->len = size; - fragments.push_back (std::unique_ptr (fragment)); + fragments.push_back (fragment); // encrypt message with session key uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; @@ -487,37 +484,33 @@ namespace transport } } - void SSUData::ScheduleIncompleteMessagesCleanup () + void SSUData::CleanUp (uint64_t ts) { - m_IncompleteMessagesCleanupTimer.cancel (); - m_IncompleteMessagesCleanupTimer.expires_from_now (boost::posix_time::seconds(INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)); - auto s = m_Session.shared_from_this(); - m_IncompleteMessagesCleanupTimer.async_wait ([s](const boost::system::error_code& ecode) - { s->m_Data.HandleIncompleteMessagesCleanupTimer (ecode); }); - } - - void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) + for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) + if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) { - if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) - { - LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); - it = m_IncompleteMessages.erase (it); - } + LogPrint (eLogWarning, "SSU: message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); + it = m_IncompleteMessages.erase (it); + } + else + ++it; + } + + if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || ts > m_LastMessageReceivedTime + DECAY_INTERVAL) + // decay + m_ReceivedMessages.clear (); + else + { + // delete old received messages + for (auto it = m_ReceivedMessages.begin (); it != m_ReceivedMessages.end ();) + { + if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT) + it = m_ReceivedMessages.erase (it); else ++it; - } - // decay - if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || - i2p::util::GetSecondsSinceEpoch () > m_LastMessageReceivedTime + DECAY_INTERVAL) - m_ReceivedMessages.clear (); - - ScheduleIncompleteMessagesCleanup (); - } - } + } + } + } } } diff --git a/libi2pd/SSUData.h b/libi2pd/SSUData.h index 902c009a..8fe33a33 100644 --- a/libi2pd/SSUData.h +++ b/libi2pd/SSUData.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,9 +11,9 @@ #include #include -#include #include -#include +#include +#include #include #include #include "I2NPProtocol.h" @@ -40,6 +40,7 @@ namespace transport const int MAX_NUM_RESENDS = 5; const int DECAY_INTERVAL = 20; // in seconds const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds + const int RECEIVED_MESSAGES_CLEANUP_TIMEOUT = 40; // in seconds const unsigned int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check const int MAX_OUTGOING_WINDOW_SIZE = 200; // how many unacked message we can store // data flags @@ -64,7 +65,7 @@ namespace transport struct FragmentCmp { - bool operator() (const std::unique_ptr& f1, const std::unique_ptr& f2) const + bool operator() (const std::shared_ptr& f1, const std::shared_ptr& f2) const { return f1->fragmentNum < f2->fragmentNum; }; @@ -76,7 +77,7 @@ namespace transport int nextFragmentNum; uint32_t lastFragmentInsertTime; // in seconds uint64_t receivedFragmentsBits; - std::set, FragmentCmp> savedFragments; + std::set, FragmentCmp> savedFragments; IncompleteMessage (std::shared_ptr m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0), receivedFragmentsBits (0) {}; @@ -85,7 +86,7 @@ namespace transport struct SentMessage { - std::vector > fragments; + std::vector > fragments; uint32_t nextResendTime; // in seconds int numResends; }; @@ -100,7 +101,8 @@ namespace transport void Start (); void Stop (); - + void CleanUp (uint64_t ts); + void ProcessMessage (uint8_t * buf, size_t len); void FlushReceivedMessage (); void Send (std::shared_ptr msg); @@ -119,17 +121,13 @@ namespace transport void ScheduleResend (); void HandleResendTimer (const boost::system::error_code& ecode); - void ScheduleIncompleteMessagesCleanup (); - void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode); - - private: SSUSession& m_Session; - std::unordered_map > m_IncompleteMessages; - std::unordered_map > m_SentMessages; - std::unordered_set m_ReceivedMessages; - boost::asio::deadline_timer m_ResendTimer, m_IncompleteMessagesCleanupTimer; + std::map > m_IncompleteMessages; + std::map > m_SentMessages; + std::unordered_map m_ReceivedMessages; // msgID -> timestamp in seconds + boost::asio::deadline_timer m_ResendTimer; int m_MaxPacketSize, m_PacketSize; i2p::I2NPMessagesHandler m_Handler; uint32_t m_LastMessageReceivedTime; // in second diff --git a/libi2pd/SSUSession.cpp b/libi2pd/SSUSession.cpp index 59ee578b..3e4eecb8 100644 --- a/libi2pd/SSUSession.cpp +++ b/libi2pd/SSUSession.cpp @@ -730,7 +730,7 @@ namespace transport (remoteIP.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled)) m_Server.Send (buf, 0, remoteEndpoint); // send HolePunch // we assume that HolePunch has been sent by this time and our SessionRequest will go through - m_Server.CreateDirectSession (it->second, remoteEndpoint, false); + m_Server.CreateDirectSession (it->second.first, remoteEndpoint, false); } // delete request m_RelayRequests.erase (it); @@ -905,7 +905,8 @@ namespace transport } uint32_t nonce; RAND_bytes ((uint8_t *)&nonce, 4); - m_RelayRequests[nonce] = to; + auto ts = i2p::util::GetSecondsSinceEpoch (); + m_RelayRequests.emplace (nonce, std::make_pair (to, ts)); SendRelayRequest (introducer, nonce); } @@ -1004,6 +1005,18 @@ namespace transport } } + void SSUSession::CleanUp (uint64_t ts) + { + m_Data.CleanUp (ts); + for (auto it = m_RelayRequests.begin (); it != m_RelayRequests.end ();) + { + if (ts > it->second.second + SSU_CONNECT_TIMEOUT) + it = m_RelayRequests.erase (it); + else + ++it; + } + } + void SSUSession::ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) { uint32_t nonce = bufbe32toh (buf); // 4 bytes diff --git a/libi2pd/SSUSession.h b/libi2pd/SSUSession.h index 43d0d595..bd4b4b93 100644 --- a/libi2pd/SSUSession.h +++ b/libi2pd/SSUSession.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -89,7 +89,8 @@ namespace transport void Done (); void Failed (); const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; - + SSUServer& GetServer () { return m_Server; }; + bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; void SendI2NPMessages (const std::vector >& msgs); void SendPeerTest (); // Alice @@ -105,6 +106,7 @@ namespace transport void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers void FlushData (); + void CleanUp (uint64_t ts); private: @@ -168,7 +170,7 @@ namespace transport SSUData m_Data; bool m_IsDataReceived; std::unique_ptr m_SignedData; // we need it for SessionConfirmed only - std::map > m_RelayRequests; // nonce->Charlie + std::map, uint64_t > > m_RelayRequests; // nonce->(Charlie, timestamp) std::shared_ptr m_DHKeysPair; // X - for client and Y - for server }; } diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index 612b1058..be3d09d8 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -104,6 +104,7 @@ namespace stream void Stream::Terminate (bool deleteFromDestination) // shoudl be called from StreamingDestination::Stop only { + m_Status = eStreamStatusTerminated; m_AckSendTimer.cancel (); m_ReceiveTimer.cancel (); m_ResendTimer.cancel (); @@ -276,7 +277,20 @@ namespace stream const uint8_t * optionData = packet->GetOptionData (); size_t optionSize = packet->GetOptionSize (); if (flags & PACKET_FLAG_DELAY_REQUESTED) + { + if (!m_IsAckSendScheduled) + { + uint16_t delayRequested = bufbe16toh (optionData); + if (delayRequested > 0 && delayRequested < m_RTT) + { + m_IsAckSendScheduled = true; + m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(delayRequested)); + m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, + shared_from_this (), std::placeholders::_1)); + } + } optionData += 2; + } if (flags & PACKET_FLAG_FROM_INCLUDED) { @@ -476,7 +490,7 @@ namespace stream handler(boost::system::error_code ()); m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ())); } - + void Stream::SendBuffer () { int numMsgs = m_WindowSize - m_SentPackets.size (); @@ -651,6 +665,42 @@ namespace stream LogPrint (eLogDebug, "Streaming: Quick Ack sent. ", (int)numNacks, " NACKs"); } + void Stream::SendPing () + { + Packet p; + uint8_t * packet = p.GetBuffer (); + size_t size = 0; + htobe32buf (packet, m_RecvStreamID); + size += 4; // sendStreamID + memset (packet + size, 0, 14); + size += 14; // all zeroes + uint16_t flags = PACKET_FLAG_ECHO | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_FROM_INCLUDED; + bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature (); + if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; + htobe16buf (packet + size, flags); + size += 2; // flags + size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen (); + size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); + uint8_t * optionsSize = packet + size; // set options size later + size += 2; // options size + m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); + size += identityLen; // from + if (isOfflineSignature) + { + const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); + memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); + size += offlineSignature.size (); // offline signature + } + uint8_t * signature = packet + size; // set it later + memset (signature, 0, signatureLen); // zeroes for now + size += signatureLen; // signature + htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size + m_LocalDestination.GetOwner ()->Sign (packet, size, signature); + p.len = size; + SendPackets (std::vector { &p }); + LogPrint (eLogDebug, "Streaming: Ping of ", p.len, " bytes sent"); + } + void Stream::Close () { LogPrint(eLogDebug, "Streaming: closing stream with sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ", status=", m_Status); @@ -793,7 +843,7 @@ namespace stream if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) { std::vector msgs; - for (auto it: packets) + for (const auto& it: packets) { auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage ( it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets ())); @@ -844,12 +894,15 @@ namespace stream void Stream::ScheduleResend () { - m_ResendTimer.cancel (); - // check for invalid value - if (m_RTO <= 0) m_RTO = INITIAL_RTO; - m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO)); - m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, - shared_from_this (), std::placeholders::_1)); + if (m_Status != eStreamStatusTerminated) + { + m_ResendTimer.cancel (); + // check for invalid value + if (m_RTO <= 0) m_RTO = INITIAL_RTO; + m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO)); + m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, + shared_from_this (), std::placeholders::_1)); + } } void Stream::HandleResendTimer (const boost::system::error_code& ecode) @@ -1023,6 +1076,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 () @@ -1050,6 +1105,7 @@ namespace stream it.second->Terminate (false); // we delete here m_Streams.clear (); m_IncomingStreams.clear (); + m_LastStream = nullptr; } } @@ -1058,9 +1114,16 @@ namespace stream uint32_t sendStreamID = packet->GetSendStreamID (); if (sendStreamID) { - auto it = m_Streams.find (sendStreamID); - if (it != m_Streams.end ()) - it->second->HandleNextPacket (packet); + if (!m_LastStream || sendStreamID != m_LastStream->GetRecvStreamID ()) + { + auto it = m_Streams.find (sendStreamID); + if (it != m_Streams.end ()) + m_LastStream = it->second; + else + m_LastStream = nullptr; + } + if (m_LastStream) + m_LastStream->HandleNextPacket (packet); else if (packet->IsEcho () && m_Owner->IsStreamingAnswerPings ()) { // ping @@ -1076,6 +1139,13 @@ namespace stream } else { + if (packet->IsEcho ()) + { + // pong + LogPrint (eLogInfo, "Streaming: Pong received rSID=", packet->GetReceiveStreamID ()); + DeletePacket (packet); + return; + } if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream { uint32_t receiveStreamID = packet->GetReceiveStreamID (); @@ -1166,16 +1236,22 @@ namespace stream { auto s = std::make_shared (m_Owner->GetService (), *this, remote, port); std::unique_lock l(m_StreamsMutex); - m_Streams[s->GetRecvStreamID ()] = s; + m_Streams.emplace (s->GetRecvStreamID (), s); return s; } + void StreamingDestination::SendPing (std::shared_ptr remote) + { + auto s = std::make_shared (m_Owner->GetService (), *this, remote, 0); + s->SendPing (); + } + std::shared_ptr StreamingDestination::CreateNewIncomingStream (uint32_t receiveStreamID) { auto s = std::make_shared (m_Owner->GetService (), *this); std::unique_lock l(m_StreamsMutex); - m_Streams[s->GetRecvStreamID ()] = s; - m_IncomingStreams[receiveStreamID] = s; + m_Streams.emplace (s->GetRecvStreamID (), s); + m_IncomingStreams.emplace (receiveStreamID, s); return s; } @@ -1186,6 +1262,7 @@ namespace stream std::unique_lock l(m_StreamsMutex); m_Streams.erase (stream->GetRecvStreamID ()); m_IncomingStreams.erase (stream->GetSendStreamID ()); + if (m_LastStream == stream) m_LastStream = nullptr; } } @@ -1270,13 +1347,17 @@ namespace stream std::shared_ptr StreamingDestination::CreateDataMessage ( const uint8_t * payload, size_t len, uint16_t toPort, bool checksum) { + size_t size; auto msg = m_I2NPMsgsPool.AcquireShared (); uint8_t * buf = msg->GetPayload (); buf += 4; // reserve for lengthlength msg->len += 4; - size_t size = (!m_Gzip || len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE)? - i2p::data::GzipNoCompression (payload, len, buf, msg->maxLen - msg->len): - 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); + if (size) { htobe32buf (msg->GetPayload (), size); // length diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index fe035136..c13dcab1 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include #include @@ -152,7 +152,8 @@ namespace stream eStreamStatusOpen, eStreamStatusReset, eStreamStatusClosing, - eStreamStatusClosed + eStreamStatusClosed, + eStreamStatusTerminated }; class StreamingDestination; @@ -178,7 +179,8 @@ namespace stream void HandlePing (Packet * packet); size_t Send (const uint8_t * buf, size_t len); void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler); - + void SendPing (); + template void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; @@ -260,13 +262,14 @@ namespace stream typedef std::function)> Acceptor; - StreamingDestination (std::shared_ptr owner, uint16_t localPort = 0, bool gzip = true); + StreamingDestination (std::shared_ptr owner, uint16_t localPort = 0, bool gzip = false); ~StreamingDestination (); void Start (); void Stop (); std::shared_ptr CreateNewOutgoingStream (std::shared_ptr remote, int port = 0); + void SendPing (std::shared_ptr remote); void DeleteStream (std::shared_ptr stream); bool DeleteStream (uint32_t recvStreamID); void SetAcceptor (const Acceptor& acceptor); @@ -297,12 +300,13 @@ namespace stream uint16_t m_LocalPort; bool m_Gzip; // gzip compression of data messages std::mutex m_StreamsMutex; - std::map > m_Streams; // sendStreamID->stream - std::map > m_IncomingStreams; // receiveStreamID->stream + std::unordered_map > m_Streams; // sendStreamID->stream + std::unordered_map > m_IncomingStreams; // receiveStreamID->stream + std::shared_ptr m_LastStream; Acceptor m_Acceptor; std::list > m_PendingIncomingStreams; boost::asio::deadline_timer m_PendingIncomingTimer; - std::map > m_SavedPackets; // receiveStreamID->packets, arrived before SYN + std::unordered_map > m_SavedPackets; // receiveStreamID->packets, arrived before SYN i2p::util::MemoryPool m_PacketsPool; i2p::util::MemoryPool > m_I2NPMsgsPool; @@ -310,7 +314,7 @@ namespace stream public: i2p::data::GzipInflator m_Inflator; - i2p::data::GzipDeflator m_Deflator; + std::unique_ptr m_Deflator; // for HTTP only const decltype(m_Streams)& GetStreams () const { return m_Streams; }; diff --git a/libi2pd/Timestamp.cpp b/libi2pd/Timestamp.cpp index 3cd336ed..ec0c25f2 100644 --- a/libi2pd/Timestamp.cpp +++ b/libi2pd/Timestamp.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -16,6 +16,7 @@ #include #include "Config.h" #include "Log.h" +#include "RouterContext.h" #include "I2PEndian.h" #include "Timestamp.h" #include "util.h" @@ -60,14 +61,43 @@ namespace util { LogPrint (eLogInfo, "Timestamp: NTP request to ", address); boost::asio::io_service service; - boost::asio::ip::udp::resolver::query query (boost::asio::ip::udp::v4 (), address, "ntp"); boost::system::error_code ec; - auto it = boost::asio::ip::udp::resolver (service).resolve (query, ec); - if (!ec && it != boost::asio::ip::udp::resolver::iterator()) + auto it = boost::asio::ip::udp::resolver (service).resolve ( + boost::asio::ip::udp::resolver::query (address, "ntp"), ec); + if (!ec) { - auto ep = (*it).endpoint (); // take first one + bool found = false; + boost::asio::ip::udp::resolver::iterator end; + boost::asio::ip::udp::endpoint ep; + while (it != end) + { + ep = *it; + if (!ep.address ().is_unspecified ()) + { + if (ep.address ().is_v4 ()) + { + if (i2p::context.SupportsV4 ()) found = true; + } + else if (ep.address ().is_v6 ()) + { + if (i2p::util::net::IsYggdrasilAddress (ep.address ())) + { + if (i2p::context.SupportsMesh ()) found = true; + } + else if (i2p::context.SupportsV6 ()) found = true; + } + } + if (found) break; + it++; + } + if (!found) + { + LogPrint (eLogError, "Timestamp: can't find compatible address for ", address); + return; + } + boost::asio::ip::udp::socket socket (service); - socket.open (boost::asio::ip::udp::v4 (), ec); + socket.open (ep.protocol (), ec); if (!ec) { uint8_t buf[48];// 48 bytes NTP request/response @@ -103,7 +133,7 @@ namespace util LogPrint (eLogError, "Timestamp: Couldn't open UDP socket"); } else - LogPrint (eLogError, "Timestamp: Couldn't resove address ", address); + LogPrint (eLogError, "Timestamp: Couldn't resolve address ", address); } NTPTimeSync::NTPTimeSync (): m_IsRunning (false), m_Timer (m_Service) diff --git a/libi2pd/TransitTunnel.cpp b/libi2pd/TransitTunnel.cpp index 73ca977c..dc7655d1 100644 --- a/libi2pd/TransitTunnel.cpp +++ b/libi2pd/TransitTunnel.cpp @@ -39,7 +39,7 @@ namespace tunnel void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) { - auto newMsg = CreateEmptyTunnelDataMsg (); + auto newMsg = CreateEmptyTunnelDataMsg (false); EncryptTunnelMsg (tunnelMsg, newMsg); m_NumTransmittedBytes += tunnelMsg->GetLength (); @@ -87,7 +87,7 @@ namespace tunnel void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) { - auto newMsg = CreateEmptyTunnelDataMsg (); + auto newMsg = CreateEmptyTunnelDataMsg (true); EncryptTunnelMsg (tunnelMsg, newMsg); LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ()); diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 1e40f88c..181c3663 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -401,7 +401,7 @@ namespace transport try { auto r = netdb.FindRouter (ident); - if (!r || r->IsUnreachable () || !r->IsCompatible (i2p::context.GetRouterInfo ())) return; + if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable { std::unique_lock l(m_PeersMutex); it = m_Peers.insert (std::pair(ident, { 0, r, {}, @@ -447,7 +447,7 @@ namespace transport std::shared_ptr address; if (!peer.numAttempts) // NTCP2 ipv6 { - if (context.GetRouterInfo ().IsNTCP2V6 () && peer.router->IsNTCP2V6 ()) + if (context.GetRouterInfo ().IsNTCP2V6 () && peer.router->IsReachableBy (RouterInfo::eNTCP2V6)) { address = peer.router->GetPublishedNTCP2V6Address (); if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) @@ -457,7 +457,7 @@ namespace transport } if (!address && peer.numAttempts == 1) // NTCP2 ipv4 { - if (context.GetRouterInfo ().IsNTCP2 (true) && peer.router->IsNTCP2 (true) && !peer.router->IsUnreachable ()) + if (context.GetRouterInfo ().IsNTCP2 (true) && peer.router->IsReachableBy (RouterInfo::eNTCP2V4)) { address = peer.router->GetPublishedNTCP2V4Address (); if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) @@ -485,7 +485,7 @@ namespace transport std::shared_ptr address; if (peer.numAttempts == 2) // SSU ipv6 { - if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsSSUV6 ()) + if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsReachableBy (RouterInfo::eSSUV6)) { address = peer.router->GetSSUV6Address (); if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) @@ -495,7 +495,7 @@ namespace transport } if (!address && peer.numAttempts == 3) // SSU ipv4 { - if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsSSU (true)) + 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)) diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index e016e9e4..1f69470c 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -23,6 +23,7 @@ #include "Tunnel.h" #include "TunnelPool.h" #include "util.h" +#include "ECIESX25519AEADRatchetSession.h" namespace i2p { @@ -30,8 +31,9 @@ namespace tunnel { Tunnel::Tunnel (std::shared_ptr config): TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()), - m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false), - m_Latency (0) + m_Config (config), m_IsShortBuildMessage (false), m_Pool (nullptr), + m_State (eTunnelStatePending), m_FarEndTransports (0), + m_IsRecreated (false), m_Latency (0) { } @@ -42,10 +44,11 @@ namespace tunnel void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel) { auto numHops = m_Config->GetNumHops (); - int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS; + const int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS; auto msg = numRecords <= STANDARD_NUM_RECORDS ? NewI2NPShortMessage () : NewI2NPMessage (); *msg->GetPayload () = numRecords; - msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1; + const size_t recordSize = m_Config->IsShort () ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; + msg->len += numRecords*recordSize + 1; // shuffle records std::vector recordIndicies; for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); @@ -55,7 +58,6 @@ namespace tunnel uint8_t * records = msg->GetPayload () + 1; TunnelHopConfig * hop = m_Config->GetFirstHop (); int i = 0; - BN_CTX * ctx = BN_CTX_new (); while (hop) { uint32_t msgID; @@ -63,80 +65,89 @@ namespace tunnel RAND_bytes ((uint8_t *)&msgID, 4); else msgID = replyMsgID; - int idx = recordIndicies[i]; - hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID, ctx); - hop->recordIndex = idx; - i++; + hop->recordIndex = recordIndicies[i]; i++; + hop->CreateBuildRequestRecord (records, msgID); hop = hop->next; } - BN_CTX_free (ctx); // fill up fake records with random data for (int i = numHops; i < numRecords; i++) { int idx = recordIndicies[i]; - RAND_bytes (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE); + RAND_bytes (records + idx*recordSize, recordSize); } // decrypt real records - i2p::crypto::CBCDecryption decryption; hop = m_Config->GetLastHop ()->prev; while (hop) { - decryption.SetKey (hop->replyKey); // decrypt records after current hop TunnelHopConfig * hop1 = hop->next; while (hop1) { - decryption.SetIV (hop->replyIV); - uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE; - decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); + hop->DecryptRecord (records, hop1->recordIndex); hop1 = hop1->next; } hop = hop->prev; } - msg->FillI2NPMessageHeader (eI2NPVariableTunnelBuild); + msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild); // send message if (outboundTunnel) + { + if (m_Config->IsShort ()) + { + auto ident = m_Config->GetFirstHop () ? m_Config->GetFirstHop ()->ident : nullptr; + if (ident && ident->GetIdentHash () != outboundTunnel->GetNextIdentHash ()) // don't encrypt if IBGW = OBEP + { + auto msg1 = i2p::garlic::WrapECIESX25519MessageForRouter (msg, ident->GetEncryptionPublicKey ()); + if (msg1) msg = msg1; + } + } outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg); + } else + { + if (m_Config->IsShort () && m_Config->GetLastHop () && + m_Config->GetLastHop ()->ident->GetIdentHash () != m_Config->GetLastHop ()->nextIdent) + { + // add garlic key/tag for reply + uint8_t key[32]; + uint64_t tag = m_Config->GetLastHop ()->GetGarlicKey (key); + if (m_Pool && m_Pool->GetLocalDestination ()) + m_Pool->GetLocalDestination ()->AddECIESx25519Key (key, tag); + else + i2p::context.AddECIESx25519Key (key, tag); + } i2p::transport::transports.SendMessage (GetNextIdentHash (), msg); + } } bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) { LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", (int)msg[0], " records."); - i2p::crypto::CBCDecryption decryption; TunnelHopConfig * hop = m_Config->GetLastHop (); while (hop) { - decryption.SetKey (hop->replyKey); - // decrypt records before and current hop - TunnelHopConfig * hop1 = hop; + // decrypt current hop + if (hop->recordIndex >= 0 && hop->recordIndex < msg[0]) + { + if (!hop->DecryptBuildResponseRecord (msg + 1)) + return false; + } + else + { + LogPrint (eLogWarning, "Tunnel: hop index ", hop->recordIndex, " is out of range"); + return false; + } + + // decrypt records before current hop + TunnelHopConfig * hop1 = hop->prev; while (hop1) { auto idx = hop1->recordIndex; if (idx >= 0 && idx < msg[0]) - { - uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE; - if (hop1 == hop && hop1->IsECIES ()) - { - uint8_t nonce[12]; - memset (nonce, 0, 12); - if (!i2p::crypto::AEADChaCha20Poly1305 (record, TUNNEL_BUILD_RECORD_SIZE - 16, - hop->m_H, 32, hop->m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE - 16, false)) // decrypt - { - LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); - return false; - } - } - else - { - decryption.SetIV (hop->replyIV); - decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); - } - } + hop->DecryptRecord (msg + 1, idx); else LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range"); hop1 = hop1->prev; @@ -148,8 +159,7 @@ namespace tunnel hop = m_Config->GetFirstHop (); while (hop) { - const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE; - uint8_t ret = record[hop->IsECIES () ? ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET : BUILD_RESPONSE_RECORD_RET_OFFSET]; + uint8_t ret = hop->GetRetCode (msg + 1); LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret); auto profile = i2p::data::netdb.FindRouterProfile (hop->ident->GetIdentHash ()); if (profile) @@ -171,6 +181,8 @@ namespace tunnel m_Hops.push_back (std::unique_ptr(tunnelHop)); hop = hop->prev; } + m_IsShortBuildMessage = m_Config->IsShort (); + m_FarEndTransports = m_Config->GetFarEndTransports (); m_Config = nullptr; } if (established) m_State = eTunnelStateEstablished; @@ -234,7 +246,7 @@ namespace tunnel void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr msg) { if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive - auto newMsg = CreateEmptyTunnelDataMsg (); + auto newMsg = CreateEmptyTunnelDataMsg (true); EncryptTunnelMsg (msg, newMsg); newMsg->from = shared_from_this (); m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); @@ -319,10 +331,11 @@ namespace tunnel for (auto& msg : msgs) { if (!msg.data) continue; + m_NumSentBytes += msg.data->GetLength (); switch (msg.deliveryType) { case eDeliveryTypeLocal: - i2p::HandleI2NPMessage (msg.data); + HandleI2NPMessage (msg.data); break; case eDeliveryTypeTunnel: i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); @@ -517,8 +530,10 @@ namespace tunnel } case eI2NPVariableTunnelBuild: case eI2NPVariableTunnelBuildReply: + case eI2NPShortTunnelBuild: + case eI2NPShortTunnelBuildReply: case eI2NPTunnelBuild: - case eI2NPTunnelBuildReply: + case eI2NPTunnelBuildReply: HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); break; default: @@ -680,7 +695,7 @@ namespace tunnel // let it die if the tunnel pool has been reconfigured and this is old if (pool && tunnel->GetNumHops() == pool->GetNumOutboundHops()) { - tunnel->SetIsRecreated (); + tunnel->SetRecreated (true); pool->RecreateOutboundTunnel (tunnel); } } @@ -703,7 +718,7 @@ namespace tunnel LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel"); CreateTunnel ( std::make_shared (std::vector > { router->GetRouterIdentity () }, - inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()) + inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash (), false), nullptr ); } } @@ -734,7 +749,7 @@ namespace tunnel // let it die if the tunnel pool was reconfigured and has different number of hops if (pool && tunnel->GetNumHops() == pool->GetNumInboundHops()) { - tunnel->SetIsRecreated (); + tunnel->SetRecreated (true); pool->RecreateInboundTunnel (tunnel); } } @@ -752,8 +767,8 @@ namespace tunnel if (m_InboundTunnels.empty ()) { LogPrint (eLogDebug, "Tunnel: Creating zero hops inbound tunnel"); - CreateZeroHopsInboundTunnel (); - CreateZeroHopsOutboundTunnel (); + CreateZeroHopsInboundTunnel (nullptr); + CreateZeroHopsOutboundTunnel (nullptr); if (!m_ExploratoryPool) { int ibLen; i2p::config::GetOption("exploratory.inbound.length", ibLen); @@ -779,7 +794,7 @@ namespace tunnel } LogPrint (eLogDebug, "Tunnel: creating one hop inbound tunnel"); CreateTunnel ( - std::make_shared (std::vector > { router->GetRouterIdentity () }) + std::make_shared (std::vector > { router->GetRouterIdentity () }, false), nullptr ); } } @@ -825,9 +840,11 @@ namespace tunnel } template - std::shared_ptr Tunnels::CreateTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel) + std::shared_ptr Tunnels::CreateTunnel (std::shared_ptr config, + std::shared_ptr pool, std::shared_ptr outboundTunnel) { auto newTunnel = std::make_shared (config); + newTunnel->SetTunnelPool (pool); uint32_t replyMsgID; RAND_bytes ((uint8_t *)&replyMsgID, 4); AddPendingTunnel (replyMsgID, newTunnel); @@ -835,20 +852,21 @@ namespace tunnel return newTunnel; } - std::shared_ptr Tunnels::CreateInboundTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel) + std::shared_ptr Tunnels::CreateInboundTunnel (std::shared_ptr config, + std::shared_ptr pool, std::shared_ptr outboundTunnel) { if (config) - return CreateTunnel(config, outboundTunnel); + return CreateTunnel(config, pool, outboundTunnel); else - return CreateZeroHopsInboundTunnel (); + return CreateZeroHopsInboundTunnel (pool); } - std::shared_ptr Tunnels::CreateOutboundTunnel (std::shared_ptr config) + std::shared_ptr Tunnels::CreateOutboundTunnel (std::shared_ptr config, std::shared_ptr pool) { if (config) - return CreateTunnel(config); + return CreateTunnel(config, pool); else - return CreateZeroHopsOutboundTunnel (); + return CreateZeroHopsOutboundTunnel (pool); } void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel) @@ -882,7 +900,7 @@ namespace tunnel { // build symmetric outbound tunnel CreateTunnel (std::make_shared(newTunnel->GetInvertedPeers (), - newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash ()), + newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash (), false), nullptr, GetNextOutboundTunnel ()); } else @@ -898,18 +916,20 @@ namespace tunnel } - std::shared_ptr Tunnels::CreateZeroHopsInboundTunnel () + std::shared_ptr Tunnels::CreateZeroHopsInboundTunnel (std::shared_ptr pool) { auto inboundTunnel = std::make_shared (); + inboundTunnel->SetTunnelPool (pool); inboundTunnel->SetState (eTunnelStateEstablished); m_InboundTunnels.push_back (inboundTunnel); m_Tunnels[inboundTunnel->GetTunnelID ()] = inboundTunnel; return inboundTunnel; } - std::shared_ptr Tunnels::CreateZeroHopsOutboundTunnel () + std::shared_ptr Tunnels::CreateZeroHopsOutboundTunnel (std::shared_ptr pool) { auto outboundTunnel = std::make_shared (); + outboundTunnel->SetTunnelPool (pool); outboundTunnel->SetState (eTunnelStateEstablished); m_OutboundTunnels.push_back (outboundTunnel); // we don't insert into m_Tunnels diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index f30eab14..2e1c9534 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -71,12 +71,13 @@ namespace tunnel std::shared_ptr GetTunnelConfig () const { return m_Config; } std::vector > GetPeers () const; std::vector > GetInvertedPeers () const; + bool IsShortBuildMessage () const { return m_IsShortBuildMessage; }; TunnelState GetState () const { return m_State; }; void SetState (TunnelState state); bool IsEstablished () const { return m_State == eTunnelStateEstablished; }; bool IsFailed () const { return m_State == eTunnelStateFailed; }; bool IsRecreated () const { return m_IsRecreated; }; - void SetIsRecreated () { m_IsRecreated = true; }; + void SetRecreated (bool recreated) { m_IsRecreated = recreated; }; int GetNumHops () const { return m_Hops.size (); }; virtual bool IsInbound() const = 0; @@ -109,9 +110,11 @@ namespace tunnel std::shared_ptr m_Config; std::vector > m_Hops; + bool m_IsShortBuildMessage; std::shared_ptr m_Pool; // pool, tunnel belongs to, or null TunnelState m_State; - bool m_IsRecreated; + i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; + bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace uint64_t m_Latency; // in milliseconds }; @@ -205,8 +208,8 @@ namespace tunnel void AddTransitTunnel (std::shared_ptr tunnel); void AddOutboundTunnel (std::shared_ptr newTunnel); void AddInboundTunnel (std::shared_ptr newTunnel); - std::shared_ptr CreateInboundTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel); - std::shared_ptr CreateOutboundTunnel (std::shared_ptr config); + std::shared_ptr CreateInboundTunnel (std::shared_ptr config, std::shared_ptr pool, std::shared_ptr outboundTunnel); + std::shared_ptr CreateOutboundTunnel (std::shared_ptr config, std::shared_ptr pool); void PostTunnelData (std::shared_ptr msg); void PostTunnelData (const std::vector >& msgs); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); @@ -219,7 +222,8 @@ namespace tunnel private: template - std::shared_ptr CreateTunnel (std::shared_ptr config, std::shared_ptr outboundTunnel = nullptr); + std::shared_ptr CreateTunnel (std::shared_ptr config, + std::shared_ptr pool, std::shared_ptr outboundTunnel = nullptr); template std::shared_ptr GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels); @@ -236,8 +240,8 @@ namespace tunnel void ManagePendingTunnels (PendingTunnels& pendingTunnels); void ManageTunnelPools (uint64_t ts); - std::shared_ptr CreateZeroHopsInboundTunnel (); - std::shared_ptr CreateZeroHopsOutboundTunnel (); + std::shared_ptr CreateZeroHopsInboundTunnel (std::shared_ptr pool); + std::shared_ptr CreateZeroHopsOutboundTunnel (std::shared_ptr pool); private: diff --git a/libi2pd/TunnelConfig.cpp b/libi2pd/TunnelConfig.cpp index 8f515c5d..4592c663 100644 --- a/libi2pd/TunnelConfig.cpp +++ b/libi2pd/TunnelConfig.cpp @@ -23,10 +23,6 @@ namespace tunnel { TunnelHopConfig::TunnelHopConfig (std::shared_ptr r) { - RAND_bytes (layerKey, 32); - RAND_bytes (ivKey, 32); - RAND_bytes (replyKey, 32); - RAND_bytes (replyIV, 16); RAND_bytes ((uint8_t *)&tunnelID, 4); if (!tunnelID) tunnelID = 1; // tunnelID can't be zero isGateway = true; @@ -77,75 +73,178 @@ namespace tunnel isGateway = false; } } - - void TunnelHopConfig::CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx) - { - uint8_t flag = 0; - if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; - if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - auto encryptor = ident->CreateEncryptor (nullptr); - if (IsECIES ()) - { - uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16); - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag; - memset (clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ()); - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); - memset (clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET); - if (encryptor) - EncryptECIES (encryptor, clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx); - } - else - { - uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); - memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, ident->GetIdentHash (), 32); - htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); - memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32); - memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32); - memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); - memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); - memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16); - clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag; - htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ()); - htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); - RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET); - if (encryptor) - encryptor->Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx, false); - } - memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); - } - void TunnelHopConfig::EncryptECIES (std::shared_ptr& encryptor, - const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx) + void TunnelHopConfig::DecryptRecord (uint8_t * records, int index) const { - uint8_t hepk[32]; - encryptor->Encrypt (nullptr, hepk, nullptr, false); - i2p::crypto::InitNoiseNState (*this, hepk); + uint8_t * record = records + index*TUNNEL_BUILD_RECORD_SIZE; + i2p::crypto::CBCDecryption decryption; + decryption.SetKey (replyKey); + decryption.SetIV (replyIV); + decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); + } + + void ECIESTunnelHopConfig::EncryptECIES (const uint8_t * plainText, size_t len, uint8_t * encrypted) + { + if (!ident) return; + i2p::crypto::InitNoiseNState (*this, ident->GetEncryptionPublicKey ()); auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32); MixHash (encrypted, 32); // h = SHA256(h || sepk) encrypted += 32; uint8_t sharedSecret[32]; - ephemeralKeys->Agree (hepk, sharedSecret); // x25519(sesk, hepk) + ephemeralKeys->Agree (ident->GetEncryptionPublicKey (), sharedSecret); // x25519(sesk, hepk) MixKey (sharedSecret); uint8_t nonce[12]; memset (nonce, 0, 12); - if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, m_H, 32, - m_CK + 32, nonce, encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16, true)) // encrypt + if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, len, m_H, 32, m_CK + 32, nonce, encrypted, len + 16, true)) // encrypt { LogPrint (eLogWarning, "Tunnel: Plaintext AEAD encryption failed"); return; } - MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext) + MixHash (encrypted, len + 16); // h = SHA256(h || ciphertext) } + + bool ECIESTunnelHopConfig::DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const + { + return i2p::crypto::AEADChaCha20Poly1305 (encrypted, len - 16, m_H, 32, key, nonce, clearText, len - 16, false); // decrypt + } + + void LongECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) + { + // generate keys + RAND_bytes (layerKey, 32); + RAND_bytes (ivKey, 32); + RAND_bytes (replyKey, 32); + RAND_bytes (replyIV, 16); + // fill clear text + uint8_t flag = 0; + if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; + if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; + uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); + htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); + memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32); + memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32); + memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); + memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); + memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16); + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag; + memset (clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility + htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ()); + htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes + htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); + memset (clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET); + // encrypt + uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE; + EncryptECIES (clearText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET); + memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); + } + + bool LongECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const + { + uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE; + uint8_t nonce[12]; + memset (nonce, 0, 12); + if (!DecryptECIES (m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE, record)) + { + LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); + return false; + } + return true; + } + + void ShortECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) + { + // fill clear text + uint8_t flag = 0; + if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; + if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; + uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE ]; + htobe32buf (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); + htobe32buf (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); + memcpy (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32); + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] = flag; + memset (clearText + SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 2); + clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE] = 0; // AES + htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ()); + htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET , 600); // +10 minutes + htobe32buf (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); + memset (clearText + SHORT_REQUEST_RECORD_PADDING_OFFSET, 0, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE - SHORT_REQUEST_RECORD_PADDING_OFFSET); + // encrypt + uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE; + EncryptECIES (clearText, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET); + // derive keys + i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelReplyKey", m_CK); + memcpy (replyKey, m_CK + 32, 32); + i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelLayerKey", m_CK); + memcpy (layerKey, m_CK + 32, 32); + if (isEndpoint) + { + i2p::crypto::HKDF (m_CK, nullptr, 0, "TunnelLayerIVKey", m_CK); + memcpy (ivKey, m_CK + 32, 32); + i2p::crypto::HKDF (m_CK, nullptr, 0, "RGarlicKeyAndTag", m_CK); // OTBRM garlic key m_CK + 32, tag first 8 bytes of m_CK + } + else + memcpy (ivKey, m_CK, 32); // last HKDF + memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); + } + + bool ShortECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const + { + uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE; + uint8_t nonce[12]; + memset (nonce, 0, 12); + nonce[4] = recordIndex; // nonce is record index + if (!DecryptECIES (replyKey, nonce, record, SHORT_TUNNEL_BUILD_RECORD_SIZE, record)) + { + LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); + return false; + } + return true; + } + + void ShortECIESTunnelHopConfig::DecryptRecord (uint8_t * records, int index) const + { + uint8_t * record = records + index*SHORT_TUNNEL_BUILD_RECORD_SIZE; + uint8_t nonce[12]; + memset (nonce, 0, 12); + nonce[4] = index; // nonce is index + i2p::crypto::ChaCha20 (record, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, record); + } + + uint64_t ShortECIESTunnelHopConfig::GetGarlicKey (uint8_t * key) const + { + uint64_t tag; + memcpy (&tag, m_CK, 8); + memcpy (key, m_CK + 32, 32); + return tag; + } + + void TunnelConfig::CreatePeers (const std::vector >& peers) + { + TunnelHopConfig * prev = nullptr; + for (const auto& it: peers) + { + TunnelHopConfig * hop = nullptr; + if (m_IsShort) + hop = new ShortECIESTunnelHopConfig (it); + else + { + if (it->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + hop = new LongECIESTunnelHopConfig (it); + else + LogPrint (eLogError, "Tunnel: ElGamal router is not supported"); + } + if (hop) + { + if (prev) + prev->SetNext (hop); + else + m_FirstHop = hop; + prev = hop; + } + } + m_LastHop = prev; + } } } \ No newline at end of file diff --git a/libi2pd/TunnelConfig.h b/libi2pd/TunnelConfig.h index 45693970..f198dffd 100644 --- a/libi2pd/TunnelConfig.h +++ b/libi2pd/TunnelConfig.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, 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,7 @@ namespace i2p { namespace tunnel { - struct TunnelHopConfig: public i2p::crypto::NoiseSymmetricState + struct TunnelHopConfig { std::shared_ptr ident; i2p::data::IdentHash nextIdent; @@ -31,32 +31,68 @@ namespace tunnel TunnelHopConfig * next, * prev; int recordIndex; // record # in tunnel build message - + TunnelHopConfig (std::shared_ptr r); - + virtual ~TunnelHopConfig () {}; + void SetNextIdent (const i2p::data::IdentHash& ident); void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent); void SetNext (TunnelHopConfig * n); - void SetPrev (TunnelHopConfig * p); + void SetPrev (TunnelHopConfig * p); - bool IsECIES () const { return ident->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; - void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx); - void EncryptECIES (std::shared_ptr& encryptor, - const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx); + virtual uint8_t GetRetCode (const uint8_t * records) const = 0; + virtual void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) = 0; + virtual bool DecryptBuildResponseRecord (uint8_t * records) const = 0; + virtual void DecryptRecord (uint8_t * records, int index) const; // AES + virtual uint64_t GetGarlicKey (uint8_t * key) const { return 0; }; // return tag }; - + + struct ECIESTunnelHopConfig: public TunnelHopConfig, public i2p::crypto::NoiseSymmetricState + { + ECIESTunnelHopConfig (std::shared_ptr r): + TunnelHopConfig (r) {}; + void EncryptECIES (const uint8_t * clearText, size_t len, uint8_t * encrypted); + bool DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const; + }; + + struct LongECIESTunnelHopConfig: public ECIESTunnelHopConfig + { + LongECIESTunnelHopConfig (std::shared_ptr r): + ECIESTunnelHopConfig (r) {}; + uint8_t GetRetCode (const uint8_t * records) const override + { return (records + recordIndex*TUNNEL_BUILD_RECORD_SIZE)[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET]; }; + void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override; + bool DecryptBuildResponseRecord (uint8_t * records) const override; + }; + + struct ShortECIESTunnelHopConfig: public ECIESTunnelHopConfig + { + ShortECIESTunnelHopConfig (std::shared_ptr r): + ECIESTunnelHopConfig (r) {}; + uint8_t GetRetCode (const uint8_t * records) const override + { return (records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE)[SHORT_RESPONSE_RECORD_RET_OFFSET]; }; + void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override; + bool DecryptBuildResponseRecord (uint8_t * records) const override; + void DecryptRecord (uint8_t * records, int index) const override; // Chacha20 + uint64_t GetGarlicKey (uint8_t * key) const override; + }; + class TunnelConfig { public: - TunnelConfig (std::vector > peers) // inbound + TunnelConfig (const std::vector >& peers, + bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports = 0): // inbound + m_IsShort (isShort), m_FarEndTransports (farEndTransports) { CreatePeers (peers); m_LastHop->SetNextIdent (i2p::context.GetIdentHash ()); } - TunnelConfig (std::vector > peers, - uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent) // outbound + TunnelConfig (const std::vector >& peers, + uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort, + i2p::data::RouterInfo::CompatibleTransports farEndTransports = 0): // outbound + m_IsShort (isShort), m_FarEndTransports (farEndTransports) { CreatePeers (peers); m_FirstHop->isGateway = false; @@ -75,6 +111,13 @@ namespace tunnel } } + bool IsShort () const { return m_IsShort; } + + i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const + { + return m_FarEndTransports; + } + TunnelHopConfig * GetFirstHop () const { return m_FirstHop; @@ -141,31 +184,20 @@ namespace tunnel protected: // this constructor can't be called from outside - TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr) + TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr), m_IsShort (false), + m_FarEndTransports (0) { } private: - template - void CreatePeers (const Peers& peers) - { - TunnelHopConfig * prev = nullptr; - for (const auto& it: peers) - { - auto hop = new TunnelHopConfig (it); - if (prev) - prev->SetNext (hop); - else - m_FirstHop = hop; - prev = hop; - } - m_LastHop = prev; - } + void CreatePeers (const std::vector >& peers); private: TunnelHopConfig * m_FirstHop, * m_LastHop; + bool m_IsShort; + i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; }; class ZeroHopsTunnelConfig: public TunnelConfig diff --git a/libi2pd/TunnelEndpoint.cpp b/libi2pd/TunnelEndpoint.cpp index eb70bdca..4885c090 100644 --- a/libi2pd/TunnelEndpoint.cpp +++ b/libi2pd/TunnelEndpoint.cpp @@ -52,24 +52,25 @@ namespace tunnel bool isFollowOnFragment = flag & 0x80, isLastFragment = true; uint32_t msgID = 0; int fragmentNum = 0; - TunnelMessageBlockEx m; if (!isFollowOnFragment) { // first fragment - - m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03); - switch (m.deliveryType) + if (m_CurrentMsgID) + AddIncompleteCurrentMessage (); // we have got a new message while previous is not complete + + m_CurrentMessage.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03); + switch (m_CurrentMessage.deliveryType) { case eDeliveryTypeLocal: // 0 break; case eDeliveryTypeTunnel: // 1 - m.tunnelID = bufbe32toh (fragment); + m_CurrentMessage.tunnelID = bufbe32toh (fragment); fragment += 4; // tunnelID - m.hash = i2p::data::IdentHash (fragment); + m_CurrentMessage.hash = i2p::data::IdentHash (fragment); fragment += 32; // hash break; case eDeliveryTypeRouter: // 2 - m.hash = i2p::data::IdentHash (fragment); + m_CurrentMessage.hash = i2p::data::IdentHash (fragment); fragment += 32; // to hash break; default: ; @@ -81,6 +82,7 @@ namespace tunnel // Message ID msgID = bufbe32toh (fragment); fragment += 4; + m_CurrentMsgID = msgID; isLastFragment = false; } } @@ -96,50 +98,60 @@ namespace tunnel uint16_t size = bufbe16toh (fragment); fragment += 2; - msg->offset = fragment - msg->buf; - msg->len = msg->offset + size; - if (msg->len > msg->maxLen) + // handle fragment + if (isFollowOnFragment) { - LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size); - return; - } - if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) - { - // this is not last message. we have to copy it - m.data = NewI2NPTunnelMessage (); - m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header - m.data->len += TUNNEL_GATEWAY_HEADER_SIZE; - *(m.data) = *msg; - } - else - m.data = msg; - - if (!isFollowOnFragment && isLastFragment) - HandleNextMessage (m); - else - { - if (msgID) // msgID is presented, assume message is fragmented + // existing message + if (m_CurrentMsgID && m_CurrentMsgID == msgID && m_CurrentMessage.nextFragmentNum == fragmentNum) + HandleCurrenMessageFollowOnFragment (fragment, size, isLastFragment); // previous + else { - if (!isFollowOnFragment) // create new incomlete message - { - m.nextFragmentNum = 1; - m.receiveTime = i2p::util::GetMillisecondsSinceEpoch (); - auto ret = m_IncompleteMessages.insert (std::pair(msgID, m)); - if (ret.second) - HandleOutOfSequenceFragments (msgID, ret.first->second); - else - LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, " already exists"); - } - else - { - m.nextFragmentNum = fragmentNum; - HandleFollowOnFragment (msgID, isLastFragment, m); - } + HandleFollowOnFragment (msgID, isLastFragment, fragmentNum, fragment, size); // another + m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; + } + } + else + { + // new message + msg->offset = fragment - msg->buf; + msg->len = msg->offset + size; + // check message size + if (msg->len > msg->maxLen) + { + LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size); + m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; + return; + } + // create new or assign I2NP message + if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) + { + // this is not last message. we have to copy it + m_CurrentMessage.data = NewI2NPTunnelMessage (true); + *(m_CurrentMessage.data) = *msg; } else + m_CurrentMessage.data = msg; + + if (isLastFragment) + { + // single message + HandleNextMessage (m_CurrentMessage); + m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; + } + else if (msgID) + { + // first fragment of a new message + m_CurrentMessage.nextFragmentNum = 1; + m_CurrentMessage.receiveTime = i2p::util::GetMillisecondsSinceEpoch (); + HandleOutOfSequenceFragments (msgID, m_CurrentMessage); + } + else + { LogPrint (eLogError, "TunnelMessage: Message is fragmented, but msgID is not presented"); - } - + m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; + } + } + fragment += size; } } @@ -147,27 +159,17 @@ namespace tunnel LogPrint (eLogError, "TunnelMessage: zero not found"); } - void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m) + void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, + uint8_t fragmentNum, const uint8_t * fragment, size_t size) { - auto fragment = m.data->GetBuffer (); - auto size = m.data->GetLength (); auto it = m_IncompleteMessages.find (msgID); if (it != m_IncompleteMessages.end()) { auto& msg = it->second; - if (m.nextFragmentNum == msg.nextFragmentNum) + if (fragmentNum == msg.nextFragmentNum) { - if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long + if (ConcatFollowOnFragment (msg, fragment, size)) { - if (msg.data->len + size > msg.data->maxLen) - { - // LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough"); - auto newMsg = NewI2NPMessage (); - *newMsg = *(msg.data); - msg.data = newMsg; - } - if (msg.data->Concat (fragment, size) < size) // concatenate fragment - LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen); if (isLastFragment) { // message complete @@ -182,26 +184,86 @@ namespace tunnel } else { - LogPrint (eLogError, "TunnelMessage: Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped"); + LogPrint (eLogError, "TunnelMessage: Fragment ", fragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped"); m_IncompleteMessages.erase (it); } } else { - LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved"); - AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data); + LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)fragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved"); + AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size); } } else { - LogPrint (eLogWarning, "TunnelMessage: First fragment of message ", msgID, " not found, saved"); - AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data); + LogPrint (eLogDebug, "TunnelMessage: First fragment of message ", msgID, " not found, saved"); + AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size); } } - void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr data) + bool TunnelEndpoint::ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const { - if (!m_OutOfSequenceFragments.insert ({{msgID, fragmentNum}, {isLastFragment, data, i2p::util::GetMillisecondsSinceEpoch () }}).second) + if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long + { + if (msg.data->len + size > msg.data->maxLen) + { + // LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough"); + auto newMsg = NewI2NPMessage (); + *newMsg = *(msg.data); + msg.data = newMsg; + } + if (msg.data->Concat (fragment, size) < size) // concatenate fragment + { + LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen); + return false; + } + } + else + return false; + return true; + } + + void TunnelEndpoint::HandleCurrenMessageFollowOnFragment (const uint8_t * fragment, size_t size, bool isLastFragment) + { + if (ConcatFollowOnFragment (m_CurrentMessage, fragment, size)) + { + if (isLastFragment) + { + // message complete + HandleNextMessage (m_CurrentMessage); + m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; + } + else + { + m_CurrentMessage.nextFragmentNum++; + HandleOutOfSequenceFragments (m_CurrentMsgID, m_CurrentMessage); + } + } + else + { + LogPrint (eLogError, "TunnelMessage: Fragment ", m_CurrentMessage.nextFragmentNum, " of message ", m_CurrentMsgID, " exceeds max I2NP message size, message dropped"); + m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; + } + } + + void TunnelEndpoint::AddIncompleteCurrentMessage () + { + if (m_CurrentMsgID) + { + auto ret = m_IncompleteMessages.emplace (m_CurrentMsgID, m_CurrentMessage); + if (!ret.second) + LogPrint (eLogError, "TunnelMessage: Incomplete message ", m_CurrentMsgID, " already exists"); + m_CurrentMessage.data = nullptr; + m_CurrentMsgID = 0; + } + } + + void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, + bool isLastFragment, const uint8_t * fragment, size_t size) + { + std::unique_ptr f(new Fragment (isLastFragment, i2p::util::GetMillisecondsSinceEpoch (), size)); + memcpy (f->data.data (), fragment, size); + if (!m_OutOfSequenceFragments.emplace ((uint64_t)msgID << 32 | fragmentNum, std::move (f)).second) LogPrint (eLogInfo, "TunnelMessage: duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID); } @@ -212,7 +274,14 @@ namespace tunnel if (!msg.nextFragmentNum) // message complete { HandleNextMessage (msg); - m_IncompleteMessages.erase (msgID); + if (&msg == &m_CurrentMessage) + { + m_CurrentMsgID = 0; + m_CurrentMessage.data = nullptr; + } + else + m_IncompleteMessages.erase (msgID); + LogPrint (eLogDebug, "TunnelMessage: All fragments of message ", msgID, " found"); break; } } @@ -220,11 +289,11 @@ namespace tunnel bool TunnelEndpoint::ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg) { - auto it = m_OutOfSequenceFragments.find ({msgID, msg.nextFragmentNum}); + auto it = m_OutOfSequenceFragments.find ((uint64_t)msgID << 32 | msg.nextFragmentNum); if (it != m_OutOfSequenceFragments.end ()) { LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found"); - size_t size = it->second.data->GetLength (); + size_t size = it->second->data.size (); if (msg.data->len + size > msg.data->maxLen) { LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough"); @@ -232,9 +301,9 @@ namespace tunnel *newMsg = *(msg.data); msg.data = newMsg; } - if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment + if (msg.data->Concat (it->second->data.data (), size) < size) // concatenate out-of-sync fragment LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen); - if (it->second.isLastFragment) + if (it->second->isLastFragment) // message complete msg.nextFragmentNum = 0; else @@ -287,7 +356,7 @@ namespace tunnel // out-of-sequence fragments for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();) { - if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) + if (ts > it->second->receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) it = m_OutOfSequenceFragments.erase (it); else ++it; diff --git a/libi2pd/TunnelEndpoint.h b/libi2pd/TunnelEndpoint.h index 43b836f1..e5a6bbc8 100644 --- a/libi2pd/TunnelEndpoint.h +++ b/libi2pd/TunnelEndpoint.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -10,8 +10,9 @@ #define TUNNEL_ENDPOINT_H__ #include -#include +#include #include +#include #include "I2NPProtocol.h" #include "TunnelBase.h" @@ -29,14 +30,15 @@ namespace tunnel struct Fragment { + Fragment (bool last, uint64_t t, size_t size): isLastFragment (last), receiveTime (t), data (size) {}; bool isLastFragment; - std::shared_ptr data; uint64_t receiveTime; // milliseconds since epoch + std::vector data; }; public: - TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {}; + TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0), m_CurrentMsgID (0) {}; ~TunnelEndpoint (); size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; void Cleanup (); @@ -45,19 +47,24 @@ namespace tunnel private: - void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m); + void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, uint8_t fragmentNum, const uint8_t * fragment, size_t size); + bool ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const; // true if success + void HandleCurrenMessageFollowOnFragment (const uint8_t * frgament, size_t size, bool isLastFragment); void HandleNextMessage (const TunnelMessageBlock& msg); - void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr data); + void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, const uint8_t * fragment, size_t size); bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg); - + void AddIncompleteCurrentMessage (); + private: - std::map m_IncompleteMessages; - std::map, Fragment> m_OutOfSequenceFragments; // (msgID, fragment#)->fragment + std::unordered_map m_IncompleteMessages; + std::unordered_map > m_OutOfSequenceFragments; // ((msgID << 8) + fragment#)->fragment bool m_IsInbound; size_t m_NumReceivedBytes; + TunnelMessageBlockEx m_CurrentMessage; + uint32_t m_CurrentMsgID; }; } } diff --git a/libi2pd/TunnelGateway.cpp b/libi2pd/TunnelGateway.cpp index 317926ae..08df569c 100644 --- a/libi2pd/TunnelGateway.cpp +++ b/libi2pd/TunnelGateway.cpp @@ -215,7 +215,7 @@ namespace tunnel const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs (); for (auto& tunnelMsg : tunnelDataMsgs) { - auto newMsg = CreateEmptyTunnelDataMsg (); + auto newMsg = CreateEmptyTunnelDataMsg (false); m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg); htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ()); newMsg->FillI2NPMessageHeader (eI2NPTunnelData); diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index e45c6090..5ed330c8 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -24,6 +24,22 @@ namespace i2p { namespace tunnel { + void Path::Add (std::shared_ptr r) + { + if (r) + { + peers.push_back (r->GetRouterIdentity ()); + if (r->GetVersion () < i2p::data::NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION || + r->GetRouterIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + isShort = false; + } + } + + void Path::Reverse () + { + std::reverse (peers.begin (), peers.end ()); + } + TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels): m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), @@ -97,6 +113,17 @@ namespace tunnel if (!m_IsActive) return; { std::unique_lock l(m_InboundTunnelsMutex); + if (createdTunnel->IsRecreated ()) + { + // find and mark old tunnel as expired + createdTunnel->SetRecreated (false); + for (auto& it: m_InboundTunnels) + if (it->IsRecreated () && it->GetNextIdentHash () == createdTunnel->GetNextIdentHash ()) + { + it->SetState (eTunnelStateExpiring); + break; + } + } m_InboundTunnels.insert (createdTunnel); } if (m_LocalDestination) @@ -420,16 +447,16 @@ namespace tunnel return hop; } - bool StandardSelectPeers(Path & peers, int numHops, bool inbound, SelectHopFunc nextHop) + bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop) { int start = 0; - auto prevHop = i2p::context.GetSharedRouterInfo (); + std::shared_ptr prevHop = i2p::context.GetSharedRouterInfo (); if(i2p::transport::transports.RoutesRestricted()) { /** if routes are restricted prepend trusted first hop */ auto hop = i2p::transport::transports.GetRestrictedPeer(); if(!hop) return false; - peers.push_back(hop->GetRouterIdentity()); + path.Add (hop); prevHop = hop; start++; } @@ -437,11 +464,11 @@ namespace tunnel (inbound && i2p::transport::transports.GetNumPeers () > 25)) { auto r = i2p::transport::transports.GetRandomPeer (); - if (r && !r->GetProfile ()->IsBad () && + if (r && r->IsECIES () && !r->GetProfile ()->IsBad () && (numHops > 1 || (r->IsV4 () && (!inbound || r->IsReachable ())))) // first inbound must be reachable { prevHop = r; - peers.push_back (r->GetRouterIdentity ()); + path.Add (r); start++; } } @@ -453,6 +480,7 @@ namespace tunnel { LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected"); hop = i2p::transport::transports.GetRandomPeer (); + if (hop && !hop->IsECIES ()) hop = nullptr; } if (!hop) { @@ -466,12 +494,14 @@ namespace tunnel if (hop1) hop = hop1; } prevHop = hop; - peers.push_back (hop->GetRouterIdentity ()); + path.Add (hop); + if (i == numHops - 1) + path.farEndTransports = hop->GetCompatibleTransports (inbound); } return true; } - bool TunnelPool::SelectPeers (std::vector >& peers, bool isInbound) + bool TunnelPool::SelectPeers (Path& path, bool isInbound) { int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; // peers is empty @@ -480,27 +510,36 @@ namespace tunnel { std::lock_guard lock(m_CustomPeerSelectorMutex); if (m_CustomPeerSelector) - return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound); + return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound); } // explicit peers in use - if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound); - return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2)); + if (m_ExplicitPeers) return SelectExplicitPeers (path, isInbound); + return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2)); } - bool TunnelPool::SelectExplicitPeers (std::vector >& peers, bool isInbound) + bool TunnelPool::SelectExplicitPeers (Path& path, bool isInbound) { - int size = m_ExplicitPeers->size (); - std::vector peerIndicies; - for (int i = 0; i < size; i++) peerIndicies.push_back(i); - std::shuffle (peerIndicies.begin(), peerIndicies.end(), std::mt19937(std::random_device()())); - int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; + if (numHops > (int)m_ExplicitPeers->size ()) numHops = m_ExplicitPeers->size (); + if (!numHops) return false; for (int i = 0; i < numHops; i++) { - auto& ident = (*m_ExplicitPeers)[peerIndicies[i]]; + auto& ident = (*m_ExplicitPeers)[i]; auto r = i2p::data::netdb.FindRouter (ident); if (r) - peers.push_back (r->GetRouterIdentity ()); + { + if (r->IsECIES ()) + { + path.Add (r); + if (i == numHops - 1) + path.farEndTransports = r->GetCompatibleTransports (isInbound); + } + else + { + LogPrint (eLogError, "Tunnels: ElGamal router ", ident.ToBase64 (), " is not supported"); + return false; + } + } else { LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ()); @@ -517,17 +556,16 @@ namespace tunnel if (!outboundTunnel) outboundTunnel = tunnels.GetNextOutboundTunnel (); LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel..."); - std::vector > peers; - if (SelectPeers (peers, true)) + Path path; + if (SelectPeers (path, true)) { std::shared_ptr config; if (m_NumInboundHops > 0) { - std::reverse (peers.begin (), peers.end ()); - config = std::make_shared (peers); + path.Reverse (); + config = std::make_shared (path.peers, path.isShort, path.farEndTransports); } - auto tunnel = tunnels.CreateInboundTunnel (config, outboundTunnel); - tunnel->SetTunnelPool (shared_from_this ()); + auto tunnel = tunnels.CreateInboundTunnel (config, shared_from_this (), outboundTunnel); if (tunnel->IsEstablished ()) // zero hops TunnelCreated (tunnel); } @@ -549,14 +587,15 @@ namespace tunnel std::shared_ptr config; if (m_NumInboundHops > 0 && tunnel->GetPeers().size()) { - config = std::make_shared(tunnel->GetPeers ()); + config = std::make_shared(tunnel->GetPeers (), tunnel->IsShortBuildMessage ()); } - if (m_NumInboundHops == 0 || config) + if (!m_NumInboundHops || config) { - auto newTunnel = tunnels.CreateInboundTunnel (config, outboundTunnel); - newTunnel->SetTunnelPool (shared_from_this()); + auto newTunnel = tunnels.CreateInboundTunnel (config, shared_from_this(), outboundTunnel); if (newTunnel->IsEstablished ()) // zero hops TunnelCreated (newTunnel); + else + newTunnel->SetRecreated (true); } } @@ -568,15 +607,27 @@ namespace tunnel if (inboundTunnel) { LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel..."); - std::vector > peers; - if (SelectPeers (peers, false)) + Path path; + if (SelectPeers (path, false)) { + if (m_LocalDestination && !m_LocalDestination->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) + path.isShort = false; // because can't handle ECIES encrypted reply + std::shared_ptr config; if (m_NumOutboundHops > 0) - config = std::make_shared(peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()); - auto tunnel = tunnels.CreateOutboundTunnel (config); - tunnel->SetTunnelPool (shared_from_this ()); - if (tunnel->IsEstablished ()) // zero hops + config = std::make_shared(path.peers, inboundTunnel->GetNextTunnelID (), + inboundTunnel->GetNextIdentHash (), path.isShort, path.farEndTransports); + + std::shared_ptr tunnel; + if (path.isShort) + { + // TODO: implement it better + tunnel = tunnels.CreateOutboundTunnel (config, inboundTunnel->GetTunnelPool ()); + tunnel->SetTunnelPool (shared_from_this ()); + } + else + tunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ()); + if (tunnel && tunnel->IsEstablished ()) // zero hops TunnelCreated (tunnel); } else @@ -602,12 +653,12 @@ namespace tunnel std::shared_ptr config; if (m_NumOutboundHops > 0 && tunnel->GetPeers().size()) { - config = std::make_shared(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()); + config = std::make_shared(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), + inboundTunnel->GetNextIdentHash (), inboundTunnel->IsShortBuildMessage ()); } if (!m_NumOutboundHops || config) { - auto newTunnel = tunnels.CreateOutboundTunnel (config); - newTunnel->SetTunnelPool (shared_from_this ()); + auto newTunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ()); if (newTunnel->IsEstablished ()) // zero hops TunnelCreated (newTunnel); } @@ -620,9 +671,9 @@ namespace tunnel { LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel..."); auto tunnel = tunnels.CreateInboundTunnel ( - m_NumOutboundHops > 0 ? std::make_shared(outboundTunnel->GetInvertedPeers ()) : nullptr, - outboundTunnel); - tunnel->SetTunnelPool (shared_from_this ()); + m_NumOutboundHops > 0 ? std::make_shared(outboundTunnel->GetInvertedPeers (), + outboundTunnel->IsShortBuildMessage ()) : nullptr, + shared_from_this (), outboundTunnel); if (tunnel->IsEstablished ()) // zero hops TunnelCreated (tunnel); } diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index 164ca7a1..875a9955 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -36,7 +36,15 @@ namespace tunnel class OutboundTunnel; typedef std::shared_ptr Peer; - typedef std::vector Path; + struct Path + { + std::vector peers; + bool isShort = true; + i2p::data::RouterInfo::CompatibleTransports farEndTransports = 0; + + void Add (std::shared_ptr r); + void Reverse (); + }; /** interface for custom tunnel peer selection algorithm */ struct ITunnelPeerSelector @@ -47,9 +55,8 @@ namespace tunnel typedef std::function(std::shared_ptr, bool)> SelectHopFunc; - // standard peer selection algorithm - bool StandardSelectPeers(Path & path, int hops, bool inbound, SelectHopFunc nextHop); - + bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop); + class TunnelPool: public std::enable_shared_from_this // per local destination { public: @@ -114,8 +121,8 @@ namespace tunnel void CreatePairedInboundTunnel (std::shared_ptr outboundTunnel); template typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const; - bool SelectPeers (std::vector >& hops, bool isInbound); - bool SelectExplicitPeers (std::vector >& hops, bool isInbound); + bool SelectPeers (Path& path, bool isInbound); + bool SelectExplicitPeers (Path& path, bool isInbound); private: diff --git a/libi2pd/util.cpp b/libi2pd/util.cpp index 69fc366a..2b5c79f2 100644 --- a/libi2pd/util.cpp +++ b/libi2pd/util.cpp @@ -129,7 +129,7 @@ namespace util pthread_set_name_np(pthread_self(), name); #elif defined(__NetBSD__) pthread_setname_np(pthread_self(), "%s", (void *)name); -#else +#elif !defined(__gnu_hurd__) pthread_setname_np(pthread_self(), name); #endif } @@ -344,7 +344,7 @@ namespace net if(fd > 0) { ifreq ifr; - strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ); // set interface for query + strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ-1); // set interface for query if(ioctl(fd, SIOCGIFMTU, &ifr) >= 0) mtu = ifr.ifr_mtu; // MTU else @@ -555,7 +555,10 @@ namespace net static const std::vector< std::pair > reservedIPv6Ranges { address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"), address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), - address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff") + address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), + address_pair_v6("ff00::", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), + address_pair_v6("::", "::"), + address_pair_v6("::1", "::1") }; boost::asio::ip::address_v6::bytes_type ipv6_address = host.to_v6 ().to_bytes (); diff --git a/libi2pd/util.h b/libi2pd/util.h index 000cb74e..282ce7aa 100644 --- a/libi2pd/util.h +++ b/libi2pd/util.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -50,6 +50,11 @@ namespace util MemoryPool (): m_Head (nullptr) {} ~MemoryPool () + { + CleanUp (); + } + + void CleanUp () { while (m_Head) { @@ -57,8 +62,8 @@ namespace util m_Head = static_cast(*(void * *)m_Head); // next ::operator delete ((void *)tmp); } - } - + } + template T * Acquire (TArgs&&... args) { diff --git a/libi2pd/version.h b/libi2pd/version.h index 028d1692..97351c40 100644 --- a/libi2pd/version.h +++ b/libi2pd/version.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -16,7 +16,7 @@ #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 38 +#define I2PD_VERSION_MINOR 39 #define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) @@ -30,7 +30,7 @@ #define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MINOR 9 -#define I2P_VERSION_MICRO 50 +#define I2P_VERSION_MICRO 51 #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) diff --git a/libi2pd_client/AddressBook.cpp b/libi2pd_client/AddressBook.cpp index ba2d8276..3d5c83c0 100644 --- a/libi2pd_client/AddressBook.cpp +++ b/libi2pd_client/AddressBook.cpp @@ -470,6 +470,20 @@ namespace client if (pos != std::string::npos) addr = addr.substr(0, pos); // remove comments + pos = name.find(".b32.i2p"); + if (pos != std::string::npos) + { + LogPrint (eLogError, "Addressbook: skipped adding of b32 address: ", name); + continue; + } + + pos = name.find(".i2p"); + if (pos == std::string::npos) + { + LogPrint (eLogError, "Addressbook: malformed domain: ", name); + continue; + } + auto ident = std::make_shared (); if (!ident->FromBase64(addr)) { LogPrint (eLogError, "Addressbook: malformed address ", addr, " for ", name); diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index 94311297..d6871fbe 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -185,7 +185,7 @@ namespace client LogPrint(eLogInfo, "Clients: stopping AddressBook"); m_AddressBook.Stop (); - { + { std::lock_guard lock(m_ForwardsMutex); m_ServerForwards.clear(); m_ClientForwards.clear(); @@ -200,6 +200,8 @@ namespace client for (auto& it: m_Destinations) it.second->Stop (); m_Destinations.clear (); + + m_SharedLocalDestination->Release (); m_SharedLocalDestination = nullptr; } @@ -209,14 +211,6 @@ namespace client /*std::string config; i2p::config::GetOption("conf", config); i2p::config::ParseConfig(config);*/ - // handle tunnels - // reset isUpdated for each tunnel - VisitTunnels ([](I2PService * s)->bool { s->isUpdated = false; return true; }); - // reload tunnels - ReadTunnels(); - // delete not updated tunnels (not in config anymore) - VisitTunnels ([](I2PService * s)->bool { return s->isUpdated; }); - // change shared local destination m_SharedLocalDestination->Release (); CreateNewSharedLocalDestination (); @@ -225,6 +219,7 @@ namespace client if (m_HttpProxy) { m_HttpProxy->Stop (); + delete m_HttpProxy; m_HttpProxy = nullptr; } ReadHttpProxy (); @@ -233,10 +228,19 @@ namespace client if (m_SocksProxy) { m_SocksProxy->Stop (); + delete m_SocksProxy; m_SocksProxy = nullptr; } ReadSocksProxy (); + // handle tunnels + // reset isUpdated for each tunnel + VisitTunnels (false); + // reload tunnels + ReadTunnels(); + // delete not updated tunnels (not in config anymore) + VisitTunnels (true); + // delete unused destinations std::unique_lock l(m_DestinationsMutex); for (auto it = m_Destinations.begin (); it != m_Destinations.end ();) @@ -457,7 +461,7 @@ namespace client options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY); options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false); options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE); - std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, isServer ? "" : "0,4"); + std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "0,4"); if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType; std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, ""); if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey; @@ -504,20 +508,15 @@ namespace client int numClientTunnels = 0, numServerTunnels = 0; std::string tunConf; i2p::config::GetOption("tunconf", tunConf); if (tunConf.empty ()) - { - // TODO: cleanup this in 2.8.0 - tunConf = i2p::fs::DataDirPath ("tunnels.cfg"); - if (i2p::fs::Exists(tunConf)) - LogPrint(eLogWarning, "Clients: please rename tunnels.cfg -> tunnels.conf here: ", tunConf); - else - tunConf = i2p::fs::DataDirPath ("tunnels.conf"); - } + tunConf = i2p::fs::DataDirPath ("tunnels.conf"); + LogPrint(eLogDebug, "Clients: tunnels config file: ", tunConf); ReadTunnels (tunConf, numClientTunnels, numServerTunnels); std::string tunDir; i2p::config::GetOption("tunnelsdir", tunDir); if (tunDir.empty ()) tunDir = i2p::fs::DataDirPath ("tunnels.d"); + if (i2p::fs::Exists (tunDir)) { std::vector files; @@ -582,7 +581,7 @@ namespace client if (it != destinations.end ()) localDestination = it->second; else - { + { i2p::data::PrivateKeys k; if(LoadPrivateKeys (k, keys, sigType, cryptoType)) { @@ -597,7 +596,7 @@ namespace client destinations[keys] = localDestination; } } - } + } } if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { @@ -609,10 +608,18 @@ namespace client bool gzip = section.second.get (I2P_CLIENT_TUNNEL_GZIP, true); auto clientTunnel = std::make_shared(name, dest, end, localDestination, destinationPort, gzip); - if(m_ClientForwards.insert(std::make_pair(end, clientTunnel)).second) + + auto ins = m_ClientForwards.insert(std::make_pair(end, clientTunnel)); + if (ins.second) + { clientTunnel->Start(); + numClientTunnels++; + } else + { + ins.first->second->isUpdated = true; LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists"); + } } else { boost::asio::ip::tcp::endpoint clientEndpoint; @@ -645,6 +652,13 @@ namespace client auto tun = std::make_shared (name, dest, address, port, localDestination, destinationPort); clientTunnel = tun; clientEndpoint = tun->GetLocalEndpoint (); + + uint32_t keepAlive = section.second.get(I2P_CLIENT_TUNNEL_KEEP_ALIVE_INTERVAL, 0); + if (keepAlive) + { + tun->SetKeepAliveInterval (keepAlive); + LogPrint(eLogInfo, "Clients: I2P Client tunnel keep alive interval set to ", keepAlive); + } } uint32_t timeout = section.second.get(I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT, 0); @@ -666,13 +680,16 @@ namespace client if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ()) { LogPrint (eLogInfo, "Clients: I2P client tunnel destination updated"); + ins.first->second->Stop (); ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ()); + ins.first->second->Start (); } ins.first->second->isUpdated = true; LogPrint (eLogInfo, "Clients: I2P client tunnel for endpoint ", clientEndpoint, " already exists"); } } } + else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC @@ -689,7 +706,7 @@ namespace client accessList=section.second.get (I2P_SERVER_TUNNEL_WHITE_LIST, ""); std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, ""); std::string webircpass = section.second.get (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, ""); - bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true); + bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, false); i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); @@ -703,26 +720,37 @@ namespace client std::shared_ptr localDestination = nullptr; auto it = destinations.find (keys); if (it != destinations.end ()) - localDestination = it->second; - else { + localDestination = it->second; + localDestination->SetPublic (true); + } + else + { i2p::data::PrivateKeys k; if(!LoadPrivateKeys (k, keys, sigType, cryptoType)) continue; localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); if (!localDestination) - { + { localDestination = CreateNewLocalDestination (k, true, &options); destinations[keys] = localDestination; - } - } + } + else + localDestination->SetPublic (true); + } if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) { // udp server tunnel // TODO: hostnames - if (address.empty ()) address = "127.0.0.1"; - auto localAddress = boost::asio::ip::address::from_string(address); boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); + if (address.empty ()) + { + if (!endpoint.address ().is_unspecified () && endpoint.address ().is_v6 ()) + address = "::1"; + else + address = "127.0.0.1"; + } + auto localAddress = boost::asio::ip::address::from_string(address); auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, port, gzip); if(!isUniqueLocal) { @@ -730,17 +758,19 @@ namespace client serverTunnel->SetUniqueLocal(isUniqueLocal); } std::lock_guard lock(m_ForwardsMutex); - if(m_ServerForwards.insert( - std::make_pair( - std::make_pair( - localDestination->GetIdentHash(), port), - serverTunnel)).second) + auto ins = m_ServerForwards.insert(std::make_pair( + std::make_pair(localDestination->GetIdentHash(), port), + serverTunnel)); + if (ins.second) { serverTunnel->Start(); LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32()); } else - LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, "already exists"); + { + ins.first->second->isUpdated = true; + LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, " already exists"); + } continue; } @@ -789,7 +819,9 @@ namespace client if (ins.first->second->GetLocalDestination () != serverTunnel->GetLocalDestination ()) { LogPrint (eLogInfo, "Clients: I2P server tunnel destination updated"); + ins.first->second->Stop (); ins.first->second->SetLocalDestination (serverTunnel->GetLocalDestination ()); + ins.first->second->Start (); } ins.first->second->isUpdated = true; LogPrint (eLogInfo, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists"); @@ -866,7 +898,7 @@ namespace client { localDestination = m_HttpProxy->GetLocalDestination (); localDestination->Acquire (); - } + } else if (socksProxyKeys.length () > 0) { i2p::data::PrivateKeys keys; @@ -914,27 +946,52 @@ namespace client } } - template - void VisitTunnelsContainer (Container& c, Visitor v) + void ClientContext::VisitTunnels (bool clean) { - for (auto it = c.begin (); it != c.end ();) + for (auto it = m_ClientTunnels.begin (); it != m_ClientTunnels.end ();) { - if (!v (it->second.get ())) - { + if(clean && !it->second->isUpdated) { it->second->Stop (); - it = c.erase (it); - } - else + it = m_ClientTunnels.erase(it); + } else { + it->second->isUpdated = false; it++; + } } - } - template - void ClientContext::VisitTunnels (Visitor v) - { - VisitTunnelsContainer (m_ClientTunnels, v); - VisitTunnelsContainer (m_ServerTunnels, v); - // TODO: implement UDP forwards + for (auto it = m_ServerTunnels.begin (); it != m_ServerTunnels.end ();) + { + if(clean && !it->second->isUpdated) { + it->second->Stop (); + it = m_ServerTunnels.erase(it); + } else { + it->second->isUpdated = false; + it++; + } + } + + /* // TODO: Write correct UDP tunnels stop + for (auto it = m_ClientForwards.begin (); it != m_ClientForwards.end ();) + { + if(clean && !it->second->isUpdated) { + it->second = nullptr; + it = m_ClientForwards.erase(it); + } else { + it->second->isUpdated = false; + it++; + } + } + + for (auto it = m_ServerForwards.begin (); it != m_ServerForwards.end ();) + { + if(clean && !it->second->isUpdated) { + it->second = nullptr; + it = m_ServerForwards.erase(it); + } else { + it->second->isUpdated = false; + it++; + } + } */ } } } diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index 076aaa5f..cb28e40e 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -22,6 +22,7 @@ #include "BOB.h" #include "I2CP.h" #include "AddressBook.h" +#include "I18N_langs.h" namespace i2p { @@ -47,6 +48,7 @@ namespace client const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport"; const char I2P_CLIENT_TUNNEL_MATCH_TUNNELS[] = "matchtunnels"; const char I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT[] = "connecttimeout"; + const char I2P_CLIENT_TUNNEL_KEEP_ALIVE_INTERVAL[] = "keepaliveinterval"; const char I2P_SERVER_TUNNEL_HOST[] = "host"; const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride"; const char I2P_SERVER_TUNNEL_PORT[] = "port"; @@ -102,6 +104,10 @@ namespace client std::vector > GetForwardInfosFor(const i2p::data::IdentHash & destination); + // i18n + std::shared_ptr GetLanguage () { return m_Language; }; + void SetLanguage (const std::shared_ptr language) { m_Language = language; }; + private: void ReadTunnels (); @@ -121,8 +127,7 @@ namespace client void CleanupUDP(const boost::system::error_code & ecode); void ScheduleCleanupUDP(); - template - void VisitTunnels (Visitor v); // Visitor: (I2PService *) -> bool, true means retain + void VisitTunnels (bool clean); void CreateNewSharedLocalDestination (); void AddLocalDestination (std::shared_ptr localDestination); @@ -150,6 +155,9 @@ namespace client std::unique_ptr m_CleanupUDPTimer; + // i18n + std::shared_ptr m_Language; + public: // for HTTP diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index d8b84e82..7acdc333 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -295,7 +295,7 @@ namespace proxy { std::string full_url = m_RequestURL.to_string(); std::stringstream ss; ss << tr("Host") <<" " << m_RequestURL.host << " " << tr("added to router's addressbook from helper") << ". "; - ss << tr("Click") << " " << tr("here") << " " << tr("to proceed") << "."; + ss << tr("Click here to proceed:") << " " << tr("Continue") << "."; GenericProxyInfo(tr("Addresshelper found"), ss.str()); return true; /* request processed */ } @@ -304,8 +304,8 @@ namespace proxy { std::string full_url = m_RequestURL.to_string(); std::stringstream ss; ss << tr("Host") << " " << m_RequestURL.host << " " << tr("already in router's addressbook") << ". "; - ss << tr("Click") << " " << tr("here") << " " << tr("to update record") << "."; + ss << tr("Click here to update record:") << " " << tr("Continue") << "."; GenericProxyInfo(tr("Addresshelper found"), ss.str()); return true; /* request processed */ } @@ -319,7 +319,7 @@ namespace proxy { auto pos = uri.find(":"); if(pos == std::string::npos || pos == uri.size() - 1) { - GenericProxyError(tr("Invalid Request"), tr("invalid request uri")); + GenericProxyError(tr("Invalid request"), tr("invalid request uri")); return true; } else diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index 25a5504a..7c76d359 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -52,12 +52,12 @@ namespace client } } - bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const + bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const { if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor) - return m_ECIESx25519Decryptor->Decrypt (encrypted, data, ctx, true); + return m_ECIESx25519Decryptor->Decrypt (encrypted, data); if (m_Decryptor) - return m_Decryptor->Decrypt (encrypted, data, ctx, true); + return m_Decryptor->Decrypt (encrypted, data); else LogPrint (eLogError, "I2CP: decryptor is not set"); return false; diff --git a/libi2pd_client/I2CP.h b/libi2pd_client/I2CP.h index 9a8bda4e..08a96af6 100644 --- a/libi2pd_client/I2CP.h +++ b/libi2pd_client/I2CP.h @@ -83,7 +83,7 @@ namespace client void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession // implements LocalDestination - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, i2p::data::CryptoKeyType preferredCrypto) const; + bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const; bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const; const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const; // for 4 only std::shared_ptr GetIdentity () const { return m_Identity; }; diff --git a/libi2pd_client/I2PTunnel.cpp b/libi2pd_client/I2PTunnel.cpp index 5476bfe2..b97270e4 100644 --- a/libi2pd_client/I2PTunnel.cpp +++ b/libi2pd_client/I2PTunnel.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -303,7 +303,7 @@ namespace client m_ProxyConnectionSent = true; } else - m_OutHeader << line << "\n"; + m_OutHeader << line << "\n"; } } else @@ -532,7 +532,7 @@ namespace client I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination, const std::string& address, int port, std::shared_ptr localDestination, int destinationPort): TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination), - m_DestinationPort (destinationPort) + m_DestinationPort (destinationPort), m_KeepAliveInterval (0) { } @@ -540,14 +540,24 @@ namespace client { TCPIPAcceptor::Start (); GetAddress (); + if (m_KeepAliveInterval) + ScheduleKeepAliveTimer (); } void I2PClientTunnel::Stop () { TCPIPAcceptor::Stop(); m_Address = nullptr; + if (m_KeepAliveTimer) m_KeepAliveTimer->cancel (); } + void I2PClientTunnel::SetKeepAliveInterval (uint32_t keepAliveInterval) + { + m_KeepAliveInterval = keepAliveInterval; + if (m_KeepAliveInterval) + m_KeepAliveTimer.reset (new boost::asio::deadline_timer (GetLocalDestination ()->GetService ())); + } + /* HACK: maybe we should create a caching IdentHash provider in AddressBook */ std::shared_ptr I2PClientTunnel::GetAddress () { @@ -569,6 +579,31 @@ namespace client return nullptr; } + void I2PClientTunnel::ScheduleKeepAliveTimer () + { + if (m_KeepAliveTimer) + { + m_KeepAliveTimer->expires_from_now (boost::posix_time::seconds(m_KeepAliveInterval)); + m_KeepAliveTimer->async_wait (std::bind (&I2PClientTunnel::HandleKeepAliveTimer, + this, std::placeholders::_1)); + } + } + + void I2PClientTunnel::HandleKeepAliveTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + if (m_Address && m_Address->IsValid ()) + { + if (m_Address->IsIdentHash ()) + GetLocalDestination ()->SendPing (m_Address->identHash); + else + GetLocalDestination ()->SendPing (m_Address->blindedPublicKey); + } + ScheduleKeepAliveTimer (); + } + } + I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address, int port, std::shared_ptr localDestination, int inport, bool gzip): I2PService (localDestination), m_IsUniqueLocal(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false) @@ -605,7 +640,47 @@ namespace client { if (!ecode) { - auto addr = (*it).endpoint ().address (); + bool found = false; + boost::asio::ip::tcp::endpoint ep; + if (m_LocalAddress) + { + boost::asio::ip::tcp::resolver::iterator end; + while (it != end) + { + ep = *it; + if (!ep.address ().is_unspecified ()) + { + if (ep.address ().is_v4 ()) + { + if (m_LocalAddress->is_v4 ()) found = true; + } + else if (ep.address ().is_v6 ()) + { + if (i2p::util::net::IsYggdrasilAddress (ep.address ())) + { + if (i2p::util::net::IsYggdrasilAddress (*m_LocalAddress)) + found = true; + } + else if (m_LocalAddress->is_v6 ()) + found = true; + } + } + if (found) break; + it++; + } + } + else + { + found = true; + ep = *it; // first available + } + if (!found) + { + LogPrint (eLogError, "I2PTunnel: Unable to resolve to compatible address"); + return; + } + + auto addr = ep.address (); LogPrint (eLogInfo, "I2PTunnel: server tunnel ", (*it).host_name (), " has been resolved to ", addr); m_Endpoint.address (addr); Accept (); @@ -848,7 +923,8 @@ namespace client LogPrint(eLogInfo, "UDPServer: done"); } - void I2PUDPServerTunnel::Start() { + void I2PUDPServerTunnel::Start() + { m_LocalDest->Start(); } @@ -888,7 +964,8 @@ namespace client RemotePort(remotePort), m_LastPort (0), m_cancel_resolve(false) { - m_LocalSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU )); + m_LocalSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU)); + m_LocalSocket.set_option (boost::asio::socket_base::reuse_address (true)); auto dgram = m_LocalDest->CreateDatagramDestination(gzip); dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this, @@ -915,7 +992,8 @@ namespace client void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred) { if(ec) { - LogPrint(eLogError, "UDP Client: ", ec.message()); + LogPrint(eLogError, "UDP Client: Reading from socket error: ", ec.message(), ". Restarting listener..."); + RecvFromLocal(); // Restart listener and continue work return; } if(!m_RemoteIdent) { @@ -940,7 +1018,7 @@ namespace client 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) + 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); @@ -1024,8 +1102,9 @@ namespace client else LogPrint(eLogWarning, "UDP Client: not tracking udp session using port ", (int) toPort); } - - I2PUDPClientTunnel::~I2PUDPClientTunnel() { + + I2PUDPClientTunnel::~I2PUDPClientTunnel() + { auto dgram = m_LocalDest->GetDatagramDestination(); if (dgram) dgram->ResetReceiver(); diff --git a/libi2pd_client/I2PTunnel.h b/libi2pd_client/I2PTunnel.h index 3b52ea1a..d21b2e11 100644 --- a/libi2pd_client/I2PTunnel.h +++ b/libi2pd_client/I2PTunnel.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -153,16 +153,22 @@ namespace client void Stop (); const char* GetName() { return m_Name.c_str (); } - + void SetKeepAliveInterval (uint32_t keepAliveInterval); + private: std::shared_ptr GetAddress (); + + void ScheduleKeepAliveTimer (); + void HandleKeepAliveTimer (const boost::system::error_code& ecode); private: std::string m_Name, m_Destination; std::shared_ptr m_Address; int m_DestinationPort; + uint32_t m_KeepAliveInterval; + std::unique_ptr m_KeepAliveTimer; }; @@ -254,6 +260,10 @@ namespace client std::vector m_Sessions; std::shared_ptr m_LocalDest; UDPSessionPtr m_LastSession; + + public: + + bool isUpdated; // transient, used during reload only }; class I2PUDPClientTunnel @@ -283,7 +293,7 @@ namespace client void TryResolving(); private: - + const std::string m_Name; std::mutex m_SessionsMutex; std::unordered_map > m_Sessions; // maps i2p port -> local udp convo @@ -298,6 +308,10 @@ namespace client uint16_t RemotePort, m_LastPort; bool m_cancel_resolve; std::shared_ptr m_LastSession; + + public: + + bool isUpdated; // transient, used during reload only }; class I2PServerTunnel: public I2PService diff --git a/libi2pd_client/MatchedDestination.cpp b/libi2pd_client/MatchedDestination.cpp index c0766096..8d17632b 100644 --- a/libi2pd_client/MatchedDestination.cpp +++ b/libi2pd_client/MatchedDestination.cpp @@ -79,23 +79,23 @@ namespace client if(!inbound && m_RemoteLeaseSet) { if(m_RemoteLeaseSet->IsExpired()) - { ResolveCurrentLeaseSet(); - } if(m_RemoteLeaseSet && !m_RemoteLeaseSet->IsExpired()) { // remote lease set is good auto leases = m_RemoteLeaseSet->GetNonExpiredLeases(); // pick lease std::shared_ptr obep; - while(!obep && leases.size() > 0) { + while(!obep && leases.size() > 0) + { auto idx = rand() % leases.size(); auto lease = leases[idx]; obep = i2p::data::netdb.FindRouter(lease->tunnelGateway); leases.erase(leases.begin()+idx); } - if(obep) { - path.push_back(obep->GetRouterIdentity()); + if(obep) + { + path.Add (obep); LogPrint(eLogDebug, "Destination: found OBEP matching IBGW"); } else LogPrint(eLogWarning, "Destination: could not find proper IBGW for matched outbound tunnel"); diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index af9ba6a8..c2983f43 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -1207,7 +1207,11 @@ namespace client void SAMSingleSession::StopLocalDestination () { localDestination->Release (); + // stop accepting new streams localDestination->StopAcceptingStreams (); + // terminate existing streams + auto s = localDestination->GetStreamingDestination (); // TODO: take care about datagrams + if (s) s->Stop (); } void SAMMasterSession::Close () diff --git a/libi2pd_wrapper/api.go b/libi2pd_wrapper/api.go new file mode 100644 index 00000000..64403aae --- /dev/null +++ b/libi2pd_wrapper/api.go @@ -0,0 +1,15 @@ +package api + +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +/* +#cgo CXXFLAGS: -I${SRCDIR}/../i18n -I${SRCDIR}/../libi2pd_client -I${SRCDIR}/../libi2pd -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-psabi -fPIC -D__AES__ -maes +#cgo LDFLAGS: -L${SRCDIR}/../ -l:libi2pd.a -l:libi2pdlang.a -latomic -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lstdc++ +*/ +import "C" diff --git a/libi2pd_wrapper/api.swigcxx b/libi2pd_wrapper/api.swigcxx new file mode 100644 index 00000000..3ef6bd36 --- /dev/null +++ b/libi2pd_wrapper/api.swigcxx @@ -0,0 +1,8 @@ +// See swig.org for more inteface options, +// e.g. map std::string to Go string + +%{ +#include "capi.h" +%} + +%include "capi.h" diff --git a/libi2pd_wrapper/capi.cpp b/libi2pd_wrapper/capi.cpp new file mode 100644 index 00000000..fc4df917 --- /dev/null +++ b/libi2pd_wrapper/capi.cpp @@ -0,0 +1,106 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include "api.h" +#include "capi.h" +#include +#include +#include +#include + + +// Uses the example from: https://stackoverflow.com/a/9210560 +// See also https://stackoverflow.com/questions/9210528/split-string-with-delimiters-in-c/9210560# +// Does not handle consecutive delimiters, this is only for passing +// lists of arguments by value to InitI2P from C_InitI2P +char** str_split(char* a_str, const char a_delim) +{ + char** result = 0; + size_t count = 0; + char* tmp = a_str; + char* last_comma = 0; + char delim[2]; + delim[0] = a_delim; + delim[1] = 0; + + /* Count how many elements will be extracted. */ + while (*tmp) + { + if (a_delim == *tmp) + { + count++; + last_comma = tmp; + } + tmp++; + } + + /* Add space for trailing token. */ + count += last_comma < (a_str + strlen(a_str) - 1); + + /* Add space for terminating null string so caller + knows where the list of returned strings ends. */ + count++; + + result = (char**) malloc(sizeof(char*) * count); + + if (result) + { + size_t idx = 0; + char* token = strtok(a_str, delim); + + while (token) + { + assert(idx < count); + *(result + idx++) = strdup(token); + token = strtok(0, delim); + } + assert(idx == count - 1); + *(result + idx) = 0; + } + + return result; +} + + +#ifdef __cplusplus +extern "C" { +#endif + +void C_InitI2P (int argc, char argv[], const char * appName) +{ + const char* delim = " "; + char* vargs = strdup(argv); + char** args = str_split(vargs, *delim); + std::cout << argv; + return i2p::api::InitI2P(argc, args, appName); +} + +void C_TerminateI2P () +{ + return i2p::api::TerminateI2P(); +} + +void C_StartI2P () +{ + std::shared_ptr logStream; + return i2p::api::StartI2P(logStream); +} + +void C_StopI2P () +{ + return i2p::api::StopI2P(); +} + +void C_RunPeerTest () +{ + return i2p::api::RunPeerTest(); +} + +#ifdef __cplusplus +} +#endif diff --git a/libi2pd_wrapper/capi.h b/libi2pd_wrapper/capi.h new file mode 100644 index 00000000..bfc4f88b --- /dev/null +++ b/libi2pd_wrapper/capi.h @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2013-2020, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef CAPI_H__ +#define CAPI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +// initialization start and stop +void C_InitI2P (int argc, char argv[], const char * appName); +//void C_InitI2P (int argc, char** argv, const char * appName); +void C_TerminateI2P (); +void C_StartI2P (); +// write system log to logStream, if not specified to .log in application's folder +void C_StopI2P (); +void C_RunPeerTest (); // should be called after UPnP + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tests/Makefile b/tests/Makefile index 4c80c37c..8eb52fde 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,4 +1,5 @@ -CXXFLAGS += -Wall -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -I../libi2pd/ -pthread -Wl,--unresolved-symbols=ignore-in-object-files +CXXFLAGS += -Wall -Wno-unused-parameter -Wextra -pedantic -O0 -g -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 -pthread -Wl,--unresolved-symbols=ignore-in-object-files +INCFLAGS += -I../libi2pd TESTS = test-gost test-gost-sig test-base-64 test-x25519 test-aeadchacha20poly1305 test-blinding test-elligator