diff -Nru i2pd-2.39.0/build/build_mingw.cmd i2pd-2.43.0/build/build_mingw.cmd --- i2pd-2.39.0/build/build_mingw.cmd 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/build/build_mingw.cmd 2022-08-21 19:40:41.000000000 +0000 @@ -1,86 +1,149 @@ -@echo off -setlocal enableextensions enabledelayedexpansion -title Building i2pd - -REM Copyright (c) 2013-2020, The PurpleI2P Project -REM This file is part of Purple i2pd project and licensed under BSD3 -REM See full license text in LICENSE file at top of project tree - -REM To use that script, you must have installed in your MSYS installation these packages: -REM Base: git make zip -REM x86_64: mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-gcc -REM i686: mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-gcc - -REM setting up variables for MSYS -REM Note: if you installed MSYS64 to different path, edit WD variable (only C:\msys64 needed to edit)! -set "WD=C:\msys64\usr\bin\" -set MSYS2_PATH_TYPE=inherit -set CHERE_INVOKING=enabled_from_arguments -REM set MSYSTEM=MSYS -set MSYSTEM=MINGW32 - -set "xSH=%WD%bash -lc" - -set "FILELIST=i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates contrib/tunnels.d" - -REM detecting number of processors -set /a threads=%NUMBER_OF_PROCESSORS% - -REM we must work in root of repo -cd .. - -REM deleting old log files -del /S build_*.log >> nul 2>&1 - -echo Receiving latest commit and cleaning up... -%xSH% "git checkout contrib/* && git pull && make clean" > build\build.log 2>&1 -echo. - -REM set to variable current commit hash -FOR /F "usebackq" %%a IN (`%xSH% 'git describe --tags'`) DO ( - set tag=%%a -) - -%xSH% "echo To use configs and certificates, move all files and certificates folder from contrib directory here. > README.txt" >> nul - -REM converting configuration files to DOS format (usable in default notepad) -%xSH% "unix2dos contrib/i2pd.conf contrib/tunnels.conf contrib/tunnels.d/*" >> build\build.log 2>&1 - -REM starting building -set MSYSTEM=MINGW32 -set bitness=32 -call :BUILDING - -set MSYSTEM=MINGW64 -set bitness=64 -call :BUILDING - -REM building for WinXP -set "WD=C:\msys64-xp\usr\bin\" -set MSYSTEM=MINGW32 -set bitness=32 -set "xSH=%WD%bash -lc" -call :BUILDING_XP -echo. - -REM compile installer -C:\PROGRA~2\INNOSE~1\ISCC.exe /dI2Pd_TextVer="%tag%" /dI2Pd_Ver="%tag%.0" build\win_installer.iss >> build\build.log 2>&1 - -del README.txt i2pd_x32.exe i2pd_x64.exe i2pd_xp.exe >> nul - -echo Build complete... -pause -exit /b 0 - -:BUILDING -%xSH% "make clean" >> nul -echo Building i2pd %tag% for win%bitness% -%xSH% "make DEBUG=no USE_UPNP=yes -j%threads% && cp i2pd.exe i2pd_x%bitness%.exe && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST% && make clean" > build\build_win%bitness%_%tag%.log 2>&1 -goto EOF - -:BUILDING_XP -%xSH% "make clean" >> nul -echo Building i2pd %tag% for winxp -%xSH% "make DEBUG=no USE_UPNP=yes USE_WINXP_FLAGS=yes -j%threads% && cp i2pd.exe i2pd_xp.exe && zip -r9 build/i2pd_%tag%_winxp_mingw.zip %FILELIST% && make clean" > build\build_winxp_%tag%.log 2>&1 - -:EOF \ No newline at end of file +@echo off +setlocal enableextensions enabledelayedexpansion +title Building i2pd + +REM Copyright (c) 2013-2022, The PurpleI2P Project +REM This file is part of Purple i2pd project and licensed under BSD3 +REM See full license text in LICENSE file at top of project tree + +REM To use that script, you must have installed in your MSYS installation these packages: +REM Base: git make zip +REM UCRT64: mingw-w64-ucrt-x86_64-boost mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-gcc +REM MINGW32: mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-gcc + +REM setting up variables for MSYS +REM Note: if you installed MSYS64 to different path, edit WD variable (only C:\msys64 needed to edit) +set MSYS2_PATH_TYPE=inherit +set CHERE_INVOKING=enabled_from_arguments +set MSYSTEM=MINGW32 + +set "WD=C:\msys64\usr\bin\" +set "xSH=%WD%bash -lc" + +set "FILELIST=i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates contrib/tunnels.d contrib/webconsole" + +REM detecting number of processors +set /a threads=%NUMBER_OF_PROCESSORS% + +REM we must work in root of repo +cd .. + +REM deleting old log files +del /S build_*.log >> nul 2>&1 + +echo Receiving latest commit and cleaning up... +%xSH% "git checkout contrib/* && git pull && make clean" > build\build.log 2>&1 + +REM set to variable current commit hash +for /F "usebackq" %%a in (`%xSH% "git describe --tags"`) DO ( + set tag=%%a +) + +REM set to variable latest released tag +for /F "usebackq" %%b in (`%xSH% "git describe --abbrev=0"`) DO ( + set reltag=%%b +) + +echo Preparing configuration files and README for packaging... + +%xSH% "echo To use configs and certificates, move all files and certificates folder from contrib directory here. > README.txt" >> nul + +REM converting configuration files to DOS format (make usable in Windows Notepad) +%xSH% "unix2dos contrib/i2pd.conf contrib/tunnels.conf contrib/tunnels.d/* contrib/webconsole/style.css" >> build\build.log 2>&1 + +REM Prepare binary signing command if signing key and password provided +if defined SIGN ( + echo Signing enabled + + for %%X in (signtool.exe) do (set xSIGNTOOL=%%~$PATH:X) + if not defined xSIGNTOOL ( + if not defined SIGNTOOL ( + echo Error: Can't find signtool. Please provide path to binary using SIGNTOOL variable. + exit /b 1 + ) else ( + set "xSIGNTOOL=%SIGNTOOL%" + ) + ) + + if defined SIGNKEY ( + set "xSIGNKEYOPTS=/f ^"%SIGNKEY%^"" + ) + + if defined SIGNPASS ( + set "xSIGNPASSOPTS=/p ^"%SIGNPASS%^"" + ) + + set "xSIGNOPTS=sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 %xSIGNKEYOPTS% %xSIGNPASSOPTS%" +) + +REM starting building +set MSYSTEM=MINGW32 +set bitness=32 +call :BUILDING + +set MSYSTEM=UCRT64 +set bitness=64 +call :BUILDING + +REM build for Windows XP +if exist C:\msys64-xp\ ( call :BUILDING_XP ) + +echo. + +REM compile installer +echo Building installer... +C:\PROGRA~2\INNOSE~1\ISCC.exe /dI2Pd_TextVer="%tag%" /dI2Pd_Ver="%reltag%.0" build\win_installer.iss >> build\build.log 2>&1 + +REM Sign binary +if defined xSIGNOPTS ( + "%xSIGNTOOL%" %xSIGNOPTS% build\setup_i2pd_v%tag%.exe +) + +%xSH% "git checkout contrib/*" >> build\build.log 2>&1 +del README.txt i2pd_x32.exe i2pd_x64.exe i2pd_xp.exe >> nul + +echo Build complete... +pause +exit /b 0 + +:BUILDING +%xSH% "make clean" >> nul +echo Building i2pd %tag% for win%bitness%... +REM Build i2pd +%xSH% "make DEBUG=no USE_UPNP=yes -j%threads%" > build\build_win%bitness%_%tag%.log 2>&1 + +REM Sign binary +if defined xSIGNOPTS ( + "%xSIGNTOOL%" %xSIGNOPTS% i2pd.exe +) + +REM Copy binary for installer and create distribution archive +%xSH% "cp i2pd.exe i2pd_x%bitness%.exe && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST%" >> build\build_win%bitness%_%tag%.log 2>&1 + +REM Clean work directory +%xSH% "make clean" >> build\build_win%bitness%_%tag%.log 2>&1 +goto EOF + +:BUILDING_XP +set MSYSTEM=MINGW32 +set bitness=32 +set "WD=C:\msys64-xp\usr\bin\" +set "xSH=%WD%bash -lc" + +%xSH% "make clean" >> nul +echo Building i2pd %tag% for winxp... +%xSH% "make DEBUG=no USE_UPNP=yes USE_WINXP_FLAGS=yes -j%threads%" > build\build_winxp_%tag%.log 2>&1 + +REM Sign binary +if defined xSIGNOPTS ( + "%xSIGNTOOL%" %xSIGNOPTS% i2pd.exe +) + +REM Copy binary for installer and create distribution archive +%xSH% "cp i2pd.exe i2pd_xp.exe && zip -r9 build/i2pd_%tag%_winxp_mingw.zip %FILELIST%" >> build\build_winxp_%tag%.log 2>&1 + +REM Clean work directory +%xSH% "make clean" >> build\build_winxp_%tag%.log 2>&1 +goto EOF + +:EOF diff -Nru i2pd-2.39.0/build/CMakeLists.txt i2pd-2.43.0/build/CMakeLists.txt --- i2pd-2.39.0/build/CMakeLists.txt 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/build/CMakeLists.txt 2022-08-21 19:40:41.000000000 +0000 @@ -1,6 +1,5 @@ -cmake_minimum_required(VERSION 2.8.12) -# this addresses CMP0059 with CMake > 3.3 for PCH flags -cmake_policy(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.7) +cmake_policy(VERSION 3.7) project("i2pd") # for debugging @@ -18,14 +17,13 @@ option(WITH_BINARY "Build binary" ON) option(WITH_STATIC "Static build" OFF) option(WITH_UPNP "Include support for UPnP client" OFF) -option(WITH_PCH "Use precompiled header" OFF) -option(WITH_MESHNET "Build for cjdns test network" OFF) +option(WITH_GIT_VERSION "Use git commit info as version" OFF) option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF) option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF) # paths set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules") -set(CMAKE_SOURCE_DIR "..") +set(CMAKE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") #Handle paths nicely include(GNUInstallDirs) @@ -91,14 +89,20 @@ "${DAEMON_SRC_DIR}/UPnP.cpp" ) -if(WITH_MESHNET) - add_definitions(-DMESHNET) -endif() - if(WITH_UPNP) add_definitions(-DUSE_UPNP) endif() +if(WITH_GIT_VERSION) + include(GetGitRevisionDescription) + git_describe(GIT_VERSION) + add_definitions(-DGITVER="${GIT_VERSION}") +endif() + +if(APPLE) + add_definitions(-DMAC_OSX) +endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Winvalid-pch -Wno-unused-parameter") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -pedantic") # TODO: The following is incompatible with static build and enabled hardening for OpenWRT. @@ -168,24 +172,13 @@ # libraries -# TODO: once CMake 3.1+ becomes mainstream, see e.g. http://stackoverflow.com/a/29871891/673826 -# use imported Threads::Threads instead set(THREADS_PREFER_PTHREAD_FLAG ON) -if(IOS) - set(CMAKE_THREAD_LIBS_INIT "-lpthread") - set(CMAKE_HAVE_THREADS_LIBRARY 1) - set(CMAKE_USE_WIN32_THREADS_INIT 0) - set(CMAKE_USE_PTHREADS_INIT 1) -else() - find_package(Threads REQUIRED) -endif() -if(THREADS_HAVE_PTHREAD_ARG) # compile time flag - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") -endif() +find_package(Threads REQUIRED) if(WITH_STATIC) set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_RUNTIME ON) + set(OPENSSL_USE_STATIC_LIBS ON) set(BUILD_SHARED_LIBS OFF) if(${CMAKE_CXX_COMPILER} MATCHES ".*-openwrt-.*") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") @@ -200,23 +193,6 @@ add_definitions(-DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_REGEX_DYN_LINK) endif() -if(WITH_PCH) - include_directories(BEFORE ${CMAKE_BINARY_DIR}) - add_library(stdafx STATIC "${LIBI2PD_SRC_DIR}/stdafx.cpp") - string(TOUPPER ${CMAKE_BUILD_TYPE} BTU) - get_directory_property(DEFS DEFINITIONS) - string(REPLACE " " ";" FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BTU}} ${DEFS}") - add_custom_command(TARGET stdafx PRE_BUILD - COMMAND ${CMAKE_CXX_COMPILER} ${FLAGS} -c ${CMAKE_CURRENT_SOURCE_DIR}/../libi2pd/stdafx.h -o ${CMAKE_BINARY_DIR}/stdafx.h.gch - ) - 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 libi2pdlang) - find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED) if(NOT DEFINED Boost_INCLUDE_DIRS) message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!") @@ -227,6 +203,10 @@ message(SEND_ERROR "Could not find OpenSSL. Please download and install it first!") endif() +if(OPENSSL_VERSION VERSION_GREATER_EQUAL "3.0.0") + add_definitions(-DOPENSSL_SUPPRESS_DEPRECATED) +endif() + if(WITH_UPNP) find_package(MiniUPnPc REQUIRED) if(NOT MINIUPNPC_FOUND) @@ -244,15 +224,7 @@ # load includes include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) -# warn if for meshnet -if(WITH_MESHNET) - message(STATUS "Building for testnet") - message(WARNING "This build will NOT work on mainline i2p") -endif() - -if(NOT MSYS) - include(CheckAtomic) -endif() +include(CheckAtomic) # show summary message(STATUS "---------------------------------------") @@ -269,8 +241,7 @@ message(STATUS " BINARY : ${WITH_BINARY}") message(STATUS " STATIC BUILD : ${WITH_STATIC}") message(STATUS " UPnP : ${WITH_UPNP}") -message(STATUS " PCH : ${WITH_PCH}") -message(STATUS " MESHNET : ${WITH_MESHNET}") +message(STATUS " GIT VERSION : ${WITH_GIT_VERSION}") message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}") message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}") message(STATUS "---------------------------------------") @@ -282,31 +253,21 @@ set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static") endif() - if(WITH_PCH) - target_compile_options("${PROJECT_NAME}" PRIVATE -include libi2pd/stdafx.h) - endif() - if(WITH_HARDENING AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-z relro -z now") endif() - if(WITH_UPNP) - set(UPNP_LIB ${MINIUPNPC_LIBRARY}) - endif() - # FindBoost pulls pthread for thread which is broken for static linking at least on Ubuntu 15.04 list(GET Boost_LIBRARIES -1 LAST_Boost_LIBRARIES) if(${LAST_Boost_LIBRARIES} MATCHES ".*pthread.*") list(REMOVE_AT Boost_LIBRARIES -1) endif() - if(WITH_STATIC) set(DL_LIB ${CMAKE_DL_LIBS}) endif() - target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY}) - target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${MINGW_EXTRA} ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES}) + target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto ${MINIUPNPC_LIBRARY} ZLIB::ZLIB Threads::Threads ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES}) install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime) set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}") diff -Nru i2pd-2.39.0/build/cmake_modules/GetGitRevisionDescription.cmake i2pd-2.43.0/build/cmake_modules/GetGitRevisionDescription.cmake --- i2pd-2.39.0/build/cmake_modules/GetGitRevisionDescription.cmake 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/build/cmake_modules/GetGitRevisionDescription.cmake 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,284 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_describe_working_tree( [ ...]) +# +# Returns the results of git describe on the working tree (--dirty option), +# and adjusting the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# git_local_changes() +# +# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. +# Uses the return code of "git diff-index --quiet HEAD --". +# Does not regard untracked files. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2020 Ryan Pavlik +# http://academic.cleardefinition.com +# +# Copyright 2009-2013, Iowa State University. +# Copyright 2013-2020, Ryan Pavlik +# Copyright 2013-2020, Contributors +# SPDX-License-Identifier: BSL-1.0 +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +# Function _git_find_closest_git_dir finds the next closest .git directory +# that is part of any directory in the path defined by _start_dir. +# The result is returned in the parent scope variable whose name is passed +# as variable _git_dir_var. If no .git directory can be found, the +# function returns an empty string via _git_dir_var. +# +# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and +# neither foo nor bar contain a file/directory .git. This wil return +# C:/bla/.git +# +function(_git_find_closest_git_dir _start_dir _git_dir_var) + set(cur_dir "${_start_dir}") + set(git_dir "${_start_dir}/.git") + while(NOT EXISTS "${git_dir}") + # .git dir not found, search parent directories + set(git_previous_parent "${cur_dir}") + get_filename_component(cur_dir "${cur_dir}" DIRECTORY) + if(cur_dir STREQUAL git_previous_parent) + # We have reached the root directory, we are not in git + set(${_git_dir_var} + "" + PARENT_SCOPE) + return() + endif() + set(git_dir "${cur_dir}/.git") + endwhile() + set(${_git_dir_var} + "${git_dir}" + PARENT_SCOPE) +endfunction() + +function(get_git_head_revision _refspecvar _hashvar) + _git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR) + + if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR") + set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE) + else() + set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE) + endif() + if(NOT "${GIT_DIR}" STREQUAL "") + file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}" + "${GIT_DIR}") + if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR) + # We've gone above the CMake root dir. + set(GIT_DIR "") + endif() + endif() + if("${GIT_DIR}" STREQUAL "") + set(${_refspecvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + set(${_hashvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + # Check if the current source dir is a git submodule or a worktree. + # In both cases .git is a file instead of a directory. + # + if(NOT IS_DIRECTORY ${GIT_DIR}) + # The following git command will return a non empty string that + # points to the super project working tree if the current + # source dir is inside a git submodule. + # Otherwise the command will return an empty string. + # + execute_process( + COMMAND "${GIT_EXECUTABLE}" rev-parse + --show-superproject-working-tree + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT "${out}" STREQUAL "") + # If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE + ${submodule}) + string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} + ABSOLUTE) + set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") + else() + # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree + file(READ ${GIT_DIR} worktree_ref) + # The .git directory contains a path to the worktree information directory + # inside the parent git repo of the worktree. + # + string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir + ${worktree_ref}) + string(STRIP ${git_worktree_dir} git_worktree_dir) + _git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR) + set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") + endif() + else() + set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${HEAD_SOURCE_FILE}") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} + "${HEAD_REF}" + PARENT_SCOPE) + set(${_hashvar} + "${HEAD_HASH}" + PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_describe_working_tree _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_local_changes _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(res EQUAL 0) + set(${_var} + "CLEAN" + PARENT_SCOPE) + else() + set(${_var} + "DIRTY" + PARENT_SCOPE) + endif() +endfunction() diff -Nru i2pd-2.39.0/build/cmake_modules/GetGitRevisionDescription.cmake.in i2pd-2.43.0/build/cmake_modules/GetGitRevisionDescription.cmake.in --- i2pd-2.39.0/build/cmake_modules/GetGitRevisionDescription.cmake.in 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/build/cmake_modules/GetGitRevisionDescription.cmake.in 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,43 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright 2009-2012, Iowa State University +# Copyright 2011-2015, Contributors +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# SPDX-License-Identifier: BSL-1.0 + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + else() + configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) + file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) + if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") + set(HEAD_HASH "${CMAKE_MATCH_1}") + endif() + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff -Nru i2pd-2.39.0/build/cmake_modules/TargetArch.cmake i2pd-2.43.0/build/cmake_modules/TargetArch.cmake --- i2pd-2.39.0/build/cmake_modules/TargetArch.cmake 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/build/cmake_modules/TargetArch.cmake 2022-08-21 19:40:41.000000000 +0000 @@ -1,16 +1,30 @@ +# Copyright (c) 2017-2022, The PurpleI2P Project +# This file is part of Purple i2pd project and licensed under BSD3 +# See full license text in LICENSE file at top of project tree + # Based on the Qt 5 processor detection code, so should be very accurate -# https://qt.gitorious.org/qt/qtbase/blobs/master/src/corelib/global/qprocessordetection.h -# Currently handles arm (v5, v6, v7), x86 (32/64), ia64, and ppc (32/64) +# https://github.com/qt/qtbase/blob/dev/src/corelib/global/qprocessordetection.h +# Currently handles arm (v5, v6, v7, v8), x86 (32/64), ia64, mips (32/64, mipsel, mips64el) and ppc (32/64) # Regarding POWER/PowerPC, just as is noted in the Qt source, # "There are many more known variants/revisions that we do not handle/detect." set(archdetect_c_code " -#if defined(__arm__) || defined(__TARGET_ARCH_ARM) +#if defined(__arm__) || defined(__TARGET_ARCH_ARM)|| defined(_M_ARM) || defined(_M_ARM64) || defined(__aarch64__) || defined(__ARM64__) + #if defined(__ARM64_ARCH_8__) \\ + || defined(__aarch64__) \\ + || defined(__ARMv8__) \\ + || defined(__ARMv8_A__) \\ + || defined(_M_ARM64) \\ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 8) + #error cmake_ARCH arm64 #if defined(__ARM_ARCH_7__) \\ || defined(__ARM_ARCH_7A__) \\ || defined(__ARM_ARCH_7R__) \\ || defined(__ARM_ARCH_7M__) \\ + || defined(__ARM_ARCH_7S__) \\ + || defined(_ARM_ARCH_7) \\ + || defined(__CORE_CORTEXA__) \\ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 7) #error cmake_ARCH armv7 #elif defined(__ARM_ARCH_6__) \\ @@ -23,6 +37,7 @@ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 6) #error cmake_ARCH armv6 #elif defined(__ARM_ARCH_5TEJ__) \\ + || defined(__ARM_ARCH_5TE__) \\ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 5) #error cmake_ARCH armv5 #else @@ -34,6 +49,18 @@ #error cmake_ARCH x86_64 #elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64) #error cmake_ARCH ia64 +#elif defined(__mips) || defined(__mips__) || defined(_M_MRX000) + #if defined(_MIPS_ARCH_MIPS64) || defined(__mips64) + #if defined(__MIPSEL__) + #error cmake_ARCH mips64el + #else + #error cmake_ARCH mips64 + #endif + #elif defined(__MIPSEL__) + #error cmake_ARCH mipsel + #else + #error cmake_ARCH mips + #endif #elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \\ || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \\ || defined(_M_MPPC) || defined(_M_PPC) @@ -47,7 +74,7 @@ #error cmake_ARCH unknown ") -# Set ppc_support to TRUE before including this file or ppc and ppc64 +# Set ppc_support to TRUE before including this file on ppc and ppc64 # will be treated as invalid architectures since they are no longer supported by Apple function(target_architecture output_var) @@ -67,12 +94,14 @@ foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES}) if("${osx_arch}" STREQUAL "ppc" AND ppc_support) set(osx_arch_ppc TRUE) + elseif("${osx_arch}" STREQUAL "ppc64" AND ppc_support) + set(osx_arch_ppc64 TRUE) elseif("${osx_arch}" STREQUAL "i386") set(osx_arch_i386 TRUE) elseif("${osx_arch}" STREQUAL "x86_64") set(osx_arch_x86_64 TRUE) - elseif("${osx_arch}" STREQUAL "ppc64" AND ppc_support) - set(osx_arch_ppc64 TRUE) + elseif("${osx_arch}" STREQUAL "arm64") + set(osx_arch_arm64 TRUE) else() message(FATAL_ERROR "Invalid OS X arch name: ${osx_arch}") endif() @@ -83,6 +112,10 @@ list(APPEND ARCH ppc) endif() + if(osx_arch_ppc64) + list(APPEND ARCH ppc64) + endif() + if(osx_arch_i386) list(APPEND ARCH i386) endif() @@ -91,8 +124,8 @@ list(APPEND ARCH x86_64) endif() - if(osx_arch_ppc64) - list(APPEND ARCH ppc64) + if(osx_arch_arm64) + list(APPEND ARCH arm64) endif() else() file(WRITE "${CMAKE_BINARY_DIR}/arch.c" "${archdetect_c_code}") diff -Nru i2pd-2.39.0/build/win_installer.iss i2pd-2.43.0/build/win_installer.iss --- i2pd-2.39.0/build/win_installer.iss 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/build/win_installer.iss 2022-08-21 19:40:41.000000000 +0000 @@ -1,8 +1,5 @@ #define I2Pd_AppName "i2pd" #define I2Pd_Publisher "PurpleI2P" -; Get application version from compiled binary -; Disabled to use definition from command line -;#define I2Pd_ver GetFileVersionString(AddBackslash(SourcePath) + "..\i2pd_x64.exe") [Setup] AppName={#I2Pd_AppName} @@ -27,7 +24,7 @@ AppID={{621A23E0-3CF4-4BD6-97BC-4835EA5206A2} AppVerName={#I2Pd_AppName} -AppCopyright=Copyright (c) 2013-2020, The PurpleI2P Project +AppCopyright=Copyright (c) 2013-2022, The PurpleI2P Project AppPublisherURL=http://i2pd.website/ AppSupportURL=https://github.com/PurpleI2P/i2pd/issues AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases @@ -47,6 +44,7 @@ Source: ..\contrib\tunnels.conf; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist Source: ..\contrib\certificates\*; DestDir: {userappdata}\i2pd\certificates; Flags: onlyifdoesntexist recursesubdirs createallsubdirs Source: ..\contrib\tunnels.d\*; DestDir: {userappdata}\i2pd\tunnels.d; Flags: onlyifdoesntexist recursesubdirs createallsubdirs +Source: ..\contrib\webconsole\*; DestDir: {userappdata}\i2pd\webconsole; Flags: onlyifdoesntexist recursesubdirs createallsubdirs [Icons] Name: {group}\I2Pd; Filename: {app}\i2pd.exe diff -Nru i2pd-2.39.0/ChangeLog i2pd-2.43.0/ChangeLog --- i2pd-2.39.0/ChangeLog 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/ChangeLog 2022-08-21 19:40:41.000000000 +0000 @@ -1,6 +1,125 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog +## [2.43.0] - 2022-08-22 +### Added +- Complete SSU2 implementation +- Localization to Chinese +- Send RouterInfo update for long live sessions +- Explicit ipv6 ranges of known tunnel brokers for MTU detection +- Always send "Connection: close" and strip out Keep-Alive for server HTTP tunnel +- Show ports for all transports in web console +- Translation of webconsole site title +- Support for Windows ProgramData path when running as service +- Ability to turn off address book +- Handle signals TSTP and CONT to stop and resume network +### Changed +- Case insensitive headers for server HTTP tunnel +- Do not show 'Address registration' line if LeaseSet is encrypted +- SSU2 transports have higher priority than SSU +- Disable ElGamal precalculated table if no SSU +- Deprecate limits.ntcpsoft, limits.ntcphard and limits.ntcpthreads config options +- SSU2 is enabled and SSU is disabled by default for new installations +### Fixed +- Typo with Referer header name in HTTP proxy +- Can't handle garlic message from an exploratory tunnel +- Incorrect encryption key for exploratory lookup reply +- Bound checks issues in LeaseSets code +- MTU detection on Windows +- Crash on stop of active server tunnel +- Send datagram to wrong destination in SAM +- Incorrect static key in RouterInfo if the keys were regenerated +- Duplicated sessions in BOB + +## [2.42.1] - 2022-05-24 +### Fixed +- Incorrect jump link in HTTP Proxy + +## [2.42.0] - 2022-05-22 +### Added +- Preliminary SSU2 implementation +- Tunnel length variance +- Localization to French +- Daily cleanup of obsolete peer profiles +- Ordered jump services list in HTTP proxy +- Win32 service +- Show port for local non-published SSU addresses in web console +### Changed +- Maximum RouterInfo length increased to 3K +- Skip unknown addresses in RouterInfo +- Don't pick own router for peer test +- Reseeds list +- Internal numeric id for families +- Use ipv6 preference only when netinet headers not used +- Close stream if delete requested +- Remove version from title in web console +- Drop MESHNET build option +- Set data path before initialization +- Don't show registration block in web console if token is not provided +### Fixed +- Encrypted LeaseSet for EdDSA signature +- Clients tunnels are not built if clock is not synced on start +- Incorrect processing of i2cp.dontPublishLeaseSet param +- UDP tunnels reload +- Build for LibreSSL 3.5.2 +- Race condition in short tunnel build message +- Race condition in local RouterInfo buffer allocation + +## [2.41.0] - 2022-02-20 +### Added +- Clock syncronization through SSU +- Drop routers older than 6 months on start +- Localization to German +- Don't send streaming ack too frequently +- Select compatible outbound tunnel for I2CP messages +- Restart webconsole's acceptor in case of exception +### Changed +- Use builtin bitswap for endian on windows +- Send SessionCreated before connection close if clock skew +- Try another floodfill for publishing if no compatible tunnels found +- Reduce memory usage for RouterInfo structures +- Avoid duplicated addresses in RouterInfo. Check presence of netId and version +- Use TCP/IP sockets for I2CP on Android instead local sockets +- Return uptime as integer in I2PControl +- Reseed servers list/cerificates +- Webconsole's dark style colors +### Fixed +- Attempt to use Yggdrasil on start on Android +- Attempts to send peer tests to itself +- Severe packets drop in SSU +- Crash on tunnel tests +- Loading addressbook subscriptions from config +- Multiple I2CP session to the same destination +- Build on Apple Silicon + +## [2.40.0] - 2021-11-29 +### Added +- Keep alive parameter for client tunnels +- Support openssl 3.0.0 +- Localization to Armenian +- Show git commit info in version +- Windows menu item for opening datadir +- Reseed if too few floodfills +- Don't publish old and replacing tunnel in LeaseSet +- Webconsole light/dark theme depending on system settings (via CSS) +### Changed +- Set gzip compression to false by default +- Build tunnel through ECIES routers only +- Removed ElGamal support for tunnels +- Moved webconsole resources to separate file +- Pick tunnels with compatible transport with another tunnel of floodfill +- Use common cleanup timer for all SSU sessions +- Reduced memory usage +- Reseed servers list +- i18n code called from ClientContext +### Fixed +- Tunnels reload +- Some typos in log messages +- Cleanup relay requests table +- Server tunnel is not published +- Build on GNU/Hurd. Disable pthread_setname_np +- Crash when incorrect sigtype used with blinding + ## [2.39.0] - 2021-08-23 ### Added - Short tunnel build messages @@ -18,11 +137,11 @@ - 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 +- 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 +- 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 @@ -46,7 +165,7 @@ ## [2.38.0] - 2021-05-17 ### Added - Publish ipv6 introducers -- Bind ipv6 or yggdrasil NTCP2 acceptor to specified address +- Bind ipv6 or yggdrasil NTCP2 acceptor to specified address - Support .b32.i2p addresses and hostnames for SAM STREAM CREATE - ipv6 peer tests - Publish iexp param for introducers @@ -82,12 +201,12 @@ - Symmetric NAT network status error - Bind server tunnel connection to specified address - lookuplocal BOB extended command -- address4 and address6 parameters to bind outgoing connections to +- address4 and address6 parameters to bind outgoing connections to - Rekey of low-bandwidth routers to ECIES - Popup notification windows when unable to parse config for Windows ### Changed - Floodfills with "U" cap are not ignored anymore -- Check transports reachability between tunnel peers and between router and floodfill +- Check transports reachability between tunnel peers and between router and floodfill - NTCP2 and reseed HTTP proxy support authorization now - Show actual IP addresses for proxy connections - Publish and handle SSU addreses without host @@ -109,22 +228,22 @@ - Dump addressbook in hosts.txt format - Request RouterInfo through exploratory tunnels if direct connection to fllodfill is not possible - Threads naming -- Check if public x25519 key is valid +- Check if public x25519 key is valid - ECIES-X25519-AEAD-Ratchet for shared local destination - LeaseSet creation timeout for I2CP session - Resend RouterInfo after some interval for longer NTCP2 sessions - Select reachable router of inbound tunnel gateway -- Reseed if no compatible routers in netdb +- Reseed if no compatible routers in netdb - Refresh on swipe in Android webconsole ### Changed - reg.i2p for default addressbook instead inr.i2p - ECIES-x25519 (crypto type 4) for new routers - Try to connect to all compatible addresses from peer's RouterInfo -- Replace LeaseSet completely if store type changes +- Replace LeaseSet completely if store type changes - Try ECIES-X25519-AEAD-Ratchet tag before ElGamal - Don't detach ECIES-X25519-AEAD-Ratchet session from destination immediately - Viewport and styles on error in HTTP proxy -- Don't create notification when Windows taskbar restarted +- Don't create notification when Windows taskbar restarted - Cumulative SSU ACK bitfields - limit tunnel length to 8 hops - Limit tunnels quantity to 16 @@ -157,7 +276,7 @@ - Transient signature length, if different from identity - Terminate I2CP session if destroyed - RouterInfo publishing confirmation -- Check if ECIES-X25519-AEAD-Ratchet session expired before generating more tags +- Check if ECIES-X25519-AEAD-Ratchet session expired before generating more tags - Correct block size for delivery type local for ECIES-X25519-AEAD-Ratchet ## [2.34.0] - 2020-10-27 @@ -168,7 +287,7 @@ - Single thread for I2CP - Shared transient destination between proxies - Database lookups from ECIES destinations with ratchets response -- Handle WebDAV HTTP methods +- Handle WebDAV HTTP methods - Don't try to connect or build tunnels if offline - Validate IP when trying connect to remote peer - Handle ICMP responses and WinAPI errors for SSU @@ -185,7 +304,7 @@ - Random crashes on I2CP session disconnect - Stream through racthets hangs if first SYN was not acked - Check "Last-Modified" instead "If-Modified-Since" for addressbook reponse -- Trim behind ECIESx25519 tags +- Trim behind ECIESx25519 tags - Few bugs with Android main activity - QT visual and layout issues @@ -196,11 +315,11 @@ - Multiple encryption keys through I2CP - Pre-calculated x25519 ephemeral keys - Change datagram routing path if nothing comes back in 10 seconds -- Shared routing path for datagram session +- Shared routing path for datagram session ### Changed - UDP tunnels send mix of repliable and raw datagrams in bulk - Encrypt SSU packet again upon resend -- Start new tunnel message if remaining buffer is too small +- Start new tunnel message if remaining buffer is too small - Use LeaseSet2 for ECIES-X25519-AEAD-Ratchet automatically - Save new ECIES-X25519-AEAD-Ratchet session with NSR tagset - Generate random padding lengths for ECIES-X25519-AEAD-Ratchet in bulk @@ -208,11 +327,11 @@ - Reseed servers list ### Fixed - Don't connect through terminated SAM destination -- Differentiate UDP server sessions by port +- Differentiate UDP server sessions by port - ECIES-X25519-AEAD-Ratchet through I2CP - Don't save invalid address to AddressBook - ECDSA signatures names in SAM -- AppArmor profile +- AppArmor profile ## [2.32.1] - 2020-06-02 ### Added diff -Nru i2pd-2.39.0/contrib/android_binary_only/.gitignore i2pd-2.43.0/contrib/android_binary_only/.gitignore --- i2pd-2.39.0/contrib/android_binary_only/.gitignore 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/android_binary_only/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -gen -tests -bin -libs -log* -obj -.gradle -.idea -.externalNativeBuild -ant.properties -local.properties -build.sh -android.iml -build -gradle -gradlew -gradlew.bat - diff -Nru i2pd-2.39.0/contrib/android_binary_only/jni/Android.mk i2pd-2.43.0/contrib/android_binary_only/jni/Android.mk --- i2pd-2.39.0/contrib/android_binary_only/jni/Android.mk 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/android_binary_only/jni/Android.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE := i2pd -LOCAL_CPP_FEATURES := rtti exceptions -LOCAL_C_INCLUDES += $(IFADDRS_PATH) $(LIB_SRC_PATH) $(LIB_CLIENT_SRC_PATH) $(DAEMON_SRC_PATH) -LOCAL_STATIC_LIBRARIES := \ - boost_system \ - boost_date_time \ - boost_filesystem \ - boost_program_options \ - crypto ssl \ - miniupnpc -LOCAL_LDLIBS := -lz - -LOCAL_SRC_FILES := $(IFADDRS_PATH)/ifaddrs.c \ - $(wildcard $(LIB_SRC_PATH)/*.cpp)\ - $(wildcard $(LIB_CLIENT_SRC_PATH)/*.cpp)\ - $(DAEMON_SRC_PATH)/UnixDaemon.cpp \ - $(DAEMON_SRC_PATH)/Daemon.cpp \ - $(DAEMON_SRC_PATH)/UPnP.cpp \ - $(DAEMON_SRC_PATH)/HTTPServer.cpp \ - $(DAEMON_SRC_PATH)/I2PControl.cpp \ - $(DAEMON_SRC_PATH)/i2pd.cpp -include $(BUILD_EXECUTABLE) - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE := boost_system -LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_system.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include -include $(PREBUILT_STATIC_LIBRARY) - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE := boost_date_time -LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include -include $(PREBUILT_STATIC_LIBRARY) - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE := boost_filesystem -LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include -include $(PREBUILT_STATIC_LIBRARY) - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE := boost_program_options -LOCAL_SRC_FILES := $(BOOST_PATH)/boost-1_72_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost-1_72_0/include -include $(PREBUILT_STATIC_LIBRARY) - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE := crypto -LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/$(TARGET_ARCH_ABI)/lib/libcrypto.a -LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/include -include $(PREBUILT_STATIC_LIBRARY) - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE := ssl -LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/$(TARGET_ARCH_ABI)/lib/libssl.a -LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.1a-clang/include -LOCAL_STATIC_LIBRARIES := crypto -include $(PREBUILT_STATIC_LIBRARY) - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE := miniupnpc -LOCAL_SRC_FILES := $(MINIUPNP_PATH)/miniupnpc-2.1/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a -LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnpc-2.1/include -include $(PREBUILT_STATIC_LIBRARY) diff -Nru i2pd-2.39.0/contrib/android_binary_only/jni/Application.mk i2pd-2.43.0/contrib/android_binary_only/jni/Application.mk --- i2pd-2.39.0/contrib/android_binary_only/jni/Application.mk 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/android_binary_only/jni/Application.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -APP_ABI := all -#APP_ABI += x86 -#APP_ABI += x86_64 -#APP_ABI += armeabi-v7a -#APP_ABI += arm64-v8a -#can be android-3 but will fail for x86 since arch-x86 is not present at ndkroot/platforms/android-3/ . libz is taken from there. -APP_PLATFORM := android-14 - -NDK_TOOLCHAIN_VERSION := clang -APP_STL := c++_static - -# Enable c++17 extensions in source code -APP_CPPFLAGS += -std=c++17 -fvisibility=default -fPIE - -APP_CPPFLAGS += -DANDROID_BINARY -DANDROID -D__ANDROID__ -DUSE_UPNP -APP_LDFLAGS += -rdynamic -fPIE -pie -ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) -APP_CPPFLAGS += -DANDROID_ARM7A -endif - -# Forcing debug optimization. Use `ndk-build NDK_DEBUG=1` instead. -#APP_OPTIM := debug - -# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -b boost-1_72_0 -# git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git -# git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git -# git clone https://github.com/PurpleI2P/android-ifaddrs.git -# change to your own -I2PD_LIBS_PATH = /path/to/libraries -BOOST_PATH = $(I2PD_LIBS_PATH)/Boost-for-Android-Prebuilt -OPENSSL_PATH = $(I2PD_LIBS_PATH)/OpenSSL-for-Android-Prebuilt -MINIUPNP_PATH = $(I2PD_LIBS_PATH)/MiniUPnP-for-Android-Prebuilt -IFADDRS_PATH = $(I2PD_LIBS_PATH)/android-ifaddrs - -# don't change me -I2PD_SRC_PATH = $(PWD)/../.. - -LIB_SRC_PATH = $(I2PD_SRC_PATH)/libi2pd -LIB_CLIENT_SRC_PATH = $(I2PD_SRC_PATH)/libi2pd_client -DAEMON_SRC_PATH = $(I2PD_SRC_PATH)/daemon diff -Nru i2pd-2.39.0/contrib/android_binary_pack/build-archive i2pd-2.43.0/contrib/android_binary_pack/build-archive --- i2pd-2.39.0/contrib/android_binary_pack/build-archive 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/android_binary_pack/build-archive 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2013-2020, The PurpleI2P Project -# -# This file is part of Purple i2pd project and licensed under BSD3 -# -# See full license text in LICENSE file at top of project tree - -GITDESC=$(git describe --tags) - -declare -A ABILIST=( - ["armeabi-v7a"]="armv7l" - ["arm64-v8a"]="aarch64" - ["x86"]="x86" - ["x86_64"]="x86_64" -) - -# Remove old files and archives -if [ -d archive ]; then - rm -r archive -fi - -if [ -f ../i2pd_*_android_binary.zip ]; then - rm i2pd_*_android_binary.zip -fi - -# Prepare files for package -mkdir archive - -for ABI in "${!ABILIST[@]}"; do - if [ -f ../android_binary_only/libs/${ABI}/i2pd ]; then - cp ../android_binary_only/libs/${ABI}/i2pd archive/i2pd-${ABILIST[$ABI]} - fi -done - -cp i2pd archive/i2pd -cp -rH ../android/assets/certificates archive/ -cp -rH ../android/assets/tunnels.conf.d archive/ -cp -H ../android/assets/i2pd.conf archive/ -cp -H ../android/assets/tunnels.conf archive/ - -# Compress files -cd archive -zip -r6 ../i2pd_${GITDESC}_android_binary.zip . - -# Remove temporary folder -cd .. -rm -r archive diff -Nru i2pd-2.39.0/contrib/android_binary_pack/.gitignore i2pd-2.43.0/contrib/android_binary_pack/.gitignore --- i2pd-2.39.0/contrib/android_binary_pack/.gitignore 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/android_binary_pack/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -archive -i2pd_*_android_binary.zip diff -Nru i2pd-2.39.0/contrib/android_binary_pack/i2pd i2pd-2.43.0/contrib/android_binary_pack/i2pd --- i2pd-2.39.0/contrib/android_binary_pack/i2pd 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/android_binary_pack/i2pd 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -#!/bin/sh - -# Copyright (c) 2013-2020, The PurpleI2P Project -# -# This file is part of Purple i2pd project and licensed under BSD3 -# -# See full license text in LICENSE file at top of project tree -# -# That script written for use with Termux. - -# https://stackoverflow.com/a/246128 -SOURCE="${0}" -while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink - DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" - SOURCE="$(readlink "$SOURCE")" - [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located -done -DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" - -arch=$(uname -m) - -screenfind=$(which screen) -if [ -z $screenfind ]; then - echo "Can't find 'screen' installed. That script needs it!"; - exit 1; -fi - -if [ -z i2pd-$arch ]; then - echo "Can't find i2pd binary for your archtecture."; - exit 1; -fi - -screen -AmdS i2pd ./i2pd-$arch --datadir=$DIR diff -Nru i2pd-2.39.0/contrib/certificates/family/stormycloud.crt i2pd-2.43.0/contrib/certificates/family/stormycloud.crt --- i2pd-2.39.0/contrib/certificates/family/stormycloud.crt 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/contrib/certificates/family/stormycloud.crt 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICKDCCAc6gAwIBAgIUcPHZXtYSqGNRCD6z8gp79WUFtI0wCgYIKoZIzj0EAwIw +gZMxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEPMA0GA1UEBwwGQXVzdGlu +MRgwFgYDVQQKDA9TdG9ybXlDbG91ZCBJbmMxIzAhBgNVBAMMGnN0b3JteWNsb3Vk +LmZhbWlseS5pMnAubmV0MSQwIgYJKoZIhvcNAQkBFhVhZG1pbkBzdG9ybXljbG91 +ZC5vcmcwHhcNMjIwMzE5MTU1MjU2WhcNMzIwMzE2MTU1MjU2WjCBkzELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMQ8wDQYDVQQHDAZBdXN0aW4xGDAWBgNVBAoM +D1N0b3JteUNsb3VkIEluYzEjMCEGA1UEAwwac3Rvcm15Y2xvdWQuZmFtaWx5Lmky +cC5uZXQxJDAiBgkqhkiG9w0BCQEWFWFkbWluQHN0b3JteWNsb3VkLm9yZzBZMBMG +ByqGSM49AgEGCCqGSM49AwEHA0IABFUli0hvJEmowNjJVjbKEIWBJhqe973S4VdL +cJuA5yY3dC4Y998abWEox7/Y1BhnBbpJuiodA341bXKkLMXQy/kwCgYIKoZIzj0E +AwIDSAAwRQIgD12F/TfY3iV1/WDF7BSKgbD5g2MfELUIy1dtUlJQuJUCIQD69mZw +V1Z9j2x0ZsuirS3i6AMfVyTDj0RFS3U1jeHzIQ== +-----END CERTIFICATE----- diff -Nru i2pd-2.39.0/contrib/certificates/reseed/echelon3_at_mail.i2p.crt i2pd-2.43.0/contrib/certificates/reseed/echelon3_at_mail.i2p.crt --- i2pd-2.39.0/contrib/certificates/reseed/echelon3_at_mail.i2p.crt 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/contrib/certificates/reseed/echelon3_at_mail.i2p.crt 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFyzCCA7OgAwIBAgIRALWNWsnQ0Vmn/99iCNT7cdQwDQYJKoZIhvcNAQELBQAw +cTELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwGA1UE +ChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGjAYBgNVBAMM +EWVjaGVsb24zQG1haWwuaTJwMB4XDTIxMTEyOTE5MzU1OVoXDTMxMTEyOTE5MzU1 +OVowcTELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwG +A1UEChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGjAYBgNV +BAMMEWVjaGVsb24zQG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEA3pccNiQWJUS1t3QHK7rBCNKAsM2dz4szN3+3SrDy1w+rOrK8Vt5aypPU +QYUQwG+odjEPacuoRtO/W14KJl5yAI3eQS+X/cYDXmxvfm4zx5JRumYptXwJD57G +rlPHnFvk8R+Hvh+/UyqgSAZ9ZaKjEzYK4AtbYEXtopaM4U2VYN8xKjvKyWlhPdxo +kI3//qcTlSqGHHeHrkItLG1LubM1EnPu+9zI2WN2zBBRcm8ZtWqHoqFJ1zgJr/49 +nMK8Lnb3I54ctva8x5+gsSk4dbG/mMsOIZekFqYJJs3+u9w5fmOYI7v9GlQr7UhE +G3MwjJ5Cj1LmLVlz/4LApZrDSd2JvwIUdGL3UW8+blaTeCPKIRvmsTeRxo1gORMF +ZH0dg39722lK7ScwOlOUX9ggzRUlYCmvnjQJZGJEUoP68QxjlQfkXZyffmMfvm6K +V6mcZ5aHMGO1lYAl40kWNJ0jGpmxJqTDhNFDEKr0TlRGVxXGWzObEOrcJ8ysRMc1 +x6oXQhh79HXZcKwhZaXLx23ZvVoTfhRm4JH0SSP6XqQm35j4NI1SllEsDns29wU3 +Re4wOWJCCYlPG3CtY32CinwQRoVgtiJk18W8+Pxw7sBFq8sL5L0Z+5bB6nTkBfV6 +7OrZGWL0i344zQE0e3yIsLih+5Wyqw6RSSMysenl3alnUB9EvE0CAwEAAaNeMFww +DgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAP +BgNVHRMBAf8EBTADAQH/MBoGA1UdDgQTBBFlY2hlbG9uM0BtYWlsLmkycDANBgkq +hkiG9w0BAQsFAAOCAgEAaUMnMYtNFBl9dFON6e4EjYo53Wknj61uIVO11dvLqjnh +7X6guPML+GgNZsPQGLu7Bqw4hVgy/cV5AlFc7SXOhzpaYo1ycpjg3Ws1VK2wrk7+ +4bvUThNcS1KZVFDdRE62549rYNfYNfPxXvccOTW9meTCC1kLHerh65ySDr9J02O6 +o5Mf685PgBasBH6dlosOLTtee2gRLNFcAluQYKerawS1gDys5239UNHPCqTgO+Od +FiKfl48OIOzPGLKEf4lXC+lkwZElewShrHhzd8aGueedTi0UHOtQuY7ocsofqXc8 +OnyT/y2X6wn/YkzviKgfxYDSI7FJiUgXCPcT0jUNmuwR168yL5BfzoQmrCvlOOQg +P7ibdBJ6UkL8pRpv/SYpvaX/kf4agYtwh5IL9FzNCwNu54ZC6JilLUhYAU38Eolq +OZ/cGiMoSFQIeBPvB3cdsqEud9W4P+MqN5A76fMzdVV77lGsIS1eCGMceR3CjOiF +6SdAskcBZWhFiRNQweC0iv57/nPCeTCuNAqbZSHd7zC1AKhNmmsKSJUJQCGijcce +P8Gl0AFfZneN2bVEFvJ/zd71pD8ll1Gkju16bfdWn0V4NRaxFiXNr2bL+ah9blud +EXOomE3R6ow1QZk+Gnpy3wh9jfwlrJuFoANvHnv4WREbdjwr//71XjBri5p1wPE= +-----END CERTIFICATE----- diff -Nru i2pd-2.39.0/contrib/certificates/reseed/echelon_at_mail.i2p.crt i2pd-2.43.0/contrib/certificates/reseed/echelon_at_mail.i2p.crt --- i2pd-2.39.0/contrib/certificates/reseed/echelon_at_mail.i2p.crt 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/certificates/reseed/echelon_at_mail.i2p.crt 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFfzCCA2egAwIBAgIESg3kkzANBgkqhkiG9w0BAQ0FADBwMQswCQYDVQQGEwJY -WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt -b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEZMBcGA1UEAwwQZWNoZWxvbkBtYWls -LmkycDAeFw0xNDA3MzExNjQ3MDJaFw0yNDA3MzAxNjQ3MDJaMHAxCzAJBgNVBAYT -AlhYMQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9u -eW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRkwFwYDVQQDDBBlY2hlbG9uQG1h -aWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmcEgLwwhzLNe -XLOMSrhwB8hWpOhfjo4s6S/wjBtjjUc8nI3D0hSn3HY26p0rvcvNEWexPUpPULmC -exGkU463nu7PiFONiORI1eJAiUFHibRiaA7Wboyo38pO73KirwjG07Y+Ua0jp+HS -+4FQ/I/9H/bPplReTOU/6hmRbgQ69U8nE68HzZHQxP68yVJ2rPHSXMPhF4R1h0G1 -1mCAT+TgTsnwHNGF77XHJnY4/M4e2cgycEZjZow36C3t2mNDVkMgF19QQeb9WmLR -zREn3nq9BJqHpUkn9yWw0kKXTZSds+7UxESfzf3BzK0+hky2fh5H+qbYAo2lz4yj -81MXTAu+4RRkg4DBLlF+2dkclhwQLxxzvkRC6tPkn5i33Yltg7EfzA9IoQ05potJ -I+iOcF+aStfFgFj9u3B5UkcF4P0cH1QD3c6BK4hIezQYqRoPly1gHqg+XdwjG/dr -4as7HA9FTz3p2E8nClpIC1x3hfgwAdfd29aeBxO1WW/z99iMF7TBAF+u5T86XEW1 -WpknqCbTli36yJ8a5fPWxZHrryBRJT5yLxejjFeadtutBSwljiVFq+Y38VqwFivq -VLiBt7IxAsZ8iilgfnnnAvBH6chWfSKb4H7kB4TJvDiV96QmmvoEaWYNHZozMhyK -tO3b5w+xqbJXyCLA3Q75jD0km76hjcECAwEAAaMhMB8wHQYDVR0OBBYEFAHQcAam -QRS/EUhuCSr9pB4Ux0rYMA0GCSqGSIb3DQEBDQUAA4ICAQBq1+1QLmgLAjrTg3tb -4XKgAVICQRoBDNUEobQg3pYeUX9eFNya2RxNljuvYpwT80ilGMPOXcjddmr5ngiK -dbGRcuuJk9MPEHtPaPT3+JJlvKQ3B3g2wva2Wz2OAyLZUGQs389K4nTbwh4QF0n2 -aHFL8BHiD62hiKnCoNaW4ZovUNNvOxo9lMyAiaFU2gqQNcdad8hP9EAllbvbxDx9 -Tjww2UbwQUIHS9rna4Tlu+f0hDXTWIutc2A51W2fJCb7L3+lYO7Wv55ND/WtryLZ -XpMp27+MpuEnN3kQmz/l9R0hIJsWc/x9GQkjm5wEaIZEyTtenqwRKGmVCtAj0Pgv -jn1L3/lWmrNq+OZHb/QeyfKtA3nXfQKVmT98ewQiK/S5i1xIAXCJPytOD887b/o1 -cdurTmCiZMwgiQ+HLJqCg3MDa5mvKqRkRdZXfE6aQWEcSbpAhpV15R17q7L+Fg0W -shLSNucxyGNU8PjiC/nOmqfqUiPiMltJjPmscxBLim8foyxjakC4+6N6m+Jzgznj -PocBehFAfKYj66XEwzIBN7Z2uuXoYH9YptkocFjTzvchcryVulDWZ4FWxreUMhpM -4oyjjhSB4tB9clXlwMqg577q3D6Ms0zLTqsztyPN3zr6jGev3jpVq7Q1GOlciHPv -JNJOWTH/Vas1W6XlwGcOOAARTQ== ------END CERTIFICATE----- diff -Nru i2pd-2.39.0/contrib/certificates/reseed/hiduser0_at_mail.i2p.crt i2pd-2.43.0/contrib/certificates/reseed/hiduser0_at_mail.i2p.crt --- i2pd-2.39.0/contrib/certificates/reseed/hiduser0_at_mail.i2p.crt 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/contrib/certificates/reseed/hiduser0_at_mail.i2p.crt 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFgTCCA2mgAwIBAgIETWAY1DANBgkqhkiG9w0BAQ0FADBxMQswCQYDVQQGEwJY +WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEaMBgGA1UEAwwRaGlkdXNlcjBAbWFp +bC5pMnAwHhcNMjExMjEzMTU0MDI3WhcNMzExMjExMTU0MDI3WjBxMQswCQYDVQQG +EwJYWDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5v +bnltb3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEaMBgGA1UEAwwRaGlkdXNlcjBA +bWFpbC5pMnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXnjJ8UQ0f +lHHpfPMiHofBPSuL4sbOJY6fOXwPhSg/h6THh9DS/ZWmJXQ3qRD0glDVtv4/Dr/9 +ldGQ5eltF9iCFXCQlMEy2HjQrBKq0nsl7RpYK12cyMaod0kkzCUk9ITLi9CmHM3Z +gQZcmG8TWjFEpDR+idx/QkQt2pcO4vzWlDit3Vh4ivnbX5jGQHbsVjQEMQWxr+pX +dsS+YQpjZ6RBmrooGTPO8QDOOeYLAn0lCjmffc/kzIH9E/p4/O0rOpyhVYbdxUD1 +5wkqN9l4yrtxmORG/PudnRQQ0r4TUq8vsxfGY0Euo9IbhgXF2Parel1ZhDxB1WZV +VwWtgLIh9jGA1UMa8SYKnEfp8LWNZ3b3mUUnZb3kMrLk6jGYRWNsHmamhd4mC7AZ +qf/8lOkEIw3bPd3YguCDRVcLui5BwIEZmqXg8uoESxfO/sW3pBrN/8M7MkTex9kN +vjitGDDXvenK27qmNgZxbBlX72yTSfys7XTYTLnxZC8AwdAo2Wz9Z6HhGiPonf2h +vZkc9ZxuE0jFIrsbJra4X7iyjXgi4vV4ARNg/9Ft6F4/OIbECgeDcBQqq4TlT2bZ +EfWVrBbqXoj5vNsLigIkd+AyUNwPYEcB5IFSiiOh98pC7BH3pg0m8U5YBjxe1i+9 +EQOOG0Qtx+JigXZHu6bGE0Twy9zy+UzoKQIDAQABoyEwHzAdBgNVHQ4EFgQUGK1b +0DkL6aLalcfBc/Uj/SF08C0wDQYJKoZIhvcNAQENBQADggIBAMpXM82bJDpH1TlH +TvhU3Z7nfZdvEhOQfujaFUYiuNripuEKcFGn948+DvAG0FUN+uNlJoqOVs8D7InD +gWlA9zpqw5Cl5Hij/Wns9QbXuAHJeA23fVUoaM2A6v9ifcIQ1A+rDuRQAo6/64KW +ChTg2e99RBpfGOyqgeh7tLLe0lPPekVpKHFuXabokaKRDuBcVHcUL4tWXe3dcyqa +Ej/PJrrS+nWL0EGZ4q80CEd2LPuDzPxNGCJt/R7ZfadENWajcgcXGceh1QBzozrB +SL/Ya6wF9SrsB7V/r5wX0LM4ZdDaLWbtmUe5Op0h/ZMH25Sa8xAXVz+O9L6sWSoO +FaiYTOvAiyyPz+nsxKa3xYryDHno7eKSt+hGOcaurhxbdZaEFY/CegEc73tCt9xK +e9qF8O/WkDLmixuErw3f5en4IfzGR7p3lJAwW/8WD8C6HS39h/eE7dVZNaWgtQnZ +SgGjgZMTJqTcQ3aZmfuCZefxGFok8w6AIkdbnd1pdMBRjYu8aXgl2hQSB9ZADDE9 +R5d3rXi0PkSFLIvsNjVa5KXrZk/tB0Hpfmepq7CufBqjP/LG9TieRoXzLYUKFF74 +QRwjP+y7AJ+VDUTpY1NV1P+k+2raubU2bOnLF3zL5DtyoyieGPhyeMMvp0fRIxdg +bSl5VHgPXHNM8mcnndMAuzvl7jEK +-----END CERTIFICATE----- diff -Nru i2pd-2.39.0/contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt i2pd-2.43.0/contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt --- i2pd-2.39.0/contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIFzTCCA7WgAwIBAgIQeUqFi0fHNQopg6BZlBLhVzANBgkqhkiG9w0BAQsFADBy +MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK +ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEbMBkGA1UEAwwS +aTJwLXJlc2VlZEBtazE2LmRlMB4XDTIyMDIwNTE3MzkzM1oXDTMyMDIwNTE3Mzkz +M1owcjELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwG +A1UEChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGzAZBgNV +BAMMEmkycC1yZXNlZWRAbWsxNi5kZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBAMYxs2D2xpN/8blGawvAlU9DemHIxApOEwaLNfh8aAvqEdB41NTqcx4U +H8VchSormCfkCvezuMHO+K2HX7ihEZ1v6tbr6aX6hY9UZUyDDYsKmJoB1oKEhddv +5UYfcWPE2eSykdFsWgTQD6Z+cRQWHEoCzb7qc+Jrw6KcnHMD0VrmBrEQPzTBxMHW +4HC97PVkSLJTDArnS6ZiX4IbWRPw/mbpJT6EoVZo8J/it0pdn/X4KodEXDcnEMSe +VRulfZH/nSmOOvKhoHPckmgz/u66BlnuSYXEIB0KfDIcAlSYiPDxGnAemTozJYXA +UVMeFMs+YE5wiPgzzu+vpC31xtZLq0gyaCfgEi1P9j2ES/8pH3Gw6W2OH4kBx+jO +TBsfI+ph6qFZ3WWT23MRVyl3ATuI/GHdczTxD9JaOn74lLI+Hnu8wXnyztVWkTMB +4sAnzjdeHkvNDyQ10vSaN0HnGfg6zuAuUSqFQujFF8Vg8ZCcsh8GouWfzYDvi9mj +9pfxx8v6UCC719I4J9CgFjWnn2Hqez3fO8fFulY61VPyCCZp4gKWbI2SIQP/n5gz +ecYJRrJoem+rYfEQ/fwxROsvm3fCO4D6dt7ILRuX286GDIw2qSvP1zZVAioMwSj3 +9CAjKLwD/BhTRiMOlpaVv6IWqjtevbiaIKvbHTnoxvkGsDqe3gJhAgMBAAGjXzBd +MA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEw +DwYDVR0TAQH/BAUwAwEB/zAbBgNVHQ4EFAQSaTJwLXJlc2VlZEBtazE2LmRlMA0G +CSqGSIb3DQEBCwUAA4ICAQAb+x6XpJdjpVYw2bvWIUbatQJwq0YaEW5W61xGLgIG +a37oll3YZbSY9Vk+N1cE0f61L3ya4Ioz6zlH/MO2zUG/dEk8vqdgIPUYJvyF7wwF +w3/G4VMaDKOJx4bAZNmaiRFGYNhCOhCnZx6uZGrLNIJ2Dc+mflrGmGwYphtXVV3e +Iv+ki3gSRgfXuMfKi4B5bLPnz7XDe4TSmwZZSRac4ly4KqmZUyntqbilRxaGTej3 +VYJ1tac8yppyk5N3VopMQNmBarNZG16wSOTD7CtKgn382jgRW8cR7BMeqhORivp0 +ZnPJFhzh4uthdlPdXXo6lxfvZjfiwlDPytvEu2QBz3urTgopGqRLcTBnLucWg9li +OSy9z7hNEnIN3iIJJAwI1wBdDa7K0h3PFBbIUa7X2ybn81VeNSfO25Lo8YTZEKsc +wcThJrNV6qOQv8rM/7aXugi6+VzPlCR+18iKRbebCnlqGR2dT1zFtj3negtOkrjo +LH4H6VUr3q2Ie56IubS2hUKiUkDm0ckP3Vum35GGntyEAzl6uyog0hJFOJb3aq30 +YQLzyVEOz8NnA+32oMRzJJdDxQ7pqG5fgq7EF4d++YSgEfdVXxvfgXQ6m3jAyC7Z +p/gX4rlxNsjeGU3Ds51wkmhH4IB1aSQr52PE6RaBhhh3SmADEv6S/3eGvE4F4MN5 +2Q== +-----END CERTIFICATE----- diff -Nru i2pd-2.39.0/contrib/certificates/reseed/rambler_at_mail.i2p.crt i2pd-2.43.0/contrib/certificates/reseed/rambler_at_mail.i2p.crt --- i2pd-2.39.0/contrib/certificates/reseed/rambler_at_mail.i2p.crt 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/contrib/certificates/reseed/rambler_at_mail.i2p.crt 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFxzCCA6+gAwIBAgIQfKAV7rmoWA8jWpLfMtDQqzANBgkqhkiG9w0BAQsFADBw +MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK +ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEZMBcGA1UEAwwQ +cmFtYmxlckBtYWlsLmkycDAeFw0yMTExMDYwNzEwMzJaFw0zMTExMDYwNzEwMzJa +MHAxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgxHjAcBgNV +BAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRkwFwYDVQQD +DBByYW1ibGVyQG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEAz4vQlIdjY56uqkFKWld9Oy3E8+06Ag9fUzBVleS2bdJfaFtmEa8xz6Pep7Bb +zJK0Q9t2CW7/xqIWuspWlYn5EYAS7BFiNOX70KX4PMpltj3C4Dpxpjll9LdydU2k +FquCflXNJESnBDdd0qDRMboMf4c9lTz0mTLwAtzInLwHGDrbxEiQ/YqPgPJreOXQ +anhjkpxJcgpLR+9od8EdLNKbShVWEeSBnYp0FcjnZKOb9KC2gjqP0sWdzlw3i1hh +CB38A7a03Q4yUcmxCw4ktM60d/2jCZ+G7KHwcbkfxDjl85r0UgEzgfF7LuIuxxmA +MNLH1eAACnLTl42O72EHdtD9VWWwZF2NuFgAzT3MEFnMKDk+OqZOeZQOEgkIfrNP +O5XYMYxHSWCf/dmSq36ZJwhC40k2S9ArS8BQNY8NvwZG5CSGDU52FKaHzFn6EwLE +4CpsrptUX2itXLaFUiNMw6I+eSgTO7x+gpahZVqpdRSQXmpE0xA5jP/DwPyt3ZVe +/4q4kn3imcSCxBP5NQHWfVszsruRkh9np4R0xVlT8UCwJmY8Yg8zwJG5UddTAck5 +JavDsaXgWMwcZ/qQboZKlH/iAdQnbkte8Yd5GL5nmTeS+vwuluwmA/y9kUzSUhk+ +86kA0eRJ1+e2HdA1/UOTRmyIoIeQ5/fhELMXzhksLcpMGTUCAwEAAaNdMFswDgYD +VR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNV +HRMBAf8EBTADAQH/MBkGA1UdDgQSBBByYW1ibGVyQG1haWwuaTJwMA0GCSqGSIb3 +DQEBCwUAA4ICAQAxRdSTZGEblnNeVuRoEQq/a/6q4egFaOkzXCPKEnDzB5yvm83g +35ImquGFZkgaoc5qUAHVeBwOQrWgUI4xHPofnbM2VsgEUMz6h3ovobPNkN3+lRT5 +30krd0y+A/Q895EHDu0lyf3BHMmtCWiKWQBttuc0dnmoLCRsQxgy+kYJCS/81jCM +4KNnyrtc6a/czqSq758CncjP2nErVucendsguQoA5JUw53YJ4FYHG/f9tYEkhm9C +D6u7L3vTUcMRUrRxSiJyNixH36nEwpM6DNHiPNc+CFKZ/Zx449R1GjcpDhTrXnWP +2H1r3cyKEM8a76VUEs2GQCaaglOR4N1goyqgYEjScf+/4VmARL3VUzfP8Oub70rM +t1fip5QD/4VDQuA/9C9g5Rr2nJ3K2jVnpSSKnBYFYf5z9RZdTOVXjXaEi72lWxpk +mjgK6c5EFOJxYoCaTbKX9Kz9ZIWVOVMrgHWwA/wDW+Qk5zgP9Ysau65xIp9P1RdB +qHgR5BcIrNky9RD8cIzxzMPCSMVgnf0eLFuHmG8uUl/xHHVRprf0pd7DYkQ44HWN +Z/g/gg3DaJdH7vvkShzgjt4iZrmOCHQIKkSGFRYZf0/Mpn6mgK9+grtO9osVgAQr +LBO+5LIxV/S5bcrzWQLOiMABTd2X/0PTOjuXpfinZ3rDSUiNFPq5kLLSlA== +-----END CERTIFICATE----- diff -Nru i2pd-2.39.0/contrib/i18n/English.po i2pd-2.43.0/contrib/i18n/English.po --- i2pd-2.39.0/contrib/i18n/English.po 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/i18n/English.po 2022-08-21 19:40:41.000000000 +0000 @@ -1,13 +1,13 @@ # i2pd -# Copyright (C) 2021 PurpleI2P team +# Copyright (C) 2021-2022 PurpleI2P team # This file is distributed under the same license as the i2pd package. -# R4SAS , 2021. +# R4SAS , 2021-2022. # msgid "" msgstr "" "Project-Id-Version: i2pd\n" "Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n" -"POT-Creation-Date: 2021-08-06 17:12\n" +"POT-Creation-Date: 2022-07-26 21:22\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -18,706 +18,712 @@ "X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n" "X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n" -#: daemon/HTTPServer.cpp:177 +#: daemon/HTTPServer.cpp:108 msgid "day" msgid_plural "days" msgstr[0] "" msgstr[1] "" -#: daemon/HTTPServer.cpp:181 +#: daemon/HTTPServer.cpp:112 msgid "hour" msgid_plural "hours" msgstr[0] "" msgstr[1] "" -#: daemon/HTTPServer.cpp:185 +#: daemon/HTTPServer.cpp:116 msgid "minute" msgid_plural "minutes" msgstr[0] "" msgstr[1] "" -#: daemon/HTTPServer.cpp:188 +#: daemon/HTTPServer.cpp:119 msgid "second" msgid_plural "seconds" msgstr[0] "" msgstr[1] "" #. tr: Kibibit -#: daemon/HTTPServer.cpp:196 daemon/HTTPServer.cpp:224 +#: daemon/HTTPServer.cpp:127 daemon/HTTPServer.cpp:155 msgid "KiB" msgstr "" #. tr: Mebibit -#: daemon/HTTPServer.cpp:198 +#: daemon/HTTPServer.cpp:129 msgid "MiB" msgstr "" #. tr: Gibibit -#: daemon/HTTPServer.cpp:200 +#: daemon/HTTPServer.cpp:131 msgid "GiB" msgstr "" -#: daemon/HTTPServer.cpp:217 +#: daemon/HTTPServer.cpp:148 msgid "building" msgstr "" -#: daemon/HTTPServer.cpp:218 +#: daemon/HTTPServer.cpp:149 msgid "failed" msgstr "" -#: daemon/HTTPServer.cpp:219 +#: daemon/HTTPServer.cpp:150 msgid "expiring" msgstr "" -#: daemon/HTTPServer.cpp:220 +#: daemon/HTTPServer.cpp:151 msgid "established" msgstr "" -#: daemon/HTTPServer.cpp:221 +#: daemon/HTTPServer.cpp:152 msgid "unknown" msgstr "" -#: daemon/HTTPServer.cpp:223 +#: daemon/HTTPServer.cpp:154 msgid "exploratory" msgstr "" -#: daemon/HTTPServer.cpp:259 +#. tr: Webconsole page title +#: daemon/HTTPServer.cpp:185 +msgid "Purple I2P Webconsole" +msgstr "" + +#: daemon/HTTPServer.cpp:190 msgid "i2pd webconsole" msgstr "" -#: daemon/HTTPServer.cpp:262 +#: daemon/HTTPServer.cpp:193 msgid "Main page" msgstr "" -#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:725 +#: daemon/HTTPServer.cpp:194 daemon/HTTPServer.cpp:700 msgid "Router commands" msgstr "" -#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:448 -#: daemon/HTTPServer.cpp:460 +#: daemon/HTTPServer.cpp:195 daemon/HTTPServer.cpp:382 +#: daemon/HTTPServer.cpp:394 msgid "Local Destinations" msgstr "" -#: 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 +#: daemon/HTTPServer.cpp:197 daemon/HTTPServer.cpp:352 +#: daemon/HTTPServer.cpp:438 daemon/HTTPServer.cpp:444 +#: daemon/HTTPServer.cpp:597 daemon/HTTPServer.cpp:640 +#: daemon/HTTPServer.cpp:644 msgid "LeaseSets" msgstr "" -#: daemon/HTTPServer.cpp:268 daemon/HTTPServer.cpp:694 +#: daemon/HTTPServer.cpp:199 daemon/HTTPServer.cpp:650 msgid "Tunnels" msgstr "" -#: daemon/HTTPServer.cpp:269 daemon/HTTPServer.cpp:425 -#: daemon/HTTPServer.cpp:787 daemon/HTTPServer.cpp:803 +#: daemon/HTTPServer.cpp:201 daemon/HTTPServer.cpp:359 +#: daemon/HTTPServer.cpp:770 daemon/HTTPServer.cpp:786 msgid "Transit Tunnels" msgstr "" -#: daemon/HTTPServer.cpp:270 daemon/HTTPServer.cpp:852 +#: daemon/HTTPServer.cpp:203 daemon/HTTPServer.cpp:839 msgid "Transports" msgstr "" -#: daemon/HTTPServer.cpp:271 +#: daemon/HTTPServer.cpp:204 msgid "I2P tunnels" msgstr "" -#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:914 -#: daemon/HTTPServer.cpp:924 +#: daemon/HTTPServer.cpp:206 daemon/HTTPServer.cpp:908 +#: daemon/HTTPServer.cpp:918 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 +#: daemon/HTTPServer.cpp:222 daemon/HTTPServer.cpp:1302 +#: daemon/HTTPServer.cpp:1305 daemon/HTTPServer.cpp:1308 +#: daemon/HTTPServer.cpp:1322 daemon/HTTPServer.cpp:1367 +#: daemon/HTTPServer.cpp:1370 daemon/HTTPServer.cpp:1373 msgid "ERROR" msgstr "" -#: daemon/HTTPServer.cpp:296 +#: daemon/HTTPServer.cpp:229 msgid "OK" msgstr "" -#: daemon/HTTPServer.cpp:297 +#: daemon/HTTPServer.cpp:230 msgid "Testing" msgstr "" -#: daemon/HTTPServer.cpp:298 +#: daemon/HTTPServer.cpp:231 msgid "Firewalled" msgstr "" -#: daemon/HTTPServer.cpp:299 daemon/HTTPServer.cpp:320 -#: daemon/HTTPServer.cpp:406 +#: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:253 +#: daemon/HTTPServer.cpp:325 msgid "Unknown" msgstr "" -#: daemon/HTTPServer.cpp:300 daemon/HTTPServer.cpp:435 -#: daemon/HTTPServer.cpp:436 daemon/HTTPServer.cpp:982 -#: daemon/HTTPServer.cpp:991 +#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:369 +#: daemon/HTTPServer.cpp:370 daemon/HTTPServer.cpp:976 +#: daemon/HTTPServer.cpp:985 msgid "Proxy" msgstr "" -#: daemon/HTTPServer.cpp:301 +#: daemon/HTTPServer.cpp:234 msgid "Mesh" msgstr "" -#: daemon/HTTPServer.cpp:304 +#: daemon/HTTPServer.cpp:237 msgid "Error" msgstr "" -#: daemon/HTTPServer.cpp:308 +#: daemon/HTTPServer.cpp:241 msgid "Clock skew" msgstr "" -#: daemon/HTTPServer.cpp:311 +#: daemon/HTTPServer.cpp:244 msgid "Offline" msgstr "" -#: daemon/HTTPServer.cpp:314 +#: daemon/HTTPServer.cpp:247 msgid "Symmetric NAT" msgstr "" -#: daemon/HTTPServer.cpp:326 +#: daemon/HTTPServer.cpp:259 msgid "Uptime" msgstr "" -#: daemon/HTTPServer.cpp:329 +#: daemon/HTTPServer.cpp:262 msgid "Network status" msgstr "" -#: daemon/HTTPServer.cpp:334 +#: daemon/HTTPServer.cpp:267 msgid "Network status v6" msgstr "" -#: daemon/HTTPServer.cpp:340 daemon/HTTPServer.cpp:347 +#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:280 msgid "Stopping in" msgstr "" -#: daemon/HTTPServer.cpp:354 +#: daemon/HTTPServer.cpp:287 msgid "Family" msgstr "" -#: daemon/HTTPServer.cpp:355 +#: daemon/HTTPServer.cpp:288 msgid "Tunnel creation success rate" msgstr "" -#: daemon/HTTPServer.cpp:356 +#: daemon/HTTPServer.cpp:289 msgid "Received" msgstr "" #. tr: Kibibit/s -#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:361 -#: daemon/HTTPServer.cpp:364 +#: daemon/HTTPServer.cpp:291 daemon/HTTPServer.cpp:294 +#: daemon/HTTPServer.cpp:297 msgid "KiB/s" msgstr "" -#: daemon/HTTPServer.cpp:359 +#: daemon/HTTPServer.cpp:292 msgid "Sent" msgstr "" -#: daemon/HTTPServer.cpp:362 +#: daemon/HTTPServer.cpp:295 msgid "Transit" msgstr "" -#: daemon/HTTPServer.cpp:365 +#: daemon/HTTPServer.cpp:298 msgid "Data path" msgstr "" -#: daemon/HTTPServer.cpp:368 +#: daemon/HTTPServer.cpp:301 msgid "Hidden content. Press on text to see." msgstr "" -#: daemon/HTTPServer.cpp:371 +#: daemon/HTTPServer.cpp:304 msgid "Router Ident" msgstr "" -#: daemon/HTTPServer.cpp:373 +#: daemon/HTTPServer.cpp:306 msgid "Router Family" msgstr "" -#: daemon/HTTPServer.cpp:374 +#: daemon/HTTPServer.cpp:307 msgid "Router Caps" msgstr "" -#: daemon/HTTPServer.cpp:375 +#: daemon/HTTPServer.cpp:308 msgid "Version" msgstr "" -#: daemon/HTTPServer.cpp:376 +#: daemon/HTTPServer.cpp:309 msgid "Our external address" msgstr "" -#: daemon/HTTPServer.cpp:384 +#: daemon/HTTPServer.cpp:337 msgid "supported" msgstr "" -#: daemon/HTTPServer.cpp:416 +#: daemon/HTTPServer.cpp:350 msgid "Routers" msgstr "" -#: daemon/HTTPServer.cpp:417 +#: daemon/HTTPServer.cpp:351 msgid "Floodfills" msgstr "" -#: daemon/HTTPServer.cpp:424 daemon/HTTPServer.cpp:968 +#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:962 msgid "Client Tunnels" msgstr "" -#: daemon/HTTPServer.cpp:434 +#: daemon/HTTPServer.cpp:368 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 +#: daemon/HTTPServer.cpp:369 daemon/HTTPServer.cpp:370 +#: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:372 +#: daemon/HTTPServer.cpp:373 daemon/HTTPServer.cpp:374 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 +#: daemon/HTTPServer.cpp:369 daemon/HTTPServer.cpp:370 +#: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:372 +#: daemon/HTTPServer.cpp:373 daemon/HTTPServer.cpp:374 msgid "Disabled" msgstr "" -#: daemon/HTTPServer.cpp:483 +#: daemon/HTTPServer.cpp:417 msgid "Encrypted B33 address" msgstr "" -#: daemon/HTTPServer.cpp:492 +#: daemon/HTTPServer.cpp:426 msgid "Address registration line" msgstr "" -#: daemon/HTTPServer.cpp:497 +#: daemon/HTTPServer.cpp:431 msgid "Domain" msgstr "" -#: daemon/HTTPServer.cpp:498 +#: daemon/HTTPServer.cpp:432 msgid "Generate" msgstr "" -#: daemon/HTTPServer.cpp:499 +#: daemon/HTTPServer.cpp:433 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:505 +#: daemon/HTTPServer.cpp:439 msgid "Address" msgstr "" -#: daemon/HTTPServer.cpp:505 +#: daemon/HTTPServer.cpp:439 msgid "Type" msgstr "" -#: daemon/HTTPServer.cpp:505 +#: daemon/HTTPServer.cpp:439 msgid "EncType" msgstr "" -#: daemon/HTTPServer.cpp:515 daemon/HTTPServer.cpp:699 +#: daemon/HTTPServer.cpp:449 daemon/HTTPServer.cpp:655 msgid "Inbound tunnels" msgstr "" #. tr: Milliseconds -#: daemon/HTTPServer.cpp:520 daemon/HTTPServer.cpp:530 -#: daemon/HTTPServer.cpp:704 daemon/HTTPServer.cpp:714 +#: daemon/HTTPServer.cpp:464 daemon/HTTPServer.cpp:484 +#: daemon/HTTPServer.cpp:669 daemon/HTTPServer.cpp:689 msgid "ms" msgstr "" -#: daemon/HTTPServer.cpp:525 daemon/HTTPServer.cpp:709 +#: daemon/HTTPServer.cpp:469 daemon/HTTPServer.cpp:674 msgid "Outbound tunnels" msgstr "" -#: daemon/HTTPServer.cpp:537 +#: daemon/HTTPServer.cpp:491 msgid "Tags" msgstr "" -#: daemon/HTTPServer.cpp:537 +#: daemon/HTTPServer.cpp:491 msgid "Incoming" msgstr "" -#: daemon/HTTPServer.cpp:544 daemon/HTTPServer.cpp:547 +#: daemon/HTTPServer.cpp:498 daemon/HTTPServer.cpp:501 msgid "Outgoing" msgstr "" -#: daemon/HTTPServer.cpp:545 daemon/HTTPServer.cpp:561 +#: daemon/HTTPServer.cpp:499 daemon/HTTPServer.cpp:515 msgid "Destination" msgstr "" -#: daemon/HTTPServer.cpp:545 +#: daemon/HTTPServer.cpp:499 msgid "Amount" msgstr "" -#: daemon/HTTPServer.cpp:552 +#: daemon/HTTPServer.cpp:506 msgid "Incoming Tags" msgstr "" -#: daemon/HTTPServer.cpp:560 daemon/HTTPServer.cpp:563 +#: daemon/HTTPServer.cpp:514 daemon/HTTPServer.cpp:517 msgid "Tags sessions" msgstr "" -#: daemon/HTTPServer.cpp:561 +#: daemon/HTTPServer.cpp:515 msgid "Status" msgstr "" -#: daemon/HTTPServer.cpp:570 daemon/HTTPServer.cpp:626 +#: daemon/HTTPServer.cpp:524 daemon/HTTPServer.cpp:582 msgid "Local Destination" msgstr "" -#: daemon/HTTPServer.cpp:580 daemon/HTTPServer.cpp:947 +#: daemon/HTTPServer.cpp:535 daemon/HTTPServer.cpp:941 msgid "Streams" msgstr "" -#: daemon/HTTPServer.cpp:602 +#: daemon/HTTPServer.cpp:558 msgid "Close stream" msgstr "" -#: daemon/HTTPServer.cpp:631 +#: daemon/HTTPServer.cpp:587 msgid "I2CP session not found" msgstr "" -#: daemon/HTTPServer.cpp:634 +#: daemon/HTTPServer.cpp:590 msgid "I2CP is not enabled" msgstr "" -#: daemon/HTTPServer.cpp:660 +#: daemon/HTTPServer.cpp:616 msgid "Invalid" msgstr "" -#: daemon/HTTPServer.cpp:663 +#: daemon/HTTPServer.cpp:619 msgid "Store type" msgstr "" -#: daemon/HTTPServer.cpp:664 +#: daemon/HTTPServer.cpp:620 msgid "Expires" msgstr "" -#: daemon/HTTPServer.cpp:669 +#: daemon/HTTPServer.cpp:625 msgid "Non Expired Leases" msgstr "" -#: daemon/HTTPServer.cpp:672 +#: daemon/HTTPServer.cpp:628 msgid "Gateway" msgstr "" -#: daemon/HTTPServer.cpp:673 +#: daemon/HTTPServer.cpp:629 msgid "TunnelID" msgstr "" -#: daemon/HTTPServer.cpp:674 +#: daemon/HTTPServer.cpp:630 msgid "EndDate" msgstr "" -#: daemon/HTTPServer.cpp:684 +#: daemon/HTTPServer.cpp:640 msgid "not floodfill" msgstr "" -#: daemon/HTTPServer.cpp:695 +#: daemon/HTTPServer.cpp:651 msgid "Queue size" msgstr "" -#: daemon/HTTPServer.cpp:726 +#: daemon/HTTPServer.cpp:701 msgid "Run peer test" msgstr "" -#: daemon/HTTPServer.cpp:731 +#: daemon/HTTPServer.cpp:706 msgid "Decline transit tunnels" msgstr "" -#: daemon/HTTPServer.cpp:733 +#: daemon/HTTPServer.cpp:708 msgid "Accept transit tunnels" msgstr "" -#: daemon/HTTPServer.cpp:737 daemon/HTTPServer.cpp:742 +#: daemon/HTTPServer.cpp:712 daemon/HTTPServer.cpp:717 msgid "Cancel graceful shutdown" msgstr "" -#: daemon/HTTPServer.cpp:739 daemon/HTTPServer.cpp:744 +#: daemon/HTTPServer.cpp:714 daemon/HTTPServer.cpp:719 msgid "Start graceful shutdown" msgstr "" -#: daemon/HTTPServer.cpp:747 +#: daemon/HTTPServer.cpp:722 msgid "Force shutdown" msgstr "" -#: daemon/HTTPServer.cpp:748 +#: daemon/HTTPServer.cpp:723 msgid "Reload external CSS styles" msgstr "" -#: daemon/HTTPServer.cpp:751 +#: daemon/HTTPServer.cpp:726 msgid "" "Note: any action done here are not persistent and not changes your " "config files." msgstr "" -#: daemon/HTTPServer.cpp:753 +#: daemon/HTTPServer.cpp:728 msgid "Logging level" msgstr "" -#: daemon/HTTPServer.cpp:761 +#: daemon/HTTPServer.cpp:736 msgid "Transit tunnels limit" msgstr "" -#: daemon/HTTPServer.cpp:766 daemon/HTTPServer.cpp:778 +#: daemon/HTTPServer.cpp:741 daemon/HTTPServer.cpp:760 msgid "Change" msgstr "" -#: daemon/HTTPServer.cpp:770 +#: daemon/HTTPServer.cpp:748 msgid "Change language" msgstr "" -#: daemon/HTTPServer.cpp:803 +#: daemon/HTTPServer.cpp:786 msgid "no transit tunnels currently built" msgstr "" -#: daemon/HTTPServer.cpp:908 daemon/HTTPServer.cpp:931 +#: daemon/HTTPServer.cpp:902 daemon/HTTPServer.cpp:925 msgid "SAM disabled" msgstr "" -#: daemon/HTTPServer.cpp:924 +#: daemon/HTTPServer.cpp:918 msgid "no sessions currently running" msgstr "" -#: daemon/HTTPServer.cpp:937 +#: daemon/HTTPServer.cpp:931 msgid "SAM session not found" msgstr "" -#: daemon/HTTPServer.cpp:942 +#: daemon/HTTPServer.cpp:936 msgid "SAM Session" msgstr "" -#: daemon/HTTPServer.cpp:999 +#: daemon/HTTPServer.cpp:993 msgid "Server Tunnels" msgstr "" -#: daemon/HTTPServer.cpp:1015 +#: daemon/HTTPServer.cpp:1009 msgid "Client Forwards" msgstr "" -#: daemon/HTTPServer.cpp:1029 +#: daemon/HTTPServer.cpp:1023 msgid "Server Forwards" msgstr "" -#: daemon/HTTPServer.cpp:1227 +#: daemon/HTTPServer.cpp:1223 msgid "Unknown page" msgstr "" -#: daemon/HTTPServer.cpp:1246 +#: daemon/HTTPServer.cpp:1242 msgid "Invalid token" msgstr "" -#: daemon/HTTPServer.cpp:1304 daemon/HTTPServer.cpp:1361 -#: daemon/HTTPServer.cpp:1401 +#: daemon/HTTPServer.cpp:1300 daemon/HTTPServer.cpp:1357 +#: daemon/HTTPServer.cpp:1397 msgid "SUCCESS" msgstr "" -#: daemon/HTTPServer.cpp:1304 +#: daemon/HTTPServer.cpp:1300 msgid "Stream closed" msgstr "" -#: daemon/HTTPServer.cpp:1306 +#: daemon/HTTPServer.cpp:1302 msgid "Stream not found or already was closed" msgstr "" -#: daemon/HTTPServer.cpp:1309 +#: daemon/HTTPServer.cpp:1305 msgid "Destination not found" msgstr "" -#: daemon/HTTPServer.cpp:1312 +#: daemon/HTTPServer.cpp:1308 msgid "StreamID can't be null" msgstr "" -#: daemon/HTTPServer.cpp:1314 daemon/HTTPServer.cpp:1379 +#: daemon/HTTPServer.cpp:1310 daemon/HTTPServer.cpp:1375 msgid "Return to destination page" msgstr "" -#: daemon/HTTPServer.cpp:1315 daemon/HTTPServer.cpp:1328 -#: daemon/HTTPServer.cpp:1403 +#: daemon/HTTPServer.cpp:1311 daemon/HTTPServer.cpp:1324 +#: daemon/HTTPServer.cpp:1399 msgid "You will be redirected in 5 seconds" msgstr "" -#: daemon/HTTPServer.cpp:1326 +#: daemon/HTTPServer.cpp:1322 msgid "Transit tunnels count must not exceed 65535" msgstr "" -#: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1402 +#: daemon/HTTPServer.cpp:1323 daemon/HTTPServer.cpp:1398 msgid "Back to commands list" msgstr "" -#: daemon/HTTPServer.cpp:1363 +#: daemon/HTTPServer.cpp:1359 msgid "Register at reg.i2p" msgstr "" -#: daemon/HTTPServer.cpp:1364 +#: daemon/HTTPServer.cpp:1360 msgid "Description" msgstr "" -#: daemon/HTTPServer.cpp:1364 +#: daemon/HTTPServer.cpp:1360 msgid "A bit information about service on domain" msgstr "" -#: daemon/HTTPServer.cpp:1365 +#: daemon/HTTPServer.cpp:1361 msgid "Submit" msgstr "" -#: daemon/HTTPServer.cpp:1371 +#: daemon/HTTPServer.cpp:1367 msgid "Domain can't end with .b32.i2p" msgstr "" -#: daemon/HTTPServer.cpp:1374 +#: daemon/HTTPServer.cpp:1370 msgid "Domain must end with .i2p" msgstr "" -#: daemon/HTTPServer.cpp:1377 +#: daemon/HTTPServer.cpp:1373 msgid "Such destination is not found" msgstr "" -#: daemon/HTTPServer.cpp:1397 +#: daemon/HTTPServer.cpp:1393 msgid "Unknown command" msgstr "" -#: daemon/HTTPServer.cpp:1401 +#: daemon/HTTPServer.cpp:1397 msgid "Command accepted" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:157 +#: libi2pd_client/HTTPProxy.cpp:163 msgid "Proxy error" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:165 +#: libi2pd_client/HTTPProxy.cpp:171 msgid "Proxy info" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:173 +#: libi2pd_client/HTTPProxy.cpp:179 msgid "Proxy error: Host not found" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:174 +#: libi2pd_client/HTTPProxy.cpp:180 msgid "Remote host not found in router's addressbook" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:175 +#: libi2pd_client/HTTPProxy.cpp:181 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:322 libi2pd_client/HTTPProxy.cpp:365 +#: libi2pd_client/HTTPProxy.cpp:282 libi2pd_client/HTTPProxy.cpp:297 +#: libi2pd_client/HTTPProxy.cpp:331 libi2pd_client/HTTPProxy.cpp:372 msgid "Invalid request" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:273 +#: libi2pd_client/HTTPProxy.cpp:282 msgid "Proxy unable to parse your request" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:288 +#: libi2pd_client/HTTPProxy.cpp:297 msgid "addresshelper is not supported" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:297 libi2pd_client/HTTPProxy.cpp:306 -#: libi2pd_client/HTTPProxy.cpp:385 +#: libi2pd_client/HTTPProxy.cpp:306 libi2pd_client/HTTPProxy.cpp:315 +#: libi2pd_client/HTTPProxy.cpp:392 msgid "Host" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:297 +#: libi2pd_client/HTTPProxy.cpp:306 msgid "added to router's addressbook from helper" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:298 +#: libi2pd_client/HTTPProxy.cpp:307 msgid "Click here to proceed:" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308 +#: libi2pd_client/HTTPProxy.cpp:307 libi2pd_client/HTTPProxy.cpp:317 msgid "Continue" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309 +#: libi2pd_client/HTTPProxy.cpp:308 libi2pd_client/HTTPProxy.cpp:318 msgid "Addresshelper found" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:306 +#: libi2pd_client/HTTPProxy.cpp:315 msgid "already in router's addressbook" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:307 +#. tr: The "record" means addressbook's record. That message appears when domain was already added to addressbook, but helper link is opened for it. +#: libi2pd_client/HTTPProxy.cpp:316 msgid "Click here to update record:" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:322 +#: libi2pd_client/HTTPProxy.cpp:331 msgid "invalid request uri" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:365 +#: libi2pd_client/HTTPProxy.cpp:372 msgid "Can't detect destination host from request" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:382 libi2pd_client/HTTPProxy.cpp:386 +#: libi2pd_client/HTTPProxy.cpp:389 libi2pd_client/HTTPProxy.cpp:393 msgid "Outproxy failure" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:382 +#: libi2pd_client/HTTPProxy.cpp:389 msgid "bad outproxy settings" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:385 +#: libi2pd_client/HTTPProxy.cpp:392 msgid "not inside I2P network, but outproxy is not enabled" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:474 +#: libi2pd_client/HTTPProxy.cpp:482 msgid "unknown outproxy url" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:480 +#: libi2pd_client/HTTPProxy.cpp:490 msgid "cannot resolve upstream proxy" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:488 +#: libi2pd_client/HTTPProxy.cpp:498 msgid "hostname too long" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:515 +#: libi2pd_client/HTTPProxy.cpp:525 msgid "cannot connect to upstream socks proxy" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:521 +#: libi2pd_client/HTTPProxy.cpp:531 msgid "Cannot negotiate with socks proxy" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:563 +#: libi2pd_client/HTTPProxy.cpp:573 msgid "CONNECT error" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:563 +#: libi2pd_client/HTTPProxy.cpp:573 msgid "Failed to Connect" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:574 libi2pd_client/HTTPProxy.cpp:600 +#: libi2pd_client/HTTPProxy.cpp:584 libi2pd_client/HTTPProxy.cpp:610 msgid "socks proxy error" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:582 +#: libi2pd_client/HTTPProxy.cpp:592 msgid "failed to send request to upstream" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:603 +#: libi2pd_client/HTTPProxy.cpp:613 msgid "No Reply From socks proxy" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:610 +#: libi2pd_client/HTTPProxy.cpp:620 msgid "cannot connect" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:610 +#: libi2pd_client/HTTPProxy.cpp:620 msgid "http out proxy not implemented" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:611 +#: libi2pd_client/HTTPProxy.cpp:621 msgid "cannot connect to upstream http proxy" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:644 +#: libi2pd_client/HTTPProxy.cpp:654 msgid "Host is down" msgstr "" -#: libi2pd_client/HTTPProxy.cpp:644 +#: libi2pd_client/HTTPProxy.cpp:654 msgid "" "Can't create connection to requested host, it may be down. Please try again " "later." diff -Nru i2pd-2.39.0/contrib/i18n/README.md i2pd-2.43.0/contrib/i18n/README.md --- i2pd-2.39.0/contrib/i18n/README.md 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/i18n/README.md 2022-08-21 19:40:41.000000000 +0000 @@ -9,7 +9,7 @@ --- ``` -in: msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\nmsgstr\[1\]\ \"(.*)\"\n(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)? +in: msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\n(msgstr\[1\]\ \"(.*)\"\n)?(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)? out: #{"$2", {"$3", "$4", "$6", "$8", "$10"}},\n ``` diff -Nru i2pd-2.39.0/contrib/i2pd.conf i2pd-2.43.0/contrib/i2pd.conf --- i2pd-2.39.0/contrib/i2pd.conf 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/i2pd.conf 2022-08-21 19:40:41.000000000 +0000 @@ -76,12 +76,14 @@ ipv6 = false ## Enable SSU transport (default = true) -# ssu = true +ssu = false ## Bandwidth configuration ## L limit bandwidth to 32KBs/sec, O - to 256KBs/sec, P - to 2048KBs/sec, ## X - unlimited -## Default is X for floodfill, L for regular node +## Default is L (regular node) and X if floodfill mode enabled. If you want to +## share more bandwidth without floodfill mode, uncomment that line and adjust +## value to your possibilities # bandwidth = L ## Max % of bandwidth limit for transit. 0-100. 100 by default # share = 100 @@ -94,6 +96,22 @@ ## Note: that mode uses much more network connections and CPU! # floodfill = true +[ntcp2] +## Enable NTCP2 transport (default = true) +# enabled = true +## Publish address in RouterInfo (default = true) +# published = true +## Port for incoming connections (default is global port option value) +# port = 4567 + +[ssu2] +## Enable SSU2 transport (default = false for 2.43.0) +enabled = true +## Publish address in RouterInfo (default = false for 2.43.0) +published = true +## Port for incoming connections (default is global port option value or port + 1 if SSU is enabled) +# port = 4567 + [http] ## Web Console settings ## Uncomment and set to 'false' to disable Web Console @@ -108,7 +126,8 @@ # user = i2pd # pass = changeme ## Select webconsole language -## Currently supported english (default), afrikaans, russian, turkmen, ukrainian and uzbek languages +## Currently supported english (default), afrikaans, armenian, chinese, french, +## german, russian, turkmen, ukrainian and uzbek languages # lang = english [httpproxy] diff -Nru i2pd-2.39.0/contrib/rpm/i2pd-git.spec i2pd-2.43.0/contrib/rpm/i2pd-git.spec --- i2pd-2.39.0/contrib/rpm/i2pd-git.spec 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/rpm/i2pd-git.spec 2022-08-21 19:40:41.000000000 +0000 @@ -1,7 +1,7 @@ %define git_hash %(git rev-parse HEAD | cut -c -7) Name: i2pd-git -Version: 2.39.0 +Version: 2.43.0 Release: git%{git_hash}%{?dist} Summary: I2P router written in C++ Conflicts: i2pd @@ -32,7 +32,7 @@ C++ implementation of I2P. %prep -%setup -q +%setup -q -n i2pd-openssl %build @@ -57,7 +57,11 @@ %endif -%if 0%{?fedora} >= 36 +%if 0%{?rhel} == 9 +pushd redhat-linux-build +%endif + +%if 0%{?fedora} >= 35 pushd redhat-linux-build %else %if 0%{?fedora} >= 33 @@ -71,6 +75,10 @@ make %{?_smp_mflags} +%if 0%{?rhel} == 9 +popd +%endif + %if 0%{?fedora} >= 33 popd %endif @@ -82,7 +90,11 @@ %install pushd build -%if 0%{?fedora} >= 36 +%if 0%{?rhel} == 9 +pushd redhat-linux-build +%endif + +%if 0%{?fedora} >= 35 pushd redhat-linux-build %else %if 0%{?fedora} >= 33 @@ -99,14 +111,14 @@ %{__install} -d -m 755 %{buildroot}%{_datadir}/i2pd %{__install} -d -m 700 %{buildroot}%{_sharedstatedir}/i2pd %{__install} -d -m 700 %{buildroot}%{_localstatedir}/log/i2pd -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/contrib/i2pd.conf %{buildroot}%{_sysconfdir}/i2pd/i2pd.conf -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/contrib/subscriptions.txt %{buildroot}%{_sysconfdir}/i2pd/subscriptions.txt -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/contrib/tunnels.conf %{buildroot}%{_sysconfdir}/i2pd/tunnels.conf -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/contrib/i2pd.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/i2pd -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/contrib/i2pd.service %{buildroot}%{_unitdir}/i2pd.service -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/debian/i2pd.1 %{buildroot}%{_mandir}/man1/i2pd.1 -%{__cp} -r %{_builddir}/%{name}-%{version}/contrib/certificates/ %{buildroot}%{_datadir}/i2pd/certificates -%{__cp} -r %{_builddir}/%{name}-%{version}/contrib/tunnels.d/ %{buildroot}%{_sysconfdir}/i2pd/tunnels.conf.d +%{__install} -D -m 644 %{_builddir}/i2pd-openssl/contrib/i2pd.conf %{buildroot}%{_sysconfdir}/i2pd/i2pd.conf +%{__install} -D -m 644 %{_builddir}/i2pd-openssl/contrib/subscriptions.txt %{buildroot}%{_sysconfdir}/i2pd/subscriptions.txt +%{__install} -D -m 644 %{_builddir}/i2pd-openssl/contrib/tunnels.conf %{buildroot}%{_sysconfdir}/i2pd/tunnels.conf +%{__install} -D -m 644 %{_builddir}/i2pd-openssl/contrib/i2pd.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/i2pd +%{__install} -D -m 644 %{_builddir}/i2pd-openssl/contrib/i2pd.service %{buildroot}%{_unitdir}/i2pd.service +%{__install} -D -m 644 %{_builddir}/i2pd-openssl/debian/i2pd.1 %{buildroot}%{_mandir}/man1/i2pd.1 +%{__cp} -r %{_builddir}/i2pd-openssl/contrib/certificates/ %{buildroot}%{_datadir}/i2pd/certificates +%{__cp} -r %{_builddir}/i2pd-openssl/contrib/tunnels.d/ %{buildroot}%{_sysconfdir}/i2pd/tunnels.conf.d ln -s %{_datadir}/%{name}/certificates %{buildroot}%{_sharedstatedir}/i2pd/certificates @@ -146,6 +158,25 @@ %changelog +* Mon Aug 22 2022 orignal - 2.43.0 +- update to 2.43.0 + +* Tue May 24 2022 r4sas - 2.42.1 +- update to 2.42.1 + +* Sun May 22 2022 orignal - 2.42.0 +- update to 2.42.0 + +* Sun Feb 20 2022 r4sas - 2.41.0 +- update to 2.41.0 +- fixed build on Fedora Copr over openssl trunk code + +* Mon Nov 29 2021 orignal - 2.40.0 +- update to 2.40.0 + +* Tue 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 diff -Nru i2pd-2.39.0/contrib/rpm/i2pd.spec i2pd-2.43.0/contrib/rpm/i2pd.spec --- i2pd-2.39.0/contrib/rpm/i2pd.spec 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/rpm/i2pd.spec 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ Name: i2pd -Version: 2.39.0 +Version: 2.43.0 Release: 1%{?dist} Summary: I2P router written in C++ Conflicts: i2pd-git @@ -54,7 +54,11 @@ %endif %endif -%if 0%{?fedora} >= 36 +%if 0%{?rhel} == 9 +pushd redhat-linux-build +%endif + +%if 0%{?fedora} >= 35 pushd redhat-linux-build %else %if 0%{?fedora} >= 33 @@ -68,6 +72,10 @@ make %{?_smp_mflags} +%if 0%{?rhel} == 9 +popd +%endif + %if 0%{?fedora} >= 33 popd %endif @@ -79,7 +87,11 @@ %install pushd build -%if 0%{?fedora} >= 36 +%if 0%{?rhel} == 9 +pushd redhat-linux-build +%endif + +%if 0%{?fedora} >= 35 pushd redhat-linux-build %else %if 0%{?fedora} >= 33 @@ -143,6 +155,24 @@ %changelog +* Mon Aug 22 2022 orignal - 2.43.0 +- update to 2.43.0 + +* Tue May 24 2022 r4sas - 2.42.1 +- update to 2.42.1 + +* Sun May 22 2022 orignal - 2.42.0 +- update to 2.42.0 + +* Sun Feb 20 2022 r4sas - 2.41.0 +- update to 2.41.0 + +* Mon Nov 29 2021 orignal - 2.40.0 +- update to 2.40.0 + +* Tue 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 diff -Nru i2pd-2.39.0/contrib/webconsole/style.css i2pd-2.43.0/contrib/webconsole/style.css --- i2pd-2.39.0/contrib/webconsole/style.css 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/contrib/webconsole/style.css 2022-08-21 19:40:41.000000000 +0000 @@ -1,35 +1,65 @@ +/* + * Copyright (c) 2021-2022, The PurpleI2P Project + * + * This file is part of Purple i2pd project and licensed under BSD3 + * + * See full license text in LICENSE file at top of project tree + * + ****************************************************************** + * + * This is style sheet for webconsole, with @media selectors for adaptive + * view on desktop and mobile devices, respecting preferred user's color + * scheme used in system/browser. + * + * Minified copy of that style sheet is bundled inside i2pd sources. +*/ + +:root { + --main-bg-color: #fafafa; + --main-text-color: #103456; + --main-link-color: #894c84; + --main-link-hover-color: #fafafa; +} + +@media (prefers-color-scheme: dark) { + :root { + --main-bg-color: #242424; + --main-text-color: #17ab5c; + --main-link-color: #bf64b7; + --main-link-hover-color: #000000; + } +} + body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; - background: #FAFAFA; - color: #103456; + background: var(--main-bg-color); + color: var(--main-text-color); } a, .slide label { text-decoration: none; - color: #894C84; + color: var(--main-link-color); } -a:hover, .slide label:hover { - color: #FAFAFA; - background: #894C84; +a:hover, .slide label:hover, button[type=submit]:hover { + color: var(--main-link-hover-color); + background: var(--main-link-color); } a.button { - -webkit-appearance: button; - -moz-appearance: button; appearance: button; text-decoration: none; padding: 0 5px; - border: 1px solid #894C84; + border: 1px solid var(--main-link-color); } .header { font-size: 2.5em; text-align: center; margin: 1em 0; - color: #894C84; + color: var(--main-link-color); } .wrapper { @@ -42,6 +72,7 @@ display: block; float: left; overflow: hidden; + padding: 4px; max-width: 12em; white-space: nowrap; text-overflow: ellipsis; @@ -63,8 +94,9 @@ .content { float: left; font-size: 1em; - margin-left: 4em; - max-width: 48em; + margin-left: 2em; + padding: 4px; + max-width: 50em; overflow: auto; } @@ -87,7 +119,7 @@ caption { font-size: 1.5em; text-align: center; - color: #894C84; + color: var(--main-link-color); } table { @@ -105,6 +137,8 @@ } textarea { + background-color: var(--main-bg-color); + color: var(--main-text-color); word-break: break-all; } @@ -133,24 +167,45 @@ color: #56B734; } +button[type=submit] { + background-color: transparent; + color: var(--main-link-color); + text-decoration: none; + padding: 5px; + border: 1px solid var(--main-link-color); + font-size: 14px; +} + +input, select, select option { + background-color: var(--main-bg-color); + color: var(--main-link-color); + padding: 5px; + border: 1px solid var(--main-link-color); + font-size: 14px; +} + +input:focus, select:focus, select option:focus { + outline: none; +} + +input[type=number]::-webkit-inner-spin-button { + -webkit-appearance: none; +} + @media screen and (max-width: 1150px) { /* adaptive style */ .wrapper { max-width: 58em; } - - .menu { - max-width: 10em; - } .content { - margin-left: 2em; - max-width: 42em; + max-width: 40em; } } @media screen and (max-width: 980px) { body { - padding: 1.5em 0 0 0; + font: 100%/1.2em sans-serif; + padding: 1.2em 0 0 0; } .menu { @@ -178,9 +233,7 @@ } a, .slide label { - /* margin-right: 10px; */ display: block; - /* font-size: 18px; */ } .header { @@ -193,13 +246,12 @@ } a.button { - -webkit-appearance: button; - -moz-appearance: button; appearance: button; text-decoration: none; margin-top: 10px; padding: 6px; - border: 1px solid #894c84; + border: 2px solid var(--main-link-color); + border-radius: 5px; width: -webkit-fill-available; } @@ -207,8 +259,7 @@ width: 35%; text-align: center; padding: 5px; - border: 2px solid #ccc; - -webkit-border-radius: 5px; + border: 2px solid var(--main-link-color); border-radius: 5px; font-size: 18px; } @@ -221,17 +272,16 @@ textarea { width: -webkit-fill-available; height: auto; - padding:5px; - border:2px solid #ccc; - -webkit-border-radius: 5px; + padding: 5px; + border: 2px solid var(--main-link-color); border-radius: 5px; font-size: 12px; } button[type=submit] { padding: 5px 15px; - background: #ccc; - border: 0 none; + background: transparent; + border: 2px solid var(--main-link-color); cursor: pointer; -webkit-border-radius: 5px; border-radius: 5px; diff -Nru i2pd-2.39.0/daemon/Daemon.cpp i2pd-2.43.0/daemon/Daemon.cpp --- i2pd-2.39.0/daemon/Daemon.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/daemon/Daemon.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -58,12 +58,16 @@ bool Daemon_Singleton::IsService () const { bool service = false; -#ifndef _WIN32 i2p::config::GetOption("service", service); -#endif return service; } + void Daemon_Singleton::setDataDir(std::string path) + { + if (path != "") + DaemonDataDir = path; + } + bool Daemon_Singleton::init(int argc, char* argv[]) { return init(argc, argv, nullptr); } @@ -73,8 +77,14 @@ i2p::config::Init(); i2p::config::ParseCmdline(argc, argv); - std::string config; i2p::config::GetOption("conf", config); - std::string datadir; i2p::config::GetOption("datadir", datadir); + std::string config; i2p::config::GetOption("conf", config); + std::string datadir; + if(DaemonDataDir != "") { + datadir = DaemonDataDir; + } else { + i2p::config::GetOption("datadir", datadir); + } + i2p::fs::DetectDataDir(datadir, IsService()); i2p::fs::Init(); @@ -99,9 +109,9 @@ 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); + std::string logs = ""; i2p::config::GetOption("log", logs); + std::string logfile = ""; i2p::config::GetOption("logfile", logfile); + std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel); bool logclftime; i2p::config::GetOption("logclftime", logclftime); /* setup logging */ @@ -118,31 +128,34 @@ i2p::log::Logger().SetLogLevel(loglevel); if (logstream) { - LogPrint(eLogInfo, "Log: will send messages to std::ostream"); + LogPrint(eLogInfo, "Log: Sending messages to std::ostream"); i2p::log::Logger().SendTo (logstream); } else if (logs == "file") { if (logfile == "") logfile = i2p::fs::DataDirPath("i2pd.log"); - LogPrint(eLogInfo, "Log: will send messages to ", logfile); + LogPrint(eLogInfo, "Log: Sending messages to ", logfile); i2p::log::Logger().SendTo (logfile); #ifndef _WIN32 } else if (logs == "syslog") { - LogPrint(eLogInfo, "Log: will send messages to syslog"); + LogPrint(eLogInfo, "Log: Sending messages to syslog"); i2p::log::Logger().SendTo("i2pd", LOG_DAEMON); #endif } else { // use stdout -- default } - 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); + LogPrint(eLogNone, "i2pd v", VERSION, " (", I2P_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); bool avx; i2p::config::GetOption("cpuext.avx", avx); bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt); + bool ssu; i2p::config::GetOption("ssu", ssu); + if (!ssu && i2p::config::IsDefault ("precomputation.elgamal")) + precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt); int netID; i2p::config::GetOption("netid", netID); @@ -151,11 +164,7 @@ bool ipv6; i2p::config::GetOption("ipv6", ipv6); bool ipv4; i2p::config::GetOption("ipv4", ipv4); -#ifdef MESHNET - // manual override for meshnet - ipv4 = false; - ipv6 = true; -#endif + // ifname -> address std::string ifname; i2p::config::GetOption("ifname", ifname); if (ipv4 && i2p::config::IsDefault ("address4")) @@ -204,7 +213,7 @@ uint16_t port; i2p::config::GetOption("port", port); if (!i2p::config::IsDefault("port")) { - LogPrint(eLogInfo, "Daemon: accepting incoming connections at port ", port); + LogPrint(eLogInfo, "Daemon: Accepting incoming connections at port ", port); i2p::context.UpdatePort (port); } i2p::context.SetSupportsV6 (ipv6); @@ -244,6 +253,18 @@ if (!ipv4 && !ipv6) i2p::context.SetStatus (eRouterStatusMesh); } + if (!ssu) i2p::context.RemoveSSUAddress (); // TODO: remove later + bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); + if (ssu2) + { + uint16_t ssu2port; i2p::config::GetOption("ssu2.port", ssu2port); + if (!ssu2port) ssu2port = ssu ? (port + 1) : port; + bool published; i2p::config::GetOption("ssu2.published", published); + if (published) + i2p::context.PublishSSU2Address (ssu2port, true, ipv4, ipv6); // publish + else + i2p::context.PublishSSU2Address (ssu2port, false, ipv4, ipv6); // unpublish + } bool transit; i2p::config::GetOption("notransit", transit); i2p::context.SetAcceptsTunnels (!transit); @@ -252,7 +273,7 @@ bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill); if (isFloodfill) { - LogPrint(eLogInfo, "Daemon: router will be floodfill"); + LogPrint(eLogInfo, "Daemon: Router configured as floodfill"); i2p::context.SetFloodfill (true); } else @@ -267,7 +288,7 @@ if (bandwidth[0] >= 'K' && bandwidth[0] <= 'X') { i2p::context.SetBandwidth (bandwidth[0]); - LogPrint(eLogInfo, "Daemon: bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps"); + LogPrint(eLogInfo, "Daemon: Bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps"); } else { @@ -275,18 +296,18 @@ if (value > 0) { i2p::context.SetBandwidth (value); - LogPrint(eLogInfo, "Daemon: bandwidth set to ", i2p::context.GetBandwidthLimit (), " KBps"); + LogPrint(eLogInfo, "Daemon: Bandwidth set to ", i2p::context.GetBandwidthLimit (), " KBps"); } else { - LogPrint(eLogInfo, "Daemon: unexpected bandwidth ", bandwidth, ". Set to 'low'"); + LogPrint(eLogInfo, "Daemon: Unexpected bandwidth ", bandwidth, ". Set to 'low'"); i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_LOW_BANDWIDTH2); } } } else if (isFloodfill) { - LogPrint(eLogInfo, "Daemon: floodfill bandwidth set to 'extra'"); + LogPrint(eLogInfo, "Daemon: Floodfill bandwidth set to 'extra'"); i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2); } else @@ -301,12 +322,12 @@ std::string family; i2p::config::GetOption("family", family); i2p::context.SetFamily (family); if (family.length () > 0) - LogPrint(eLogInfo, "Daemon: family set to ", family); + LogPrint(eLogInfo, "Daemon: Router family set to ", family); bool trust; i2p::config::GetOption("trust.enabled", trust); if (trust) { - LogPrint(eLogInfo, "Daemon: explicit trust enabled"); + LogPrint(eLogInfo, "Daemon: Explicit trust enabled"); std::string fam; i2p::config::GetOption("trust.family", fam); std::string routers; i2p::config::GetOption("trust.routers", routers); bool restricted = false; @@ -336,18 +357,18 @@ pos = comma + 1; } while (comma != std::string::npos); - LogPrint(eLogInfo, "Daemon: setting restricted routes to use ", idents.size(), " trusted routers"); + LogPrint(eLogInfo, "Daemon: Setting restricted routes to use ", idents.size(), " trusted routers"); i2p::transport::transports.RestrictRoutesToRouters(idents); restricted = idents.size() > 0; } if(!restricted) - LogPrint(eLogError, "Daemon: no trusted routers of families specified"); + LogPrint(eLogError, "Daemon: No trusted routers of families specified"); } bool hidden; i2p::config::GetOption("trust.hidden", hidden); if (hidden) { - LogPrint(eLogInfo, "Daemon: using hidden mode"); + LogPrint(eLogInfo, "Daemon: Hidden mode enabled"); i2p::data::netdb.SetHidden(true); } @@ -360,7 +381,7 @@ bool Daemon_Singleton::start() { i2p::log::Logger().Start(); - LogPrint(eLogInfo, "Daemon: starting NetDB"); + LogPrint(eLogInfo, "Daemon: Starting NetDB"); i2p::data::netdb.Start(); bool upnp; i2p::config::GetOption("upnp.enabled", upnp); @@ -377,19 +398,20 @@ } bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); bool ssu; i2p::config::GetOption("ssu", ssu); bool checkInReserved; i2p::config::GetOption("reservedrange", checkInReserved); - LogPrint(eLogInfo, "Daemon: starting Transports"); - if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled"); - if(!ntcp2) LogPrint(eLogInfo, "Daemon: ntcp2 disabled"); + LogPrint(eLogInfo, "Daemon: Starting Transports"); + if(!ssu) LogPrint(eLogInfo, "Daemon: SSU disabled"); + if(!ntcp2) LogPrint(eLogInfo, "Daemon: NTCP2 disabled"); i2p::transport::transports.SetCheckReserved(checkInReserved); - i2p::transport::transports.Start(ntcp2, ssu); - if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2()) + i2p::transport::transports.Start(ntcp2, ssu, ssu2); + if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2()) LogPrint(eLogInfo, "Daemon: Transports started"); else { - LogPrint(eLogError, "Daemon: failed to start Transports"); + LogPrint(eLogError, "Daemon: Failed to start Transports"); /** shut down netdb right away */ i2p::transport::transports.Stop(); i2p::data::netdb.Stop(); @@ -400,7 +422,7 @@ if (http) { std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); - LogPrint(eLogInfo, "Daemon: starting webconsole at ", httpAddr, ":", httpPort); + LogPrint(eLogInfo, "Daemon: Starting Webconsole at ", httpAddr, ":", httpPort); try { d.httpServer = std::unique_ptr(new i2p::http::HTTPServer(httpAddr, httpPort)); @@ -408,16 +430,16 @@ } catch (std::exception& ex) { - LogPrint (eLogError, "Daemon: failed to start webconsole: ", ex.what ()); + LogPrint (eLogError, "Daemon: Failed to start Webconsole: ", ex.what ()); ThrowFatal ("Unable to start webconsole at ", httpAddr, ":", httpPort, ": ", ex.what ()); } } - LogPrint(eLogInfo, "Daemon: starting Tunnels"); + LogPrint(eLogInfo, "Daemon: Starting Tunnels"); i2p::tunnel::tunnels.Start(); - LogPrint(eLogInfo, "Daemon: starting Client"); + LogPrint(eLogInfo, "Daemon: Starting Client"); i2p::client::context.Start (); // I2P Control Protocol @@ -425,7 +447,7 @@ if (i2pcontrol) { std::string i2pcpAddr; i2p::config::GetOption("i2pcontrol.address", i2pcpAddr); uint16_t i2pcpPort; i2p::config::GetOption("i2pcontrol.port", i2pcpPort); - LogPrint(eLogInfo, "Daemon: starting I2PControl at ", i2pcpAddr, ":", i2pcpPort); + LogPrint(eLogInfo, "Daemon: Starting I2PControl at ", i2pcpAddr, ":", i2pcpPort); try { d.m_I2PControlService = std::unique_ptr(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort)); @@ -433,7 +455,7 @@ } catch (std::exception& ex) { - LogPrint (eLogError, "Daemon: failed to start I2PControl: ", ex.what ()); + LogPrint (eLogError, "Daemon: Failed to start I2PControl: ", ex.what ()); ThrowFatal ("Unable to start I2PControl service at ", i2pcpAddr, ":", i2pcpPort, ": ", ex.what ()); } } @@ -442,10 +464,10 @@ bool Daemon_Singleton::stop() { - LogPrint(eLogInfo, "Daemon: shutting down"); - LogPrint(eLogInfo, "Daemon: stopping Client"); + LogPrint(eLogInfo, "Daemon: Shutting down"); + LogPrint(eLogInfo, "Daemon: Stopping Client"); i2p::client::context.Stop(); - LogPrint(eLogInfo, "Daemon: stopping Tunnels"); + LogPrint(eLogInfo, "Daemon: Stopping Tunnels"); i2p::tunnel::tunnels.Stop(); if (d.UPnP) @@ -460,18 +482,18 @@ d.m_NTPSync = nullptr; } - LogPrint(eLogInfo, "Daemon: stopping Transports"); + LogPrint(eLogInfo, "Daemon: Stopping Transports"); i2p::transport::transports.Stop(); - LogPrint(eLogInfo, "Daemon: stopping NetDB"); + LogPrint(eLogInfo, "Daemon: Stopping NetDB"); i2p::data::netdb.Stop(); if (d.httpServer) { - LogPrint(eLogInfo, "Daemon: stopping HTTP Server"); + LogPrint(eLogInfo, "Daemon: Stopping HTTP Server"); d.httpServer->Stop(); d.httpServer = nullptr; } if (d.m_I2PControlService) { - LogPrint(eLogInfo, "Daemon: stopping I2PControl"); + LogPrint(eLogInfo, "Daemon: Stopping I2PControl"); d.m_I2PControlService->Stop (); d.m_I2PControlService = nullptr; } diff -Nru i2pd-2.39.0/daemon/Daemon.h i2pd-2.43.0/daemon/Daemon.h --- i2pd-2.39.0/daemon/Daemon.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/daemon/Daemon.h 2022-08-21 19:40:41.000000000 +0000 @@ -20,27 +20,33 @@ class Daemon_Singleton_Private; class Daemon_Singleton { - public: + public: - virtual bool init(int argc, char* argv[], std::shared_ptr logstream); - virtual bool init(int argc, char* argv[]); - virtual bool start(); - virtual bool stop(); - virtual void run () {}; + virtual bool init (int argc, char* argv[], std::shared_ptr logstream); + virtual bool init (int argc, char* argv[]); + virtual bool start (); + virtual bool stop (); + virtual void run () {}; - bool isDaemon; - bool running; + virtual void setDataDir (std::string path); - protected: + bool isDaemon; + bool running; - Daemon_Singleton(); - virtual ~Daemon_Singleton(); + protected: - bool IsService () const; + Daemon_Singleton (); + virtual ~Daemon_Singleton (); - // d-pointer for httpServer, httpProxy, etc. - class Daemon_Singleton_Private; - Daemon_Singleton_Private &d; + bool IsService () const; + + // d-pointer for httpServer, httpProxy, etc. + class Daemon_Singleton_Private; + Daemon_Singleton_Private &d; + + private: + + std::string DaemonDataDir; }; #if defined(QT_GUI_LIB) // check if QT diff -Nru i2pd-2.39.0/daemon/HTTPServer.cpp i2pd-2.43.0/daemon/HTTPServer.cpp --- i2pd-2.39.0/daemon/HTTPServer.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/daemon/HTTPServer.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -36,81 +36,12 @@ #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=="; - - // Bundled style - const std::string internalCSS = - "\r\n"; - - // for external style sheet - std::string externalCSS; - static void LoadExtCSS () { std::stringstream s; @@ -229,7 +160,7 @@ if (level == "none" || level == "error" || level == "warn" || level == "info" || level == "debug") i2p::log::Logger().SetLogLevel(level); else { - LogPrint(eLogError, "HTTPServer: unknown loglevel set attempted"); + LogPrint(eLogError, "HTTPServer: Unknown loglevel set attempted"); return; } i2p::log::Logger().Reopen (); @@ -240,7 +171,7 @@ std::string webroot; i2p::config::GetOption("http.webroot", webroot); // Page language - std::string currLang = i2p::context.GetLanguage ()->GetLanguage(); // get current used language + 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; @@ -251,7 +182,7 @@ " \r\n" " \r\n" " \r\n" - " Purple I2P " VERSION " Webconsole\r\n"; + " " << tr(/* tr: Webconsole page title */ "Purple I2P Webconsole") << "\r\n"; GetStyles(s); s << "\r\n" @@ -265,8 +196,10 @@ if (i2p::context.IsFloodfill ()) s << " " << tr("LeaseSets") << "
\r\n"; s << - " " << tr("Tunnels") << "
\r\n" - " " << tr("Transit Tunnels") << "
\r\n" + " " << tr("Tunnels") << "
\r\n"; + if (i2p::context.AcceptsTunnels () || i2p::tunnel::tunnels.CountTransitTunnels()) + s << " " << tr("Transit Tunnels") << "
\r\n"; + s << " " << tr ("Transports") << "
\r\n" " " << tr("I2P tunnels") << "
\r\n"; if (i2p::client::context.GetSAMBridge ()) @@ -289,7 +222,7 @@ s << "" << tr("ERROR") << ": " << string << "
\r\n"; } - static void ShowNetworkStatus (std::stringstream& s, RouterStatus status) + static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, RouterError error) { switch (status) { @@ -302,7 +235,7 @@ case eRouterStatusError: { s << tr("Error"); - switch (i2p::context.GetError ()) + switch (error) { case eRouterErrorClockSkew: s << " - " << tr("Clock skew"); @@ -327,12 +260,12 @@ ShowUptime(s, i2p::context.GetUptime ()); s << "
\r\n"; s << "" << tr("Network status") << ": "; - ShowNetworkStatus (s, i2p::context.GetStatus ()); + ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetError ()); s << "
\r\n"; if (i2p::context.SupportsV6 ()) { s << "" << tr("Network status v6") << ": "; - ShowNetworkStatus (s, i2p::context.GetStatusV6 ()); + ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetErrorV6 ()); s << "
\r\n"; } #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) @@ -364,10 +297,10 @@ 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) { + if ((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) { s << "\r\n\r\n
\r\n"; } - if(includeHiddenContent) { + if (includeHiddenContent) { s << "" << tr("Router Ident") << ": " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; if (!i2p::context.GetRouterInfo().GetProperty("family").empty()) s << "" << tr("Router Family") << ": " << i2p::context.GetRouterInfo().GetProperty("family") << "
\r\n"; @@ -376,41 +309,42 @@ s << ""<< tr("Our external address") << ":" << "
\r\n\r\n"; for (const auto& address : i2p::context.GetRouterInfo().GetAddresses()) { - s << "\r\n"; - if (address->IsNTCP2 () && !address->IsPublishedNTCP2 ()) - { - s << "\r\n\r\n"; - continue; - } + s << "\r\n\r\n"; - break; - } + s << "NTCP2"; + break; case i2p::data::RouterInfo::eTransportSSU: - { - s << "\r\n"; - break; - } + s << "SSU"; + break; + case i2p::data::RouterInfo::eTransportSSU2: + s << "SSU2"; + break; default: - s << "\r\n"; + s << tr("Unknown"); + } + if (address->IsV6 ()) + { + if (address->IsV4 ()) s << "v4"; + s << "v6"; + } + s << "\r\n"; + if (address->published) + s << "\r\n"; + else + { + s << "\r\n"; } - s << "\r\n\r\n"; + s << "\r\n"; } s << "
NTCP2"; - if (address->host.is_v6 ()) s << "v6"; - s << "" << tr("supported") << "
"; switch (address->transportStyle) { case i2p::data::RouterInfo::eTransportNTCP: - { - s << "NTCP"; - if (address->IsPublishedNTCP2 ()) s << "2"; - if (address->host.is_v6 ()) s << "v6"; - s << "SSU"; - if (address->host.is_v6 ()) - s << "v6"; - s << "" << tr("Unknown") << "" << address->host.to_string() << ":" << address->port << "" << tr("supported"); + if (address->port) + s << " :" << address->port; + s << "" << address->host.to_string() << ":" << address->port << "
\r\n"; } s << "
\r\n
\r\n"; - if(outputFormat == OutputFormatEnum::forQtUi) { + if (outputFormat == OutputFormatEnum::forQtUi) { s << "
"; } s << "" << tr("Routers") << ": " << i2p::data::netdb.GetNumRouters () << " "; @@ -424,7 +358,7 @@ s << "" << tr("Client Tunnels") << ": " << std::to_string(clientTunnelCount) << " "; s << "" << tr("Transit Tunnels") << ": " << std::to_string(transitTunnelCount) << "
\r\n
\r\n"; - if(outputFormat==OutputFormatEnum::forWebConsole) { + if (outputFormat==OutputFormatEnum::forWebConsole) { bool httpproxy = i2p::client::context.GetHttpProxy () ? true : false; bool socksproxy = i2p::client::context.GetSocksProxy () ? true : false; bool bob = i2p::client::context.GetBOBCommandChannel () ? true : false; @@ -485,7 +419,7 @@ s << "\r\n\r\n"; } - if(dest->IsPublic()) + if (dest->IsPublic() && token && !dest->IsEncryptedLeaseSet ()) { std::string webroot; i2p::config::GetOption("http.webroot", webroot); auto base32 = dest->GetIdentHash ().ToBase32 (); @@ -499,7 +433,7 @@ "\r\n" << tr("Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.") << "\r\n\r\n\r\n
\r\n"; } - if(dest->GetNumRemoteLeaseSets()) + if (dest->GetNumRemoteLeaseSets()) { s << "
\r\n\r\n
\r\n"; @@ -515,8 +449,18 @@ s << "" << tr("Inbound tunnels") << ":
\r\n
\r\n"; for (auto & it : pool->GetInboundTunnels ()) { s << "
"; - it->Print(s); - if(it->LatencyIsKnown()) + // for each tunnel hop if not zero-hop + if (it->GetNumHops ()) + { + it->VisitTunnelHops( + [&s](std::shared_ptr hopIdent) + { + s << "⇒ " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " "; + } + ); + } + s << "⇒ " << it->GetTunnelID () << ":me"; + if (it->LatencyIsKnown()) s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )"; ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); s << "
\r\n"; @@ -525,8 +469,18 @@ s << "" << tr("Outbound tunnels") << ":
\r\n
\r\n"; for (auto & it : pool->GetOutboundTunnels ()) { s << "
"; - it->Print(s); - if(it->LatencyIsKnown()) + s << it->GetTunnelID () << ":me ⇒"; + // for each tunnel hop if not zero-hop + if (it->GetNumHops ()) + { + it->VisitTunnelHops( + [&s](std::shared_ptr hopIdent) + { + s << " " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " ⇒"; + } + ); + } + if (it->LatencyIsKnown()) s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ()); s << "
\r\n"; @@ -577,19 +531,21 @@ ShowLeaseSetDestination (s, dest, token); // Print table with streams information - s << "
"<< tr("Address") << "" << tr("Type") << "" << tr("EncType") << "
\r\n\r\n\r\n"; - s << ""; - s << ""; - s << ""; - s << ""; - s << ""; - s << ""; - s << ""; - s << ""; - s << ""; - s << ""; - s << "\r\n\r\n\r\n"; + s << "
" << tr("Streams") << "
StreamID"; // Stream closing button column - s << "DestinationSentReceivedOutInBufRTTWindowStatus
\r\n\r\n\r\n" + << "" + << "" + << "" + << "" + << "" + << "" + << "" + << "" + << "" + << "" + << "\r\n\r\n\r\n"; for (const auto& it: dest->GetAllStreams ()) { @@ -699,8 +655,17 @@ s << "" << tr("Inbound tunnels") << ":
\r\n
\r\n"; for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) { s << "
"; - it->Print(s); - if(it->LatencyIsKnown()) + if (it->GetNumHops ()) + { + it->VisitTunnelHops( + [&s](std::shared_ptr hopIdent) + { + s << "⇒ " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " "; + } + ); + } + s << "⇒ " << it->GetTunnelID () << ":me"; + if (it->LatencyIsKnown()) s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ()); s << "
\r\n"; @@ -709,8 +674,18 @@ s << "" << tr("Outbound tunnels") << ":
\r\n
\r\n"; for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) { s << "
"; - it->Print(s); - if(it->LatencyIsKnown()) + s << it->GetTunnelID () << ":me ⇒"; + // for each tunnel hop if not zero-hop + if (it->GetNumHops ()) + { + it->VisitTunnelHops( + [&s](std::shared_ptr hopIdent) + { + s << " " << i2p::data::GetIdentHashAbbreviation (hopIdent->GetIdentHash ()) << " ⇒"; + } + ); + } + if (it->LatencyIsKnown()) s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ()); s << "
\r\n"; @@ -766,23 +741,31 @@ s << " \r\n"; s << "\r\n
\r\n"; - std::string currLang = i2p::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" + << " \r\n" + << " \r\n"; - s << " \r\n"; - s << "\r\n
\r\n"; + + s << " \r\n" + << " \r\n" + << "\r\n
\r\n"; } void ShowTransitTunnels (std::stringstream& s) { - if(i2p::tunnel::tunnels.CountTransitTunnels()) + if (i2p::tunnel::tunnels.CountTransitTunnels()) { s << "" << tr("Transit Tunnels") << ":
\r\n
\r\n"; for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) @@ -805,30 +788,35 @@ } template - static void ShowNTCPTransports (std::stringstream& s, const Sessions& sessions, const std::string name) + static void ShowTransportSessions (std::stringstream& s, const Sessions& sessions, const std::string name) { std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0; for (const auto& it: sessions ) { - if (it.second && it.second->IsEstablished () && !it.second->GetRemoteEndpoint ().address ().is_v6 ()) + auto endpoint = it.second->GetRemoteEndpoint (); + if (it.second && it.second->IsEstablished () && endpoint.address ().is_v4 ()) { tmp_s << "
\r\n"; if (it.second->IsOutgoing ()) tmp_s << " ⇒ "; tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " - << it.second->GetRemoteEndpoint ().address ().to_string (); + << endpoint.address ().to_string () << ":" << endpoint.port (); if (!it.second->IsOutgoing ()) tmp_s << " ⇒ "; tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + if (it.second->GetRelayTag ()) + tmp_s << " [itag:" << it.second->GetRelayTag () << "]"; tmp_s << "
\r\n" << std::endl; cnt++; } - if (it.second && it.second->IsEstablished () && it.second->GetRemoteEndpoint ().address ().is_v6 ()) + if (it.second && it.second->IsEstablished () && endpoint.address ().is_v6 ()) { tmp_s6 << "
\r\n"; if (it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " - << "[" << it.second->GetRemoteEndpoint ().address ().to_string () << "]"; + << "[" << endpoint.address ().to_string () << "]:" << endpoint.port (); if (!it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + if (it.second->GetRelayTag ()) + tmp_s6 << " [itag:" << it.second->GetRelayTag () << "]"; tmp_s6 << "
\r\n" << std::endl; cnt6++; } @@ -855,7 +843,7 @@ { auto sessions = ntcp2Server->GetNTCP2Sessions (); if (!sessions.empty ()) - ShowNTCPTransports (s, sessions, "NTCP2"); + ShowTransportSessions (s, sessions, "NTCP2"); } auto ssuServer = i2p::transport::transports.GetSSUServer (); if (ssuServer) @@ -897,6 +885,13 @@ s << "
\r\n
\r\n"; } } + auto ssu2Server = i2p::transport::transports.GetSSU2Server (); + if (ssu2Server) + { + auto sessions = ssu2Server->GetSSU2Sessions (); + if (!sessions.empty ()) + ShowTransportSessions (s, sessions, "SSU2"); + } } void ShowSAMSessions (std::stringstream& s) @@ -909,7 +904,7 @@ return; } - if(sam->GetSessions ().size ()) + if (sam->GetSessions ().size ()) { s << "" << tr("SAM sessions") << ":
\r\n
\r\n"; for (auto& it: sam->GetSessions ()) @@ -1108,7 +1103,7 @@ if (expected == provided) return true; } - LogPrint(eLogWarning, "HTTPServer: auth failure from ", m_Socket->remote_endpoint().address ()); + LogPrint(eLogWarning, "HTTPServer: Auth failure from ", m_Socket->remote_endpoint().address ()); return false; } @@ -1118,7 +1113,7 @@ std::string content; HTTPRes res; - LogPrint(eLogDebug, "HTTPServer: request: ", req.uri); + LogPrint(eLogDebug, "HTTPServer: Request: ", req.uri); if (needAuth && !CheckAuth(req)) { res.code = 401; @@ -1126,6 +1121,7 @@ SendReply(res, content); return; } + bool strictheaders; i2p::config::GetOption("http.strictheaders", strictheaders); if (strictheaders) @@ -1148,6 +1144,7 @@ return; } } + // HTML head start ShowPageHead (s); if (req.uri.find("page=") != std::string::npos) { @@ -1268,7 +1265,7 @@ else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) { i2p::context.SetAcceptsTunnels (true); -#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) +#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) Daemon.gracefulShutdownInterval = 0; #elif defined(WIN32_APP) i2p::win32::StopGracefulShutdown (); @@ -1300,7 +1297,7 @@ { if (dest) { - if(dest->DeleteStream (streamID)) + if (dest->DeleteStream (streamID)) s << "" << tr("SUCCESS") << ": " << tr("Stream closed") << "
\r\n
\r\n"; else s << "" << tr("ERROR") << ": " << tr("Stream not found or already was closed") << "
\r\n
\r\n"; @@ -1382,7 +1379,7 @@ else if (cmd == HTTP_COMMAND_SETLANGUAGE) { std::string lang = params["lang"]; - std::string currLang = i2p::context.GetLanguage ()->GetLanguage(); + std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); if (currLang.compare(lang) != 0) i2p::i18n::SetLanguage(lang); @@ -1446,7 +1443,7 @@ pass[i] = alnum[random[i] % (sizeof(alnum) - 1)]; } i2p::config::SetOption("http.pass", pass); - LogPrint(eLogInfo, "HTTPServer: password set to ", pass); + LogPrint(eLogInfo, "HTTPServer: Password set to ", pass); } m_IsRunning = true; @@ -1460,7 +1457,13 @@ void HTTPServer::Stop () { m_IsRunning = false; + + boost::system::error_code ec; + m_Acceptor.cancel(ec); + if (ec) + LogPrint (eLogDebug, "HTTPServer: Error while cancelling operations on acceptor: ", ec.message ()); m_Acceptor.close(); + m_Service.stop (); if (m_Thread) { @@ -1481,7 +1484,7 @@ } catch (std::exception& ex) { - LogPrint (eLogError, "HTTPServer: runtime exception: ", ex.what ()); + LogPrint (eLogError, "HTTPServer: Runtime exception: ", ex.what ()); } } } @@ -1496,15 +1499,13 @@ void HTTPServer::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr newSocket) { - if (ecode) + if (!ecode) + CreateConnection(newSocket); + else { - if(newSocket) newSocket->close(); - LogPrint(eLogError, "HTTP Server: error handling accept ", ecode.message()); - if(ecode != boost::asio::error::operation_aborted) - Accept(); - return; + if (newSocket) newSocket->close(); + LogPrint(eLogError, "HTTP Server: Error handling accept: ", ecode.message()); } - CreateConnection(newSocket); Accept (); } diff -Nru i2pd-2.39.0/daemon/HTTPServer.h i2pd-2.43.0/daemon/HTTPServer.h --- i2pd-2.39.0/daemon/HTTPServer.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/daemon/HTTPServer.h 2022-08-21 19:40:41.000000000 +0000 @@ -98,8 +98,8 @@ void ShowSAMSessions (std::stringstream& s); void ShowI2PTunnels (std::stringstream& s); void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token); - void ShowSAMSession (std::stringstream& s, const std::string& id); - void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id); + void ShowSAMSession (std::stringstream& s, const std::string& id); + void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id); } // http } // i2p diff -Nru i2pd-2.39.0/daemon/HTTPServerResources.h i2pd-2.43.0/daemon/HTTPServerResources.h --- i2pd-2.39.0/daemon/HTTPServerResources.h 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/daemon/HTTPServerResources.h 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,97 @@ +/* +* Copyright (c) 2013-2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef 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 -Nru i2pd-2.39.0/daemon/I2PControl.cpp i2pd-2.43.0/daemon/I2PControl.cpp --- i2pd-2.39.0/daemon/I2PControl.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/daemon/I2PControl.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,3 +1,11 @@ +/* +* Copyright (c) 2013-2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include @@ -48,39 +56,38 @@ if (i2pcp_key.at(0) != '/') i2pcp_key = i2p::fs::DataDirPath(i2pcp_key); if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) { - LogPrint (eLogInfo, "I2PControl: creating new certificate for control connection"); + LogPrint (eLogInfo, "I2PControl: Creating new certificate for control connection"); CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str()); } else { - LogPrint(eLogDebug, "I2PControl: using cert from ", i2pcp_crt); + LogPrint(eLogDebug, "I2PControl: Using cert from ", i2pcp_crt); } m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem); m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem); // handlers - m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler; - m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler; - m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler; - m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler; - m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler; - m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler; - m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler; + m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler; + m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler; + m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler; + m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler; + m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler; + m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler; + m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler; // I2PControl m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler; // RouterInfo - m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler; - m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler; - m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler; - m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler; - m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler; - m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S; - m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S; - m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler; + m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler; + m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler; + m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler; + m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler; + m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler; + m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S; + m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S; + m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler; m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler; - m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = -&I2PControlService::TunnelsSuccessRateHandler; + m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = &I2PControlService::TunnelsSuccessRateHandler; m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes; m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes; @@ -96,10 +103,10 @@ // ClientServicesInfo m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlService::I2PTunnelInfoHandler; m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlService::HTTPProxyInfoHandler; - m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler; - m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler; - m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler; - m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler; + m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler; + m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler; + m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler; + m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler; } I2PControlService::~I2PControlService () @@ -142,7 +149,7 @@ try { m_Service.run (); } catch (std::exception& ex) { - LogPrint (eLogError, "I2PControl: runtime exception: ", ex.what ()); + LogPrint (eLogError, "I2PControl: Runtime exception: ", ex.what ()); } } } @@ -160,10 +167,10 @@ Accept (); if (ecode) { - LogPrint (eLogError, "I2PControl: accept error: ", ecode.message ()); + LogPrint (eLogError, "I2PControl: Accept error: ", ecode.message ()); return; } - LogPrint (eLogDebug, "I2PControl: new request from ", socket->lowest_layer ().remote_endpoint ()); + LogPrint (eLogDebug, "I2PControl: New request from ", socket->lowest_layer ().remote_endpoint ()); Handshake (socket); } @@ -176,7 +183,7 @@ void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr socket) { if (ecode) { - LogPrint (eLogError, "I2PControl: handshake error: ", ecode.message ()); + LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ()); return; } //std::this_thread::sleep_for (std::chrono::milliseconds(5)); @@ -202,7 +209,7 @@ { if (ecode) { - LogPrint (eLogError, "I2PControl: read error: ", ecode.message ()); + LogPrint (eLogError, "I2PControl: Read error: ", ecode.message ()); return; } else @@ -225,7 +232,7 @@ } if (ss.eof ()) { - LogPrint (eLogError, "I2PControl: malformed request, HTTP header expected"); + LogPrint (eLogError, "I2PControl: Malformed request, HTTP header expected"); return; // TODO: } std::streamoff rem = contentLength + ss.tellg () - bytes_transferred; // more bytes to read @@ -250,7 +257,7 @@ } else { - LogPrint (eLogWarning, "I2PControl: unknown method ", method); + LogPrint (eLogWarning, "I2PControl: Unknown method ", method); response << "{\"id\":null,\"error\":"; response << "{\"code\":-32601,\"message\":\"Method not found\"},"; response << "\"jsonrpc\":\"2.0\"}"; @@ -259,7 +266,7 @@ } catch (std::exception& ex) { - LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ()); + LogPrint (eLogError, "I2PControl: Exception when handle request: ", ex.what ()); std::ostringstream response; response << "{\"id\":null,\"error\":"; response << "{\"code\":-32700,\"message\":\"" << ex.what () << "\"},"; @@ -268,7 +275,7 @@ } catch (...) { - LogPrint (eLogError, "I2PControl: handle request unknown exception"); + LogPrint (eLogError, "I2PControl: Handle request unknown exception"); } } } @@ -278,11 +285,16 @@ ss << "\"" << name << "\":" << value; } - void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const + void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value, bool quotes) const { ss << "\"" << name << "\":"; if (value.length () > 0) - ss << "\"" << value << "\""; + { + if (quotes) + ss << "\"" << value << "\""; + else + ss << value; + } else ss << "null"; } @@ -329,7 +341,7 @@ std::shared_ptr socket, std::shared_ptr buf) { if (ecode) { - LogPrint (eLogError, "I2PControl: write error: ", ecode.message ()); + LogPrint (eLogError, "I2PControl: Write error: ", ecode.message ()); } } @@ -379,7 +391,7 @@ void I2PControlService::PasswordHandler (const std::string& value) { - LogPrint (eLogWarning, "I2PControl: new password=", value, ", to make it persistent you should update your config!"); + LogPrint (eLogWarning, "I2PControl: New password=", value, ", to make it persistent you should update your config!"); m_Password = value; m_Tokens.clear (); } @@ -395,8 +407,8 @@ auto it1 = m_RouterInfoHandlers.find (it->first); if (it1 != m_RouterInfoHandlers.end ()) { - if (!first) results << ","; - else first = false; + if (!first) results << ","; + else first = false; (this->*(it1->second))(results); } else @@ -406,7 +418,7 @@ void I2PControlService::UptimeHandler (std::ostringstream& results) { - InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL)); + InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL), false); } void I2PControlService::VersionHandler (std::ostringstream& results) @@ -466,7 +478,7 @@ void I2PControlService::NetTotalSentBytes (std::ostringstream& results) { - InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ()); + InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ()); } @@ -494,7 +506,7 @@ m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent m_ShutdownTimer.async_wait ( [](const boost::system::error_code& ecode) - { + { Daemon.running = 0; }); } @@ -508,7 +520,7 @@ m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second m_ShutdownTimer.async_wait ( [](const boost::system::error_code& ecode) - { + { Daemon.running = 0; }); } @@ -577,11 +589,11 @@ // save cert if ((f = fopen (crt_path, "wb")) != NULL) { - LogPrint (eLogInfo, "I2PControl: saving new cert to ", crt_path); + LogPrint (eLogInfo, "I2PControl: Saving new cert to ", crt_path); PEM_write_X509 (f, x509); fclose (f); } else { - LogPrint (eLogError, "I2PControl: can't write cert: ", strerror(errno)); + LogPrint (eLogError, "I2PControl: Can't write cert: ", strerror(errno)); } // save key @@ -590,12 +602,12 @@ PEM_write_PrivateKey (f, pkey, NULL, NULL, 0, NULL, NULL); fclose (f); } else { - LogPrint (eLogError, "I2PControl: can't write key: ", strerror(errno)); + LogPrint (eLogError, "I2PControl: Can't write key: ", strerror(errno)); } X509_free (x509); } else { - LogPrint (eLogError, "I2PControl: can't create RSA key for certificate"); + LogPrint (eLogError, "I2PControl: Can't create RSA key for certificate"); } EVP_PKEY_free (pkey); } diff -Nru i2pd-2.39.0/daemon/I2PControl.h i2pd-2.43.0/daemon/I2PControl.h --- i2pd-2.39.0/daemon/I2PControl.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/daemon/I2PControl.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -65,7 +65,7 @@ void InsertParam (std::ostringstream& ss, const std::string& name, int value) const; void InsertParam (std::ostringstream& ss, const std::string& name, double value) const; - void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const; + void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value, bool quotes = true) const; void InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const; // methods diff -Nru i2pd-2.39.0/daemon/UnixDaemon.cpp i2pd-2.43.0/daemon/UnixDaemon.cpp --- i2pd-2.39.0/daemon/UnixDaemon.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/daemon/UnixDaemon.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -24,6 +24,7 @@ #include "Tunnel.h" #include "RouterContext.h" #include "ClientContext.h" +#include "Transports.h" void handle_signal(int sig) { @@ -54,6 +55,14 @@ case SIGPIPE: LogPrint(eLogInfo, "SIGPIPE received"); break; + case SIGTSTP: + LogPrint(eLogInfo, "Daemon: Got SIGTSTP, disconnecting from network..."); + i2p::transport::transports.SetOnline(false); + break; + case SIGCONT: + LogPrint(eLogInfo, "Daemon: Got SIGCONT, restoring connection to network..."); + i2p::transport::transports.SetOnline(true); + break; } } @@ -72,7 +81,8 @@ if (pid < 0) // error { - LogPrint(eLogError, "Daemon: could not fork: ", strerror(errno)); + LogPrint(eLogError, "Daemon: Could not fork: ", strerror(errno)); + std::cerr << "i2pd: Could not fork: " << strerror(errno) << std::endl; return false; } @@ -81,13 +91,15 @@ int sid = setsid(); if (sid < 0) { - LogPrint(eLogError, "Daemon: could not create process group."); + LogPrint(eLogError, "Daemon: Could not create process group."); + std::cerr << "i2pd: Could not create process group." << std::endl; return false; } std::string d = i2p::fs::GetDataDir(); if (chdir(d.c_str()) != 0) { - LogPrint(eLogError, "Daemon: could not chdir: ", strerror(errno)); + LogPrint(eLogError, "Daemon: Could not chdir: ", strerror(errno)); + std::cerr << "i2pd: Could not chdir: " << strerror(errno) << std::endl; return false; } @@ -102,14 +114,14 @@ uint16_t nfiles; i2p::config::GetOption("limits.openfiles", nfiles); getrlimit(RLIMIT_NOFILE, &limit); if (nfiles == 0) { - LogPrint(eLogInfo, "Daemon: using system limit in ", limit.rlim_cur, " max open files"); + LogPrint(eLogInfo, "Daemon: Using system limit in ", limit.rlim_cur, " max open files"); } else if (nfiles <= limit.rlim_max) { limit.rlim_cur = nfiles; if (setrlimit(RLIMIT_NOFILE, &limit) == 0) { - LogPrint(eLogInfo, "Daemon: set max number of open files to ", + LogPrint(eLogInfo, "Daemon: Set max number of open files to ", nfiles, " (system limit is ", limit.rlim_max, ")"); } else { - LogPrint(eLogError, "Daemon: can't set max number of open files: ", strerror(errno)); + LogPrint(eLogError, "Daemon: Can't set max number of open files: ", strerror(errno)); } } else { LogPrint(eLogError, "Daemon: limits.openfiles exceeds system limit: ", limit.rlim_max); @@ -122,11 +134,11 @@ if (cfsize <= limit.rlim_max) { limit.rlim_cur = cfsize; if (setrlimit(RLIMIT_CORE, &limit) != 0) { - LogPrint(eLogError, "Daemon: can't set max size of coredump: ", strerror(errno)); + LogPrint(eLogError, "Daemon: Can't set max size of coredump: ", strerror(errno)); } else if (cfsize == 0) { LogPrint(eLogInfo, "Daemon: coredumps disabled"); } else { - LogPrint(eLogInfo, "Daemon: set max size of core files to ", cfsize / 1024, "Kb"); + LogPrint(eLogInfo, "Daemon: Set max size of core files to ", cfsize / 1024, "Kb"); } } else { LogPrint(eLogError, "Daemon: limits.coresize exceeds system limit: ", limit.rlim_max); @@ -143,14 +155,16 @@ pidFH = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600); if (pidFH < 0) { - LogPrint(eLogError, "Daemon: could not create pid file ", pidfile, ": ", strerror(errno)); + LogPrint(eLogError, "Daemon: Could not create pid file ", pidfile, ": ", strerror(errno)); + std::cerr << "i2pd: Could not create pid file " << pidfile << ": " << strerror(errno) << std::endl; return false; } #ifndef ANDROID if (lockf(pidFH, F_TLOCK, 0) != 0) { - LogPrint(eLogError, "Daemon: could not lock pid file ", pidfile, ": ", strerror(errno)); + LogPrint(eLogError, "Daemon: Could not lock pid file ", pidfile, ": ", strerror(errno)); + std::cerr << "i2pd: Could not lock pid file " << pidfile << ": " << strerror(errno) << std::endl; return false; } #endif @@ -159,12 +173,16 @@ ftruncate(pidFH, 0); if (write(pidFH, pid, strlen(pid)) < 0) { - LogPrint(eLogError, "Daemon: could not write pidfile: ", strerror(errno)); + LogPrint(eLogError, "Daemon: Could not write pidfile ", pidfile, ": ", strerror(errno)); + std::cerr << "i2pd: Could not write pidfile " << pidfile << ": " << strerror(errno) << std::endl; return false; } } gracefulShutdownInterval = 0; // not specified + // handle signal TSTP + bool handleTSTP; i2p::config::GetOption("unix.handle_sigtstp", handleTSTP); + // Signal handler struct sigaction sa; sa.sa_handler = handle_signal; @@ -176,6 +194,11 @@ sigaction(SIGTERM, &sa, 0); sigaction(SIGINT, &sa, 0); sigaction(SIGPIPE, &sa, 0); + if (handleTSTP) + { + sigaction(SIGTSTP, &sa, 0); + sigaction(SIGCONT, &sa, 0); + } return Daemon_Singleton::start(); } diff -Nru i2pd-2.39.0/daemon/UPnP.cpp i2pd-2.43.0/daemon/UPnP.cpp --- i2pd-2.39.0/daemon/UPnP.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/daemon/UPnP.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -29,7 +29,7 @@ { if (m_IsRunning) { - LogPrint(eLogInfo, "UPnP: stopping"); + LogPrint(eLogInfo, "UPnP: Stopping"); m_IsRunning = false; m_Timer.cancel (); m_Service.stop (); @@ -46,7 +46,7 @@ void UPnP::Start() { m_IsRunning = true; - LogPrint(eLogInfo, "UPnP: starting"); + LogPrint(eLogInfo, "UPnP: Starting"); m_Service.post (std::bind (&UPnP::Discover, this)); std::unique_lock l(m_StartedMutex); m_Thread.reset (new std::thread (std::bind (&UPnP::Run, this))); @@ -72,7 +72,7 @@ } catch (std::exception& ex) { - LogPrint (eLogError, "UPnP: runtime exception: ", ex.what ()); + LogPrint (eLogError, "UPnP: Runtime exception: ", ex.what ()); PortMapping (); } } @@ -93,7 +93,7 @@ #endif isError = err != UPNPDISCOVER_SUCCESS; -#else // MINIUPNPC_API_VERSION >= 8 +#else // MINIUPNPC_API_VERSION >= 8 err = 0; m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0); isError = m_Devlist == NULL; @@ -106,7 +106,7 @@ if (isError) { - LogPrint (eLogError, "UPnP: unable to discover Internet Gateway Devices: error ", err); + LogPrint (eLogError, "UPnP: Unable to discover Internet Gateway Devices: error ", err); return; } @@ -117,22 +117,22 @@ err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress); if(err != UPNPCOMMAND_SUCCESS) { - LogPrint (eLogError, "UPnP: unable to get external address: error ", err); + LogPrint (eLogError, "UPnP: Unable to get external address: error ", err); return; } else { - LogPrint (eLogError, "UPnP: found Internet Gateway Device ", m_upnpUrls.controlURL); + LogPrint (eLogError, "UPnP: Found Internet Gateway Device ", m_upnpUrls.controlURL); if (!m_externalIPAddress[0]) { - LogPrint (eLogError, "UPnP: found Internet Gateway Device doesn't know our external address"); + LogPrint (eLogError, "UPnP: Found Internet Gateway Device doesn't know our external address"); return; } } } else { - LogPrint (eLogError, "UPnP: unable to find valid Internet Gateway Device: error ", err); + LogPrint (eLogError, "UPnP: Unable to find valid Internet Gateway Device: error ", err); return; } @@ -183,7 +183,7 @@ err = CheckMapping (strPort.c_str (), strType.c_str ()); if (err != UPNPCOMMAND_SUCCESS) // if mapping not found { - LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err); + LogPrint (eLogDebug, "UPnP: Port ", strPort, " is possibly not forwarded: return code ", err); #if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS)) err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL, NULL); @@ -192,18 +192,18 @@ #endif if (err != UPNPCOMMAND_SUCCESS) { - LogPrint (eLogError, "UPnP: port forwarding to ", m_NetworkAddr, ":", strPort, " failed: return code ", err); + LogPrint (eLogError, "UPnP: Port forwarding to ", m_NetworkAddr, ":", strPort, " failed: return code ", err); return; } else { - LogPrint (eLogInfo, "UPnP: port successfully forwarded (", m_externalIPAddress ,":", strPort, " type ", strType, " -> ", m_NetworkAddr ,":", strPort ,")"); + LogPrint (eLogInfo, "UPnP: Port successfully forwarded (", m_externalIPAddress ,":", strPort, " type ", strType, " -> ", m_NetworkAddr ,":", strPort ,")"); return; } } else { - LogPrint (eLogDebug, "UPnP: external forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device"); + LogPrint (eLogDebug, "UPnP: External forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device"); return; } } @@ -225,7 +225,7 @@ } std::string strType (GetProto (address)), strPort (std::to_string (address->port)); int err = UPNPCOMMAND_SUCCESS; - + err = CheckMapping (strPort.c_str (), strType.c_str ()); if (err == UPNPCOMMAND_SUCCESS) { diff -Nru i2pd-2.39.0/daemon/UPnP.h i2pd-2.43.0/daemon/UPnP.h --- i2pd-2.39.0/daemon/UPnP.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/daemon/UPnP.h 2022-08-21 19:40:41.000000000 +0000 @@ -51,7 +51,7 @@ private: void Discover (); - int CheckMapping (const char* port, const char* type); + int CheckMapping (const char* port, const char* type); void PortMapping (); void TryPortMapping (std::shared_ptr address); void CloseMapping (); @@ -80,7 +80,7 @@ } } -#else // USE_UPNP +#else // USE_UPNP namespace i2p { namespace transport { /* class stub */ diff -Nru i2pd-2.39.0/debian/changelog i2pd-2.43.0/debian/changelog --- i2pd-2.39.0/debian/changelog 2021-11-26 10:20:21.000000000 +0000 +++ i2pd-2.43.0/debian/changelog 2022-10-23 09:38:49.000000000 +0000 @@ -1,9 +1,17 @@ -i2pd (2.39.0-1ubuntu1) jammy; urgency=medium +i2pd (2.43.0-1) unstable; urgency=medium - * d/p/OpenSSL3.patch: Cherry-picked from upstream mainline to fix the build - against OpenSSL 3. + * New upstream release + * Bump Standards-Version to 4.6.1 - -- Simon Chopin Fri, 26 Nov 2021 11:20:21 +0100 + -- Yangfl Sun, 23 Oct 2022 17:38:49 +0800 + +i2pd (2.41.0-1) unstable; urgency=medium + + * New upstream release + + Fix FTBFS with OpenSSL 3.0 (Closes: #1006243) + + Fix crash due to SSU2 (Closes: #1008087) + + -- Yangfl Sun, 03 Apr 2022 14:22:27 +0800 i2pd (2.39.0-1) unstable; urgency=medium diff -Nru i2pd-2.39.0/debian/control i2pd-2.43.0/debian/control --- i2pd-2.39.0/debian/control 2021-11-26 10:20:21.000000000 +0000 +++ i2pd-2.43.0/debian/control 2022-10-23 09:38:49.000000000 +0000 @@ -1,11 +1,10 @@ Source: i2pd Section: net Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Yangfl +Maintainer: Yangfl Build-Depends: debhelper-compat (= 13), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev, cmake, libwebsocketpp-dev, dh-apparmor, Rules-Requires-Root: no -Standards-Version: 4.6.0 +Standards-Version: 4.6.1 Homepage: https://i2pd.website Vcs-Git: https://salsa.debian.org/yangfl-guest/i2pd.git Vcs-Browser: https://salsa.debian.org/yangfl-guest/i2pd diff -Nru i2pd-2.39.0/debian/copyright i2pd-2.43.0/debian/copyright --- i2pd-2.39.0/debian/copyright 2021-02-27 16:14:46.000000000 +0000 +++ i2pd-2.43.0/debian/copyright 2022-10-23 09:38:49.000000000 +0000 @@ -3,14 +3,14 @@ Source: https://github.com/PurpleI2P/i2pd Files: * -Copyright: 2013-2020 The PurpleI2P Project +Copyright: 2013-2022 The PurpleI2P Project License: BSD-3-clause Files: debian/* Copyright: 2013-2015 Kill Your TV 2014-2016 hagen 2016-2020 R4SAS - 2017-2020 Yangfl + 2017-2022 Yangfl License: GPL-2+ License: BSD-3-Clause diff -Nru i2pd-2.39.0/debian/patches/OpenSSL3.patch i2pd-2.43.0/debian/patches/OpenSSL3.patch --- i2pd-2.39.0/debian/patches/OpenSSL3.patch 2021-11-26 10:20:07.000000000 +0000 +++ i2pd-2.43.0/debian/patches/OpenSSL3.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -From 921ec9ec12ead861994c98d96087b93a31051ef5 Mon Sep 17 00:00:00 2001 -From: orignal -Date: Sat, 23 Oct 2021 18:10:02 -0400 -Subject: [PATCH] fix build with openssl 3.0.0 - ---- - libi2pd/Crypto.cpp | 2 +- - libi2pd/Reseed.cpp | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp -index 427bbbd6b..d38d489ae 100644 ---- a/libi2pd/Crypto.cpp -+++ b/libi2pd/Crypto.cpp -@@ -1277,7 +1277,7 @@ namespace crypto - EVP_PKEY_CTX_set1_hkdf_key (pctx, tempKey, len); - } - if (info.length () > 0) -- EVP_PKEY_CTX_add1_hkdf_info (pctx, info.c_str (), info.length ()); -+ EVP_PKEY_CTX_add1_hkdf_info (pctx, (const uint8_t *)info.c_str (), info.length ()); - EVP_PKEY_derive (pctx, out, &outLen); - EVP_PKEY_CTX_free (pctx); - #else -diff --git a/libi2pd/Reseed.cpp b/libi2pd/Reseed.cpp -index aec683d45..2e8eccf70 100644 ---- a/libi2pd/Reseed.cpp -+++ b/libi2pd/Reseed.cpp -@@ -478,7 +478,7 @@ namespace data - if (terminator) terminator[0] = 0; - } - // extract RSA key (we need n only, e = 65537) -- RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert)); -+ const RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert)); - const BIGNUM * n, * e, * d; - RSA_get0_key(key, &n, &e, &d); - PublicKey value; diff -Nru i2pd-2.39.0/debian/patches/series i2pd-2.43.0/debian/patches/series --- i2pd-2.39.0/debian/patches/series 2021-11-26 10:20:13.000000000 +0000 +++ i2pd-2.43.0/debian/patches/series 2022-10-23 09:38:49.000000000 +0000 @@ -1,2 +1 @@ systemd.patch -OpenSSL3.patch diff -Nru i2pd-2.39.0/debian/rules i2pd-2.43.0/debian/rules --- i2pd-2.39.0/debian/rules 2021-08-31 13:38:56.000000000 +0000 +++ i2pd-2.43.0/debian/rules 2022-10-23 09:38:49.000000000 +0000 @@ -17,11 +17,11 @@ %: - dh $@ + dh $@ --buildsystem=cmake .PHONY: build build: - dh $@ + dh $@ --buildsystem=cmake override_dh_auto_clean: dh_auto_clean --buildsystem=cmake --sourcedirectory=build/ @@ -30,8 +30,5 @@ dh_auto_configure --buildsystem=cmake --sourcedirectory=build/ -- \ -DWITH_UPNP=ON -DWITH_WEBSOCKETS=ON -override_dh_auto_build: - dh_auto_build --buildsystem=cmake - execute_after_dh_install: dh_apparmor -p i2pd --profile-name=usr.sbin.i2pd diff -Nru i2pd-2.39.0/.gitattributes i2pd-2.43.0/.gitattributes --- i2pd-2.39.0/.gitattributes 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/.gitattributes 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1 @@ +/build/build_mingw.cmd eol=crlf \ No newline at end of file diff -Nru i2pd-2.39.0/.github/workflows/build-freebsd.yml i2pd-2.43.0/.github/workflows/build-freebsd.yml --- i2pd-2.39.0/.github/workflows/build-freebsd.yml 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/.github/workflows/build-freebsd.yml 2022-08-21 19:40:41.000000000 +0000 @@ -4,15 +4,16 @@ jobs: build: - runs-on: macos-latest + runs-on: macos-12 name: with UPnP steps: - uses: actions/checkout@v2 - name: Test in FreeBSD id: test - uses: vmactions/freebsd-vm@v0.1.4 + uses: vmactions/freebsd-vm@v0.2.0 with: usesh: true + mem: 2048 prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc run: | cd build diff -Nru i2pd-2.39.0/.github/workflows/build-windows.yml i2pd-2.43.0/.github/workflows/build-windows.yml --- i2pd-2.39.0/.github/workflows/build-windows.yml 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/.github/workflows/build-windows.yml 2022-08-21 19:40:41.000000000 +0000 @@ -8,14 +8,15 @@ jobs: build: - name: Building for ${{ matrix.arch }} + name: Building using ${{ matrix.arch }} toolchain runs-on: windows-latest strategy: fail-fast: true matrix: include: [ - { msystem: MINGW64, arch: x86_64 }, - { msystem: MINGW32, arch: i686 } + { msystem: UCRT64, arch: ucrt-x86_64, arch_short: x64-ucrt }, + { msystem: MINGW64, arch: x86_64, arch_short: x64 }, + { msystem: MINGW32, arch: i686, arch_short: x86 } ] steps: - uses: actions/checkout@v2 @@ -25,11 +26,50 @@ msystem: ${{ matrix.msystem }} install: base-devel mingw-w64-${{ matrix.arch }}-gcc mingw-w64-${{ matrix.arch }}-boost mingw-w64-${{ matrix.arch }}-openssl mingw-w64-${{ matrix.arch }}-miniupnpc update: true - - name: build application + - name: Build application run: | mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon - make USE_UPNP=yes DEBUG=no -j3 + make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes -j3 - name: Upload artifacts uses: actions/upload-artifact@v2 with: + name: i2pd-${{ matrix.arch_short }}.exe + path: i2pd.exe + build-xp: + name: Building for Windows XP + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW32 + install: base-devel git mingw-w64-i686-gcc mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-miniupnpc + update: true + - name: Build WinXP-capable CRT packages + run: | + git clone https://github.com/msys2/MINGW-packages + pushd MINGW-packages + pushd mingw-w64-headers-git + sed -i 's/0x601/0x501/' PKGBUILD + MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm + pacman --noconfirm -U mingw-w64-i686-headers-git-*-any.pkg.tar.zst + popd + pushd mingw-w64-crt-git + MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm + pacman --noconfirm -U mingw-w64-i686-crt-git-*-any.pkg.tar.zst + popd + pushd mingw-w64-winpthreads-git + MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm + pacman --noconfirm -U mingw-w64-i686-libwinpthread-git-*-any.pkg.tar.zst mingw-w64-i686-winpthreads-git-*-any.pkg.tar.zst + popd + popd + - name: Build application + run: | + mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon + make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes USE_WINXP_FLAGS=yes -j3 + - name: Upload artifacts + uses: actions/upload-artifact@v2 + with: + name: i2pd-xp.exe path: i2pd.exe diff -Nru i2pd-2.39.0/.github/workflows/build.yml i2pd-2.43.0/.github/workflows/build.yml --- i2pd-2.39.0/.github/workflows/build.yml 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/.github/workflows/build.yml 2022-08-21 19:40:41.000000000 +0000 @@ -49,7 +49,7 @@ run: | sudo apt-get update sudo apt-get install devscripts - debchange -v "`git describe --tags`-stretch" -M --distribution stretch "trunk build" + debchange -v "`git describe --tags`-stretch" -b -M --distribution stretch "trunk build" - uses: singingwolfboy/build-dpkg-stretch@v1 id: build with: @@ -73,7 +73,7 @@ run: | sudo apt-get update sudo apt-get install devscripts - debchange -v "`git describe --tags`-buster" -M --distribution buster "trunk build" + debchange -v "`git describe --tags`-buster" -b -M --distribution buster "trunk build" - uses: singingwolfboy/build-dpkg-buster@v1 id: build with: diff -Nru i2pd-2.39.0/.github/workflows/docker.yml i2pd-2.43.0/.github/workflows/docker.yml --- i2pd-2.39.0/.github/workflows/docker.yml 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/.github/workflows/docker.yml 2022-08-21 19:40:41.000000000 +0000 @@ -1,6 +1,11 @@ name: Build containers -on: [push] +on: + push: + branches: + - openssl + tags: + - '*' jobs: docker: @@ -58,6 +63,8 @@ push: true tags: | purplei2p/i2pd:latest + purplei2p/i2pd:latest-release purplei2p/i2pd:release-${{ env.RELEASE_VERSION }} ghcr.io/purplei2p/i2pd:latest + ghcr.io/purplei2p/i2pd:latest-release ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }} diff -Nru i2pd-2.39.0/.gitignore i2pd-2.43.0/.gitignore --- i2pd-2.39.0/.gitignore 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/.gitignore 2022-08-21 19:40:41.000000000 +0000 @@ -260,6 +260,7 @@ build/Makefile # debian stuff +debian/i2pd.1.gz .pc/ # qt diff -Nru i2pd-2.39.0/i18n/Armenian.cpp i2pd-2.43.0/i18n/Armenian.cpp --- i2pd-2.39.0/i18n/Armenian.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/i18n/Armenian.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,215 @@ +/* +* 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" + +// Armenian localization file + +namespace i2p +{ +namespace i18n +{ +namespace armenian // language namespace +{ + // language name in lowercase + static std::string language = "armenian"; + + // 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", "ԿիԲ"}, + {"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", "ԼԱՎ"}, + {"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", "Floodfills-ներ"}, + {"Client Tunnels", "Oգտատիրական թունելներ"}, + {"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.", " Նշում. արդյունքի տողը կարող է օգտագործվել միայն 2LD տիրույթներ գրանցելու համար (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", "ոչ 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", "Ստորև Դուք կարող եք գտնել այս հոսթը jump ծառայությունների միջոցով"}, + {"Invalid request", "Սխալ հարցում"}, + {"Proxy unable to parse your request", "Պրոկսին չի կարող հասկանալ Ձեր հարցումը"}, + {"addresshelper is not supported", "addresshelper-ը համատեղելի չէ"}, + {"Host", "Հոսթ"}, + {"added to router's addressbook from helper", "Ավելացված է երթուղիչի հասցեագրքում 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", "Սխալ արտաքին պրոքսի"}, + {"bad outproxy settings", "Սխալ արտաքին պրոկսի կարգավորումներ"}, + {"not inside I2P network, but outproxy is not enabled", "Հարցումը I2P ցանցից դուրս է, բայց արտաքին պրոքսին միացված չէ"}, + {"unknown outproxy url", "արտաքին պրոքսիի անհայտ URL"}, + {"cannot resolve upstream proxy", "Չհաջողվեց որոշել վերադաս պրոկսին"}, + {"hostname too long", "Հոսթի անունը չափազանց երկար է"}, + {"cannot connect to upstream socks proxy", "չհաջողվեց միանալ վերադաս socks պրոկսիին"}, + {"Cannot negotiate with socks proxy", "Չհաջողվեց պայմանավորվել վերադաս socks պրոկսիի հետ"}, + {"CONNECT error", "Սխալ CONNECT հարցում"}, + {"Failed to Connect", "Միանալ չhաջողվեց"}, + {"socks proxy error", "Սխալ SOCKS պրոկսի"}, + {"failed to send request to upstream", "Չհաջողվեց հարցումն ուղարկել վերադաս պրոկսիին"}, + {"No Reply From socks proxy", "Բացակայում է պատասխանը SOCKS պրոկսի սերվերի կողմից"}, + {"cannot connect", "Հնարավոր չե միանալ"}, + {"http out proxy not implemented", "Արտաքին http պրոկսին դեռ իրականացված չէ"}, + {"cannot connect to upstream http proxy", "Չհաջողվեց միանալ վերադաս http պրոկսի սերվերին"}, + {"Host is down", "Հոսթն անհասանելի է"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Հոսթի հետ կապը հաստատել չհաջողվեց, հնարավոր է այն անջատված է, փորձեք միանալ քիչ ուշ"}, + {"", ""}, + }; + + static std::map> plurals + { + {"days", {"օր", "օր"}}, + {"hours", {"ժամ", "ժամ"}}, + {"minutes", {"րոպե", "րոպե"}}, + {"seconds", {"վարկյան", "վարկյան"}}, + {"", {"", ""}}, + }; + + std::shared_ptr GetLocale() + { + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); + } + +} // language +} // i18n +} // i2p diff -Nru i2pd-2.39.0/i18n/Chinese.cpp i2pd-2.43.0/i18n/Chinese.cpp --- i2pd-2.39.0/i18n/Chinese.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/i18n/Chinese.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,217 @@ +/* +* Copyright (c) 2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include +#include +#include +#include "I18N.h" + +// Simplified Chinese localization file +// This is an example translation file without strings in it. + +namespace i2p +{ +namespace i18n +{ +namespace chinese // language namespace +{ + // language name in lowercase + static std::string language = "chinese"; + + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural (int n) { + return 0; + } + + static std::map strings + { + {"KiB", "KiB"}, + {"MiB", "MiB"}, + {"GiB", "GiB"}, + {"building", "正在构建"}, + {"failed", "连接失败"}, + {"expiring", "即将过期"}, + {"established", "连接已建立"}, + {"unknown", "未知"}, + {"exploratory", "探测"}, + {"Purple I2P Webconsole", "Purple I2P 网页控制台"}, + {"i2pd webconsole", "i2pd 网页控制台"}, + {"Main page", "主页"}, + {"Router commands", "路由命令"}, + {"Local Destinations", "本地目标"}, + {"LeaseSets", "租契集"}, + {"Tunnels", "隧道"}, + {"Transit Tunnels", "中转隧道"}, + {"Transports", "传输"}, + {"I2P tunnels", "I2P 隧道"}, + {"SAM sessions", "SAM 会话"}, + {"ERROR", "错误"}, + {"OK", "良好"}, + {"Testing", "测试中"}, + {"Firewalled", "受到防火墙限制"}, + {"Unknown", "未知"}, + {"Proxy", "代理"}, + {"Mesh", "Mesh组网"}, + {"Error", "错误"}, + {"Clock skew", "时钟偏移"}, + {"Offline", "离线"}, + {"Symmetric NAT", "对称 NAT"}, + {"Uptime", "运行时间"}, + {"Network status", "IPv4 网络状态"}, + {"Network status v6", "IPv6 网络状态"}, + {"Stopping in", "距停止还有:"}, + {"Family", "家族"}, + {"Tunnel creation success rate", "隧道创建成功率"}, + {"Received", "已接收"}, + {"KiB/s", "KiB/s"}, + {"Sent", "已发送"}, + {"Transit", "中转"}, + {"Data path", "数据文件路径"}, + {"Hidden content. Press on text to see.", "隐藏内容 请点击此处查看。"}, + {"Router Ident", "路由身份"}, + {"Router Family", "路由器家族"}, + {"Router Caps", "路由器类型"}, + {"Version", "版本"}, + {"Our external address", "外部地址"}, + {"supported", "支持"}, + {"Routers", "路由节点"}, + {"Floodfills", "洪泛节点"}, + {"Client Tunnels", "客户端隧道"}, + {"Services", "服务"}, + {"Enabled", "启用"}, + {"Disabled", "禁用"}, + {"Encrypted B33 address", "加密的 B33 地址"}, + {"Address registration line", "地址域名注册"}, + {"Domain", "域名"}, + {"Generate", "生成"}, + {"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", "未到期的租约"}, + {"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", "您可以尝试在下方的跳转服务上找到这个主机"}, + {"Invalid request", "无效请求"}, + {"Proxy unable to parse your request", "代理无法解析您的请求"}, + {"addresshelper is not supported", "不支持地址助手"}, + {"Host", "主机"}, + {"added to router's addressbook from helper", "将此地址从地址助手添加到地址簿"}, + {"Click here to proceed:", "点击此处继续:"}, + {"Continue", "继续"}, + {"Addresshelper found", "找到地址助手"}, + {"already in router's addressbook", "已在路由器的地址簿中"}, + {"Click here to update record:", "点击此处更新地址簿记录"}, + {"invalid request uri", "无效的 URL 请求"}, + {"Can't detect destination host from request", "无法从请求中检测到目标主机"}, + {"Outproxy failure", "出口代理失效"}, + {"bad outproxy settings", "错误的出口代理设置"}, + {"not inside I2P network, but outproxy is not enabled", "该地址不在 I2P 网络内,但未启用出口代理"}, + {"unknown outproxy url", "未知的出口代理地址"}, + {"cannot resolve upstream proxy", "无法解析上游代理"}, + {"hostname too long", "主机名过长"}, + {"cannot connect to upstream socks proxy", "无法连接到上游 socks 代理"}, + {"Cannot negotiate with socks proxy", "无法与 socks 代理协商"}, + {"CONNECT error", "连接错误"}, + {"Failed to Connect", "连接失败"}, + {"socks proxy error", "socks 代理错误"}, + {"failed to send request to upstream", "向上游发送请求失败"}, + {"No Reply From socks proxy", "没有来自 socks 代理的回复"}, + {"cannot connect", "无法连接"}, + {"http out proxy not implemented", "http 出口代理未实现"}, + {"cannot connect to upstream http proxy", "无法连接到上游 http 代理"}, + {"Host is down", "主机已关闭"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "无法创建到目标主机的连接。主机可能已下线,请稍后再试。"}, + {"", ""}, + }; + + static std::map> plurals + { + {"days", {"天"}}, + {"hours", {"时"}}, + {"minutes", {"分"}}, + {"seconds", {"秒"}}, + {"", {""}}, + }; + + std::shared_ptr GetLocale() + { + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); + } + +} // language +} // i18n +} // i2p diff -Nru i2pd-2.39.0/i18n/French.cpp i2pd-2.43.0/i18n/French.cpp --- i2pd-2.39.0/i18n/French.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/i18n/French.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,211 @@ +/* +* Copyright (c) 2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include +#include +#include +#include "I18N.h" + +// French localization file + +namespace i2p +{ +namespace i18n +{ +namespace french // language namespace +{ + // language name in lowercase + static std::string language = "french"; + + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural (int n) { + return n != 1 ? 1 : 0; + } + + static std::map strings + { + {"KiB", "Kio"}, + {"MiB", "Mio"}, + {"GiB", "Gio"}, + {"building", "En construction"}, + {"failed", "échoué"}, + {"expiring", "expiré"}, + {"established", "établi"}, + {"unknown", "inconnu"}, + {"exploratory", "exploratoire"}, + {"Purple I2P Webconsole", "Console web Purple I2P"}, + {"i2pd webconsole", "Console web i2pd"}, + {"Main page", "Page principale"}, + {"Router commands", "Commandes du routeur"}, + {"Local Destinations", "Destinations locales"}, + {"LeaseSets", "Jeu de baux"}, + {"Tunnels", "Tunnels"}, + {"Transit Tunnels", "Tunnels transitoires"}, + {"Transports", "Transports"}, + {"I2P tunnels", "Tunnels I2P"}, + {"SAM sessions", "Sessions SAM"}, + {"ERROR", "ERREUR"}, + {"OK", "OK"}, + {"Testing", "Test en cours"}, + {"Firewalled", "Derrière un pare-feu"}, + {"Unknown", "Inconnu"}, + {"Proxy", "Proxy"}, + {"Mesh", "Maillé"}, + {"Error", "Erreur"}, + {"Clock skew", "Horloge décalée"}, + {"Offline", "Hors ligne"}, + {"Symmetric NAT", "NAT symétrique"}, + {"Uptime", "Temps de fonctionnement"}, + {"Network status", "État du réseau"}, + {"Network status v6", "État du réseau v6"}, + {"Stopping in", "Arrêt dans"}, + {"Family", "Famille"}, + {"Tunnel creation success rate", "Taux de succès de création de tunnels"}, + {"Received", "Reçu"}, + {"KiB/s", "kio/s"}, + {"Sent", "Envoyé"}, + {"Transit", "Transité"}, + {"Data path", "Emplacement des données"}, + {"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour afficher."}, + {"Router Ident", "Identifiant du routeur"}, + {"Router Family", "Famille du routeur"}, + {"Router Caps", "Limiteurs du routeur"}, + {"Version", "Version"}, + {"Our external address", "Notre adresse externe"}, + {"supported", "supporté"}, + {"Routers", "Routeurs"}, + {"Client Tunnels", "Tunnels clients"}, + {"Services", "Services"}, + {"Enabled", "Activé"}, + {"Disabled", "Désactivé"}, + {"Encrypted B33 address", "Adresse B33 chiffrée"}, + {"Address registration line", "Ligne d'inscription de l'adresse"}, + {"Domain", "Domaine"}, + {"Generate", "Générer"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Note: La chaîne résultante peut seulement être utilisée pour enregistrer les domaines 2LD (exemple.i2p). Pour enregistrer des sous-domaines, veuillez utiliser i2pd-tools."}, + {"Address", "Adresse"}, + {"Type", "Type"}, + {"Inbound tunnels", "Tunnels entrants"}, + {"ms", "ms"}, + {"Outbound tunnels", "Tunnels sortants"}, + {"Tags", "Balises"}, + {"Incoming", "Entrant"}, + {"Outgoing", "Sortant"}, + {"Destination", "Destination"}, + {"Amount", "Quantité"}, + {"Incoming Tags", "Balises entrantes"}, + {"Tags sessions", "Sessions des balises"}, + {"Status", "Statut"}, + {"Local Destination", "Destination locale"}, + {"Streams", "Flux"}, + {"Close stream", "Fermer le flux"}, + {"I2CP session not found", "Session I2CP introuvable"}, + {"I2CP is not enabled", "I2CP est désactivé"}, + {"Invalid", "Invalide"}, + {"Store type", "Type de stockage"}, + {"Expires", "Expire"}, + {"Non Expired Leases", "Baux non expirés"}, + {"Gateway", "Passerelle"}, + {"TunnelID", "ID du tunnel"}, + {"EndDate", "Date de fin"}, + {"Queue size", "Longueur de la file"}, + {"Run peer test", "Lancer test des pairs"}, + {"Decline transit tunnels", "Refuser les tunnels transitoires"}, + {"Accept transit tunnels", "Accepter les tunnels transitoires"}, + {"Cancel graceful shutdown", "Annuler l'arrêt gracieux"}, + {"Start graceful shutdown", "Démarrer l'arrêt gracieux"}, + {"Force shutdown", "Forcer l'arrêt"}, + {"Reload external CSS styles", "Rafraîchir les styles CSS externes"}, + {"Note: any action done here are not persistent and not changes your config files.", "Note: Toute action effectuée ici n'est pas permanente et ne modifie pas vos fichiers de configuration."}, + {"Logging level", "Niveau de journalisation"}, + {"Transit tunnels limit", "Limite sur les tunnels transitoires"}, + {"Change", "Changer"}, + {"Change language", "Changer la langue"}, + {"no transit tunnels currently built", "aucun tunnel transitoire présentement établi"}, + {"SAM disabled", "SAM désactivé"}, + {"no sessions currently running", "aucune session présentement en cours"}, + {"SAM session not found", "session SAM introuvable"}, + {"SAM Session", "Session SAM"}, + {"Server Tunnels", "Tunnels serveurs"}, + {"Unknown page", "Page inconnue"}, + {"Invalid token", "Jeton invalide"}, + {"SUCCESS", "SUCCÈS"}, + {"Stream closed", "Flux fermé"}, + {"Stream not found or already was closed", "Flux introuvable ou déjà fermé"}, + {"Destination not found", "Destination introuvable"}, + {"StreamID can't be null", "StreamID ne peut pas être vide"}, + {"Return to destination page", "Retourner à la page de destination"}, + {"You will be redirected in 5 seconds", "Vous allez être redirigé dans cinq secondes"}, + {"Transit tunnels count must not exceed 65535", "Le nombre de tunnels transitoires ne doit pas dépasser 65535"}, + {"Back to commands list", "Retour à la liste des commandes"}, + {"Register at reg.i2p", "Inscription à reg.i2p"}, + {"Description", "Description"}, + {"A bit information about service on domain", "Un peu d'information à propos des services disponibles dans le domaine"}, + {"Submit", "Soumettre"}, + {"Domain can't end with .b32.i2p", "Le domaine ne peut pas terminer par .b32.i2p"}, + {"Domain must end with .i2p", "Le domaine doit terminer par .i2p"}, + {"Such destination is not found", "Cette destination est introuvable"}, + {"Unknown command", "Commande inconnue"}, + {"Command accepted", "Commande acceptée"}, + {"Proxy error", "Erreur de proxy"}, + {"Proxy info", "Information sur le proxy"}, + {"Proxy error: Host not found", "Erreur de proxy: Hôte introuvable"}, + {"Remote host not found in router's addressbook", "Hôte distant introuvable dans le carnet d'adresse du routeur"}, + {"You may try to find this host on jump services below", "Vous pouvez essayer de trouver cet hôte sur des services de redirection ci-dessous"}, + {"Invalid request", "Requête invalide"}, + {"Proxy unable to parse your request", "Proxy incapable de comprendre votre requête"}, + {"addresshelper is not supported", "Assistant d'adresse non supporté"}, + {"Host", "Hôte"}, + {"added to router's addressbook from helper", "Ajouté au carnet d'adresse du routeur par l'assistant"}, + {"Click here to proceed:", "Cliquez ici pour continuer:"}, + {"Continue", "Continuer"}, + {"Addresshelper found", "Assistant d'adresse trouvé"}, + {"already in router's addressbook", "déjà dans le carnet d'adresses du routeur"}, + {"Click here to update record:", "Cliquez ici pour mettre à jour le carnet d'adresse:"}, + {"invalid request uri", "uri de la requête invalide"}, + {"Can't detect destination host from request", "Impossible de détecter l'hôte de destination à partir de la requête"}, + {"Outproxy failure", "Échec de proxy de sortie"}, + {"bad outproxy settings", "Mauvaise configuration du proxy de sortie"}, + {"not inside I2P network, but outproxy is not enabled", "pas dans le réseau I2P, mais le proxy de sortie n'est pas activé"}, + {"unknown outproxy url", "URL du proxy de sortie inconnu"}, + {"cannot resolve upstream proxy", "impossible de résoudre l'adresse du proxy en amont"}, + {"hostname too long", "nom d'hôte trop long"}, + {"cannot connect to upstream socks proxy", "impossible de se connecter au proxy socks en amont"}, + {"Cannot negotiate with socks proxy", "Impossible de négocier avec le proxy socks"}, + {"CONNECT error", "Erreur de connexion"}, + {"Failed to Connect", "Échec de connexion"}, + {"socks proxy error", "Erreur de proxy socks"}, + {"failed to send request to upstream", "Erreur lors de l'envoie de la requête en amont"}, + {"No Reply From socks proxy", "Pas de réponse du proxy socks"}, + {"cannot connect", "impossible de connecter"}, + {"http out proxy not implemented", "Proxy de sortie HTTP non implémenté"}, + {"cannot connect to upstream http proxy", "impossible de se connecter au proxy HTTP en amont"}, + {"Host is down", "Hôte hors service"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Impossible d'établir une connexion avec l'hôte, il est peut-être hors service. Veuillez réessayer plus tard."}, + {"", ""}, + }; + + static std::map> plurals + { + {"days", {"jour", "jours"}}, + {"hours", {"heure", "heures"}}, + {"minutes", {"minute", "minutes"}}, + {"seconds", {"seconde", "secondes"}}, + {"", {"", ""}}, + }; + + std::shared_ptr GetLocale() + { + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); + } + +} // language +} // i18n +} // i2p diff -Nru i2pd-2.39.0/i18n/German.cpp i2pd-2.43.0/i18n/German.cpp --- i2pd-2.39.0/i18n/German.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/i18n/German.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,216 @@ +/* +* Copyright (c) 2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include +#include +#include +#include "I18N.h" + +// German localization file + +namespace i2p +{ +namespace i18n +{ +namespace german // language namespace +{ + // language name in lowercase + static std::string language = "german"; + + // 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 + { + {"Purple I2P Webconsole", "Purple-I2P-Webkonsole"}, + {"KiB", "KiB"}, + {"MiB", "MiB"}, + {"GiB", "GiB"}, + {"building", "In Bau"}, + {"failed", "fehlgeschlagen"}, + {"expiring", "läuft ab"}, + {"established", "hergestellt"}, + {"unknown", "Unbekannt"}, + {"exploratory", "erforschend"}, + {"i2pd webconsole", "i2pd-Webkonsole"}, + {"Main page", "Startseite"}, + {"Router commands", "Routerbefehle"}, + {"Local Destinations", "Lokale Ziele"}, + {"LeaseSets", "LeaseSets"}, + {"Tunnels", "Tunnel"}, + {"Transit Tunnels", "Transittunnel"}, + {"Transports", "Transporte"}, + {"I2P tunnels", "I2P-Tunnel"}, + {"SAM sessions", "SAM-Sitzungen"}, + {"ERROR", "FEHLER"}, + {"OK", "OK"}, + {"Testing", "Testen"}, + {"Firewalled", "Hinter einer Firewall"}, + {"Unknown", "Unbekannt"}, + {"Proxy", "Proxy"}, + {"Mesh", "Mesh"}, + {"Error", "Fehler"}, + {"Clock skew", "Zeitabweichung"}, + {"Offline", "Offline"}, + {"Symmetric NAT", "Symmetrisches NAT"}, + {"Uptime", "Laufzeit"}, + {"Network status", "Netzwerkstatus"}, + {"Network status v6", "Netzwerkstatus v6"}, + {"Stopping in", "Stoppt in"}, + {"Family", "Familie"}, + {"Tunnel creation success rate", "Erfolgsrate der Tunnelerstellung"}, + {"Received", "Eingegangen"}, + {"KiB/s", "KiB/s"}, + {"Sent", "Gesendet"}, + {"Transit", "Transit"}, + {"Data path", "Datenpfad"}, + {"Hidden content. Press on text to see.", "Versteckter Inhalt. Klicke hier, um ihn zu sehen."}, + {"Router Ident", "Routeridentität"}, + {"Router Family", "Routerfamilie"}, + {"Router Caps", "Routerattribute"}, + {"Version", "Version"}, + {"Our external address", "Unsere externe Adresse"}, + {"supported", "unterstützt"}, + {"Routers", "Router"}, + {"Floodfills", "Floodfills"}, + {"Client Tunnels", "Clienttunnel"}, + {"Services", "Services"}, + {"Enabled", "Aktiviert"}, + {"Disabled", "Deaktiviert"}, + {"Encrypted B33 address", "Verschlüsselte B33-Adresse"}, + {"Address registration line", "Adressregistrierungszeile"}, + {"Domain", "Domain"}, + {"Generate", "Generieren"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Hinweis: Der resultierende String kann nur für die Registrierung einer 2LD-Domain (beispiel.i2p) benutzt werden. Für die Registrierung von Subdomains kann i2pd-tools verwendet werden."}, + {"Address", "Adresse"}, + {"Type", "Typ"}, + {"EncType", "Verschlüsselungstyp"}, + {"Inbound tunnels", "Eingehende Tunnel"}, + {"ms", "ms"}, + {"Outbound tunnels", "Ausgehende Tunnel"}, + {"Tags", "Tags"}, + {"Incoming", "Eingehend"}, + {"Outgoing", "Ausgehend"}, + {"Destination", "Ziel"}, + {"Amount", "Anzahl"}, + {"Incoming Tags", "Eingehende Tags"}, + {"Tags sessions", "Tags-Sitzungen"}, + {"Status", "Status"}, + {"Local Destination", "Lokales Ziel"}, + {"Streams", "Streams"}, + {"Close stream", "Stream schließen"}, + {"I2CP session not found", "I2CP-Sitzung nicht gefunden"}, + {"I2CP is not enabled", "I2CP ist nicht aktiviert"}, + {"Invalid", "Ungültig"}, + {"Store type", "Speichertyp"}, + {"Expires", "Ablaufdatum"}, + {"Non Expired Leases", "Nicht abgelaufene Leases"}, + {"Gateway", "Gateway"}, + {"TunnelID", "TunnelID"}, + {"EndDate", "Enddatum"}, + {"not floodfill", "kein Floodfill"}, + {"Queue size", "Größe der Warteschlange"}, + {"Run peer test", "Peer-Test durchführen"}, + {"Decline transit tunnels", "Transittunnel ablehnen"}, + {"Accept transit tunnels", "Transittunnel akzeptieren"}, + {"Cancel graceful shutdown", "Beende das kontrollierte Herunterfahren"}, + {"Start graceful shutdown", "Starte das kontrollierte Herunterfahren"}, + {"Force shutdown", "Herunterfahren erzwingen"}, + {"Reload external CSS styles", "Lade externe CSS-Stile neu"}, + {"Note: any action done here are not persistent and not changes your config files.", "Hinweis: Alle hier durchgeführten Aktionen sind nicht dauerhaft und ändern die Konfigurationsdateien nicht."}, + {"Logging level", "Protokollierungslevel"}, + {"Transit tunnels limit", "Limit für Transittunnel"}, + {"Change", "Ändern"}, + {"Change language", "Sprache ändern"}, + {"no transit tunnels currently built", "derzeit keine Transittunnel aufgebaut"}, + {"SAM disabled", "SAM deaktiviert"}, + {"no sessions currently running", "Derzeit keine laufenden Sitzungen"}, + {"SAM session not found", "SAM-Sitzung nicht gefunden"}, + {"SAM Session", "SAM-Sitzung"}, + {"Server Tunnels", "Servertunnel"}, + {"Client Forwards", "Client-Weiterleitungen"}, + {"Server Forwards", "Server-Weiterleitungen"}, + {"Unknown page", "Unbekannte Seite"}, + {"Invalid token", "Ungültiger Token"}, + {"SUCCESS", "ERFOLGREICH"}, + {"Stream closed", "Stream geschlossen"}, + {"Stream not found or already was closed", "Stream nicht gefunden oder bereits geschlossen"}, + {"Destination not found", "Ziel nicht gefunden"}, + {"StreamID can't be null", "StreamID kann nicht null sein"}, + {"Return to destination page", "Zurück zur Ziel-Seite"}, + {"You will be redirected in 5 seconds", "Du wirst in 5 Sekunden weitergeleitet"}, + {"Transit tunnels count must not exceed 65535", "Es darf maximal 65535 Transittunnel geben"}, + {"Back to commands list", "Zurück zur Befehlsliste"}, + {"Register at reg.i2p", "Auf reg.i2p registrieren"}, + {"Description", "Beschreibung"}, + {"A bit information about service on domain", "Ein paar Informationen über den Service auf der Domain"}, + {"Submit", "Absenden"}, + {"Domain can't end with .b32.i2p", "Domain kann nicht auf .b32.i2p enden"}, + {"Domain must end with .i2p", "Domain muss auf .i2p enden"}, + {"Such destination is not found", "Ein solches Ziel konnte nicht gefunden werden"}, + {"Unknown command", "Unbekannter Befehl"}, + {"Command accepted", "Befehl akzeptiert"}, + {"Proxy error", "Proxy-Fehler"}, + {"Proxy info", "Proxy-Info"}, + {"Proxy error: Host not found", "Proxy-Fehler: Host nicht gefunden"}, + {"Remote host not found in router's addressbook", "Remote-Host nicht im Router-Adressbuch gefunden"}, + {"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einem der nachfolgenden Jump-Services finden"}, + {"Invalid request", "Ungültige Anfrage"}, + {"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht verarbeiten"}, + {"addresshelper is not supported", "Addresshelfer wird nicht unterstützt"}, + {"Host", "Host"}, + {"added to router's addressbook from helper", "vom Helfer zum Router-Adressbuch hinzugefügt"}, + {"Click here to proceed:", "Klicke hier um fortzufahren:"}, + {"Continue", "Fortsetzen"}, + {"Addresshelper found", "Adresshelfer gefunden"}, + {"already in router's addressbook", "bereits im Adressbuch des Routers"}, + {"Click here to update record:", "Klicke hier, um den Eintrag zu aktualisieren:"}, + {"invalid request uri", "ungültige Anfrage-URI"}, + {"Can't detect destination host from request", "Kann den Ziel-Host von der Anfrage nicht erkennen"}, + {"Outproxy failure", "Outproxy-Fehler"}, + {"bad outproxy settings", "ungültige Outproxy-Einstellungen"}, + {"not inside I2P network, but outproxy is not enabled", "außerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"}, + {"unknown outproxy url", "unbekannte Outproxy-URL"}, + {"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"}, + {"hostname too long", "Hostname zu lang"}, + {"cannot connect to upstream socks proxy", "Kann keine Verbindung zum Upstream-Socks-Proxy herstellen"}, + {"Cannot negotiate with socks proxy", "Kann nicht mit Socks-Proxy verhandeln"}, + {"CONNECT error", "CONNECT-Fehler"}, + {"Failed to Connect", "Verbindung konnte nicht hergestellt werden"}, + {"socks proxy error", "Socks-Proxy-Fehler"}, + {"failed to send request to upstream", "Anfrage an den Upstream zu senden ist gescheitert"}, + {"No Reply From socks proxy", "Keine Antwort vom Socks-Proxy"}, + {"cannot connect", "kann nicht verbinden"}, + {"http out proxy not implemented", "HTTP-Outproxy nicht implementiert"}, + {"cannot connect to upstream http proxy", "Kann nicht zu Upstream-HTTP-Proxy verbinden"}, + {"Host is down", "Host ist offline"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Konnte keine Verbindung zum angefragten Host aufbauen, vielleicht ist er offline. Versuche es später noch mal."}, + {"", ""}, + }; + + static std::map> plurals + { + {"days", {"Tag", "Tage"}}, + {"hours", {"Stunde", "Stunden"}}, + {"minutes", {"Minute", "Minuten"}}, + {"seconds", {"Sekunde", "Sekunden"}}, + {"", {"", ""}}, + }; + + std::shared_ptr GetLocale() + { + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); + } + +} // language +} // i18n +} // i2p diff -Nru i2pd-2.39.0/i18n/I18N.h i2pd-2.43.0/i18n/I18N.h --- i2pd-2.39.0/i18n/I18N.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/i18n/I18N.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021, The PurpleI2P Project +* Copyright (c) 2021-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -9,7 +9,7 @@ #ifndef __I18N_H__ #define __I18N_H__ -#include "RouterContext.h" +#include "ClientContext.h" namespace i2p { @@ -19,19 +19,19 @@ { const auto it = i2p::i18n::languages.find(lang); if (it == i2p::i18n::languages.end()) // fallback - i2p::context.SetLanguage (i2p::i18n::english::GetLocale()); + i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale()); else - i2p::context.SetLanguage (it->second.LocaleFunc()); + 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 -Nru i2pd-2.39.0/i18n/I18N_langs.h i2pd-2.43.0/i18n/I18N_langs.h --- i2pd-2.39.0/i18n/I18N_langs.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/i18n/I18N_langs.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021, The PurpleI2P Project +* Copyright (c) 2021-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -72,19 +72,27 @@ // Add localization here with language name as namespace namespace afrikaans { std::shared_ptr GetLocale (); } + namespace armenian { std::shared_ptr GetLocale (); } + namespace chinese { std::shared_ptr GetLocale (); } namespace english { std::shared_ptr GetLocale (); } + namespace french { std::shared_ptr GetLocale (); } + namespace german { 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 + * That map contains international language name lower-case, name in it's language and it's code */ static std::map languages { { "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} }, + { "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} }, + { "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} }, { "english", {"English", "en", i2p::i18n::english::GetLocale} }, + { "french", {"Français", "fr", i2p::i18n::french::GetLocale} }, + { "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} }, { "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} }, { "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} }, { "ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} }, diff -Nru i2pd-2.39.0/i18n/Russian.cpp i2pd-2.43.0/i18n/Russian.cpp --- i2pd-2.39.0/i18n/Russian.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/i18n/Russian.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -26,7 +26,7 @@ // See for language plural forms here: // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html static int plural (int n) { - return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; + return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2; } static std::map strings diff -Nru i2pd-2.39.0/i18n/Turkmen.cpp i2pd-2.43.0/i18n/Turkmen.cpp --- i2pd-2.39.0/i18n/Turkmen.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/i18n/Turkmen.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021, The PurpleI2P Project +* Copyright (c) 2021-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -198,7 +198,6 @@ static std::map> plurals { - // ShowUptime {"days", {"gün", "gün"}}, {"hours", {"sagat", "sagat"}}, {"minutes", {"minut", "minut"}}, diff -Nru i2pd-2.39.0/i18n/Uzbek.cpp i2pd-2.43.0/i18n/Uzbek.cpp --- i2pd-2.39.0/i18n/Uzbek.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/i18n/Uzbek.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2021, The PurpleI2P Project +* Copyright (c) 2021-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -34,20 +34,21 @@ {"KiB", "KiB"}, {"MiB", "MiB"}, {"GiB", "GiB"}, - {"building", "qurilish"}, + {"building", "yaratilmoqda"}, {"failed", "muvaffaqiyatsiz"}, {"expiring", "muddati tugaydi"}, {"established", "aloqa o'rnatildi"}, {"unknown", "noma'lum"}, {"exploratory", "tadqiqiy"}, - {"i2pd webconsole", "i2pd veb -konsoli"}, + {"i2pd webconsole", "i2pd veb-konsoli"}, {"Main page", "Asosiy sahifa"}, {"Router commands", "Router buyruqlari"}, + {"Local Destinations", "Mahalliy joylanishlar"}, {"LeaseSets", "LeaseSets"}, {"Tunnels", "Tunnellar"}, - {"Transit Tunnels", "Tranzit Tunellar"}, + {"Transit Tunnels", "Tranzit Tunellari"}, {"Transports", "Transportlar"}, - {"I2P tunnels", "I2P tunnellar"}, + {"I2P tunnels", "I2P tunnellari"}, {"SAM sessions", "SAM sessiyalari"}, {"ERROR", "XATO"}, {"OK", "OK"}, @@ -70,25 +71,25 @@ {"KiB/s", "KiB/s"}, {"Sent", "Yuborilgan"}, {"Transit", "Tranzit"}, - {"Data path", "Ma'lumotlar yo'li"}, + {"Data path", "Ma'lumotlar joylanishi"}, {"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"}, + {"Router Family", "Router oilasi"}, + {"Router Caps", "Router Bayroqlari"}, {"Version", "Versiya"}, {"Our external address", "Bizning tashqi manzilimiz"}, - {"supported", "qo'llab -quvvatlanadi"}, + {"supported", "qo'llab-quvvatlanadi"}, {"Routers", "Routerlar"}, {"Floodfills", "Floodfills"}, - {"Client Tunnels", "Mijoz tunellari"}, + {"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."}, + {"Generate", "Yaratish"}, + {"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"}, @@ -99,10 +100,11 @@ {"Incoming", "Kiruvchi"}, {"Outgoing", "Chiquvchi"}, {"Destination", "Manzilgoh"}, - {"Amount", "Yig'indi"}, + {"Amount", "Soni"}, {"Incoming Tags", "Kiruvchi teglar"}, {"Tags sessions", "Teglar sessiyalari"}, {"Status", "Holat"}, + {"Local Destination", "Mahalliy joylanish"}, {"Streams", "Strim"}, {"Close stream", "Strimni o'chirish"}, {"I2CP session not found", "I2CP sessiyasi topilmadi"}, @@ -117,14 +119,15 @@ {"not floodfill", "floodfill emas"}, {"Queue size", "Navbat hajmi"}, {"Run peer test", "Sinovni boshlang"}, - {"Decline transit tunnels", "Tranzit tunnellarni rad etish"}, + {"Decline transit tunnels", "Tranzit tunnellarini 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"}, + {"Cancel graceful shutdown", "Yumshoq to'xtashni bekor qilish"}, + {"Start graceful shutdown", "Yumshoq to'xtashni boshlash"}, + {"Force shutdown", "Majburiy to'xtatish"}, {"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"}, + {"Note: any action done here are not persistent and not changes your config files.", "Eslatma: shu yerda qilingan har qanday harakat doimiy emas va konfiguratsiya fayllarini o'zgartirmaydi."}, + {"Logging level", "Jurnal darajasi"}, + {"Transit tunnels limit", "Tranzit tunellarning chegarasi"}, {"Change", "O'zgartirish"}, {"Change language", "Tilni o'zgartirish"}, {"no transit tunnels currently built", "qurilgan tranzit tunnellari yo'q"}, @@ -142,8 +145,8 @@ {"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"}, + {"Return to destination page", "Manzilgoh sahifasiga qaytish"}, + {"You will be redirected in 5 seconds", "Siz 5 soniya ichida 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"}, @@ -159,29 +162,35 @@ {"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"}, + {"You may try to find this host on jump services below", "Siz xost quyida o'tish xizmatlari orqali topishga harakat qilishingiz mumkin"}, {"Invalid request", "Noto‘g‘ri so‘rov"}, - {"Proxy unable to parse your request", "Proksi sizning so'rovingizni tahlil qila olmaydi"}, + {"Proxy unable to parse your request", "Proksi sizning so'rovingizni aniqlab ololmayapti"}, {"addresshelper is not supported", "addresshelper qo'llab -quvvatlanmaydi"}, {"Host", "Xost"}, + {"added to router's addressbook from helper", "'helper'dan routerning 'addressbook'ga qo'shildi"}, + {"Click here to proceed:", "Davom etish uchun shu yerni bosing:"}, + {"Continue", "Davom etish"}, {"Addresshelper found", "Addresshelper topildi"}, + {"already in router's addressbook", "allaqachon 'addressbook'da yozilgan"}, + {"Click here to update record:", "Yozuvni yangilash uchun shu yerni bosing:"}, {"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"}, + {"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"}, + {"cannot resolve upstream proxy", "yuqoridagi 'proxy-server'ni aniqlab olib bolmayapti"}, {"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"}, + {"cannot connect to upstream socks proxy", "yuqori 'socks proxy'ga ulanib bo'lmayapti"}, + {"Cannot negotiate with socks proxy", "'Socks proxy' 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"}, + {"Failed to Connect", "Ulanib bo'lmayapti"}, + {"socks proxy error", "'socks proxy' xatosi"}, + {"failed to send request to upstream", "yuqori http proksi-serveriga so'rovni uborib bo'lmadi"}, + {"No Reply From socks proxy", "'Socks proxy'dan javob yo'q"}, + {"cannot connect", "ulanib bo'lmaydi"}, + {"http out proxy not implemented", "tashqi HTTP proksi-serverni qo'llab-quvvatlash amalga oshirilmagan"}, + {"cannot connect to upstream http proxy", "yuqori http 'proxy-server'iga ulanib bo'lmayapti"}, {"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."}, {"", ""}, @@ -189,10 +198,10 @@ static std::map> plurals { - {"days", {"kun", "kunlar"}}, + {"days", {"kun", "kun"}}, {"hours", {"soat", "soat"}}, - {"minutes", {"daqiqa", "daqiqalar"}}, - {"seconds", {"soniya", "soniyalar"}}, + {"minutes", {"daqiqa", "daqiqa"}}, + {"seconds", {"soniya", "soniya"}}, {"", {"", ""}}, }; diff -Nru i2pd-2.39.0/libi2pd/api.cpp i2pd-2.43.0/libi2pd/api.cpp --- i2pd-2.39.0/libi2pd/api.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/api.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -60,22 +60,22 @@ else i2p::log::Logger().SendTo (i2p::fs::DataDirPath (i2p::fs::GetAppName () + ".log")); i2p::log::Logger().Start (); - LogPrint(eLogInfo, "API: starting NetDB"); + LogPrint(eLogInfo, "API: Starting NetDB"); i2p::data::netdb.Start(); - LogPrint(eLogInfo, "API: starting Transports"); + LogPrint(eLogInfo, "API: Starting Transports"); i2p::transport::transports.Start(); - LogPrint(eLogInfo, "API: starting Tunnels"); + LogPrint(eLogInfo, "API: Starting Tunnels"); i2p::tunnel::tunnels.Start(); } void StopI2P () { - LogPrint(eLogInfo, "API: shutting down"); - LogPrint(eLogInfo, "API: stopping Tunnels"); + LogPrint(eLogInfo, "API: Shutting down"); + LogPrint(eLogInfo, "API: Stopping Tunnels"); i2p::tunnel::tunnels.Stop(); - LogPrint(eLogInfo, "API: stopping Transports"); + LogPrint(eLogInfo, "API: Stopping Transports"); i2p::transport::transports.Stop(); - LogPrint(eLogInfo, "API: stopping NetDB"); + LogPrint(eLogInfo, "API: Stopping NetDB"); i2p::data::netdb.Stop(); i2p::log::Logger().Stop (); } diff -Nru i2pd-2.39.0/libi2pd/Base.cpp i2pd-2.43.0/libi2pd/Base.cpp --- i2pd-2.39.0/libi2pd/Base.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Base.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -185,10 +185,7 @@ if (InCount && !m) outCount = 3 * n; else - { - outCount = 0; return 0; - } ps = (unsigned char *)(InBuffer + InCount - 1); while ( *ps-- == P64 ) @@ -196,7 +193,7 @@ ps = (unsigned char *)InBuffer; if (outCount > len) - return -1; + return 0; pd = OutBuffer; auto endOfOutBuffer = OutBuffer + outCount; diff -Nru i2pd-2.39.0/libi2pd/Base.h i2pd-2.43.0/libi2pd/Base.h --- i2pd-2.39.0/libi2pd/Base.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Base.h 2022-08-21 19:40:41.000000000 +0000 @@ -24,8 +24,8 @@ size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen); /** - Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes - */ + * Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes + */ size_t Base64EncodingBufferSize(const size_t input_size); std::string ToBase64Standard (const std::string& in); // using standard table, for Proxy-Authorization diff -Nru i2pd-2.39.0/libi2pd/Blinding.cpp i2pd-2.43.0/libi2pd/Blinding.cpp --- i2pd-2.39.0/libi2pd/Blinding.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Blinding.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -99,7 +99,7 @@ static size_t BlindECDSA (i2p::data::SigningKeyType sigType, const uint8_t * key, const uint8_t * seed, Fn blind, Args&&...args) // blind is BlindEncodedPublicKeyECDSA or BlindEncodedPrivateKeyECDSA { - size_t publicKeyLength = 0; + size_t publicKeyLength = 0; EC_GROUP * group = nullptr; switch (sigType) { @@ -122,7 +122,7 @@ break; } default: - LogPrint (eLogError, "Blinding: signature type ", (int)sigType, " is not ECDSA"); + LogPrint (eLogError, "Blinding: Signature type ", (int)sigType, " is not ECDSA"); } if (group) { @@ -146,7 +146,10 @@ m_PublicKey.resize (len); memcpy (m_PublicKey.data (), identity->GetSigningPublicKeyBuffer (), len); m_SigType = identity->GetSigningKeyType (); - m_BlindedSigType = m_SigType; + if (m_SigType == i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519) + m_BlindedSigType = i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519; // 7 -> 11 + else + m_BlindedSigType = m_SigType; } BlindedPublicKey::BlindedPublicKey (const std::string& b33): @@ -156,7 +159,7 @@ size_t l = i2p::data::Base32ToByteStream (b33.c_str (), b33.length (), addr, 40); if (l < 32) { - LogPrint (eLogError, "Blinding: malformed b33 ", b33); + LogPrint (eLogError, "Blinding: Malformed b33 ", b33); return; } uint32_t checksum = crc32 (0, addr + 3, l - 3); @@ -186,10 +189,10 @@ memcpy (m_PublicKey.data (), addr + offset, len); } else - LogPrint (eLogError, "Blinding: public key in b33 address is too short for signature type ", (int)m_SigType); + LogPrint (eLogError, "Blinding: Public key in b33 address is too short for signature type ", (int)m_SigType); } else - LogPrint (eLogError, "Blinding: unknown signature type ", (int)m_SigType, " in b33"); + LogPrint (eLogError, "Blinding: Unknown signature type ", (int)m_SigType, " in b33"); } std::string BlindedPublicKey::ToB33 () const @@ -256,7 +259,7 @@ publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; break; default: - LogPrint (eLogError, "Blinding: can't blind signature type ", (int)m_SigType); + LogPrint (eLogError, "Blinding: Can't blind signature type ", (int)m_SigType); } return publicKeyLength; } @@ -272,21 +275,21 @@ case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384: case i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521: publicKeyLength = BlindECDSA (m_SigType, priv, seed, BlindEncodedPrivateKeyECDSA, blindedPriv, blindedPub); - break; + break; case i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: i2p::crypto::GetEd25519 ()->BlindPrivateKey (priv, seed, blindedPriv, blindedPub); publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; break; - case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: + case i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519: { - uint8_t exp[64]; - i2p::crypto::Ed25519::ExpandPrivateKey (priv, exp); + uint8_t exp[64]; + i2p::crypto::Ed25519::ExpandPrivateKey (priv, exp); i2p::crypto::GetEd25519 ()->BlindPrivateKey (exp, seed, blindedPriv, blindedPub); - publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; + publicKeyLength = i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; break; - } + } default: - LogPrint (eLogError, "Blinding: can't blind signature type ", (int)m_SigType); + LogPrint (eLogError, "Blinding: Can't blind signature type ", (int)m_SigType); } return publicKeyLength; } @@ -324,7 +327,7 @@ SHA256_Final ((uint8_t *)hash, &ctx); } else - LogPrint (eLogError, "Blinding: blinded key type ", (int)m_BlindedSigType, " is not supported"); + LogPrint (eLogError, "Blinding: Blinded key type ", (int)m_BlindedSigType, " is not supported"); return hash; } diff -Nru i2pd-2.39.0/libi2pd/Blinding.h i2pd-2.43.0/libi2pd/Blinding.h --- i2pd-2.39.0/libi2pd/Blinding.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Blinding.h 2022-08-21 19:40:41.000000000 +0000 @@ -28,8 +28,8 @@ const uint8_t * GetPublicKey () const { return m_PublicKey.data (); }; size_t GetPublicKeyLen () const { return m_PublicKey.size (); }; - SigningKeyType GetSigType () const { return m_SigType; }; - SigningKeyType GetBlindedSigType () const { return m_BlindedSigType; }; + SigningKeyType GetSigType () const { return m_SigType; }; + SigningKeyType GetBlindedSigType () const { return m_BlindedSigType; }; bool IsValid () const { return GetSigType (); }; // signature type 0 means invalid void GetSubcredential (const uint8_t * blinded, size_t len, uint8_t * subcredential) const; // 32 bytes diff -Nru i2pd-2.39.0/libi2pd/Config.cpp i2pd-2.43.0/libi2pd/Config.cpp --- i2pd-2.39.0/libi2pd/Config.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Config.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -61,7 +61,7 @@ ("service", bool_switch()->default_value(false), "Router will use system folders like '/var/lib/i2pd' (default: disabled)") ("notransit", bool_switch()->default_value(false), "Router will not accept transit tunnels at startup (default: disabled)") ("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)") - ("bandwidth", value()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") + ("bandwidth", value()->default_value(""), "Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") ("share", value()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") ("ntcp", bool_switch()->default_value(false), "Ignored. Always false") ("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)") @@ -78,9 +78,9 @@ ("limits.coresize", value()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("limits.openfiles", value()->default_value(0), "Maximum number of open files (0 - use system default)") ("limits.transittunnels", value()->default_value(2500), "Maximum active transit sessions (default:2500)") - ("limits.ntcpsoft", value()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)") - ("limits.ntcphard", value()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)") - ("limits.ntcpthreads", value()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)") + ("limits.ntcpsoft", value()->default_value(0), "Ignored") + ("limits.ntcphard", value()->default_value(0), "Ignored") + ("limits.ntcpthreads", value()->default_value(1), "Ignored") ; options_description httpserver("HTTP Server options"); @@ -109,6 +109,8 @@ ("httpproxy.outbound.length", value()->default_value("3"), "HTTP proxy outbound tunnel length") ("httpproxy.inbound.quantity", value()->default_value("5"), "HTTP proxy inbound tunnels quantity") ("httpproxy.outbound.quantity", value()->default_value("5"), "HTTP proxy outbound tunnels quantity") + ("httpproxy.inbound.lengthVariance", value()->default_value("0"), "HTTP proxy inbound tunnels length variance") + ("httpproxy.outbound.lengthVariance", value()->default_value("0"), "HTTP proxy outbound tunnels length variance") ("httpproxy.latency.min", value()->default_value("0"), "HTTP proxy min latency for tunnels") ("httpproxy.latency.max", value()->default_value("0"), "HTTP proxy max latency for tunnels") ("httpproxy.outproxy", value()->default_value(""), "HTTP proxy upstream out proxy url") @@ -130,6 +132,8 @@ ("socksproxy.outbound.length", value()->default_value("3"), "SOCKS proxy outbound tunnel length") ("socksproxy.inbound.quantity", value()->default_value("5"), "SOCKS proxy inbound tunnels quantity") ("socksproxy.outbound.quantity", value()->default_value("5"), "SOCKS proxy outbound tunnels quantity") + ("socksproxy.inbound.lengthVariance", value()->default_value("0"), "SOCKS proxy inbound tunnels length variance") + ("socksproxy.outbound.lengthVariance", value()->default_value("0"), "SOCKS proxy outbound tunnels length variance") ("socksproxy.latency.min", value()->default_value("0"), "SOCKS proxy min latency for tunnels") ("socksproxy.latency.max", value()->default_value("0"), "SOCKS proxy max latency for tunnels") ("socksproxy.outproxy.enabled", value()->default_value(false), "Enable or disable SOCKS outproxy") @@ -203,28 +207,36 @@ ("reseed.zipfile", value()->default_value(""), "Path to local .zip file to reseed from") ("reseed.proxy", value()->default_value(""), "url for reseed proxy, supports http/socks") ("reseed.urls", value()->default_value( - "https://reseed.i2p-projekt.de/," + "https://reseed2.i2p.net/," "https://reseed.diva.exchange/," "https://reseed-fr.i2pd.xyz/," "https://reseed.memcpy.io/," "https://reseed.onion.im/," "https://i2pseed.creativecowpat.net:8443/," "https://reseed.i2pgit.org/," - "https://i2p.novg.net/" + "https://i2p.novg.net/," + "https://banana.incognet.io/," + "https://reseed-pl.i2pd.xyz/," + "https://www2.mk16.de/" ), "Reseed URLs, separated by comma") ("reseed.yggurls", value()->default_value( "http://[324:71e:281a:9ed3::ace]:7070/," - "http://[301:65b9:c7cd:9a36::1]:18801/," - "http://[320:8936:ec1a:31f1::216]/" + "http://[301:65b9:c7cd:9a36::1]:18801/," + "http://[320:8936:ec1a:31f1::216]/," + "http://[306:3834:97b9:a00a::1]/," + "http://[316:f9e0:f22e:a74f::216]/" ), "Reseed URLs through the Yggdrasil, separated by comma") ; options_description addressbook("AddressBook options"); addressbook.add_options() + ("addressbook.enabled", value()->default_value(true), "Enable address book lookups and subscritions (default: enabled)") ("addressbook.defaulturl", value()->default_value( "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt" ), "AddressBook subscription URL for initial setup") - ("addressbook.subscriptions", value()->default_value("http://reg.i2p/hosts.txt"), "AddressBook subscriptions URLs, separated by comma") + ("addressbook.subscriptions", value()->default_value( + "http://reg.i2p/hosts.txt" + ), "AddressBook subscriptions URLs, separated by comma") ("addressbook.hostsfile", value()->default_value(""), "File to dump addresses in hosts.txt format"); options_description trust("Trust options"); @@ -260,6 +272,13 @@ ("ntcp2.proxy", value()->default_value(""), "Proxy URL for NTCP2 transport") ; + options_description ssu2("SSU2 Options"); + ssu2.add_options() + ("ssu2.enabled", value()->default_value(false), "Enable SSU2 (default: disabled)") + ("ssu2.published", value()->default_value(false), "Publish SSU2 (default: disabled)") + ("ssu2.port", value()->default_value(0), "Port to listen for incoming SSU2 packets (default: auto)") + ; + options_description nettime("Time sync options"); nettime.add_options() ("nettime.enabled", value()->default_value(false), "Disable time sync (default: disabled)") @@ -268,8 +287,9 @@ "1.pool.ntp.org," "2.pool.ntp.org," "3.pool.ntp.org" - ), "Comma separated list of NTCP servers") + ), "Comma separated list of NTP servers") ("nettime.ntpsyncinterval", value()->default_value(72), "NTP sync interval in hours (default: 72)") + ("nettime.frompeers", value()->default_value(true), "Sync clock from transport peers (default: enabled)") ; options_description persist("Network information persisting options"); @@ -291,6 +311,13 @@ ("meshnets.yggaddress", value()->default_value(""), "Yggdrasil address to publish") ; +#ifdef __linux__ + options_description unix_specific("UNIX-specific options"); + unix_specific.add_options() + ("unix.handle_sigtstp", bool_switch()->default_value(false), "Handle SIGTSTP and SIGCONT signals (default: disabled)") + ; +#endif + m_OptionsDesc .add(general) .add(limits) @@ -309,10 +336,14 @@ .add(websocket) // deprecated .add(exploratory) .add(ntcp2) + .add(ssu2) .add(nettime) .add(persist) .add(cpuext) .add(meshnets) +#ifdef __linux__ + .add(unix_specific) +#endif ; } diff -Nru i2pd-2.39.0/libi2pd/Config.h i2pd-2.43.0/libi2pd/Config.h --- i2pd-2.39.0/libi2pd/Config.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Config.h 2022-08-21 19:40:41.000000000 +0000 @@ -29,16 +29,16 @@ extern boost::program_options::variables_map m_Options; /** - * @brief Initialize list of acceptable parameters + * @brief Initialize list of acceptable parameters * * Should be called before any Parse* functions. */ void Init(); /** - * @brief Parse cmdline parameters, and show help if requested - * @param argc Cmdline arguments count, should be passed from main(). - * @param argv Cmdline parameters array, should be passed from main() + * @brief Parse cmdline parameters, and show help if requested + * @param argc Cmdline arguments count, should be passed from main(). + * @param argv Cmdline parameters array, should be passed from main() * * If --help is given in parameters, shows its list with description * and terminates the program with exitcode 0. @@ -52,8 +52,8 @@ void ParseCmdline(int argc, char* argv[], bool ignoreUnknown = false); /** - * @brief Load and parse given config file - * @param path Path to config file + * @brief Load and parse given config file + * @param path Path to config file * * If error occurred when opening file path is points to, * we show the error message and terminate program. @@ -67,14 +67,14 @@ void ParseConfig(const std::string& path); /** - * @brief Used to combine options from cmdline, config and default values + * @brief Used to combine options from cmdline, config and default values */ void Finalize(); /** - * @brief Accessor to parameters by name - * @param name Name of the requested parameter - * @param value Variable where to store option + * @brief Accessor to parameters by name + * @param name Name of the requested parameter + * @param value Variable where to store option * @return this function returns false if parameter not found * * Example: uint16_t port; GetOption("sam.port", port); @@ -98,9 +98,9 @@ bool GetOptionAsAny(const std::string& name, boost::any& value); /** - * @brief Set value of given parameter - * @param name Name of settable parameter - * @param value New parameter value + * @brief Set value of given parameter + * @param name Name of settable parameter + * @param value New parameter value * @return true if value set up successful, false otherwise * * Example: uint16_t port = 2827; SetOption("bob.port", port); @@ -116,8 +116,8 @@ } /** - * @brief Check is value explicitly given or default - * @param name Name of checked parameter + * @brief Check is value explicitly given or default + * @param name Name of checked parameter * @return true if value set to default, false otherwise */ bool IsDefault(const char *name); diff -Nru i2pd-2.39.0/libi2pd/Crypto.cpp i2pd-2.43.0/libi2pd/Crypto.cpp --- i2pd-2.39.0/libi2pd/Crypto.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Crypto.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -389,7 +389,7 @@ { size_t len = 32; EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); - } + } #else memcpy (m_PrivateKey, priv, 32); if (calculatePublic) @@ -398,8 +398,9 @@ } // 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 @@ 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 @@ } // 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 @@ 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 @@ 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 @@ 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 @@ EC_POINT_free (p); BN_CTX_end (ctx); + BN_CTX_free (ctx); return ret; } @@ -1302,7 +1277,7 @@ EVP_PKEY_CTX_set1_hkdf_key (pctx, tempKey, len); } if (info.length () > 0) - EVP_PKEY_CTX_add1_hkdf_info (pctx, info.c_str (), info.length ()); + EVP_PKEY_CTX_add1_hkdf_info (pctx, (const uint8_t *)info.c_str (), info.length ()); EVP_PKEY_derive (pctx, out, &outLen); EVP_PKEY_CTX_free (pctx); #else @@ -1320,7 +1295,7 @@ } // Noise - + void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len) { SHA256_CTX ctx; @@ -1330,13 +1305,23 @@ SHA256_Final (m_H, &ctx); } + void NoiseSymmetricState::MixHash (const std::vector >& bufs) + { + SHA256_CTX ctx; + SHA256_Init (&ctx); + SHA256_Update (&ctx, m_H, 32); + for (const auto& it: bufs) + SHA256_Update (&ctx, it.first, it.second); + SHA256_Final (m_H, &ctx); + } + void NoiseSymmetricState::MixKey (const uint8_t * sharedSecret) { HKDF (m_CK, sharedSecret, 32, "", m_CK); // new ck is m_CK[0:31], key is m_CK[32:63] } - static void InitNoiseState (NoiseSymmetricState& state, const uint8_t * ck, + static void InitNoiseState (NoiseSymmetricState& state, const uint8_t * ck, const uint8_t * hh, const uint8_t * pub) { // pub is Bob's public static key, hh = SHA256(h) @@ -1345,23 +1330,23 @@ SHA256_Init (&ctx); SHA256_Update (&ctx, hh, 32); SHA256_Update (&ctx, pub, 32); - SHA256_Final (state.m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub) - } - + SHA256_Final (state.m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub) + } + void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub) { static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars static const uint8_t hh[32] = { - 0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1, + 0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1, 0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, 0x54 }; // hh = SHA256(protocol_name || 0) InitNoiseState (state, (const uint8_t *)protocolName, hh, pub); // ck = protocol_name || 0 - } - + } + void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub) { - static const uint8_t protocolNameHash[] = + static const uint8_t protocolNameHash[32] = { 0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed, 0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71 @@ -1371,8 +1356,23 @@ 0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5, 0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e }; // SHA256 (protocolNameHash) - InitNoiseState (state, protocolNameHash, hh, pub); - } + InitNoiseState (state, protocolNameHash, hh, pub); + } + + void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub) + { + static const uint8_t protocolNameHash[32] = + { + 0xb1, 0x37, 0x22, 0x81, 0x74, 0x23, 0xa8, 0xfd, 0xf4, 0x2d, 0xf2, 0xe6, 0x0e, 0xd1, 0xed, 0xf4, + 0x1b, 0x93, 0x07, 0x1d, 0xb1, 0xec, 0x24, 0xa3, 0x67, 0xf7, 0x84, 0xec, 0x27, 0x0d, 0x81, 0x32 + }; // SHA256 ("Noise_XKchaobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256") + static const uint8_t hh[32] = + { + 0xdc, 0x85, 0xe6, 0xaf, 0x7b, 0x02, 0x65, 0x0c, 0xf1, 0xf9, 0x0d, 0x71, 0xfb, 0xc6, 0xd4, 0x53, + 0xa7, 0xcf, 0x6d, 0xbf, 0xbd, 0x52, 0x5e, 0xa5, 0xb5, 0x79, 0x1c, 0x47, 0xb3, 0x5e, 0xbc, 0x33 + }; // SHA256 (protocolNameHash) + InitNoiseState (state, protocolNameHash, hh, pub); + } void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub) { @@ -1386,9 +1386,9 @@ 0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52, 0x32, 0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b, 0x5c }; // SHA256 (protocolNameHash) - InitNoiseState (state, protocolNameHash, hh, pub); - } - + InitNoiseState (state, protocolNameHash, hh, pub); + } + // init and terminate /* std::vector > m_OpenSSLMutexes; diff -Nru i2pd-2.39.0/libi2pd/Crypto.h i2pd-2.43.0/libi2pd/Crypto.h --- i2pd-2.39.0/libi2pd/Crypto.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Crypto.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,21 +29,25 @@ #include "CPU.h" // recognize openssl version and features -#if ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL -# define LEGACY_OPENSSL 1 -# define X509_getm_notBefore X509_get_notBefore -# define X509_getm_notAfter X509_get_notAfter +#if (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3050200fL)) // LibreSSL 3.5.2 and above +# define LEGACY_OPENSSL 0 +#elif ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL +# define LEGACY_OPENSSL 1 +# define X509_getm_notBefore X509_get_notBefore +# define X509_getm_notAfter X509_get_notAfter #else -# define LEGACY_OPENSSL 0 -# if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1 -# define OPENSSL_HKDF 1 -# define OPENSSL_EDDSA 1 -# define OPENSSL_X25519 1 -# define OPENSSL_SIPHASH 1 -# endif -# if !defined OPENSSL_NO_CHACHA && !defined OPENSSL_NO_POLY1305 // some builds might not include them -# define OPENSSL_AEAD_CHACHA20_POLY1305 1 -# endif +# define LEGACY_OPENSSL 0 +# if (OPENSSL_VERSION_NUMBER >= 0x010101000) // 1.1.1 +# define OPENSSL_HKDF 1 +# define OPENSSL_EDDSA 1 +# define OPENSSL_X25519 1 +# if (OPENSSL_VERSION_NUMBER != 0x030000000) // 3.0.0, regression in SipHash +# define OPENSSL_SIPHASH 1 +# endif +# endif +# if !defined OPENSSL_NO_CHACHA && !defined OPENSSL_NO_POLY1305 // some builds might not include them +# define OPENSSL_AEAD_CHACHA20_POLY1305 1 +# endif #endif namespace i2p @@ -93,7 +97,7 @@ bool IsElligatorIneligible () const { return m_IsElligatorIneligible; } void SetElligatorIneligible () { m_IsElligatorIneligible = true; } - + private: uint8_t m_PublicKey[32]; @@ -104,17 +108,17 @@ BN_CTX * m_Ctx; uint8_t m_PrivateKey[32]; #endif - bool m_IsElligatorIneligible = false; // true if definitly ineligible + bool m_IsElligatorIneligible = false; // true if definitely ineligible }; // 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 @@ -315,13 +319,15 @@ uint8_t m_H[32] /*h*/, m_CK[64] /*[ck, k]*/; void MixHash (const uint8_t * buf, size_t len); - void MixKey (const uint8_t * sharedSecret); + void MixHash (const std::vector >& bufs); + void MixKey (const uint8_t * sharedSecret); }; void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_N (tunnels, router) void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (NTCP2) + void InitNoiseXKState1 (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (SSU2) void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets) - + // init and terminate void InitCrypto (bool precomputation, bool aesni, bool avx, bool force); void TerminateCrypto (); @@ -379,7 +385,7 @@ if (dh->p) BN_free (dh->p); if (dh->q) BN_free (dh->q); if (dh->g) BN_free (dh->g); - dh->p = p; dh->q = q; dh->g = g; return 1; + dh->p = p; dh->q = q; dh->g = g; return 1; } inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) { diff -Nru i2pd-2.39.0/libi2pd/CryptoKey.cpp i2pd-2.43.0/libi2pd/CryptoKey.cpp --- i2pd-2.39.0/libi2pd/CryptoKey.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/CryptoKey.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 -Nru i2pd-2.39.0/libi2pd/CryptoKey.h i2pd-2.43.0/libi2pd/CryptoKey.h --- i2pd-2.39.0/libi2pd/CryptoKey.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/CryptoKey.h 2022-08-21 19:40:41.000000000 +0000 @@ -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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 -Nru i2pd-2.39.0/libi2pd/Datagram.cpp i2pd-2.43.0/libi2pd/Datagram.cpp --- i2pd-2.39.0/libi2pd/Datagram.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Datagram.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -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 @@ 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 @@ 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 @@ -295,7 +303,7 @@ } } - if (!m_RoutingSession || m_RoutingSession->IsTerminated () || !m_RoutingSession->IsReadyToSend ()) + if (!m_RoutingSession || m_RoutingSession->IsTerminated () || !m_RoutingSession->IsReadyToSend ()) { bool found = false; for (auto& it: m_PendingRoutingSessions) @@ -316,7 +324,7 @@ auto path = m_RoutingSession->GetSharedRoutingPath(); if (path && m_RoutingSession->IsRatchets () && - m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT) + m_LastUse > m_RoutingSession->GetLastActivityTimestamp ()*1000 + DATAGRAM_SESSION_PATH_TIMEOUT) { m_RoutingSession->SetSharedRoutingPath (nullptr); path = nullptr; @@ -363,8 +371,6 @@ { // no current path, make one path = std::make_shared(); - path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(); - if (!path->outboundTunnel) return nullptr; if (m_RemoteLeaseSet) { @@ -378,6 +384,11 @@ } else return nullptr; + + auto leaseRouter = i2p::data::netdb.FindRouter (path->remoteLease->tunnelGateway); + path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(nullptr, + leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports); + if (!path->outboundTunnel) return nullptr; } else { diff -Nru i2pd-2.39.0/libi2pd/Datagram.h i2pd-2.43.0/libi2pd/Datagram.h --- i2pd-2.39.0/libi2pd/Datagram.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Datagram.h 2022-08-21 19:40:41.000000000 +0000 @@ -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 * @@ -117,12 +117,12 @@ void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); void SendRawDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash & ident, uint16_t fromPort = 0, uint16_t toPort = 0); // TODO: implement calls from other thread from SAM - + std::shared_ptr GetSession(const i2p::data::IdentHash & ident); void SendDatagram (std::shared_ptr session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); void SendRawDatagram (std::shared_ptr session, const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort); void FlushSendQueue (std::shared_ptr session); - + void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, bool isRaw = false); void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; @@ -164,7 +164,7 @@ 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 -Nru i2pd-2.39.0/libi2pd/Destination.cpp i2pd-2.43.0/libi2pd/Destination.cpp --- i2pd-2.39.0/libi2pd/Destination.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Destination.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -13,7 +13,6 @@ #include #include #include "Crypto.h" -#include "Config.h" #include "Log.h" #include "FS.h" #include "Timestamp.h" @@ -35,6 +34,8 @@ int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY; int outLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH; int outQty = DEFAULT_OUTBOUND_TUNNELS_QUANTITY; + int inVar = DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE; + int outVar = DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE; int numTags = DEFAULT_TAGS_TO_SEND; std::shared_ptr > explicitPeers; try @@ -53,10 +54,16 @@ it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY); if (it != params->end ()) outQty = std::stoi(it->second); + it = params->find (I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE); + if (it != params->end ()) + inVar = std::stoi(it->second); + it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE); + if (it != params->end ()) + outVar = std::stoi(it->second); it = params->find (I2CP_PARAM_TAGS_TO_SEND); if (it != params->end ()) numTags = std::stoi(it->second); - LogPrint (eLogInfo, "Destination: parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags"); + LogPrint (eLogInfo, "Destination: Parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags"); it = params->find (I2CP_PARAM_RATCHET_INBOUND_TAGS); if (it != params->end ()) SetNumRatchetInboundTags (std::stoi(it->second)); @@ -86,10 +93,8 @@ if (it != params->end ()) { // oveeride isPublic - bool dontpublish = false; - i2p::config::GetOption (it->second, dontpublish); - m_IsPublic = !dontpublish; - } + m_IsPublic = (it->second != "true"); + } it = params->find (I2CP_PARAM_LEASESET_TYPE); if (it != params->end ()) m_LeaseSetType = std::stoi(it->second); @@ -112,7 +117,7 @@ m_LeaseSetPrivKey.reset (new i2p::data::Tag<32>()); if (m_LeaseSetPrivKey->FromBase64 (it->second) != 32) { - LogPrint(eLogError, "Destination: invalid value i2cp.leaseSetPrivKey ", it->second); + LogPrint(eLogError, "Destination: Invalid value i2cp.leaseSetPrivKey ", it->second); m_LeaseSetPrivKey.reset (nullptr); } } @@ -120,10 +125,10 @@ } catch (std::exception & ex) { - LogPrint(eLogError, "Destination: unable to parse parameters for destination: ", ex.what()); + LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what()); } SetNumTags (numTags); - m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty); + m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty, inVar, outVar); if (explicitPeers) m_Pool->SetExplicitPeers (explicitPeers); if(params) @@ -136,7 +141,7 @@ auto minlatency = std::stoi(itr->second); if ( minlatency > 0 && maxlatency > 0 ) { // set tunnel pool latency - LogPrint(eLogInfo, "Destination: requiring tunnel latency [", minlatency, "ms, ", maxlatency, "ms]"); + LogPrint(eLogInfo, "Destination: Requiring tunnel latency [", minlatency, "ms, ", maxlatency, "ms]"); m_Pool->RequireLatency(minlatency, maxlatency); } } @@ -251,7 +256,7 @@ } else { - LogPrint (eLogWarning, "Destination: remote LeaseSet expired"); + LogPrint (eLogWarning, "Destination: Remote LeaseSet expired"); std::lock_guard lock(m_RemoteLeaseSetsMutex); m_RemoteLeaseSets.erase (ident); return nullptr; @@ -331,6 +336,22 @@ return true; } + void LeaseSetDestination::SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) + { + struct + { + uint8_t k[32]; + uint64_t t; + } data; + memcpy (data.k, key, 32); + data.t = tag; + auto s = shared_from_this (); + m_Service.post ([s,data](void) + { + s->AddECIESx25519Key (data.k, data.t); + }); + } + void LeaseSetDestination::ProcessGarlicMessage (std::shared_ptr msg) { m_Service.post (std::bind (&LeaseSetDestination::HandleGarlicMessage, shared_from_this (), msg)); @@ -367,8 +388,8 @@ HandleDatabaseSearchReplyMessage (payload, len); break; case eI2NPShortTunnelBuildReply: // might come as garlic encrypted - i2p::HandleI2NPMessage (CreateI2NPMessage (typeID, payload, len, msgID)); - break; + i2p::HandleI2NPMessage (CreateI2NPMessage (typeID, payload, len, msgID)); + break; default: LogPrint (eLogWarning, "Destination: Unexpected I2NP message type ", typeID); return false; @@ -395,8 +416,8 @@ LogPrint (eLogDebug, "Destination: Remote LeaseSet"); std::lock_guard lock(m_RemoteLeaseSetsMutex); auto it = m_RemoteLeaseSets.find (key); - if (it != m_RemoteLeaseSets.end () && - it->second->GetStoreType () == buf[DATABASE_STORE_TYPE_OFFSET]) // update only if same type + if (it != m_RemoteLeaseSets.end () && + it->second->GetStoreType () == buf[DATABASE_STORE_TYPE_OFFSET]) // update only if same type { leaseSet = it->second; if (leaseSet->IsNewer (buf + offset, len - offset)) @@ -487,7 +508,7 @@ i2p::data::IdentHash peerHash (buf + 33 + i*32); if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash)) { - LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); + LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); i2p::data::netdb.RequestDestination (peerHash, nullptr, false); // through exploratory } } @@ -555,16 +576,9 @@ shared_from_this (), std::placeholders::_1)); return; } - auto outbound = m_Pool->GetNextOutboundTunnel (); - if (!outbound) + if (!m_Pool->GetInboundTunnels ().size () || !m_Pool->GetOutboundTunnels ().size ()) { - LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels"); - return; - } - auto inbound = m_Pool->GetNextInboundTunnel (); - if (!inbound) - { - LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels"); + LogPrint (eLogError, "Destination: Can't publish LeaseSet. Destination is not ready"); return; } auto floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills); @@ -574,6 +588,33 @@ m_ExcludedFloodfills.clear (); return; } + auto outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)); + auto inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)); + if (!outbound || !inbound) + { + LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill"); + m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); + floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills); + if (floodfill) + { + outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)); + if (outbound) + { + inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)); + if (!inbound) + LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels"); + } + else + LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels"); + } + else + LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); + if (!floodfill || !outbound || !inbound) + { + m_ExcludedFloodfills.clear (); + return; + } + } m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4); @@ -618,7 +659,7 @@ auto ls = GetLeaseSetMt (); if (!ls) { - LogPrint (eLogWarning, "Destination: couldn't verify LeaseSet for ", GetIdentHash().ToBase32()); + LogPrint (eLogWarning, "Destination: Couldn't verify LeaseSet for ", GetIdentHash().ToBase32()); return; } auto s = shared_from_this (); @@ -630,7 +671,7 @@ if (*ls == *leaseSet) { // we got latest LeasetSet - LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", s->GetIdentHash().ToBase32()); + LogPrint (eLogDebug, "Destination: Published LeaseSet verified for ", s->GetIdentHash().ToBase32()); s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL)); s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1)); return; @@ -639,7 +680,7 @@ LogPrint (eLogDebug, "Destination: LeaseSet is different than just published for ", s->GetIdentHash().ToBase32()); } else - LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", s->GetIdentHash().ToBase32()); + LogPrint (eLogWarning, "Destination: Couldn't find published LeaseSet for ", s->GetIdentHash().ToBase32()); // we have to publish again s->Publish (); }); @@ -751,10 +792,10 @@ std::shared_ptr nextFloodfill, std::shared_ptr request) { if (!request->replyTunnel || !request->replyTunnel->IsEstablished ()) - request->replyTunnel = m_Pool->GetNextInboundTunnel (); + request->replyTunnel = m_Pool->GetNextInboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (true)); if (!request->replyTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no inbound tunnels found"); if (!request->outboundTunnel || !request->outboundTunnel->IsEstablished ()) - request->outboundTunnel = m_Pool->GetNextOutboundTunnel (); + request->outboundTunnel = m_Pool->GetNextOutboundTunnel (nullptr, nextFloodfill->GetCompatibleTransports (false)); if (!request->outboundTunnel) LogPrint (eLogError, "Destination: Can't send LeaseSet request, no outbound tunnels found"); if (request->replyTunnel && request->outboundTunnel) @@ -767,11 +808,11 @@ uint8_t replyKey[32], replyTag[32]; RAND_bytes (replyKey, 32); // random session key RAND_bytes (replyTag, isECIES ? 8 : 32); // random session tag - if (isECIES) + if (isECIES) AddECIESx25519Key (replyKey, replyTag); - else + else AddSessionKey (replyKey, replyTag); - auto msg = WrapMessageForRouter (nextFloodfill, CreateLeaseSetDatabaseLookupMsg (dest, + auto msg = WrapMessageForRouter (nextFloodfill, CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES)); request->outboundTunnel->SendTunnelDataMsg ( { @@ -866,8 +907,8 @@ ClientDestination::ClientDestination (boost::asio::io_service& service, const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params): - LeaseSetDestination (service, isPublic, params), - m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), + LeaseSetDestination (service, isPublic, params), + m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS), m_DatagramDestination (nullptr), m_RefCounter (0), m_ReadyChecker(service) @@ -910,22 +951,22 @@ for (auto& it: encryptionKeyTypes) { auto encryptionKey = new EncryptionKey (it); - if (isPublic) + if (IsPublic ()) PersistTemporaryKeys (encryptionKey, isSingleKey); else encryptionKey->GenerateKeys (); encryptionKey->CreateDecryptor (); if (it == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - { + { m_ECIESx25519EncryptionKey.reset (encryptionKey); if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_LEASESET) - SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Rathets must use LeaseSet2 - } + SetLeaseSetType (i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2); // Rathets must use LeaseSet2 + } else m_StandardEncryptionKey.reset (encryptionKey); } - if (isPublic) + if (IsPublic ()) LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created"); try @@ -938,8 +979,8 @@ m_StreamingAckDelay = std::stoi(it->second); it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS); if (it != params->end ()) - i2p::config::GetOption (it->second, m_IsStreamingAnswerPings); - + m_IsStreamingAnswerPings = (it->second == "true"); + if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) { // authentication for encrypted LeaseSet @@ -966,7 +1007,7 @@ } catch (std::exception & ex) { - LogPrint(eLogError, "Destination: unable to parse parameters for destination: ", ex.what()); + LogPrint(eLogError, "Destination: Unable to parse parameters for destination: ", ex.what()); } } @@ -1042,7 +1083,7 @@ LogPrint (eLogError, "Destination: Missing raw datagram destination"); break; default: - LogPrint (eLogError, "Destination: Data: unexpected protocol ", buf[9]); + LogPrint (eLogError, "Destination: Data: Unexpected protocol ", buf[9]); } } @@ -1050,7 +1091,7 @@ { if (!streamRequestComplete) { - LogPrint (eLogError, "Destination: request callback is not specified in CreateStream"); + LogPrint (eLogError, "Destination: Request callback is not specified in CreateStream"); return; } auto leaseSet = FindLeaseSet (dest); @@ -1074,7 +1115,7 @@ { if (!streamRequestComplete) { - LogPrint (eLogError, "Destination: request callback is not specified in CreateStream"); + LogPrint (eLogError, "Destination: Request callback is not specified in CreateStream"); return; } auto s = GetSharedFromThis (); @@ -1096,6 +1137,35 @@ 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) @@ -1153,18 +1223,18 @@ auto ret = it->second; m_StreamingDestinationsByPorts.erase (it); return ret; - } + } } return nullptr; - } - + } + i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination (bool gzip) { if (m_DatagramDestination == nullptr) m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis (), gzip); return m_DatagramDestination; - } - + } + std::vector > ClientDestination::GetAllStreams () const { std::vector > ret; @@ -1245,15 +1315,15 @@ 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"); + LogPrint (eLogError, "Destinations: Decryptor is not set"); return false; } diff -Nru i2pd-2.39.0/libi2pd/Destination.h i2pd-2.43.0/libi2pd/Destination.h --- i2pd-2.39.0/libi2pd/Destination.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Destination.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -53,6 +53,10 @@ const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5; const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity"; const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5; + const char I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE[] = "inbound.lengthVariance"; + const int DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE = 0; + const char I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE[] = "outbound.lengthVariance"; + const int DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE = 0; const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers"; const int STREAM_REQUEST_TIMEOUT = 60; //in seconds const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend"; @@ -68,8 +72,8 @@ const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64 const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType"; const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn - const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn - + const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn + // latency const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min"; const int DEFAULT_MIN_TUNNEL_LATENCY = 0; @@ -80,7 +84,7 @@ const char I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY[] = "i2p.streaming.initialAckDelay"; const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings"; - const int DEFAULT_ANSWER_PINGS = true; + const int DEFAULT_ANSWER_PINGS = true; typedef std::function stream)> StreamRequestComplete; @@ -134,11 +138,13 @@ // override GarlicDestination bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); + void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag); void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatusMessage (std::shared_ptr msg); void SetLeaseSetUpdated (); bool IsPublic () const { return m_IsPublic; }; + void SetPublic (bool pub) { m_IsPublic = pub; }; protected: @@ -242,19 +248,21 @@ 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; void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor); int GetStreamingAckDelay () const { return m_StreamingAckDelay; } bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; } - + // datagram i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; 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 -Nru i2pd-2.39.0/libi2pd/ECIESX25519AEADRatchetSession.cpp i2pd-2.43.0/libi2pd/ECIESX25519AEADRatchetSession.cpp --- i2pd-2.39.0/libi2pd/ECIESX25519AEADRatchetSession.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/ECIESX25519AEADRatchetSession.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -90,24 +90,24 @@ } void RatchetTagSet::DeleteSymmKey (int index) - { + { m_ItermediateSymmKeys.erase (index); } - + void ReceiveRatchetTagSet::Expire () { if (!m_ExpirationTimestamp) m_ExpirationTimestamp = i2p::util::GetSecondsSinceEpoch () + ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT; } - bool ReceiveRatchetTagSet::IsExpired (uint64_t ts) const - { - return m_ExpirationTimestamp && ts > m_ExpirationTimestamp; - } - - bool ReceiveRatchetTagSet::IsIndexExpired (int index) const - { - return index < m_TrimBehindIndex; + bool ReceiveRatchetTagSet::IsExpired (uint64_t ts) const + { + return m_ExpirationTimestamp && ts > m_ExpirationTimestamp; + } + + bool ReceiveRatchetTagSet::IsIndexExpired (int index) const + { + return index < m_TrimBehindIndex; } bool ReceiveRatchetTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index) @@ -115,21 +115,21 @@ auto session = GetSession (); if (!session) return false; return session->HandleNextMessage (buf, len, shared_from_this (), index); - } + } SymmetricKeyTagSet::SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key): - ReceiveRatchetTagSet (nullptr), m_Destination (destination) - { - memcpy (m_Key, key, 32); - Expire (); + ReceiveRatchetTagSet (nullptr), m_Destination (destination) + { + memcpy (m_Key, key, 32); + Expire (); } - + bool SymmetricKeyTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index) { if (len < 24) return false; uint8_t nonce[12]; memset (nonce, 0, 12); // n = 0 - size_t offset = 8; // first 8 bytes is reply tag used as AD + size_t offset = 8; // first 8 bytes is reply tag used as AD len -= 16; // poly1305 if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len - offset, buf, 8, m_Key, nonce, buf + offset, len - offset, false)) // decrypt { @@ -137,33 +137,33 @@ return false; } // we assume 1 I2NP block with delivery type local - if (offset + 3 > len) - { + if (offset + 3 > len) + { LogPrint (eLogWarning, "Garlic: Symmetric key tagset is too short ", len); return false; - } + } if (buf[offset] != eECIESx25519BlkGalicClove) { LogPrint (eLogWarning, "Garlic: Symmetric key tagset unexpected block ", (int)buf[offset]); return false; - } + } offset++; auto size = bufbe16toh (buf + offset); offset += 2; - if (offset + size > len) + if (offset + size > len) { LogPrint (eLogWarning, "Garlic: Symmetric key tagset block is too long ", size); return false; - } + } if (m_Destination) - m_Destination->HandleECIESx25519GarlicClove (buf + offset, size); + m_Destination->HandleECIESx25519GarlicClove (buf + offset, size); return true; - } - + } + ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSetNS): GarlicRoutingSession (owner, true) { - if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate); + if (!attachLeaseSetNS) SetLeaseSetUpdateStatus (eLeaseSetUpToDate); RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0; } @@ -181,11 +181,11 @@ { bool ineligible = false; while (!ineligible) - { + { m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); ineligible = m_EphemeralKeys->IsElligatorIneligible (); if (!ineligible) // we haven't tried it yet - { + { if (i2p::crypto::GetElligator ()->Encode (m_EphemeralKeys->GetPublicKey (), buf)) return true; // success // otherwise return back @@ -194,7 +194,7 @@ } else i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys); - } + } // we still didn't find elligator eligible pair for (int i = 0; i < 25; i++) { @@ -208,7 +208,7 @@ // let NTCP2 use it m_EphemeralKeys->SetElligatorIneligible (); i2p::transport::transports.ReuseX25519KeysPair (m_EphemeralKeys); - } + } } LogPrint (eLogError, "Garlic: Can't generate elligator eligible x25519 keys"); return false; @@ -229,7 +229,7 @@ // we are Bob // KDF1 i2p::crypto::InitNoiseIKState (GetNoiseState (), GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk - + if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) { LogPrint (eLogError, "Garlic: Can't decode elligator"); @@ -239,11 +239,11 @@ 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; - } + } MixKey (sharedSecret); // decrypt flags/static @@ -263,11 +263,11 @@ { // 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; - } + } MixKey (sharedSecret); } else // all zeros flags @@ -280,13 +280,13 @@ LogPrint (eLogWarning, "Garlic: Payload section AEAD verification failed"); return false; } - + m_State = eSessionStateNewSessionReceived; - if (isStatic) - { + if (isStatic) + { MixHash (buf, len); // h = SHA256(h || ciphertext) GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); - } + } HandlePayload (payload.data (), len - 16, nullptr, 0); return true; @@ -314,7 +314,7 @@ GetOwner ()->HandleECIESx25519GarlicClove (buf + offset, size); break; case eECIESx25519BlkNextKey: - LogPrint (eLogDebug, "Garlic: next key"); + LogPrint (eLogDebug, "Garlic: Next key"); if (receiveTagset) HandleNextKey (buf + offset, size, receiveTagset); else @@ -322,7 +322,7 @@ break; case eECIESx25519BlkAck: { - LogPrint (eLogDebug, "Garlic: ack"); + LogPrint (eLogDebug, "Garlic: Ack"); int numAcks = size >> 2; // /4 auto offset1 = offset; for (auto i = 0; i < numAcks; i++) @@ -334,24 +334,24 @@ } case eECIESx25519BlkAckRequest: { - LogPrint (eLogDebug, "Garlic: ack request"); + LogPrint (eLogDebug, "Garlic: Ack request"); m_AckRequests.push_back ({receiveTagset->GetTagSetID (), index}); break; } case eECIESx25519BlkTermination: - LogPrint (eLogDebug, "Garlic: termination"); + LogPrint (eLogDebug, "Garlic: Termination"); if (GetOwner ()) GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey); if (receiveTagset) receiveTagset->Expire (); break; case eECIESx25519BlkDateTime: - LogPrint (eLogDebug, "Garlic: datetime"); + LogPrint (eLogDebug, "Garlic: Datetime"); break; case eECIESx25519BlkOptions: - LogPrint (eLogDebug, "Garlic: options"); + LogPrint (eLogDebug, "Garlic: Options"); break; case eECIESx25519BlkPadding: - LogPrint (eLogDebug, "Garlic: padding"); + LogPrint (eLogDebug, "Garlic: Padding"); break; default: LogPrint (eLogWarning, "Garlic: Unknown block type ", (int)blk); @@ -381,7 +381,7 @@ newTagset->NextSessionTagRatchet (); m_SendTagset = newTagset; m_SendForwardKey = false; - LogPrint (eLogDebug, "Garlic: next send tagset ", newTagset->GetTagSetID (), " created"); + LogPrint (eLogDebug, "Garlic: Next send tagset ", newTagset->GetTagSetID (), " created"); } else LogPrint (eLogDebug, "Garlic: Unexpected next key ", keyID); @@ -424,7 +424,7 @@ GenerateMoreReceiveTags (newTagset, (GetOwner () && GetOwner ()->GetNumRatchetInboundTags () > 0) ? GetOwner ()->GetNumRatchetInboundTags () : ECIESX25519_MAX_NUM_GENERATED_TAGS); receiveTagset->Expire (); - LogPrint (eLogDebug, "Garlic: next receive tagset ", tagsetID, " created"); + LogPrint (eLogDebug, "Garlic: Next receive tagset ", tagsetID, " created"); } } @@ -446,7 +446,7 @@ m_NextSendRatchet->key = i2p::transport::transports.GetNextX25519KeysPair (); m_SendForwardKey = true; - LogPrint (eLogDebug, "Garlic: new send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created"); + LogPrint (eLogDebug, "Garlic: New send ratchet ", m_NextSendRatchet->newKey ? "new" : "old", " key ", m_NextSendRatchet->keyID, " created"); } bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic) @@ -468,7 +468,7 @@ { LogPrint (eLogWarning, "Garlic: Incorrect Bob static key"); return false; - } + } MixKey (sharedSecret); // encrypt flags/static key section uint8_t nonce[12]; @@ -478,7 +478,7 @@ fs = GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); else { - memset (out + offset, 0, 32); // all zeros flags section + memset (out + offset, 0, 32); // all zeros flags section fs = out + offset; } if (!i2p::crypto::AEADChaCha20Poly1305 (fs, 32, m_H, 32, m_CK + 32, nonce, out + offset, 48, true)) // encrypt @@ -486,14 +486,14 @@ LogPrint (eLogWarning, "Garlic: Flags/static section AEAD encryption failed "); return false; } - + MixHash (out + offset, 48); // h = SHA256(h || ciphertext) offset += 48; // KDF2 if (isStatic) - { - GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk) - MixKey (sharedSecret); + { + GetOwner ()->Decrypt (m_RemoteStaticKey, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk) + MixKey (sharedSecret); } else CreateNonce (1, nonce); @@ -503,10 +503,10 @@ LogPrint (eLogWarning, "Garlic: Payload section AEAD encryption failed"); return false; } - + m_State = eSessionStateNewSessionSent; if (isStatic) - { + { MixHash (out + offset, len + 16); // h = SHA256(h || ciphertext) if (GetOwner ()) { @@ -514,11 +514,11 @@ InitNewSessionTagset (tagsetNsr); tagsetNsr->Expire (); // let non-replied session expire GenerateMoreReceiveTags (tagsetNsr, ECIESX25519_NSR_NUM_GENERATED_TAGS); - } + } } return true; } - + bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) { // we are Bob @@ -534,7 +534,7 @@ LogPrint (eLogError, "Garlic: Can't encode elligator"); return false; } - memcpy (m_NSREncodedKey, out + offset, 56); // for possible next NSR + memcpy (m_NSREncodedKey, out + offset, 32); // for possible next NSR memcpy (m_NSRH, m_H, 32); offset += 32; // KDF for Reply Key Section @@ -545,13 +545,13 @@ { LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key"); return false; - } + } MixKey (sharedSecret); if (!m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret)) // sharedSecret = x25519(besk, apk) { LogPrint (eLogWarning, "Garlic: Incorrect Alice static key"); return false; - } + } MixKey (sharedSecret); uint8_t nonce[12]; CreateNonce (0, nonce); @@ -584,10 +584,10 @@ } m_State = eSessionStateNewSessionReplySent; m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch (); - + return true; } - + bool ECIESX25519AEADRatchetSession::NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) { // we are Bob and sent NSR already @@ -618,7 +618,7 @@ bool ECIESX25519AEADRatchetSession::HandleNewOutgoingSessionReply (uint8_t * buf, size_t len) { // we are Alice - LogPrint (eLogDebug, "Garlic: reply received"); + LogPrint (eLogDebug, "Garlic: Reply received"); const uint8_t * tag = buf; buf += 8; len -= 8; // tag uint8_t bepk[32]; // Bob's ephemeral key @@ -637,9 +637,9 @@ { LogPrint (eLogWarning, "Garlic: Incorrect Bob ephemeral key"); 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]; @@ -700,11 +700,11 @@ uint64_t tag = m_SendTagset->GetNextSessionTag (); if (!tag) { - LogPrint (eLogError, "Garlic: can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset"); + LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset"); if (GetOwner ()) GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey); return false; - } + } memcpy (out, &tag, 8); // ad = The session tag, 8 bytes // ciphertext = ENCRYPT(k, n, payload, ad) @@ -736,7 +736,7 @@ } HandlePayload (payload, len - 16, receiveTagset, index); if (GetOwner ()) - { + { int moreTags = 0; if (GetOwner ()->GetNumRatchetInboundTags () > 0) // override in settings? { @@ -745,17 +745,17 @@ index -= GetOwner ()->GetNumRatchetInboundTags (); // trim behind } else - { + { moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4 if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS; moreTags -= (receiveTagset->GetNextIndex () - index); index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind - } + } if (moreTags > 0) GenerateMoreReceiveTags (receiveTagset, moreTags); if (index > 0) receiveTagset->SetTrimBehind (index); - } + } return true; } @@ -774,13 +774,13 @@ #endif case eSessionStateEstablished: if (receiveTagset->IsNS ()) - { - // our of sequence NSR - LogPrint (eLogDebug, "Garlic: check for out of order NSR with index ", index); + { + // our of sequence NSR + LogPrint (eLogDebug, "Garlic: Check for out of order NSR with index ", index); if (receiveTagset->GetNextIndex () - index < ECIESX25519_NSR_NUM_GENERATED_TAGS/2) GenerateMoreReceiveTags (receiveTagset, ECIESX25519_NSR_NUM_GENERATED_TAGS); return HandleNewOutgoingSessionReply (buf, len); - } + } else return HandleExistingSessionMessage (buf, len, receiveTagset, index); case eSessionStateNew: @@ -792,7 +792,7 @@ } return true; } - + std::shared_ptr ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr msg) { uint8_t * payload = GetOwner ()->GetPayloadBuffer (); @@ -829,7 +829,7 @@ if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false)) return nullptr; len += 96; - break; + break; default: return nullptr; } @@ -844,18 +844,18 @@ { m_State = eSessionStateOneTime; return WrapSingleMessage (msg); - } - + } + size_t ECIESX25519AEADRatchetSession::CreatePayload (std::shared_ptr msg, bool first, uint8_t * payload) { uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); size_t payloadLen = 0; if (first) payloadLen += 7;// datatime if (msg) - { + { payloadLen += msg->GetPayloadLength () + 13; if (m_Destination) payloadLen += 32; - } + } if (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT) { // resubmit non-confirmed LeaseSet @@ -896,9 +896,9 @@ paddingSize = m_PaddingSizes[m_NextPaddingSize++] & 0x0F; // 0 - 15 if (m_NextPaddingSize >= 32) { - RAND_bytes (m_PaddingSizes, 32); + RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0; - } + } if (delta > 3) { delta -= 3; @@ -912,9 +912,9 @@ { if (payloadLen > I2NP_MAX_MESSAGE_SIZE) { - LogPrint (eLogError, "Garlic: payload length ", payloadLen, " is too long"); + LogPrint (eLogError, "Garlic: Payload length ", payloadLen, " is too long"); return 0; - } + } m_LastSentTimestamp = ts; size_t offset = 0; // DateTime @@ -993,7 +993,7 @@ htobe16buf (payload + offset, paddingSize); offset += 2; memset (payload + offset, 0, paddingSize); offset += paddingSize; } - } + } return payloadLen; } @@ -1050,17 +1050,17 @@ void ECIESX25519AEADRatchetSession::GenerateMoreReceiveTags (std::shared_ptr receiveTagset, int numTags) { if (GetOwner ()) - { + { for (int i = 0; i < numTags; i++) - { + { auto tag = GetOwner ()->AddECIESx25519SessionNextTag (receiveTagset); if (!tag) { - LogPrint (eLogError, "Garlic: can't create new ECIES-X25519-AEAD-Ratchet tag for receive tagset"); + LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for receive tagset"); break; - } - } - } + } + } + } } bool ECIESX25519AEADRatchetSession::CheckExpired (uint64_t ts) @@ -1073,9 +1073,9 @@ RouterIncomingRatchetSession::RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState): ECIESX25519AEADRatchetSession (&i2p::context, false) { - SetLeaseSetUpdateStatus (eLeaseSetDoNotSend); + SetLeaseSetUpdateStatus (eLeaseSetDoNotSend); SetNoiseState (initState); - } + } bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len) { @@ -1084,16 +1084,16 @@ // 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; - } + } m_CurrentNoiseState.MixKey (sharedSecret); - buf += 32; len -= 32; + buf += 32; len -= 32; uint8_t nonce[12]; CreateNonce (0, nonce); - std::vector payload (len - 16); + std::vector payload (len - 16); if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_CurrentNoiseState.m_H, 32, m_CurrentNoiseState.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt { @@ -1102,20 +1102,20 @@ } HandlePayload (payload.data (), len - 16, nullptr, 0); return true; - } + } - static size_t CreateGarlicPayload (std::shared_ptr msg, uint8_t * payload, + 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; + payload[0] = eECIESx25519BlkDateTime; htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); + htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); len = 7; - } + } // I2NP payload += len; uint16_t cloveSize = msg->GetPayloadLength () + 10; @@ -1139,14 +1139,14 @@ delta -= 3; if (paddingSize > delta) paddingSize %= delta; } - payload[0] = eECIESx25519BlkPadding; - htobe16buf (payload + 1, paddingSize); + payload[0] = eECIESx25519BlkPadding; + htobe16buf (payload + 1, paddingSize); if (paddingSize) memset (payload + 3, 0, paddingSize); len += paddingSize + 3; - } + } return len; - } - + } + std::shared_ptr WrapECIESX25519Message (std::shared_ptr msg, const uint8_t * key, uint64_t tag) { auto m = NewI2NPMessage (); @@ -1188,8 +1188,8 @@ { LogPrint (eLogWarning, "Garlic: Incorrect Bob static key"); return nullptr; - } - noiseState.MixKey (sharedSecret); + } + 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]; @@ -1205,6 +1205,6 @@ m->len += offset + 4; m->FillI2NPMessageHeader (eI2NPGarlic); return m; - } + } } } diff -Nru i2pd-2.39.0/libi2pd/ECIESX25519AEADRatchetSession.h i2pd-2.43.0/libi2pd/ECIESX25519AEADRatchetSession.h --- i2pd-2.39.0/libi2pd/ECIESX25519AEADRatchetSession.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/ECIESX25519AEADRatchetSession.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,4 +1,3 @@ - /* * Copyright (c) 2013-2021, The PurpleI2P Project * @@ -28,7 +27,7 @@ { const int ECIESX25519_RESTART_TIMEOUT = 120; // number of second since session creation we can restart session after const int ECIESX25519_INACTIVITY_TIMEOUT = 90; // number of seconds we receive nothing and should restart if we can - const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after + const int ECIESX25519_SEND_INACTIVITY_TIMEOUT = 5000; // number of milliseconds we can send empty(pyaload only) packet after const int ECIESX25519_SEND_EXPIRATION_TIMEOUT = 480; // in seconds const int ECIESX25519_RECEIVE_EXPIRATION_TIMEOUT = 600; // in seconds const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180 @@ -46,7 +45,7 @@ RatchetTagSet () {}; virtual ~RatchetTagSet () {}; - + void DHInitialize (const uint8_t * rootKey, const uint8_t * k); void NextSessionTagRatchet (); uint64_t GetNextSessionTag (); @@ -57,14 +56,14 @@ int GetTagSetID () const { return m_TagSetID; }; void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; }; - + private: 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; - + int m_TagSetID = 0; }; @@ -74,27 +73,27 @@ { public: - ReceiveRatchetTagSet (std::shared_ptr session, bool isNS = false): + ReceiveRatchetTagSet (std::shared_ptr session, bool isNS = false): m_Session (session), m_IsNS (isNS) {}; bool IsNS () const { return m_IsNS; }; std::shared_ptr GetSession () { return m_Session; }; - void SetTrimBehind (int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; }; + void SetTrimBehind (int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; }; int GetTrimBehind () const { return m_TrimBehindIndex; }; - + void Expire (); - bool IsExpired (uint64_t ts) const; - + bool IsExpired (uint64_t ts) const; + virtual bool IsIndexExpired (int index) const; virtual bool HandleNextMessage (uint8_t * buf, size_t len, int index); - + private: - + int m_TrimBehindIndex = 0; std::shared_ptr m_Session; bool m_IsNS; uint64_t m_ExpirationTimestamp = 0; - }; + }; class SymmetricKeyTagSet: public ReceiveRatchetTagSet { @@ -104,13 +103,13 @@ bool IsIndexExpired (int index) const { return false; }; bool HandleNextMessage (uint8_t * buf, size_t len, int index); - + private: GarlicDestination * m_Destination; uint8_t m_Key[32]; - }; - + }; + enum ECIESx25519BlockType { eECIESx25519BlkDateTime = 0, @@ -128,7 +127,7 @@ const uint8_t ECIESX25519_NEXT_KEY_REVERSE_KEY_FLAG = 0x02; const uint8_t ECIESX25519_NEXT_KEY_REQUEST_REVERSE_KEY_FLAG = 0x04; - class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, + class ECIESX25519AEADRatchetSession: public GarlicRoutingSession, private i2p::crypto::NoiseSymmetricState, public std::enable_shared_from_this { @@ -158,10 +157,10 @@ 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); - + const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } - + void Terminate () { m_IsTerminated = true; } void SetDestination (const i2p::data::IdentHash& dest) // TODO: { @@ -171,19 +170,19 @@ bool CheckExpired (uint64_t ts); // true is expired bool CanBeRestarted (uint64_t ts) const { return ts > m_SessionCreatedTimestamp + ECIESX25519_RESTART_TIMEOUT; } bool IsInactive (uint64_t ts) const { return ts > m_LastActivityTimestamp + ECIESX25519_INACTIVITY_TIMEOUT && CanBeRestarted (ts); } - + bool IsRatchets () const { return true; }; bool IsReadyToSend () const { return m_State != eSessionStateNewSessionSent; }; bool IsTerminated () const { return m_IsTerminated; } uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; }; - protected: - + protected: + i2p::crypto::NoiseSymmetricState& GetNoiseState () { return *this; }; void SetNoiseState (const i2p::crypto::NoiseSymmetricState& state) { GetNoiseState () = state; }; void CreateNonce (uint64_t seqn, uint8_t * nonce); void HandlePayload (const uint8_t * buf, size_t len, const std::shared_ptr& receiveTagset, int index); - + private: bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes @@ -198,7 +197,7 @@ 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); - + 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); @@ -213,7 +212,7 @@ uint8_t m_NSREncodedKey[32], m_NSRH[32], m_NSRKey[32]; // new session reply, for incoming only std::shared_ptr m_EphemeralKeys; SessionState m_State = eSessionStateNew; - uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds) + uint64_t m_SessionCreatedTimestamp = 0, m_LastActivityTimestamp = 0, // incoming (in seconds) m_LastSentTimestamp = 0; // in milliseconds std::shared_ptr m_SendTagset, m_NSRSendTagset; std::unique_ptr m_Destination;// TODO: might not need it @@ -221,7 +220,7 @@ bool m_SendReverseKey = false, m_SendForwardKey = false, m_IsTerminated = false; std::unique_ptr m_NextReceiveRatchet, m_NextSendRatchet; uint8_t m_PaddingSizes[32], m_NextPaddingSize; - + public: // for HTTP only @@ -230,7 +229,7 @@ { return m_Destination ? *m_Destination : i2p::data::IdentHash (); } - }; + }; // single session for all incoming messages class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession @@ -240,12 +239,12 @@ RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState); bool HandleNextMessage (const uint8_t * buf, size_t len); i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; - + private: i2p::crypto::NoiseSymmetricState m_CurrentNoiseState; - }; - + }; + 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); } diff -Nru i2pd-2.39.0/libi2pd/Ed25519.cpp i2pd-2.43.0/libi2pd/Ed25519.cpp --- i2pd-2.39.0/libi2pd/Ed25519.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Ed25519.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -33,7 +33,7 @@ BN_add (l, l, tmp); BN_sub_word (two_252_2, 2); // 2^252 - 2 - // -121665*inv(121666) + // -121665*inv(121666) d = BN_new (); BN_set_word (tmp, 121666); BN_mod_inverse (tmp, tmp, q, ctx); @@ -61,7 +61,7 @@ BN_mod (By, By, q, ctx); // % q // precalculate Bi256 table - Bi256Carry = { Bx, By }; // B + Bi256Carry = { Bx, By }; // B for (int i = 0; i < 32; i++) { Bi256[i][0] = Bi256Carry; // first point @@ -215,7 +215,7 @@ if (!t1) { t1 = BN_CTX_get (ctx); BN_mul (t1, p1.x, p1.y, ctx); } if (!t2) { t2 = BN_CTX_get (ctx); BN_mul (t2, p2.x, p2.y, ctx); } BN_mul (t3, t1, t2, ctx); - BN_mul (t3, t3, d, ctx); // C = d*t1*t2 + BN_mul (t3, t3, d, ctx); // C = d*t1*t2 if (p1.z) { @@ -264,9 +264,9 @@ else { BN_mul (t2, p.x, p.y, ctx); // t = x*y - BN_sqr (t2, t2, ctx); // t2 = t^2 + BN_sqr (t2, t2, ctx); // t2 = t^2 } - BN_mul (t2, t2, d, ctx); // t2 = C = d*t^2 + BN_mul (t2, t2, d, ctx); // t2 = C = d*t^2 if (p.z) BN_sqr (z2, p.z, ctx); // z2 = D = z^2 else @@ -349,7 +349,7 @@ BN_mod_inverse (y, p.z, q, ctx); BN_mod_mul (x, p.x, y, q, ctx); // x = x/z BN_mod_mul (y, p.y, y, q, ctx); // y = y/z - return EDDSAPoint{x, y}; + return EDDSAPoint{x, y}; } else return EDDSAPoint{BN_dup (p.x), BN_dup (p.y)}; @@ -506,13 +506,13 @@ std::swap (z2, z3); } BN_mod_inverse (z2, z2, q, ctx); - BIGNUM * res = BN_new (); // not from ctx + BIGNUM * res = BN_new (); // not from ctx BN_mod_mul(res, x2, z2, q, ctx); BN_CTX_end (ctx); return res; } - void Ed25519::ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const + void Ed25519::ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const { BIGNUM * p1 = DecodeBN<32> (p); uint8_t k[32]; @@ -524,7 +524,7 @@ BN_free (p1); BN_free (n); BN_free (q1); } - void Ed25519::ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const + void Ed25519::ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const { BIGNUM *p1 = BN_new (); BN_set_word (p1, 9); uint8_t k[32]; diff -Nru i2pd-2.39.0/libi2pd/Ed25519.h i2pd-2.43.0/libi2pd/Ed25519.h --- i2pd-2.39.0/libi2pd/Ed25519.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Ed25519.h 2022-08-21 19:40:41.000000000 +0000 @@ -85,8 +85,8 @@ EDDSAPoint DecodePublicKey (const uint8_t * buf, BN_CTX * ctx) const; void EncodePublicKey (const EDDSAPoint& publicKey, uint8_t * buf, BN_CTX * ctx) const; #if !OPENSSL_X25519 - void ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; // p is point, e is number for x25519 - void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; + void ScalarMul (const uint8_t * p, const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; // p is point, e is number for x25519 + void ScalarMulB (const uint8_t * e, uint8_t * buf, BN_CTX * ctx) const; #endif void BlindPublicKey (const uint8_t * pub, const uint8_t * seed, uint8_t * blinded); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32 void BlindPrivateKey (const uint8_t * priv, const uint8_t * seed, uint8_t * blindedPriv, uint8_t * blindedPub); // for encrypted LeaseSet2, pub - 32, seed - 64, blinded - 32 diff -Nru i2pd-2.39.0/libi2pd/Elligator.cpp i2pd-2.43.0/libi2pd/Elligator.cpp --- i2pd-2.39.0/libi2pd/Elligator.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Elligator.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -189,7 +189,7 @@ // assume a < p, so don't check for a % p = 0, but a = 0 only if (BN_is_zero(a)) return 0; BIGNUM * r = BN_CTX_get (ctx); - BN_mod_exp (r, a, p12, p, ctx); // r = a^((p-1)/2) mod p + BN_mod_exp (r, a, p12, p, ctx); // r = a^((p-1)/2) mod p if (BN_is_word(r, 1)) return 1; else if (BN_is_zero(r)) diff -Nru i2pd-2.39.0/libi2pd/Family.cpp i2pd-2.43.0/libi2pd/Family.cpp --- i2pd-2.39.0/libi2pd/Family.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Family.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -88,7 +88,7 @@ } EVP_PKEY_free (pkey); if (verifier && cn) - m_SigningKeys[cn] = verifier; + m_SigningKeys.emplace (cn, std::make_pair(verifier, m_SigningKeys.size () + 1)); } SSL_free (ssl); } @@ -121,7 +121,7 @@ } bool Families::VerifyFamily (const std::string& family, const IdentHash& ident, - const char * signature, const char * key) + const char * signature, const char * key) const { uint8_t buf[100], signatureBuf[64]; size_t len = family.length (), signatureLen = strlen (signature); @@ -137,11 +137,19 @@ Base64ToByteStream (signature, signatureLen, signatureBuf, 64); auto it = m_SigningKeys.find (family); if (it != m_SigningKeys.end ()) - return it->second->Verify (buf, len, signatureBuf); + return it->second.first->Verify (buf, len, signatureBuf); // TODO: process key return true; } + FamilyID Families::GetFamilyID (const std::string& family) const + { + auto it = m_SigningKeys.find (family); + if (it != m_SigningKeys.end ()) + return it->second.second; + return 0; + } + std::string CreateFamilySignature (const std::string& family, const IdentHash& ident) { auto filename = i2p::fs::DataDirPath("family", (family + ".key")); diff -Nru i2pd-2.39.0/libi2pd/Family.h i2pd-2.43.0/libi2pd/Family.h --- i2pd-2.39.0/libi2pd/Family.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Family.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -19,6 +19,7 @@ { namespace data { + typedef int FamilyID; class Families { public: @@ -27,7 +28,8 @@ ~Families (); void LoadCertificates (); bool VerifyFamily (const std::string& family, const IdentHash& ident, - const char * signature, const char * key = nullptr); + const char * signature, const char * key = nullptr) const; + FamilyID GetFamilyID (const std::string& family) const; private: @@ -35,7 +37,7 @@ private: - std::map > m_SigningKeys; + std::map, FamilyID> > m_SigningKeys; // family -> (verifier, id) }; std::string CreateFamilySignature (const std::string& family, const IdentHash& ident); diff -Nru i2pd-2.39.0/libi2pd/FS.cpp i2pd-2.43.0/libi2pd/FS.cpp --- i2pd-2.39.0/libi2pd/FS.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/FS.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -60,10 +60,38 @@ } void DetectDataDir(const std::string & cmdline_param, bool isService) { + // with 'datadir' option if (cmdline_param != "") { dataDir = cmdline_param; return; } + +#if !defined(MAC_OSX) && !defined(ANDROID) + // with 'service' option + if (isService) { +#ifdef _WIN32 + wchar_t commonAppData[MAX_PATH]; + if(SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, commonAppData) != S_OK) + { +#ifdef WIN32_APP + MessageBox(NULL, TEXT("Unable to get common AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); +#else + fprintf(stderr, "Error: Unable to get common AppData path!"); +#endif + exit(1); + } + else + { + dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName; + } +#else + dataDir = "/var/lib/" + appName; +#endif + return; + } +#endif + + // detect directory as usual #ifdef _WIN32 wchar_t localAppData[MAX_PATH]; @@ -117,12 +145,10 @@ dataDir = std::string (ext) + "/" + appName; return; } -#endif - // otherwise use /data/files +#endif // ANDROID + // use /home/user/.i2pd or /tmp/i2pd char *home = getenv("HOME"); - if (isService) { - dataDir = "/var/lib/" + appName; - } else if (home != NULL && strlen(home) > 0) { + if (home != NULL && strlen(home) > 0) { dataDir = std::string(home) + "/." + appName; } else { dataDir = "/tmp/" + appName; diff -Nru i2pd-2.39.0/libi2pd/FS.h i2pd-2.43.0/libi2pd/FS.h --- i2pd-2.39.0/libi2pd/FS.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/FS.h 2022-08-21 19:40:41.000000000 +0000 @@ -83,8 +83,8 @@ /** * @brief Set datadir either from cmdline option or using autodetection - * @param cmdline_param Value of cmdline parameter --datadir= - * @param isService Value of cmdline parameter --service + * @param cmdline_param Value of cmdline parameter --datadir= + * @param isService Value of cmdline parameter --service * * Examples of autodetected paths: * @@ -93,11 +93,11 @@ * 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= + * @param cmdline_param Value of cmdline parameter --certsdir= * * Examples of autodetected paths: * @@ -106,7 +106,7 @@ * 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); + void SetCertsDir(const std::string & cmdline_certsdir); /** * @brief Create subdirectories inside datadir @@ -115,7 +115,7 @@ /** * @brief Get list of files in directory - * @param path Path to directory + * @param path Path to directory * @param files Vector to store found files * @return true on success and false if directory not exists */ diff -Nru i2pd-2.39.0/libi2pd/Garlic.cpp i2pd-2.43.0/libi2pd/Garlic.cpp --- i2pd-2.39.0/libi2pd/Garlic.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Garlic.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -164,9 +164,7 @@ 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; @@ -273,7 +271,7 @@ (*numCloves)++; } } - if (msg) // clove message ifself if presented + if (msg) // clove message itself if presented { size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false); (*numCloves)++; @@ -295,14 +293,14 @@ size_t size = 0; if (isDestination) { - buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination + buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination size++; memcpy (buf + size, m_Destination->GetIdentHash (), 32); size += 32; } else { - buf[size] = 0;// delivery instructions flag local + buf[size] = 0;// delivery instructions flag local size++; } @@ -435,12 +433,10 @@ GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default 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; } @@ -456,7 +452,7 @@ { it.second->Terminate (); it.second->SetOwner (nullptr); - } + } m_ECIESx25519Sessions.clear (); m_ECIESx25519Tags.clear (); } @@ -474,27 +470,32 @@ 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 (tag, ECIESX25519AEADRatchetIndexTagset{0, tagset}); - } - + } + bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) { AddSessionKey (key, tag); return true; } + void GarlicDestination::SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) + { + AddECIESx25519Key (key, tag); + } + void GarlicDestination::HandleGarlicMessage (std::shared_ptr msg) { uint8_t * buf = msg->GetPayload (); uint32_t length = bufbe32toh (buf); if (length > msg->GetLength ()) { - LogPrint (eLogWarning, "Garlic: message length ", length, " exceeds I2NP message length ", msg->GetLength ()); + LogPrint (eLogWarning, "Garlic: Message length ", length, " exceeds I2NP message length ", msg->GetLength ()); return; } auto mod = length & 0x0f; // %16 @@ -502,10 +503,10 @@ bool found = false; if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) - // try ECIESx25519 tag + // try ECIESx25519 tag found = HandleECIESx25519TagMessage (buf, length); if (!found) - { + { auto it = !mod ? m_Tags.find (SessionTag(buf)) : m_Tags.end (); // AES block is multiple of 16 // AES tag might be used even if encryption type is not ElGamal/AES if (it != m_Tags.end ()) // try AES tag @@ -523,7 +524,7 @@ found = true; } else - LogPrint (eLogWarning, "Garlic: message length ", length, " is less than 32 bytes"); + LogPrint (eLogWarning, "Garlic: Message length ", length, " is less than 32 bytes"); } if (!found) // assume new session { @@ -531,7 +532,7 @@ // 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 @@ -546,37 +547,37 @@ auto session = std::make_shared (this, false); // incoming if (!session->HandleNextMessage (buf, length, nullptr, 0)) { - // try to gererate more tags for last tagset + // try to generate 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"); + LogPrint (eLogWarning, "Garlic: Trying to generate more ECIES-X25519-AEAD-Ratchet tags"); for (int i = 0; i < maxTags; i++) { auto nextTag = AddECIESx25519SessionNextTag (m_LastTagset); if (!nextTag) { - LogPrint (eLogError, "Garlic: can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset"); + LogPrint (eLogError, "Garlic: Can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset"); break; - } + } if (nextTag == missingTag) { LogPrint (eLogDebug, "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated"); if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[nextTag].index)) found = true; break; - } + } } if (!found) m_LastTagset = nullptr; } if (!found) - LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message"); - } + LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); + } } else LogPrint (eLogError, "Garlic: Failed to decrypt message"); - } + } } } @@ -589,14 +590,14 @@ { 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"); + 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) { @@ -633,7 +634,7 @@ SHA256 (buf, payloadSize, digest); if (memcmp (payloadHash, digest, 32)) // payload hash doesn't match { - LogPrint (eLogError, "Garlic: wrong payload hash"); + LogPrint (eLogError, "Garlic: Wrong payload hash"); return; } HandleGarlicPayload (buf, payloadSize, from); @@ -643,7 +644,7 @@ { if (len < 1) { - LogPrint (eLogError, "Garlic: payload is too short"); + LogPrint (eLogError, "Garlic: Payload is too short"); return; } int numCloves = buf[0]; @@ -658,7 +659,7 @@ if (flag & 0x80) // encrypted? { // TODO: implement - LogPrint (eLogWarning, "Garlic: clove encrypted"); + LogPrint (eLogWarning, "Garlic: Clove encrypted"); buf += 32; } ptrdiff_t offset = buf - buf1; @@ -666,35 +667,35 @@ switch (deliveryType) { case eGarlicDeliveryTypeLocal: - LogPrint (eLogDebug, "Garlic: type local"); + LogPrint (eLogDebug, "Garlic: Type local"); if (offset > (int)len) { - LogPrint (eLogError, "Garlic: message is too short"); + LogPrint (eLogError, "Garlic: Message is too short"); break; } HandleI2NPMessage (buf, len - offset); break; case eGarlicDeliveryTypeDestination: - LogPrint (eLogDebug, "Garlic: type destination"); + LogPrint (eLogDebug, "Garlic: Type destination"); buf += 32; // destination. check it later or for multiple destinations offset = buf - buf1; if (offset > (int)len) { - LogPrint (eLogError, "Garlic: message is too short"); + LogPrint (eLogError, "Garlic: Message is too short"); break; } HandleI2NPMessage (buf, len - offset); break; case eGarlicDeliveryTypeTunnel: { - LogPrint (eLogDebug, "Garlic: type tunnel"); + LogPrint (eLogDebug, "Garlic: Type tunnel"); // gwHash and gwTunnel sequence is reverted uint8_t * gwHash = buf; buf += 32; offset = buf - buf1; if (offset + 4 > (int)len) { - LogPrint (eLogError, "Garlic: message is too short"); + LogPrint (eLogError, "Garlic: Message is too short"); break; } uint32_t gwTunnel = bufbe32toh (buf); @@ -725,32 +726,32 @@ { if (offset > (int)len) { - LogPrint (eLogError, "Garlic: message is too short"); + LogPrint (eLogError, "Garlic: Message is too short"); break; } i2p::transport::transports.SendMessage (ident, CreateI2NPMessage (buf, GetI2NPMessageLength (buf, len - offset))); } else - LogPrint (eLogWarning, "Garlic: type router for inbound tunnels not supported"); + LogPrint (eLogWarning, "Garlic: Type router for inbound tunnels not supported"); break; } default: - LogPrint (eLogWarning, "Garlic: unknown delivery type ", (int)deliveryType); + LogPrint (eLogWarning, "Garlic: Unknown delivery type ", (int)deliveryType); } if (offset > (int)len) { - LogPrint (eLogError, "Garlic: message is too short"); + LogPrint (eLogError, "Garlic: Message is too short"); break; } - buf += GetI2NPMessageLength (buf, len - offset); // I2NP + buf += GetI2NPMessageLength (buf, len - offset); // I2NP buf += 4; // CloveID buf += 8; // Date buf += 3; // Certificate offset = buf - buf1; if (offset > (int)len) { - LogPrint (eLogError, "Garlic: clove is too long"); + LogPrint (eLogError, "Garlic: Clove is too long"); break; } len -= offset; @@ -763,10 +764,10 @@ if (router->GetEncryptionType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) return WrapECIESX25519MessageForRouter (msg, router->GetIdentity ()->GetEncryptionPublicKey ()); else - { + { auto session = GetRoutingSession (router, false); return session->WrapSingleMessage (msg); - } + } } std::shared_ptr GarlicDestination::GetRoutingSession ( @@ -777,17 +778,17 @@ { 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 ()) - { + { session = it->second; if (session->IsInactive (i2p::util::GetSecondsSinceEpoch ())) { - LogPrint (eLogDebug, "Garlic: session restarted"); + LogPrint (eLogDebug, "Garlic: Session restarted"); session = nullptr; - } - } + } + } if (!session) { session = std::make_shared (this, true); @@ -844,7 +845,7 @@ it->second->GetSharedRoutingPath (); // delete shared path if necessary if (!it->second->CleanupExpiredTags ()) { - LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted"); + LogPrint (eLogInfo, "Garlic: Routing session to ", it->first.ToBase32 (), " deleted"); it->second->SetOwner (nullptr); it = m_Sessions.erase (it); } @@ -883,18 +884,18 @@ it->second.tagset->DeleteSymmKey (it->second.index); it = m_ECIESx25519Tags.erase (it); numExpiredTags++; - } + } else { auto session = it->second.tagset->GetSession (); if (!session || session->IsTerminated()) - { + { it = m_ECIESx25519Tags.erase (it); numExpiredTags++; - } + } else ++it; - } + } } if (numExpiredTags > 0) LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ()); @@ -929,7 +930,7 @@ if (session) { session->MessageConfirmed (msgID); - LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged"); + LogPrint (eLogDebug, "Garlic: Message ", msgID, " acknowledged"); } } @@ -1023,7 +1024,7 @@ uint32_t ts = i2p::util::GetSecondsSinceEpoch (); for (auto it: files) if (ts >= i2p::fs::GetLastUpdateTime (it) + INCOMING_TAGS_EXPIRATION_TIMEOUT) - i2p::fs::Remove (it); + i2p::fs::Remove (it); } void GarlicDestination::HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len) @@ -1034,7 +1035,7 @@ switch (deliveryType) { case eGarlicDeliveryTypeDestination: - LogPrint (eLogDebug, "Garlic: type destination"); + LogPrint (eLogDebug, "Garlic: Type destination"); buf += 32; // TODO: check destination #if (__cplusplus >= 201703L) // C++ 17 or higher [[fallthrough]]; @@ -1042,7 +1043,7 @@ // no break here case eGarlicDeliveryTypeLocal: { - LogPrint (eLogDebug, "Garlic: type local"); + LogPrint (eLogDebug, "Garlic: Type local"); I2NPMessageType typeID = (I2NPMessageType)(buf[0]); buf++; // typeid int32_t msgID = bufbe32toh (buf); buf += 4; // msgID buf += 4; // expiration @@ -1050,19 +1051,19 @@ if (offset <= (int)len) HandleCloveI2NPMessage (typeID, buf, len - offset, msgID); else - LogPrint (eLogError, "Garlic: clove is too long"); + LogPrint (eLogError, "Garlic: Clove is too long"); break; } case eGarlicDeliveryTypeTunnel: { - LogPrint (eLogDebug, "Garlic: type tunnel"); + LogPrint (eLogDebug, "Garlic: Type tunnel"); // gwHash and gwTunnel sequence is reverted const uint8_t * gwHash = buf; buf += 32; ptrdiff_t offset = buf - buf1; if (offset + 13 > (int)len) { - LogPrint (eLogError, "Garlic: message is too short"); + LogPrint (eLogError, "Garlic: Message is too short"); break; } uint32_t gwTunnel = bufbe32toh (buf); buf += 4; @@ -1083,7 +1084,7 @@ break; } default: - LogPrint (eLogWarning, "Garlic: unexpected delivery type ", (int)deliveryType); + LogPrint (eLogWarning, "Garlic: Unexpected delivery type ", (int)deliveryType); } } @@ -1103,13 +1104,13 @@ if (it != m_ECIESx25519Sessions.end ()) { if (it->second->CanBeRestarted (i2p::util::GetSecondsSinceEpoch ())) - { + { it->second->Terminate (); // detach m_ECIESx25519Sessions.erase (it); - } + } else { - LogPrint (eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64 (), " already exists"); + LogPrint (eLogInfo, "Garlic: ECIESx25519 session with static key ", staticKeyTag.ToBase64 (), " already exists"); return; } } @@ -1131,6 +1132,6 @@ if (!m_PayloadBuffer) m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE]; return m_PayloadBuffer; - } + } } } diff -Nru i2pd-2.39.0/libi2pd/Garlic.h i2pd-2.43.0/libi2pd/Garlic.h --- i2pd-2.39.0/libi2pd/Garlic.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Garlic.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -115,7 +115,7 @@ virtual bool MessageConfirmed (uint32_t msgID); virtual bool IsRatchets () const { return false; }; virtual bool IsReadyToSend () const { return true; }; - virtual bool IsTerminated () const { return !GetOwner (); }; + virtual bool IsTerminated () const { return !GetOwner (); }; virtual uint64_t GetLastActivityTimestamp () const { return 0; }; // non-zero for rathets only void SetLeaseSetUpdated () @@ -245,13 +245,14 @@ void AddSessionKey (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 + virtual void SubmitECIESx25519Key (const uint8_t * key, uint64_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 (); @@ -279,7 +280,6 @@ private: - BN_CTX * m_Ctx; // incoming // outgoing sessions int m_NumTags; std::mutex m_SessionsMutex; diff -Nru i2pd-2.39.0/libi2pd/Gost.cpp i2pd-2.43.0/libi2pd/Gost.cpp --- i2pd-2.39.0/libi2pd/Gost.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Gost.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -96,7 +96,7 @@ EC_POINT * C = EC_POINT_new (m_Group); EC_POINT_mul (m_Group, C, z1, pub, z2, ctx); // z1*P + z2*pub BIGNUM * x = BN_CTX_get (ctx); - GetXY (C, x, nullptr); // Cx + GetXY (C, x, nullptr); // Cx BN_mod (x, x, q, ctx); // Cx % q bool ret = !BN_cmp (x, r); // Cx = r ? EC_POINT_free (C); @@ -111,8 +111,8 @@ BN_CTX * ctx = BN_CTX_new (); BN_CTX_start (ctx); EC_POINT * C = EC_POINT_new (m_Group); // C = k*P = (rx, ry) - EC_POINT * Q = nullptr; - if (EC_POINT_set_compressed_coordinates_GFp (m_Group, C, r, isNegativeY ? 1 : 0, ctx)) + EC_POINT * Q = nullptr; + if (EC_POINT_set_compressed_coordinates_GFp (m_Group, C, r, isNegativeY ? 1 : 0, ctx)) { EC_POINT * S = EC_POINT_new (m_Group); // S = s*P EC_POINT_mul (m_Group, S, s, nullptr, nullptr, ctx); diff -Nru i2pd-2.39.0/libi2pd/HTTP.cpp i2pd-2.43.0/libi2pd/HTTP.cpp --- i2pd-2.39.0/libi2pd/HTTP.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/HTTP.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -14,9 +14,9 @@ #include "Base.h" #include "HTTP.h" -namespace i2p +namespace i2p { -namespace http +namespace http { const std::vector HTTP_METHODS = { "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods @@ -279,7 +279,7 @@ method = tokens[0]; uri = tokens[1]; version = tokens[2]; - expect = HEADER_LINE; + expect = HEADER_LINE; } else { @@ -363,7 +363,7 @@ return false; /* no header */ if (it->second.find("gzip") != std::string::npos) return true; /* gotcha! */ - if (includingI2PGzip && it->second.find("x-i2p-gzip") != std::string::npos) + if (includingI2PGzip && it->second.find("x-i2p-gzip") != std::string::npos) return true; return false; } @@ -409,7 +409,7 @@ /* all ok */ version = tokens[0]; status = tokens[2]; - expect = HEADER_LINE; + expect = HEADER_LINE; } else { std::string line = str.substr(pos, eol - pos); auto p = parse_header_line(line); @@ -460,7 +460,7 @@ case 304: ptr = "Not Modified"; break; case 307: ptr = "Temporary Redirect"; break; /* client error */ - case 400: ptr = "Bad Request"; break; + case 400: ptr = "Bad Request"; break; case 401: ptr = "Unauthorized"; break; case 403: ptr = "Forbidden"; break; case 404: ptr = "Not Found"; break; @@ -471,19 +471,19 @@ case 502: ptr = "Bad Gateway"; break; case 503: ptr = "Not Implemented"; break; case 504: ptr = "Gateway Timeout"; break; - default: ptr = "Unknown Status"; break; + default: ptr = "Unknown Status"; break; } return ptr; } - std::string UrlDecode(const std::string& data, bool allow_null) + std::string UrlDecode(const std::string& data, bool allow_null) { std::string decoded(data); size_t pos = 0; - while ((pos = decoded.find('%', pos)) != std::string::npos) + while ((pos = decoded.find('%', pos)) != std::string::npos) { char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16); - if (c == '\0' && !allow_null) + if (c == '\0' && !allow_null) { pos += 3; continue; @@ -494,10 +494,10 @@ return decoded; } - bool MergeChunkedResponse (std::istream& in, std::ostream& out) + bool MergeChunkedResponse (std::istream& in, std::ostream& out) { std::string hexLen; - while (!in.eof ()) + while (!in.eof ()) { std::getline (in, hexLen); errno = 0; @@ -522,6 +522,6 @@ if (user.empty () && pass.empty ()) return ""; return "Basic " + i2p::data::ToBase64Standard (user + ":" + pass); } - + } // http } // i2p diff -Nru i2pd-2.39.0/libi2pd/HTTP.h i2pd-2.43.0/libi2pd/HTTP.h --- i2pd-2.39.0/libi2pd/HTTP.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/HTTP.h 2022-08-21 19:40:41.000000000 +0000 @@ -161,14 +161,14 @@ /** * @brief Merge HTTP response content with Transfer-Encoding: chunked - * @param in Input stream + * @param in Input stream * @param out Output stream * @return true on success, false otherwise */ bool MergeChunkedResponse (std::istream& in, std::ostream& out); - std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass); - + std::string CreateBasicAuthorizationString (const std::string& user, const std::string& pass); + } // http } // i2p diff -Nru i2pd-2.39.0/libi2pd/I2NPProtocol.cpp i2pd-2.43.0/libi2pd/I2NPProtocol.cpp --- i2pd-2.39.0/libi2pd/I2NPProtocol.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/I2NPProtocol.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -38,20 +38,7 @@ std::shared_ptr NewI2NPTunnelMessage (bool endpoint) { - 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); + return i2p::tunnel::tunnels.NewI2NPTunnelMessage (endpoint); } std::shared_ptr NewI2NPMessage (size_t len) @@ -88,7 +75,7 @@ { auto msg = NewI2NPMessage (len); if (msg->Concat (buf, len) < len) - LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length ", msg->maxLen); + LogPrint (eLogError, "I2NP: Message length ", len, " exceeds max length ", msg->maxLen); msg->FillI2NPMessageHeader (msgType, replyMsgID); return msg; } @@ -103,7 +90,7 @@ msg->from = from; } else - LogPrint (eLogError, "I2NP: message length ", len, " exceeds max length"); + LogPrint (eLogError, "I2NP: Message length ", len, " exceeds max length"); return msg; } @@ -183,8 +170,8 @@ std::shared_ptr CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, const std::set& excludedFloodfills, - std::shared_ptr replyTunnel, const uint8_t * replyKey, - const uint8_t * replyTag, bool replyECIES) + std::shared_ptr replyTunnel, const uint8_t * replyKey, + const uint8_t * replyTag, bool replyECIES) { int cnt = excludedFloodfills.size (); auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage (); @@ -222,12 +209,12 @@ { 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); @@ -256,7 +243,7 @@ return m; } - std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr router, + std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr router, uint32_t replyToken, std::shared_ptr replyTunnel) { if (!router) // we send own RouterInfo @@ -267,7 +254,7 @@ LogPrint (eLogError, "I2NP: Invalid RouterInfo buffer for DatabaseStore"); return nullptr; } - + auto m = NewI2NPShortMessage (); uint8_t * payload = m->GetPayload (); @@ -285,12 +272,12 @@ 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; @@ -303,7 +290,7 @@ { i2p::data::GzipDeflator deflator; size = deflator.Deflate (router->GetBuffer (), router->GetBufferLen (), buf, m->maxLen -m->len); - } + } if (size) { htobe16buf (sizePtr, size); // size @@ -391,78 +378,50 @@ 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; } @@ -499,75 +458,28 @@ } 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))); - } - } } } 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"); } static void HandleTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len, bool isShort) @@ -603,11 +515,6 @@ 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) @@ -640,8 +547,8 @@ { 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)) - { + if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) + { LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i); return; } @@ -649,19 +556,19 @@ { 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]; + 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); + 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 memcpy (ivKey, noiseState.m_CK , 32); @@ -673,7 +580,7 @@ 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), @@ -684,46 +591,46 @@ 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, + 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 { LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed"); return; - } + } } else - i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); - reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; + 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 (), + 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); + 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))); - } + i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag))); + } else { // IBGW is local @@ -733,18 +640,18 @@ tunnel->SendTunnelDataMsg (replyMsg); else LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); - } - } - else + } + } + 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 (false); @@ -778,7 +685,7 @@ htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); msg->len += TUNNEL_GATEWAY_HEADER_SIZE; if (msg->Concat (buf, len) < len) - LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen); + LogPrint (eLogError, "I2NP: Tunnel gateway buffer overflow ", msg->maxLen); msg->FillI2NPMessageHeader (eI2NPTunnelGateway); return msg; } @@ -809,7 +716,7 @@ msg->offset += gatewayMsgOffset; msg->len += gatewayMsgOffset; if (msg->Concat (buf, len) < len) - LogPrint (eLogError, "I2NP: tunnel gateway buffer overflow ", msg->maxLen); + LogPrint (eLogError, "I2NP: Tunnel gateway buffer overflow ", msg->maxLen); msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message len = msg->GetLength (); msg->offset -= gatewayMsgOffset; @@ -824,13 +731,13 @@ { if (len < I2NP_HEADER_SIZE_OFFSET + 2) { - LogPrint (eLogError, "I2NP: message length ", len, " is smaller than header"); + LogPrint (eLogError, "I2NP: Message length ", len, " is smaller than header"); return len; } auto l = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE; if (l > len) { - LogPrint (eLogError, "I2NP: message length ", l, " exceeds buffer length ", len); + LogPrint (eLogError, "I2NP: Message length ", l, " exceeds buffer length ", len); l = len; } return l; @@ -840,18 +747,18 @@ { if (len < I2NP_HEADER_SIZE) { - LogPrint (eLogError, "I2NP: message length ", len, " is smaller than header"); + LogPrint (eLogError, "I2NP: Message length ", len, " is smaller than header"); return; } uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET]; uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET); - LogPrint (eLogDebug, "I2NP: msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID); + LogPrint (eLogDebug, "I2NP: Msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID); uint8_t * buf = msg + I2NP_HEADER_SIZE; auto size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET); len -= I2NP_HEADER_SIZE; if (size > len) { - LogPrint (eLogError, "I2NP: payload size ", size, " exceeds buffer length ", len); + LogPrint (eLogError, "I2NP: Payload size ", size, " exceeds buffer length ", len); size = len; } switch (typeID) @@ -861,13 +768,13 @@ 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; + break; case eI2NPTunnelBuild: HandleTunnelBuildMsg (buf, size); break; @@ -895,13 +802,8 @@ break; case eI2NPGarlic: { - if (msg->from) - { - if (msg->from->GetTunnelPool ()) - msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg); - else - LogPrint (eLogInfo, "I2NP: Local destination for garlic doesn't exist anymore"); - } + if (msg->from && msg->from->GetTunnelPool ()) + msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg); else i2p::context.ProcessGarlicMessage (msg); break; @@ -924,8 +826,8 @@ case eI2NPVariableTunnelBuildReply: case eI2NPTunnelBuild: case eI2NPTunnelBuildReply: - case eI2NPShortTunnelBuild: - case eI2NPShortTunnelBuildReply: + case eI2NPShortTunnelBuild: + case eI2NPShortTunnelBuildReply: // forward to tunnel thread i2p::tunnel::tunnels.PostTunnelData (msg); break; @@ -940,7 +842,7 @@ Flush (); } - void I2NPMessagesHandler::PutNextMessage (std::shared_ptr msg) + void I2NPMessagesHandler::PutNextMessage (std::shared_ptr&& msg) { if (msg) { diff -Nru i2pd-2.39.0/libi2pd/I2NPProtocol.h i2pd-2.43.0/libi2pd/I2NPProtocol.h --- i2pd-2.39.0/libi2pd/I2NPProtocol.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/I2NPProtocol.h 2022-08-21 19:40:41.000000000 +0000 @@ -56,32 +56,11 @@ // TunnelBuild const size_t TUNNEL_BUILD_RECORD_SIZE = 528; 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; @@ -119,7 +98,7 @@ // ShortResponseRecord const size_t SHORT_RESPONSE_RECORD_OPTIONS_OFFSET = 0; const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201; - + enum I2NPMessageType { eI2NPDummyMsg = 0, @@ -171,7 +150,7 @@ std::shared_ptr from; I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2), - offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header + offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header // header accessors uint8_t * GetHeader () { return GetBuffer (); }; @@ -294,9 +273,9 @@ std::shared_ptr CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, uint32_t replyTunnelID, bool exploratory = false, std::set * excludedPeers = nullptr); std::shared_ptr CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, - const std::set& excludedFloodfills, - std::shared_ptr replyTunnel, - const uint8_t * replyKey, const uint8_t * replyTag, bool replyECIES = false); + const std::set& excludedFloodfills, + std::shared_ptr replyTunnel, + const uint8_t * replyKey, const uint8_t * replyTag, bool replyECIES = false); std::shared_ptr CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector routers); std::shared_ptr CreateDatabaseStoreMsg (std::shared_ptr router = nullptr, uint32_t replyToken = 0, std::shared_ptr replyTunnel = nullptr); @@ -322,7 +301,7 @@ public: ~I2NPMessagesHandler (); - void PutNextMessage (std::shared_ptr msg); + void PutNextMessage (std::shared_ptr&& msg); void Flush (); private: diff -Nru i2pd-2.39.0/libi2pd/I2PEndian.cpp i2pd-2.43.0/libi2pd/I2PEndian.cpp --- i2pd-2.39.0/libi2pd/I2PEndian.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/I2PEndian.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -50,42 +50,3 @@ return u64.raw_value; } #endif - -/* it can be used in Windows 8 -#include - -uint16_t htobe16(uint16_t int16) -{ - return htons(int16); -} - -uint32_t htobe32(uint32_t int32) -{ - return htonl(int32); -} - -uint64_t htobe64(uint64_t int64) -{ - // http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx - //return htonll(int64); - return 0; -} - - -uint16_t be16toh(uint16_t big16) -{ - return ntohs(big16); -} - -uint32_t be32toh(uint32_t big32) -{ - return ntohl(big32); -} - -uint64_t be64toh(uint64_t big64) -{ - // http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx - //return ntohll(big64); - return 0; -} -*/ \ No newline at end of file diff -Nru i2pd-2.39.0/libi2pd/I2PEndian.h i2pd-2.43.0/libi2pd/I2PEndian.h --- i2pd-2.39.0/libi2pd/I2PEndian.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/I2PEndian.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -13,10 +13,11 @@ #if defined(__FreeBSD__) || defined(__NetBSD__) #include + #elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__GLIBC__) #include -#elif defined(__APPLE__) && defined(__MACH__) +#elif defined(__APPLE__) && defined(__MACH__) #include #define htobe16(x) OSSwapHostToBigInt16(x) @@ -34,6 +35,22 @@ #define be64toh(x) OSSwapBigToHostInt64(x) #define le64toh(x) OSSwapLittleToHostInt64(x) +#elif defined(_WIN32) +#define htobe16(x) __builtin_bswap16(x) +#define htole16(x) (x) +#define be16toh(x) __builtin_bswap16(x) +#define le16toh(x) (x) + +#define htobe32(x) __builtin_bswap32(x) +#define htole32(x) (x) +#define be32toh(x) __builtin_bswap32(x) +#define le32toh(x) (x) + +#define htobe64(x) __builtin_bswap64(x) +#define htole64(x) (x) +#define be64toh(x) __builtin_bswap64(x) +#define le64toh(x) (x) + #else #define NEEDS_LOCAL_ENDIAN #include diff -Nru i2pd-2.39.0/libi2pd/Identity.cpp i2pd-2.43.0/libi2pd/Identity.cpp --- i2pd-2.39.0/libi2pd/Identity.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Identity.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -19,7 +19,8 @@ Identity& Identity::operator=(const Keys& keys) { // copy public and signing keys together - memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey)); + memcpy (publicKey, keys.publicKey, sizeof (publicKey)); + memcpy (signingKey, keys.signingKey, sizeof (signingKey)); memset (certificate, 0, sizeof (certificate)); return *this; } @@ -42,7 +43,7 @@ } IdentityEx::IdentityEx (): - m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + m_ExtendedLen (0) { } @@ -52,9 +53,9 @@ { memcpy (m_StandardIdentity.publicKey, publicKey, 32); RAND_bytes (m_StandardIdentity.publicKey + 32, 224); - } - else - memcpy (m_StandardIdentity.publicKey, publicKey, 256); + } + else + memcpy (m_StandardIdentity.publicKey, publicKey, 256); if (type != SIGNING_KEY_TYPE_DSA_SHA1) { size_t excessLen = 0; @@ -63,7 +64,7 @@ { case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: { - size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 + size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 RAND_bytes (m_StandardIdentity.signingKey, padding); memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH); break; @@ -119,11 +120,15 @@ 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 +141,6 @@ memset (m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate)); m_IdentHash = m_StandardIdentity.Hash (); m_ExtendedLen = 0; - m_ExtendedBuffer = nullptr; } CreateVerifier (); } @@ -154,26 +158,25 @@ } 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 +185,12 @@ 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 +203,6 @@ m_StandardIdentity = standard; m_IdentHash = m_StandardIdentity.Hash (); - delete[] m_ExtendedBuffer; - m_ExtendedBuffer = nullptr; m_ExtendedLen = 0; delete m_Verifier; @@ -217,20 +215,17 @@ { if (len < DEFAULT_IDENTITY_SIZE) { - LogPrint (eLogError, "Identity: buffer length ", len, " is too small"); + LogPrint (eLogError, "Identity: Buffer length ", len, " is too small"); return 0; } 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 +236,7 @@ } } else - { m_ExtendedLen = 0; - m_ExtendedBuffer = nullptr; - } SHA256(buf, GetFullLen (), m_IdentHash); delete m_Verifier; @@ -258,7 +250,7 @@ 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; } @@ -488,7 +480,7 @@ size_t ret = m_Public->FromBuffer (buf, len); auto cryptoKeyLen = GetPrivateKeyLen (); if (!ret || ret + cryptoKeyLen > len) return 0; // overflow - memcpy (m_PrivateKey, buf + ret, cryptoKeyLen); + memcpy (m_PrivateKey, buf + ret, cryptoKeyLen); ret += cryptoKeyLen; size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen (); if(signingPrivateKeySize + ret > len || signingPrivateKeySize > 128) return 0; // overflow @@ -517,7 +509,7 @@ if (m_Public->GetSignatureLen () + ret > len) return 0; if (!m_Public->Verify (offlineInfo, keyLen + 6, buf + ret)) { - LogPrint (eLogError, "Identity: offline signature verification failed"); + LogPrint (eLogError, "Identity: Offline signature verification failed"); return 0; } ret += m_Public->GetSignatureLen (); @@ -662,9 +654,9 @@ size_t PrivateKeys::GetPrivateKeyLen () const { // private key length always 256, but type 4 - return (m_Public->GetCryptoKeyType () == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) ? 32 : 256; - } - + return (m_Public->GetCryptoKeyType () == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) ? 32 : 256; + } + uint8_t * PrivateKeys::GetPadding() { if(m_Public->GetSigningKeyType () == SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519) @@ -689,7 +681,7 @@ break; case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: return std::make_shared(key); - break; + break; case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST: return std::make_shared(key); @@ -796,7 +788,7 @@ keys.m_OfflineSignature.resize (pubKeyLen + m_Public->GetSignatureLen () + 6); htobe32buf (keys.m_OfflineSignature.data (), expires); // expires htobe16buf (keys.m_OfflineSignature.data () + 4, type); // type - GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, keys.m_OfflineSignature.data () + 6); // public key + GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, keys.m_OfflineSignature.data () + 6); // public key Sign (keys.m_OfflineSignature.data (), pubKeyLen + 6, keys.m_OfflineSignature.data () + 6 + pubKeyLen); // signature // recreate signer keys.m_Signer = nullptr; diff -Nru i2pd-2.39.0/libi2pd/Identity.h i2pd-2.43.0/libi2pd/Identity.h --- i2pd-2.39.0/libi2pd/Identity.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Identity.h 2022-08-21 19:40:41.000000000 +0000 @@ -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,6 +84,7 @@ 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: @@ -119,7 +120,7 @@ CryptoKeyType GetCryptoKeyType () const; void DropVerifier () const; // to save memory - bool operator == (const IdentityEx & other) const { return GetIdentHash() == other.GetIdentHash(); } + bool operator == (const IdentityEx & other) const { return GetIdentHash() == other.GetIdentHash(); } void RecalculateIdentHash(uint8_t * buff=nullptr); static i2p::crypto::Verifier * CreateVerifier (SigningKeyType keyType); @@ -137,7 +138,7 @@ 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 @@ -221,8 +222,8 @@ RoutingDestination () {}; 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 std::shared_ptr GetIdentity () const = 0; + 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 @@ 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 -Nru i2pd-2.39.0/libi2pd/LeaseSet.cpp i2pd-2.43.0/libi2pd/LeaseSet.cpp --- i2pd-2.39.0/libi2pd/LeaseSet.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/LeaseSet.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -59,9 +59,9 @@ if (readIdentity || !m_Identity) m_Identity = std::make_shared(m_Buffer, m_BufferLen); size_t size = m_Identity->GetFullLen (); - if (size > m_BufferLen) + if (size + 256 > m_BufferLen) { - LogPrint (eLogError, "LeaseSet: identity length ", size, " exceeds buffer size ", m_BufferLen); + LogPrint (eLogError, "LeaseSet: Identity length ", int(size), " exceeds buffer size ", int(m_BufferLen)); m_IsValid = false; return; } @@ -74,26 +74,26 @@ size += m_Identity->GetSigningPublicKeyLen (); // unused signing key if (size + 1 > m_BufferLen) { - LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen); + LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen)); m_IsValid = false; return; - } + } uint8_t num = m_Buffer[size]; size++; // num - LogPrint (eLogDebug, "LeaseSet: read num=", (int)num); + LogPrint (eLogDebug, "LeaseSet: Read num=", (int)num); if (!num || num > MAX_NUM_LEASES) { - LogPrint (eLogError, "LeaseSet: incorrect number of leases", (int)num); + LogPrint (eLogError, "LeaseSet: Rncorrect number of leases", (int)num); m_IsValid = false; return; } - if (size + num*LEASE_SIZE > m_BufferLen) - { - LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen); + if (size + num*LEASE_SIZE > m_BufferLen) + { + LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen)); m_IsValid = false; return; - } - + } + UpdateLeasesBegin (); // process leases m_ExpirationTime = 0; @@ -112,7 +112,7 @@ } if (!m_ExpirationTime) { - LogPrint (eLogWarning, "LeaseSet: all leases are expired. Dropped"); + LogPrint (eLogWarning, "LeaseSet: All leases are expired. Dropped"); m_IsValid = false; return; } @@ -121,16 +121,16 @@ // verify if (verifySignature) - { + { auto signedSize = leases - m_Buffer; if (signedSize + m_Identity->GetSignatureLen () > m_BufferLen) { - LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", m_BufferLen); + LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", int(m_BufferLen)); m_IsValid = false; - } + } else if (!m_Identity->Verify (m_Buffer, signedSize, leases)) { - LogPrint (eLogWarning, "LeaseSet: verification failed"); + LogPrint (eLogWarning, "LeaseSet: Verification failed"); m_IsValid = false; } } @@ -172,7 +172,7 @@ m_ExpirationTime = lease.endDate; if (m_StoreLeases) { - auto ret = m_Leases.insert (std::make_shared(lease)); + auto ret = m_Leases.insert (i2p::data::netdb.NewLease (lease)); if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing (*ret.first)->isUpdated = true; } @@ -254,12 +254,12 @@ 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) @@ -274,7 +274,7 @@ { if (len <= m_BufferLen) m_BufferLen = len; else - LogPrint (eLogError, "LeaseSet2: actual buffer size ", len , " exceeds full buffer size ", m_BufferLen); + LogPrint (eLogError, "LeaseSet2: Actual buffer size ", int(len) , " exceeds full buffer size ", int(m_BufferLen)); } LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto): @@ -320,7 +320,7 @@ else identity = GetIdentity (); size_t offset = identity->GetFullLen (); - if (offset + 8 >= len) return; + if (offset + 8 > len) return; m_PublishedTimestamp = bufbe32toh (buf + offset); offset += 4; // published timestamp (seconds) uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds) SetExpirationTime ((m_PublishedTimestamp + expires)*1000LL); // in milliseconds @@ -331,7 +331,7 @@ m_TransientVerifier = ProcessOfflineSignature (identity, buf, len, offset); if (!m_TransientVerifier) { - LogPrint (eLogError, "LeaseSet2: offline signature failed"); + LogPrint (eLogError, "LeaseSet2: Offline signature failed"); return; } } @@ -364,6 +364,10 @@ SetIsValid (verified); } offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen (); + if (offset > len) { + LogPrint (eLogWarning, "LeaseSet2: short buffer: wanted ", int(offset), "bytes, have ", int(len)); + return; + } SetBufferLen (offset); } @@ -378,7 +382,7 @@ bool verified = verifier->Verify (buf - 1, signatureOffset + 1, buf + signatureOffset); const_cast(buf)[-1] = c; if (!verified) - LogPrint (eLogWarning, "LeaseSet2: verification failed"); + LogPrint (eLogWarning, "LeaseSet2: Verification failed"); return verified; } @@ -388,17 +392,17 @@ // properties uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2; offset += propertiesLen; // skip for now. TODO: implement properties - if (offset + 1 >= len) return 0; // key sections CryptoKeyType preferredKeyType = m_EncryptionType; bool preferredKeyFound = false; + if (offset + 1 > len) return 0; int numKeySections = buf[offset]; offset++; for (int i = 0; i < numKeySections; i++) { + if (offset + 4 > len) return 0; uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption key type - if (offset + 2 >= len) return 0; uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2; - if (offset + encryptionKeyLen >= len) return 0; + if (offset + encryptionKeyLen > len) return 0; if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only { // we pick first valid key if preferred not found @@ -413,7 +417,7 @@ offset += encryptionKeyLen; } // leases - if (offset + 1 >= len) return 0; + if (offset + 1 > len) return 0; int numLeases = buf[offset]; offset++; auto ts = i2p::util::GetMillisecondsSinceEpoch (); if (IsStoreLeases ()) @@ -432,7 +436,8 @@ } else offset += numLeases*LEASE2_SIZE; // 40 bytes per lease - return offset; + + return (offset > len ? 0 : offset); } size_t LeaseSet2::ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len) @@ -442,18 +447,18 @@ uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2; offset += propertiesLen; // skip for now. TODO: implement properties // entries - if (offset + 1 >= len) return 0; + if (offset + 1 > len) return 0; int numEntries = buf[offset]; offset++; for (int i = 0; i < numEntries; i++) { - if (offset + 40 >= len) return 0; + if (offset + LEASE2_SIZE > len) return 0; offset += 32; // hash offset += 3; // flags offset += 1; // cost offset += 4; // expires } // revocations - if (offset + 1 >= len) return 0; + if (offset + 1 > len) return 0; int numRevocations = buf[offset]; offset++; for (int i = 0; i < numRevocations; i++) { @@ -489,7 +494,7 @@ m_TransientVerifier = ProcessOfflineSignature (blindedVerifier, buf, len, offset); if (!m_TransientVerifier) { - LogPrint (eLogError, "LeaseSet2: offline signature failed"); + LogPrint (eLogError, "LeaseSet2: Offline signature failed"); return; } } @@ -515,7 +520,7 @@ key->GetBlindedKey (date, blinded.data ()); if (memcmp (blindedPublicKey, blinded.data (), blindedKeyLen)) { - LogPrint (eLogError, "LeaseSet2: blinded public key doesn't match"); + LogPrint (eLogError, "LeaseSet2: Blinded public key doesn't match"); return; } } @@ -569,7 +574,7 @@ ReadFromBuffer (innerPlainText.data () + 1, lenInnerPlaintext - 1); } else - LogPrint (eLogError, "LeaseSet2: unexpected LeaseSet type ", (int)innerPlainText[0], " inside encrypted LeaseSet"); + LogPrint (eLogError, "LeaseSet2: Unexpected LeaseSet type ", (int)innerPlainText[0], " inside encrypted LeaseSet"); } else { @@ -582,7 +587,7 @@ // helper for ExtractClientAuthData static inline bool GetAuthCookie (const uint8_t * authClients, int numClients, const uint8_t * okm, uint8_t * authCookie) { - // try to find clientCookie_i for clientID_i = okm[44:51] + // try to find clientCookie_i for clientID_i = okm[44:51] for (int i = 0; i < numClients; i++) { if (!memcmp (okm + 44, authClients + i*40, 8)) // clientID_i @@ -606,7 +611,7 @@ { const uint8_t * ephemeralPublicKey = buf + offset; offset += 32; // ephemeralPublicKey uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients - const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients + const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients if (offset > len) { LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in DH auth data"); @@ -632,7 +637,7 @@ { const uint8_t * authSalt = buf + offset; offset += 32; // authSalt uint16_t numClients = bufbe16toh (buf + offset); offset += 2; // clients - const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients + const uint8_t * authClients = buf + offset; offset += numClients*40; // authClients if (offset > len) { LogPrint (eLogError, "LeaseSet2: Too many clients ", numClients, " in PSK auth data"); @@ -653,16 +658,16 @@ LogPrint (eLogError, "LeaseSet2: Can't calculate authCookie: psk_i is not provided"); } else - LogPrint (eLogError, "LeaseSet2: unknown client auth type ", (int)flag); + LogPrint (eLogError, "LeaseSet2: Unknown client auth type ", (int)flag); } 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 @@ -737,7 +742,7 @@ htobe64buf (m_Buffer + offset, ts); offset += 8; // end date } - // we don't sign it yet. must be signed later on + // we don't sign it yet. must be signed later on } LocalLeaseSet::LocalLeaseSet (std::shared_ptr identity, const uint8_t * buf, size_t len): @@ -768,7 +773,7 @@ size_t size = ident.GetFullLen (); if (size > sz) { - LogPrint (eLogError, "LeaseSet: identity length ", size, " exceeds buffer size ", sz); + LogPrint (eLogError, "LeaseSet: Identity length ", size, " exceeds buffer size ", sz); return false; } // encryption key @@ -779,7 +784,7 @@ ++size; if (!numLeases || numLeases > MAX_NUM_LEASES) { - LogPrint (eLogError, "LeaseSet: incorrect number of leases", (int)numLeases); + LogPrint (eLogError, "LeaseSet: Incorrect number of leases", (int)numLeases); return false; } const uint8_t * leases = ptr + size; @@ -863,17 +868,17 @@ } // update expiration if (expirationTime) - { + { SetExpirationTime (expirationTime*1000LL); auto expires = (int)expirationTime - timestamp; htobe16buf (expiresBuf, expires > 0 ? expires : 0); - } + } else { // no tunnels or withdraw SetExpirationTime (timestamp*1000LL); memset (expiresBuf, 0, 2); // expires immeditely - } + } // sign keys.Sign (m_Buffer, offset, m_Buffer + offset); // LS + leading store type } @@ -912,6 +917,11 @@ 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 @@ -979,7 +989,7 @@ m_StoreHash = blindedKey->GetStoreHash (); } else - LogPrint (eLogError, "LeaseSet2: couldn't extract inner layer"); + LogPrint (eLogError, "LeaseSet2: Couldn't extract inner layer"); } void LocalEncryptedLeaseSet2::CreateClientAuthData (const uint8_t * subcredential, int authType, std::shared_ptr > authKeys, const uint8_t * authCookie, uint8_t * authData) const @@ -990,7 +1000,7 @@ ek.GenerateKeys (); // esk and epk memcpy (authData, ek.GetPublicKey (), 32); authData += 32; // epk htobe16buf (authData, authKeys->size ()); authData += 2; // num clients - uint8_t authInput[100]; // sharedSecret || cpk_i || subcredential || publishedTimestamp + uint8_t authInput[100]; // sharedSecret || cpk_i || subcredential || publishedTimestamp memcpy (authInput + 64, subcredential, 36); for (auto& it: *authKeys) { diff -Nru i2pd-2.39.0/libi2pd/LeaseSet.h i2pd-2.43.0/libi2pd/LeaseSet.h --- i2pd-2.39.0/libi2pd/LeaseSet.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/LeaseSet.h 2022-08-21 19:40:41.000000000 +0000 @@ -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 @@ // 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: @@ -128,8 +128,8 @@ }; /** - validate lease set buffer signature and extract expiration timestamp - @returns true if the leaseset is well formed and signature is valid + * validate lease set buffer signature and extract expiration timestamp + * @returns true if the leaseset is well formed and signature is valid */ bool LeaseSetBufferValidate(const uint8_t * ptr, size_t sz, uint64_t & expires); @@ -156,7 +156,7 @@ 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 -Nru i2pd-2.39.0/libi2pd/Log.cpp i2pd-2.43.0/libi2pd/Log.cpp --- i2pd-2.39.0/libi2pd/Log.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Log.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -46,7 +46,7 @@ #ifndef _WIN32 /** - * @brief Maps our log levels to syslog one + * @brief Maps our log levels to syslog one * @return syslog priority LOG_*, as defined in syslog.h */ static inline int GetSyslogPrio (enum LogLevel l) { @@ -113,11 +113,11 @@ std::string str_tolower(std::string s) { std::transform(s.begin(), s.end(), s.begin(), - // static_cast(std::tolower) // wrong - // [](int c){ return std::tolower(c); } // wrong - // [](char c){ return std::tolower(c); } // wrong - [](unsigned char c){ return std::tolower(c); } // correct - ); + // static_cast(std::tolower) // wrong + // [](int c){ return std::tolower(c); } // wrong + // [](char c){ return std::tolower(c); } // wrong + [](unsigned char c){ return std::tolower(c); } // correct + ); return s; } @@ -129,10 +129,10 @@ else if (level == "info") { m_MinLevel = eLogInfo; } else if (level == "debug") { m_MinLevel = eLogDebug; } else { - LogPrint(eLogError, "Log: unknown loglevel: ", level); + LogPrint(eLogError, "Log: Unknown loglevel: ", level); return; } - LogPrint(eLogInfo, "Log: min messages level set to ", level); + LogPrint(eLogInfo, "Log: Logging level set to ", level); } const char * Log::TimeAsString(std::time_t t) { @@ -170,7 +170,7 @@ break; case eLogStdout: default: - std::cout << TimeAsString(msg->timestamp) + std::cout << TimeAsString(msg->timestamp) << "@" << short_tid << "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels] << " - " << msg->text << std::endl; @@ -212,7 +212,7 @@ m_LogStream = os; return; } - LogPrint(eLogError, "Log: can't open file ", path); + LogPrint(eLogError, "Log: Can't open file ", path); } void Log::SendTo (std::shared_ptr os) { diff -Nru i2pd-2.39.0/libi2pd/Log.h i2pd-2.43.0/libi2pd/Log.h --- i2pd-2.39.0/libi2pd/Log.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Log.h 2022-08-21 19:40:41.000000000 +0000 @@ -52,7 +52,7 @@ { private: - enum LogType m_Destination; + enum LogType m_Destination; enum LogLevel m_MinLevel; std::shared_ptr m_LogStream; std::string m_Logfile; @@ -75,7 +75,7 @@ /** * @brief Makes formatted string from unix timestamp - * @param ts Second since epoch + * @param ts Second since epoch * * This function internally caches the result for last provided value */ @@ -86,52 +86,52 @@ Log (); ~Log (); - LogType GetLogType () { return m_Destination; }; + LogType GetLogType () { return m_Destination; }; LogLevel GetLogLevel () { return m_MinLevel; }; void Start (); void Stop (); /** - * @brief Sets minimal allowed level for log messages - * @param level String with wanted minimal msg level + * @brief Sets minimal allowed level for log messages + * @param level String with wanted minimal msg level */ - void SetLogLevel (const std::string& level); + void SetLogLevel (const std::string& level); /** * @brief Sets log destination to logfile - * @param path Path to logfile + * @param path Path to logfile */ void SendTo (const std::string &path); /** * @brief Sets log destination to given output stream - * @param os Output stream + * @param os Output stream */ void SendTo (std::shared_ptr os); /** - * @brief Sets format for timestamps in log - * @param format String with timestamp format + * @brief Sets format for timestamps in log + * @param format String with timestamp format */ void SetTimeFormat (std::string format) { m_TimeFormat = format; }; #ifndef _WIN32 /** * @brief Sets log destination to syslog - * @param name Wanted program name + * @param name Wanted program name * @param facility Wanted log category */ void SendTo (const char *name, int facility); #endif /** - * @brief Format log message and write to output stream/syslog - * @param msg Pointer to processed message + * @brief Format log message and write to output stream/syslog + * @param msg Pointer to processed message */ void Append(std::shared_ptr &); - /** @brief Reopen log file */ + /** @brief Reopen log file */ void Reopen(); }; @@ -144,8 +144,8 @@ */ struct LogMsg { std::time_t timestamp; - std::string text; /**< message text as single string */ - LogLevel level; /**< message level */ + std::string text; /**< message text as single string */ + LogLevel level; /**< message level */ std::thread::id tid; /**< id of thread that generated message */ LogMsg (LogLevel lvl, std::time_t ts, std::string&& txt): timestamp(ts), text(std::move(txt)), level(lvl) {} @@ -153,7 +153,7 @@ Log & Logger(); - typedef std::function ThrowFunction; + typedef std::function ThrowFunction; ThrowFunction GetThrowFunction (); void SetThrowFunction (ThrowFunction f); } // log diff -Nru i2pd-2.39.0/libi2pd/NetDb.cpp i2pd-2.43.0/libi2pd/NetDb.cpp --- i2pd-2.39.0/libi2pd/NetDb.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/NetDb.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -55,8 +55,10 @@ 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 @@ -64,14 +66,14 @@ if (it != m_RouterInfos.end ()) { // remove own router - m_RouterInfos.erase (it); m_Floodfills.remove (it->second); + m_RouterInfos.erase (it); } // 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); m_IsRunning = true; @@ -105,7 +107,10 @@ { i2p::util::SetThreadName("NetDB"); - uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0; + uint64_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0; + uint64_t lastProfilesCleanup = i2p::util::GetSecondsSinceEpoch (); + int16_t profilesCleanupVariance = 0; + while (m_IsRunning) { try @@ -116,7 +121,7 @@ int numMsgs = 0; while (msg) { - LogPrint(eLogDebug, "NetDb: got request with type ", (int) msg->GetTypeID ()); + LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ()); switch (msg->GetTypeID ()) { case eI2NPDatabaseStore: @@ -136,7 +141,7 @@ HandleNTCP2RouterInfoMsg (msg); break; default: // WTF? - LogPrint (eLogError, "NetDb: unexpected message type ", (int) msg->GetTypeID ()); + LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ()); //i2p::HandleI2NPMessage (msg); } if (numMsgs > 100) break; @@ -146,13 +151,14 @@ } if (!m_IsRunning) break; if (!i2p::transport::transports.IsOnline ()) continue; // don't manage netdb when offline - + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); if (ts - lastManageRequest >= 15) // manage requests every 15 seconds { m_Requests.ManageRequests (); lastManageRequest = ts; } + if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute { if (lastSave) @@ -162,14 +168,22 @@ } lastSave = ts; } + if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT) { i2p::context.CleanupDestination (); lastDestinationCleanup = ts; } - // publish - if (!m_HiddenMode && i2p::transport::transports.IsOnline ()) + if (ts - lastProfilesCleanup >= (uint64_t)(i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT + profilesCleanupVariance)) + { + DeleteObsoleteProfiles (); + lastProfilesCleanup = ts; + profilesCleanupVariance = (rand () % (2 * i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE) - i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE); + } + + // publish + if (!m_HiddenMode && i2p::transport::transports.IsOnline ()) { bool publish = false; if (m_PublishReplyToken) @@ -177,15 +191,15 @@ // next publishing attempt if (ts - lastPublish >= NETDB_PUBLISH_CONFIRMATION_TIMEOUT) publish = true; } - else if (i2p::context.GetLastUpdateTime () > lastPublish || - ts - lastPublish >= NETDB_PUBLISH_INTERVAL) - { + else if (i2p::context.GetLastUpdateTime () > lastPublish || + 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); @@ -193,6 +207,7 @@ lastPublish = ts; } } + if (ts - lastExploratory >= 30) // exploratory every 30 seconds { auto numRouters = m_RouterInfos.size (); @@ -214,7 +229,7 @@ } catch (std::exception& ex) { - LogPrint (eLogError, "NetDb: runtime exception: ", ex.what ()); + LogPrint (eLogError, "NetDb: Runtime exception: ", ex.what ()); } } } @@ -225,11 +240,10 @@ m_HiddenMode = hide; } - bool NetDb::AddRouterInfo (const uint8_t * buf, int len) + std::shared_ptr NetDb::AddRouterInfo (const uint8_t * buf, int len) { bool updated; - AddRouterInfo (buf, len, updated); - return updated; + return AddRouterInfo (buf, len, updated); } std::shared_ptr NetDb::AddRouterInfo (const uint8_t * buf, int len, bool& updated) @@ -257,7 +271,10 @@ if (r->IsNewer (buf, len)) { bool wasFloodfill = r->IsFloodfill (); - r->Update (buf, len); + { + std::unique_lock l(m_RouterInfosMutex); + r->Update (buf, len); + } LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64()); if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated { @@ -288,7 +305,7 @@ if (inserted) { LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64()); - if (r->IsFloodfill () && r->IsEligibleFloodfill ()) + if (r->IsFloodfill () && r->IsEligibleFloodfill ()) { std::unique_lock l(m_FloodfillsMutex); m_Floodfills.push_back (r); @@ -341,7 +358,7 @@ updated = true; } else - LogPrint (eLogError, "NetDb: new LeaseSet validation failed: ", ident.ToBase32()); + LogPrint (eLogError, "NetDb: New LeaseSet validation failed: ", ident.ToBase32()); } return updated; } @@ -371,7 +388,7 @@ } } else - LogPrint (eLogError, "NetDb: new LeaseSet2 validation failed: ", ident.ToBase32()); + LogPrint (eLogError, "NetDb: New LeaseSet2 validation failed: ", ident.ToBase32()); return false; } @@ -421,14 +438,17 @@ // try reseeding from floodfill first if specified std::string riPath; - if(i2p::config::GetOption("reseed.floodfill", riPath)) { + if(i2p::config::GetOption("reseed.floodfill", riPath)) + { auto ri = std::make_shared(riPath); - if (ri->IsFloodfill()) { + if (ri->IsFloodfill()) + { const uint8_t * riData = ri->GetBuffer(); int riLen = ri->GetBufferLen(); - if(!i2p::data::netdb.AddRouterInfo(riData, riLen)) { + if (!i2p::data::netdb.AddRouterInfo(riData, riLen)) + { // bad router info - LogPrint(eLogError, "NetDb: bad router info"); + LogPrint(eLogError, "NetDb: Bad router info"); return; } m_FloodfillBootstrap = ri; @@ -443,7 +463,7 @@ void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills) { - LogPrint(eLogInfo, "NetDB: reseeding from floodfill ", ri.GetIdentHashBase64()); + LogPrint(eLogInfo, "NetDB: Reseeding from floodfill ", ri.GetIdentHashBase64()); std::vector > requests; i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash(); @@ -470,22 +490,22 @@ i2p::transport::transports.SendMessages(ih, requests); } - bool NetDb::LoadRouterInfo (const std::string & path) + bool NetDb::LoadRouterInfo (const std::string& path, uint64_t ts) { auto r = std::make_shared(path); - if (r->GetRouterIdentity () && !r->IsUnreachable () && r->HasValidAddresses ()) - { + if (r->GetRouterIdentity () && !r->IsUnreachable () && r->HasValidAddresses () && + ts < r->GetTimestamp () + 24*60*60*NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT*1000LL) + { r->DeleteBuffer (); - r->ClearProperties (); // properties are not used for regular routers if (m_RouterInfos.emplace (r->GetIdentHash (), r).second) - { + { if (r->IsFloodfill () && r->IsEligibleFloodfill ()) m_Floodfills.push_back (r); - } + } } else { - LogPrint(eLogWarning, "NetDb: RI from ", path, " is invalid. Delete"); + LogPrint(eLogWarning, "NetDb: RI from ", path, " is invalid or too old. Delete"); i2p::fs::Remove(path); } return true; @@ -566,19 +586,20 @@ m_RouterInfos.clear (); m_Floodfills.clear (); - m_LastLoad = i2p::util::GetSecondsSinceEpoch(); + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); std::vector files; m_Storage.Traverse(files); for (const auto& path : files) - LoadRouterInfo(path); + LoadRouterInfo (path, ts); LogPrint (eLogInfo, "NetDb: ", m_RouterInfos.size(), " routers loaded (", m_Floodfills.size (), " floodfils)"); } 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 (); @@ -588,24 +609,24 @@ 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 (); + 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 ()) { - it.second->SaveToFile (path); + it.second->SaveToFile (m_Storage.Path(ident)); it.second->SetUpdated (false); it.second->SetUnreachable (false); it.second->DeleteBuffer (); updatedCount++; continue; } - // make router reachable back if too few routers - if (it.second->IsUnreachable () && total - deletedCount < NETDB_MIN_ROUTERS) - it.second->SetUnreachable (false); + // 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)) { @@ -618,18 +639,21 @@ if (it.second->IsUnreachable ()) { + if (it.second->IsFloodfill ()) deletedFloodfillsCount++; // delete RI file m_Storage.Remove(ident); deletedCount++; - if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false; + if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false; } } // m_RouterInfos iteration + m_RouterInfoBuffersPool.CleanUpMt (); + if (updatedCount > 0) - LogPrint (eLogInfo, "NetDb: saved ", updatedCount, " new/updated routers"); + LogPrint (eLogInfo, "NetDb: Saved ", updatedCount, " new/updated routers"); if (deletedCount > 0) { - LogPrint (eLogInfo, "NetDb: deleting ", deletedCount, " unreachable routers"); + LogPrint (eLogInfo, "NetDb: Deleting ", deletedCount, " unreachable routers"); // clean up RouterInfos table { std::unique_lock l(m_RouterInfosMutex); @@ -661,7 +685,7 @@ auto dest = m_Requests.CreateRequest (destination, false, requestComplete); // non-exploratory if (!dest) { - LogPrint (eLogWarning, "NetDb: destination ", destination.ToBase64(), " is requested already"); + LogPrint (eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already"); return; } @@ -669,24 +693,24 @@ if (floodfill) { if (direct && !floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) && - !i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) + !i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) direct = false; // floodfill can't be reached directly if (direct) transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); else { auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; - auto inbound = pool ? pool->GetNextInboundTunnel () : nullptr; + auto outbound = pool ? pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)) : nullptr; + auto inbound = pool ? pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)) : nullptr; if (outbound && inbound) outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, dest->CreateRequestMessage (floodfill, inbound)); else { LogPrint (eLogError, "NetDb: ", destination.ToBase64(), " destination requested, but no tunnels found"); - m_Requests.RequestComplete (destination, nullptr); - } - } - } + m_Requests.RequestComplete (destination, nullptr); + } + } + } else { LogPrint (eLogError, "NetDb: ", destination.ToBase64(), " destination requested, but no floodfills found"); @@ -700,10 +724,10 @@ auto dest = m_Requests.CreateRequest (destination, exploritory, requestComplete); // non-exploratory if (!dest) { - LogPrint (eLogWarning, "NetDb: destination ", destination.ToBase64(), " is requested already"); + LogPrint (eLogWarning, "NetDb: Destination ", destination.ToBase64(), " is requested already"); return; } - LogPrint(eLogInfo, "NetDb: destination ", destination.ToBase64(), " being requested directly from ", from.ToBase64()); + LogPrint(eLogInfo, "NetDb: Destination ", destination.ToBase64(), " being requested directly from ", from.ToBase64()); // direct transports.SendMessage (from, dest->CreateRequestMessage (nullptr, nullptr)); } @@ -727,7 +751,7 @@ IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET); if (ident.IsZero ()) { - LogPrint (eLogDebug, "NetDb: database store with zero ident, dropped"); + LogPrint (eLogDebug, "NetDb: Database store with zero ident, dropped"); return; } uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); @@ -746,14 +770,14 @@ if (outbound) outbound->SendTunnelDataMsg (buf + offset, tunnelID, deliveryStatus); else - LogPrint (eLogWarning, "NetDb: no outbound tunnels for DatabaseStore reply found"); + LogPrint (eLogWarning, "NetDb: No outbound tunnels for DatabaseStore reply found"); } offset += 32; } // we must send reply back before this check if (ident == i2p::context.GetIdentHash ()) { - LogPrint (eLogDebug, "NetDb: database store with own RouterInfo received, dropped"); + LogPrint (eLogDebug, "NetDb: Database store with own RouterInfo received, dropped"); return; } size_t payloadOffset = offset; @@ -766,24 +790,24 @@ { if (storeType == NETDB_STORE_TYPE_LEASESET) // 1 { - LogPrint (eLogDebug, "NetDb: store request: LeaseSet for ", ident.ToBase32()); + LogPrint (eLogDebug, "NetDb: Store request: LeaseSet for ", ident.ToBase32()); updated = AddLeaseSet (ident, buf + offset, len - offset); } else // all others are considered as LeaseSet2 { - LogPrint (eLogDebug, "NetDb: store request: LeaseSet2 of type ", storeType, " for ", ident.ToBase32()); + LogPrint (eLogDebug, "NetDb: Store request: LeaseSet2 of type ", storeType, " for ", ident.ToBase32()); updated = AddLeaseSet2 (ident, buf + offset, len - offset, storeType); } } } else // RouterInfo { - LogPrint (eLogDebug, "NetDb: store request: RouterInfo"); + LogPrint (eLogDebug, "NetDb: Store request: RouterInfo"); size_t size = bufbe16toh (buf + offset); offset += 2; if (size > MAX_RI_BUFFER_SIZE || size > len - offset) { - LogPrint (eLogError, "NetDb: invalid RouterInfo length ", (int)size); + LogPrint (eLogError, "NetDb: Invalid RouterInfo length ", (int)size); return; } uint8_t uncompressed[MAX_RI_BUFFER_SIZE]; @@ -792,7 +816,7 @@ updated = AddRouterInfo (ident, uncompressed, uncompressedSize); else { - LogPrint (eLogInfo, "NetDb: decompression failed ", uncompressedSize); + LogPrint (eLogInfo, "NetDb: Decompression failed ", uncompressedSize); return; } } @@ -867,7 +891,7 @@ m_Requests.RequestComplete (ident, nullptr); } else if(!m_FloodfillBootstrap) - LogPrint (eLogWarning, "NetDb: requested destination for ", key, " not found"); + LogPrint (eLogWarning, "NetDb: Requested destination for ", key, " not found"); // try responses for (int i = 0; i < num; i++) @@ -882,7 +906,7 @@ if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL) { // router with ident not found or too old (1 hour) - LogPrint (eLogDebug, "NetDb: found new/outdated router. Requesting RouterInfo ..."); + LogPrint (eLogDebug, "NetDb: Found new/outdated router. Requesting RouterInfo..."); if(m_FloodfillBootstrap) RequestDestinationFrom(router, m_FloodfillBootstrap->GetIdentHash(), true); else @@ -921,21 +945,22 @@ } uint16_t numExcluded = bufbe16toh (excluded); excluded += 2; - if (numExcluded > 512) + if (numExcluded > 512 || (excluded - buf) + numExcluded*32 > (int)msg->GetPayloadLength ()) { - LogPrint (eLogWarning, "NetDb: number of excluded peers", numExcluded, " exceeds 512"); + LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " is too much"); return; } std::shared_ptr replyMsg; if (lookupType == DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP) { - LogPrint (eLogInfo, "NetDb: exploratory close to ", key, " ", numExcluded, " excluded"); + LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded"); std::set excludedRouters; + const uint8_t * excluded_ident = excluded; for (int i = 0; i < numExcluded; i++) { - excludedRouters.insert (excluded); - excluded += 32; + excludedRouters.insert (excluded_ident); + excluded_ident += 32; } std::vector routers; for (int i = 0; i < 3; i++) @@ -952,13 +977,13 @@ else { if (lookupType == DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP || - lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP) + lookupType == DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP) { auto router = FindRouter (ident); if (router) { - LogPrint (eLogDebug, "NetDb: requested RouterInfo ", key, " found"); - router->LoadBuffer (); + LogPrint (eLogDebug, "NetDb: Requested RouterInfo ", key, " found"); + PopulateRouterInfoBuffer (router); if (router->GetBuffer ()) replyMsg = CreateDatabaseStoreMsg (router); } @@ -971,11 +996,11 @@ if (!leaseSet) { // no lease set found - LogPrint(eLogDebug, "NetDb: requested LeaseSet not found for ", ident.ToBase32()); + LogPrint(eLogDebug, "NetDb: Requested LeaseSet not found for ", ident.ToBase32()); } else if (!leaseSet->IsExpired ()) // we don't send back our LeaseSets { - LogPrint (eLogDebug, "NetDb: requested LeaseSet ", key, " found"); + LogPrint (eLogDebug, "NetDb: Requested LeaseSet ", key, " found"); replyMsg = CreateDatabaseStoreMsg (ident, leaseSet); } } @@ -993,7 +1018,7 @@ if (closestFloodfills.empty ()) LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded"); replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills); - } + } } excluded += numExcluded * 32; if (replyMsg) @@ -1020,10 +1045,10 @@ replyMsg = garlic.WrapSingleMessage (replyMsg); } if (!replyMsg) - LogPrint (eLogError, "NetDb: failed to wrap message"); + LogPrint (eLogError, "NetDb: Failed to wrap message"); } else - LogPrint(eLogWarning, "NetDb: encrypted reply requested but no tags provided"); + LogPrint(eLogWarning, "NetDb: Encrypted reply requested but no tags provided"); } auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; @@ -1045,8 +1070,8 @@ m_PublishExcluded.clear (); m_PublishReplyToken = 0; } - } - + } + void NetDb::Explore (int numDestinations) { // new requests @@ -1057,14 +1082,14 @@ uint8_t randomHash[32]; std::vector msgs; - LogPrint (eLogInfo, "NetDb: exploring new ", numDestinations, " routers ..."); + LogPrint (eLogInfo, "NetDb: Exploring new ", numDestinations, " routers ..."); for (int i = 0; i < numDestinations; i++) { RAND_bytes (randomHash, 32); auto dest = m_Requests.CreateRequest (randomHash, true); // exploratory if (!dest) { - LogPrint (eLogWarning, "NetDb: exploratory destination is requested already"); + LogPrint (eLogWarning, "NetDb: Exploratory destination is requested already"); return; } auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ()); @@ -1106,7 +1131,7 @@ LogPrint (eLogError, "NetDb: Couldn't publish our RouterInfo to ", NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS, " closest routers. Try again"); m_PublishExcluded.clear (); } - + auto floodfill = GetClosestFloodfill (i2p::context.GetIdentHash (), m_PublishExcluded); if (floodfill) { @@ -1116,19 +1141,19 @@ m_PublishExcluded.insert (floodfill->GetIdentHash ()); m_PublishReplyToken = replyToken; if (floodfill->IsReachableFrom (i2p::context.GetRouterInfo ()) || // are we able to connect? - i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) // already connected ? + i2p::transport::transports.IsConnected (floodfill->GetIdentHash ())) // already connected ? // send directly transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken)); else { - // otherwise through exploratory + // otherwise through exploratory auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; - auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : nullptr; + auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)) : nullptr; + auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)) : nullptr; if (inbound && outbound) - outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, - CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken, inbound)); - } + outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, + CreateDatabaseStoreMsg (i2p::context.GetSharedRouterInfo (), replyToken, inbound)); + } } } @@ -1168,7 +1193,8 @@ { return !router->IsHidden () && router != compatibleWith && (reverse ? compatibleWith->IsReachableFrom (*router) : - router->IsReachableFrom (*compatibleWith)); + router->IsReachableFrom (*compatibleWith)) && + router->IsECIES (); }); } @@ -1177,17 +1203,27 @@ 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 ()); }); } + std::shared_ptr NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::set& excluded) const + { + return GetRandomRouter ( + [v4, &excluded](std::shared_ptr router)->bool + { + return !router->IsHidden () && router->IsECIES () && + router->IsSSU2PeerTesting (v4) && !excluded.count (router->GetIdentHash ()); + }); + } + std::shared_ptr NetDb::GetRandomSSUV6Router () const { return GetRandomRouter ( [](std::shared_ptr router)->bool { - return !router->IsHidden () && router->IsSSUV6 (); + return !router->IsHidden () && router->IsECIES () && router->IsSSUV6 (); }); } @@ -1196,11 +1232,21 @@ 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 ()); }); } + std::shared_ptr NetDb::GetRandomSSU2Introducer (bool v4, const std::set& excluded) const + { + return GetRandomRouter ( + [v4, &excluded](std::shared_ptr router)->bool + { + return !router->IsHidden () && router->IsSSU2Introducer (v4) && + !excluded.count (router->GetIdentHash ()); + }); + } + std::shared_ptr NetDb::GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse) const { return GetRandomRouter ( @@ -1210,11 +1256,8 @@ (reverse ? compatibleWith->IsReachableFrom (*router) : router->IsReachableFrom (*compatibleWith)) && (router->GetCaps () & RouterInfo::eHighBandwidth) && -#if defined(__x86_64__) - router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION; -#else - router->GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; -#endif + router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION && + router->IsECIES (); }); } @@ -1234,12 +1277,12 @@ return it->second; // try some routers around auto it1 = m_RouterInfos.begin (); - if (inds[0]) + if (inds[0]) { // before - inds[1] %= inds[0]; + inds[1] %= inds[0]; std::advance (it1, (inds[1] + inds[0])/2); - } + } else it1 = it; auto it2 = it; @@ -1250,14 +1293,14 @@ std::advance (it2, inds[2]); } // it1 - from, it2 - to - it = it1; + 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 + // still not found, try from the beginning it = m_RouterInfos.begin (); while (it != it1 && it != m_RouterInfos.end ()) { @@ -1265,7 +1308,7 @@ return it->second; it++; } - // still not found, try to the begining + // still not found, try to the beginning it = it2; while (it != m_RouterInfos.end ()) { @@ -1359,7 +1402,8 @@ return res; } - std::shared_ptr NetDb::GetRandomRouterInFamily(const std::string & fam) const { + std::shared_ptr NetDb::GetRandomRouterInFamily (FamilyID fam) const + { return GetRandomRouter( [fam](std::shared_ptr router)->bool { @@ -1403,6 +1447,13 @@ else ++it; } + m_LeasesPool.CleanUpMt (); } + + void NetDb::PopulateRouterInfoBuffer (std::shared_ptr r) + { + if (!r || r->GetBuffer ()) return; + r->LoadBuffer (m_Storage.Path (r->GetIdentHashBase64 ())); + } } } diff -Nru i2pd-2.39.0/libi2pd/NetDb.hpp i2pd-2.43.0/libi2pd/NetDb.hpp --- i2pd-2.39.0/libi2pd/NetDb.hpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/NetDb.hpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -30,19 +30,22 @@ #include "NetDbRequests.h" #include "Family.h" #include "version.h" +#include "util.h" 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 const int NETDB_MAX_EXPIRATION_TIMEOUT = 27 * 60 * 60; // 27 hours + const int NETDB_MAX_OFFLINE_EXPIRATION_TIMEOUT = 180; // in days const int NETDB_PUBLISH_INTERVAL = 60 * 40; const int NETDB_PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds - const int NETDB_MAX_PUBLISH_EXCLUDED_FLOODFILLS = 15; + const int NETDB_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, 38); // 0.9.38 const int NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION = MAKE_VERSION_NUMBER(0, 9, 51); // 0.9.51 @@ -66,7 +69,7 @@ void Start (); void Stop (); - bool AddRouterInfo (const uint8_t * buf, int len); + std::shared_ptr AddRouterInfo (const uint8_t * buf, int len); bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len); bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len); bool AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType); @@ -87,13 +90,15 @@ std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith, bool reverse) const; std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith, bool reverse) const; std::shared_ptr GetRandomPeerTestRouter (bool v4, const std::set& excluded) const; + std::shared_ptr GetRandomSSU2PeerTestRouter (bool v4, const std::set& excluded) const; std::shared_ptr GetRandomSSUV6Router () const; // TODO: change to v6 peer test later std::shared_ptr GetRandomIntroducer (bool v4, const std::set& excluded) const; + std::shared_ptr GetRandomSSU2Introducer (bool v4, const std::set& excluded) const; std::shared_ptr GetClosestFloodfill (const IdentHash& destination, const std::set& excluded, bool closeThanUsOnly = false) const; std::vector GetClosestFloodfills (const IdentHash& destination, size_t num, std::set& excluded, bool closeThanUsOnly = false) const; std::shared_ptr GetClosestNonFloodfill (const IdentHash& destination, const std::set& excluded) const; - std::shared_ptr GetRandomRouterInFamily(const std::string & fam) const; + std::shared_ptr GetRandomRouterInFamily (FamilyID fam) const; void SetUnreachable (const IdentHash& ident, bool unreachable); void PostI2NPMsg (std::shared_ptr msg); @@ -119,13 +124,16 @@ size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n); void ClearRouterInfos () { m_RouterInfos.clear (); }; + std::shared_ptr NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); }; + void PopulateRouterInfoBuffer (std::shared_ptr r); + std::shared_ptr NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); }; uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; }; private: void Load (); - bool LoadRouterInfo (const std::string & path); + bool LoadRouterInfo (const std::string& path, uint64_t ts); void SaveUpdated (); void Run (); // exploratory thread void Explore (int numDestinations); @@ -152,7 +160,6 @@ std::list > m_Floodfills; bool m_IsRunning; - uint64_t m_LastLoad; std::thread * m_Thread; i2p::util::Queue > m_Queue; // of I2NPDatabaseStoreMsg @@ -174,6 +181,9 @@ std::set m_PublishExcluded; uint32_t m_PublishReplyToken = 0; + + i2p::util::MemoryPoolMt m_RouterInfoBuffersPool; + i2p::util::MemoryPoolMt m_LeasesPool; }; extern NetDb netdb; diff -Nru i2pd-2.39.0/libi2pd/NetDbRequests.h i2pd-2.43.0/libi2pd/NetDbRequests.h --- i2pd-2.39.0/libi2pd/NetDbRequests.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/NetDbRequests.h 2022-08-21 19:40:41.000000000 +0000 @@ -60,7 +60,7 @@ void Start (); void Stop (); - std::shared_ptr CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr); + std::shared_ptr CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr); void RequestComplete (const IdentHash& ident, std::shared_ptr r); std::shared_ptr FindRequest (const IdentHash& ident) const; void ManageRequests (); diff -Nru i2pd-2.39.0/libi2pd/NTCP2.cpp i2pd-2.43.0/libi2pd/NTCP2.cpp --- i2pd-2.39.0/libi2pd/NTCP2.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/NTCP2.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -23,7 +23,7 @@ #include "HTTP.h" #include "util.h" -#ifdef __linux__ +#if defined(__linux__) && !defined(_NETINET_IN_H) #include #endif @@ -32,14 +32,12 @@ 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; } @@ -61,14 +59,14 @@ void NTCP2Establisher::KDF1Bob () { - KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetStaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); + KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); } void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub) { MixHash (sessionRequest + 32, 32); // encrypted payload - int paddingLength = sessionRequestLen - 64; + int paddingLength = sessionRequestLen - 64; if (paddingLength > 0) MixHash (sessionRequest + 64, paddingLength); MixHash (epub, 32); @@ -93,7 +91,7 @@ void NTCP2Establisher::KDF3Alice () { uint8_t inputKeyMaterial[32]; - i2p::context.GetStaticKeys ().Agree (GetRemotePub (), inputKeyMaterial); + i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial); MixKey (inputKeyMaterial); } @@ -112,9 +110,8 @@ 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; @@ -133,7 +130,7 @@ // m3p2Len auto bufLen = i2p::context.GetRouterInfo ().GetBufferLen (); m3p2Len = bufLen + 4 + 16; // (RI header + RI + MAC for now) TODO: implement options - htobe16buf (options + 4, m3p2Len); + htobe16buf (options + 4, m3p2Len); // fill m3p2 payload (RouterInfo block) m_SessionConfirmedBuffer = new uint8_t[m3p2Len + 48]; // m3p1 is 48 bytes uint8_t * m3p2 = m_SessionConfirmedBuffer + 48; @@ -152,9 +149,8 @@ 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; @@ -199,8 +195,9 @@ MixHash (m3p2, m3p2Len); //h = SHA256(h || ciphertext) } - bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen) + bool NTCP2Establisher::ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew) { + clockSkew = false; // decrypt X i2p::crypto::CBCDecryption decryption; decryption.SetKey (i2p::context.GetIdentHash ()); @@ -236,7 +233,8 @@ if (tsA < ts - NTCP2_CLOCK_SKEW || tsA > ts + NTCP2_CLOCK_SKEW) { LogPrint (eLogWarning, "NTCP2: SessionRequest time difference ", (int)(ts - tsA), " exceeds clock skew"); - return false; + clockSkew = true; + // we send SessionCreate to let Alice know our time and then close session } } else @@ -322,26 +320,27 @@ } NTCP2Session::NTCP2Session (NTCP2Server& server, std::shared_ptr in_RemoteRouter, - std::shared_ptr addr): + std::shared_ptr addr): TransportSession (in_RemoteRouter, NTCP2_ESTABLISH_TIMEOUT), m_Server (server), m_Socket (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false), m_Establisher (new NTCP2Establisher), - m_SendSipKey (nullptr), m_ReceiveSipKey (nullptr), #if OPENSSL_SIPHASH m_SendMDCtx(nullptr), m_ReceiveMDCtx (nullptr), +#else + m_SendSipKey (nullptr), m_ReceiveSipKey (nullptr), #endif m_NextReceivedLen (0), m_NextReceivedBuffer (nullptr), m_NextSendBuffer (nullptr), - m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), m_IsSending (false), - m_NextPaddingSize (16) + m_NextReceivedBufferSize (0), m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), + m_IsSending (false), m_IsReceiving (false), m_NextPaddingSize (16) { if (in_RemoteRouter) // Alice { m_Establisher->m_RemoteIdentHash = GetRemoteIdentity ()->GetIdentHash (); if (addr) { - memcpy (m_Establisher->m_RemoteStaticKey, addr->ntcp2->staticKey, 32); - memcpy (m_Establisher->m_IV, addr->ntcp2->iv, 16); + memcpy (m_Establisher->m_RemoteStaticKey, addr->s, 32); + memcpy (m_Establisher->m_IV, addr->i, 16); m_RemoteEndpoint = boost::asio::ip::tcp::endpoint (addr->host, addr->port); } else @@ -356,8 +355,6 @@ delete[] m_NextReceivedBuffer; delete[] m_NextSendBuffer; #if OPENSSL_SIPHASH - if (m_SendSipKey) EVP_PKEY_free (m_SendSipKey); - if (m_ReceiveSipKey) EVP_PKEY_free (m_ReceiveSipKey); if (m_SendMDCtx) EVP_MD_CTX_destroy (m_SendMDCtx); if (m_ReceiveMDCtx) EVP_MD_CTX_destroy (m_ReceiveMDCtx); #endif @@ -377,7 +374,7 @@ transports.PeerDisconnected (shared_from_this ()); m_Server.RemoveNTCP2Session (shared_from_this ()); m_SendQueue.clear (); - LogPrint (eLogDebug, "NTCP2: session terminated"); + LogPrint (eLogDebug, "NTCP2: Session terminated"); } } @@ -405,6 +402,29 @@ 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 () { @@ -435,12 +455,11 @@ (void) bytes_transferred; if (ecode) { - LogPrint (eLogWarning, "NTCP2: couldn't send SessionRequest message: ", ecode.message ()); + LogPrint (eLogWarning, "NTCP2: Couldn't send SessionRequest message: ", ecode.message ()); Terminate (); } 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)); @@ -459,18 +478,25 @@ { LogPrint (eLogDebug, "NTCP2: SessionRequest received ", bytes_transferred); uint16_t paddingLen = 0; - if (m_Establisher->ProcessSessionRequestMessage (paddingLen)) + bool clockSkew = false; + if (m_Establisher->ProcessSessionRequestMessage (paddingLen, clockSkew)) { - if (paddingLen > 0) + if (clockSkew) { - if (paddingLen <= 287 - 64) // session request is 287 bytes max + // we don't care about padding, send SessionCreated and close session + SendSessionCreated (); + m_Server.GetService ().post (std::bind (&NTCP2Session::Terminate, shared_from_this ())); + } + else if (paddingLen > 0) + { + 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)); } else { - LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long"); + LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long"); Terminate (); } } @@ -516,14 +542,14 @@ { 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)); } else { - LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long"); + LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long"); Terminate (); } } @@ -566,7 +592,7 @@ (void) bytes_transferred; if (ecode) { - LogPrint (eLogWarning, "NTCP2: couldn't send SessionConfirmed message: ", ecode.message ()); + LogPrint (eLogWarning, "NTCP2: Couldn't send SessionConfirmed message: ", ecode.message ()); Terminate (); } else @@ -593,7 +619,7 @@ (void) bytes_transferred; if (ecode) { - LogPrint (eLogWarning, "NTCP2: couldn't send SessionCreated message: ", ecode.message ()); + LogPrint (eLogWarning, "NTCP2: Couldn't send SessionCreated message: ", ecode.message ()); Terminate (); } else @@ -636,7 +662,7 @@ // process RI if (buf[0] != eNTCP2BlkRouterInfo) { - LogPrint (eLogWarning, "NTCP2: unexpected block ", (int)buf[0], " in SessionConfirmed"); + LogPrint (eLogWarning, "NTCP2: Unexpected block ", (int)buf[0], " in SessionConfirmed"); Terminate (); return; } @@ -664,7 +690,7 @@ auto addr = ri.GetNTCP2AddressWithStaticKey (m_Establisher->m_RemoteStaticKey); if (!addr) { - LogPrint (eLogError, "NTCP2: No NTCP2 address wth static key found in SessionConfirmed"); + LogPrint (eLogError, "NTCP2: No NTCP2 address with static key found in SessionConfirmed"); Terminate (); return; } @@ -693,17 +719,19 @@ void NTCP2Session::SetSipKeys (const uint8_t * sendSipKey, const uint8_t * receiveSipKey) { #if OPENSSL_SIPHASH - m_SendSipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, sendSipKey, 16); + EVP_PKEY * sipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, sendSipKey, 16); m_SendMDCtx = EVP_MD_CTX_create (); EVP_PKEY_CTX *ctx = nullptr; - EVP_DigestSignInit (m_SendMDCtx, &ctx, nullptr, nullptr, m_SendSipKey); + EVP_DigestSignInit (m_SendMDCtx, &ctx, nullptr, nullptr, sipKey); EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); + EVP_PKEY_free (sipKey); - m_ReceiveSipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, receiveSipKey, 16); + sipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, receiveSipKey, 16); m_ReceiveMDCtx = EVP_MD_CTX_create (); ctx = nullptr; - EVP_DigestSignInit (m_ReceiveMDCtx, &ctx, NULL, NULL, m_ReceiveSipKey); + EVP_DigestSignInit (m_ReceiveMDCtx, &ctx, NULL, NULL, sipKey); EVP_PKEY_CTX_ctrl (ctx, -1, EVP_PKEY_OP_SIGNCTX, EVP_PKEY_CTRL_SET_DIGEST_SIZE, 8, nullptr); + EVP_PKEY_free (sipKey); #else m_SendSipKey = sendSipKey; m_ReceiveSipKey = receiveSipKey; @@ -719,7 +747,6 @@ 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)); @@ -730,7 +757,7 @@ if (IsTerminated ()) return; #ifdef __linux__ const int one = 1; - setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one)); + setsockopt(m_Socket.native_handle(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one)); #endif boost::asio::async_read (m_Socket, boost::asio::buffer(&m_NextReceivedLen, 2), boost::asio::transfer_all (), std::bind(&NTCP2Session::HandleReceivedLength, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -741,7 +768,7 @@ if (ecode) { if (ecode != boost::asio::error::operation_aborted) - LogPrint (eLogWarning, "NTCP2: receive length read error: ", ecode.message ()); + LogPrint (eLogWarning, "NTCP2: Receive length read error: ", ecode.message ()); Terminate (); } else @@ -756,11 +783,10 @@ #endif // m_NextReceivedLen comes from the network in BigEndian m_NextReceivedLen = be16toh (m_NextReceivedLen) ^ le16toh (m_ReceiveIV.key); - LogPrint (eLogDebug, "NTCP2: received length ", m_NextReceivedLen); + 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) @@ -774,7 +800,7 @@ } else { - LogPrint (eLogError, "NTCP2: received length ", m_NextReceivedLen, " is too short"); + LogPrint (eLogError, "NTCP2: Received length ", m_NextReceivedLen, " is too short"); Terminate (); } } @@ -787,6 +813,7 @@ 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)); } @@ -796,7 +823,7 @@ if (ecode) { if (ecode != boost::asio::error::operation_aborted) - LogPrint (eLogWarning, "NTCP2: receive read error: ", ecode.message ()); + LogPrint (eLogWarning, "NTCP2: Receive read error: ", ecode.message ()); Terminate (); } else @@ -808,9 +835,9 @@ CreateNonce (m_ReceiveSequenceNumber, nonce); m_ReceiveSequenceNumber++; if (i2p::crypto::AEADChaCha20Poly1305 (m_NextReceivedBuffer, m_NextReceivedLen-16, nullptr, 0, m_ReceiveKey, nonce, m_NextReceivedBuffer, m_NextReceivedLen, false)) { - LogPrint (eLogDebug, "NTCP2: received message decrypted"); + 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 @@ -839,10 +866,10 @@ switch (blk) { case eNTCP2BlkDateTime: - LogPrint (eLogDebug, "NTCP2: datetime"); + LogPrint (eLogDebug, "NTCP2: Datetime"); break; case eNTCP2BlkOptions: - LogPrint (eLogDebug, "NTCP2: options"); + LogPrint (eLogDebug, "NTCP2: Options"); break; case eNTCP2BlkRouterInfo: { @@ -858,25 +885,29 @@ LogPrint (eLogError, "NTCP2: I2NP block is too long ", size); break; } - auto nextMsg = NewI2NPMessage (size); - nextMsg->Align (12); // for possible tunnel msg + auto nextMsg = (frame[offset] == eI2NPTunnelData) ? NewI2NPTunnelMessage (true) : NewI2NPMessage (size); nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header - memcpy (nextMsg->GetNTCP2Header (), frame + offset, size); - nextMsg->FromNTCP2 (); - m_Handler.PutNextMessage (nextMsg); + if (nextMsg->len <= nextMsg->maxLen) + { + memcpy (nextMsg->GetNTCP2Header (), frame + offset, size); + nextMsg->FromNTCP2 (); + m_Handler.PutNextMessage (std::move (nextMsg)); + } + else + LogPrint (eLogError, "NTCP2: I2NP block is too long for I2NP message"); break; } case eNTCP2BlkTermination: if (size >= 9) { - LogPrint (eLogDebug, "NTCP2: termination. reason=", (int)(frame[offset + 8])); + LogPrint (eLogDebug, "NTCP2: Termination. reason=", (int)(frame[offset + 8])); Terminate (); } else LogPrint (eLogWarning, "NTCP2: Unexpected termination block size ", size); break; case eNTCP2BlkPadding: - LogPrint (eLogDebug, "NTCP2: padding"); + LogPrint (eLogDebug, "NTCP2: Padding"); break; default: LogPrint (eLogWarning, "NTCP2: Unknown block type ", (int)blk); @@ -888,7 +919,7 @@ void NTCP2Session::SetNextSentFrameLength (size_t frameLen, uint8_t * lengthBuf) { - #if OPENSSL_SIPHASH +#if OPENSSL_SIPHASH EVP_DigestSignInit (m_SendMDCtx, nullptr, nullptr, nullptr, nullptr); EVP_DigestSignUpdate (m_SendMDCtx, m_SendIV.buf, 8); size_t l = 8; @@ -898,7 +929,7 @@ #endif // length must be in BigEndian htobe16buf (lengthBuf, frameLen ^ le16toh (m_SendIV.key)); - LogPrint (eLogDebug, "NTCP2: sent length ", frameLen); + LogPrint (eLogDebug, "NTCP2: Sent length ", frameLen); } void NTCP2Session::SendI2NPMsgs (std::vector >& msgs) @@ -951,7 +982,7 @@ { // allocate send buffer m_NextSendBuffer = new uint8_t[287]; // can be any size > 16, we just allocate 287 frequently - // crate padding block + // create padding block auto paddingLen = CreatePaddingBlock (totalLen, m_NextSendBuffer, 287 - 16); // and padding block to encrypt and send if (paddingLen) @@ -1059,15 +1090,15 @@ 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) + 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); @@ -1093,7 +1124,13 @@ void NTCP2Session::SendTermination (NTCP2TerminationReason reason) { - if (!m_SendKey || !m_SendSipKey) return; + if (!m_SendKey || +#if OPENSSL_SIPHASH + !m_SendMDCtx +#else + !m_SendSipKey +#endif + ) return; m_NextSendBuffer = new uint8_t[49]; // 49 = 12 bytes message + 16 bytes MAC + 2 bytes size + up to 19 padding block // termination block m_NextSendBuffer[2] = eNTCP2BlkTermination; @@ -1126,21 +1163,21 @@ SendQueue (); else if (m_SendQueue.size () > NTCP2_MAX_OUTGOING_QUEUE_SIZE) { - LogPrint (eLogWarning, "NTCP2: outgoing messages queue size to ", - GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); + LogPrint (eLogWarning, "NTCP2: Outgoing messages queue size to ", + GetIdentHashBase64(), " exceeds ", NTCP2_MAX_OUTGOING_QUEUE_SIZE); Terminate (); } } - void NTCP2Session::SendLocalRouterInfo () + void NTCP2Session::SendLocalRouterInfo (bool update) { - if (!IsOutgoing ()) // we send it in SessionConfirmed + if (update || !IsOutgoing ()) // we send it in SessionConfirmed for ougoing session m_Server.GetService ().post (std::bind (&NTCP2Session::SendRouterInfo, shared_from_this ())); } NTCP2Server::NTCP2Server (): RunnableServiceWithWork ("NTCP2"), m_TerminationTimer (GetService ()), - m_ProxyType(eNoProxy), m_Resolver(GetService ()) + m_ProxyType(eNoProxy), m_Resolver(GetService ()) { } @@ -1206,7 +1243,7 @@ m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); -#ifdef __linux__ +#if defined(__linux__) && !defined(_NETINET_IN_H) if (!m_Address6 && !m_YggdrasilAddress) // only if not binded to address { // Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others @@ -1232,7 +1269,7 @@ } catch ( std::exception & ex ) { - LogPrint(eLogError, "NTCP2: failed to bind to v6 port ", address->port, ": ", ex.what()); + LogPrint(eLogError, "NTCP2: Failed to bind to v6 port ", address->port, ": ", ex.what()); ThrowFatal ("Unable to start IPv6 NTCP2 transport at port ", address->port, ": ", ex.what ()); continue; } @@ -1273,7 +1310,7 @@ auto it = m_NTCP2Sessions.find (ident); if (it != m_NTCP2Sessions.end ()) { - LogPrint (eLogWarning, "NTCP2: session to ", ident.ToBase64 (), " already exists"); + LogPrint (eLogWarning, "NTCP2: Session to ", ident.ToBase64 (), " already exists"); if (incoming) // replace by new session it->second->Terminate (); @@ -1342,7 +1379,7 @@ boost::system::error_code ec; conn->GetSocket ().bind (*localAddress, ec); if (ec) - LogPrint (eLogError, "NTCP2: can't bind to ", localAddress->address ().to_string (), ": ", ec.message ()); + LogPrint (eLogError, "NTCP2: Can't bind to ", localAddress->address ().to_string (), ": ", ec.message ()); } conn->GetSocket ().async_connect (conn->GetRemoteEndpoint (), std::bind (&NTCP2Server::HandleConnect, this, std::placeholders::_1, conn, timer)); } @@ -1448,6 +1485,8 @@ 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 ();) { @@ -1509,7 +1548,7 @@ { if (ecode) { - LogPrint(eLogWarning, "NTCP2: failed to connect to proxy ", ecode.message()); + LogPrint(eLogWarning, "NTCP2: Failed to connect to proxy ", ecode.message()); timer->cancel(); conn->Terminate(); return; @@ -1526,7 +1565,7 @@ (void) transferred; if(ec) { - LogPrint(eLogWarning, "NTCP2: socks5 write error ", ec.message()); + LogPrint(eLogWarning, "NTCP2: SOCKS5 write error ", ec.message()); } }); auto readbuff = std::make_shared >(2); @@ -1535,7 +1574,7 @@ { if(ec) { - LogPrint(eLogError, "NTCP2: socks5 read error ", ec.message()); + LogPrint(eLogError, "NTCP2: SOCKS5 read error ", ec.message()); timer->cancel(); conn->Terminate(); return; @@ -1549,14 +1588,14 @@ } else if ((*readbuff)[1] == 0xff) { - LogPrint(eLogError, "NTCP2: socks5 proxy rejected authentication"); + LogPrint(eLogError, "NTCP2: SOCKS5 proxy rejected authentication"); timer->cancel(); conn->Terminate(); return; } LogPrint(eLogError, "NTCP2:", (int)(*readbuff)[1]); } - LogPrint(eLogError, "NTCP2: socks5 server gave invalid response"); + LogPrint(eLogError, "NTCP2: SOCKS5 server gave invalid response"); timer->cancel(); conn->Terminate(); }); @@ -1584,7 +1623,7 @@ { (void) transferred; if(ec) - LogPrint(eLogError, "NTCP2: http proxy write error ", ec.message()); + LogPrint(eLogError, "NTCP2: HTTP proxy write error ", ec.message()); }); boost::asio::streambuf * readbuff = new boost::asio::streambuf; @@ -1593,7 +1632,7 @@ { if(ec) { - LogPrint(eLogError, "NTCP2: http proxy read error ", ec.message()); + LogPrint(eLogError, "NTCP2: HTTP proxy read error ", ec.message()); timer->cancel(); conn->Terminate(); } @@ -1611,10 +1650,10 @@ return; } else - LogPrint(eLogError, "NTCP2: http proxy rejected request ", res.code); + LogPrint(eLogError, "NTCP2: HTTP proxy rejected request ", res.code); } else - LogPrint(eLogError, "NTCP2: http proxy gave malformed response"); + LogPrint(eLogError, "NTCP2: HTTP proxy gave malformed response"); timer->cancel(); conn->Terminate(); delete readbuff; @@ -1623,7 +1662,7 @@ break; } default: - LogPrint(eLogError, "NTCP2: unknown proxy type, invalid state"); + LogPrint(eLogError, "NTCP2: Unknown proxy type, invalid state"); } } @@ -1664,7 +1703,7 @@ { if(ec) { - LogPrint(eLogError, "NTCP2: failed to write handshake to socks proxy ", ec.message()); + LogPrint(eLogError, "NTCP2: Failed to write handshake to socks proxy ", ec.message()); return; } }); @@ -1674,7 +1713,7 @@ { if(e) { - LogPrint(eLogError, "NTCP2: socks proxy read error ", e.message()); + LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message()); } else if(transferred == sz) { diff -Nru i2pd-2.39.0/libi2pd/NTCP2.h i2pd-2.43.0/libi2pd/NTCP2.h --- i2pd-2.39.0/libi2pd/NTCP2.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/NTCP2.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -28,12 +28,15 @@ { 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 @@ -104,7 +107,7 @@ void CreateSessionConfirmedMessagePart1 (const uint8_t * nonce); void CreateSessionConfirmedMessagePart2 (const uint8_t * nonce); - bool ProcessSessionRequestMessage (uint16_t& paddingLen); + bool ProcessSessionRequestMessage (uint16_t& paddingLen, bool& clockSkew); bool ProcessSessionCreatedMessage (uint16_t& paddingLen); bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce); bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf); @@ -115,7 +118,8 @@ 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,6 +136,7 @@ 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; }; @@ -143,7 +148,7 @@ void ClientLogin (); // Alice void ServerLogin (); // Bob - void SendLocalRouterInfo (); // after handshake + void SendLocalRouterInfo (bool update); // after handshake or by update void SendI2NPMessages (const std::vector >& msgs); private: @@ -151,6 +156,7 @@ 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); @@ -199,13 +205,13 @@ uint8_t m_Kab[32], m_Kba[32], m_Sipkeysab[32], m_Sipkeysba[32]; const uint8_t * m_SendKey, * m_ReceiveKey; #if OPENSSL_SIPHASH - EVP_PKEY * m_SendSipKey, * m_ReceiveSipKey; EVP_MD_CTX * m_SendMDCtx, * m_ReceiveMDCtx; #else const uint8_t * m_SendSipKey, * m_ReceiveSipKey; #endif uint16_t m_NextReceivedLen; uint8_t * m_NextReceivedBuffer, * m_NextSendBuffer; + size_t m_NextReceivedBufferSize; union { uint8_t buf[8]; @@ -215,12 +221,12 @@ 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; + int m_NextPaddingSize; }; class NTCP2Server: private i2p::util::RunnableServiceWithWork @@ -252,7 +258,7 @@ void UseProxy(ProxyType proxy, const std::string& address, uint16_t port, const std::string& user, const std::string& pass); void SetLocalAddress (const boost::asio::ip::address& localAddress); - + private: void HandleAccept (std::shared_ptr conn, const boost::system::error_code& error); @@ -261,7 +267,7 @@ void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); void AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer); - + // timer void ScheduleTermination (); void HandleTerminationTimer (const boost::system::error_code& ecode); @@ -279,7 +285,7 @@ boost::asio::ip::tcp::resolver m_Resolver; std::unique_ptr m_ProxyEndpoint; std::shared_ptr m_Address4, m_Address6, m_YggdrasilAddress; - + public: // for HTTP/I2PControl diff -Nru i2pd-2.39.0/libi2pd/Poly1305.cpp i2pd-2.43.0/libi2pd/Poly1305.cpp --- i2pd-2.39.0/libi2pd/Poly1305.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Poly1305.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,12 +1,13 @@ -#include "Poly1305.h" /** - This code is licensed under the MCGSI Public License - Copyright 2018 Jeff Becker - - Kovri go write your own code - + * This code is licensed under the MCGSI Public License + * Copyright 2018 Jeff Becker + * + *Kovri go write your own code + * */ +#include "Poly1305.h" + #if !OPENSSL_AEAD_CHACHA20_POLY1305 namespace i2p { diff -Nru i2pd-2.39.0/libi2pd/Poly1305.h i2pd-2.43.0/libi2pd/Poly1305.h --- i2pd-2.39.0/libi2pd/Poly1305.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Poly1305.h 2022-08-21 19:40:41.000000000 +0000 @@ -5,6 +5,7 @@ * Kovri go write your own code * */ + #ifndef LIBI2PD_POLY1305_H #define LIBI2PD_POLY1305_H #include diff -Nru i2pd-2.39.0/libi2pd/Profiling.cpp i2pd-2.43.0/libi2pd/Profiling.cpp --- i2pd-2.39.0/libi2pd/Profiling.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Profiling.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -73,7 +73,7 @@ if (!i2p::fs::Exists(path)) { - LogPrint(eLogWarning, "Profiling: no profile yet for ", ident); + LogPrint(eLogWarning, "Profiling: No profile yet for ", ident); return; } @@ -115,7 +115,7 @@ } catch (boost::property_tree::ptree_bad_path& ex) { - LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident); + LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident); } } else @@ -194,7 +194,7 @@ continue; } if (((now - st.st_mtime) / 3600) >= PEER_PROFILE_EXPIRATION_TIMEOUT) { - LogPrint(eLogDebug, "Profiling: removing expired peer profile: ", path); + LogPrint(eLogDebug, "Profiling: Removing expired peer profile: ", path); i2p::fs::Remove(path); } } diff -Nru i2pd-2.39.0/libi2pd/Profiling.h i2pd-2.43.0/libi2pd/Profiling.h --- i2pd-2.39.0/libi2pd/Profiling.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Profiling.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,6 +29,8 @@ const char PEER_PROFILE_USAGE_REJECTED[] = "rejected"; const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days) + const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day) + const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours) class RouterProfile { diff -Nru i2pd-2.39.0/libi2pd/Queue.h i2pd-2.43.0/libi2pd/Queue.h --- i2pd-2.39.0/libi2pd/Queue.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Queue.h 2022-08-21 19:40:41.000000000 +0000 @@ -28,7 +28,7 @@ void Put (Element e) { - std::unique_lock l(m_QueueMutex); + std::unique_lock l(m_QueueMutex); m_Queue.push (std::move(e)); m_NonEmpty.notify_one (); } @@ -38,7 +38,7 @@ { if (!vec.empty ()) { - std::unique_lock l(m_QueueMutex); + std::unique_lock l(m_QueueMutex); for (const auto& it: vec) m_Queue.push (std::move(it)); m_NonEmpty.notify_one (); diff -Nru i2pd-2.39.0/libi2pd/Reseed.cpp i2pd-2.43.0/libi2pd/Reseed.cpp --- i2pd-2.39.0/libi2pd/Reseed.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Reseed.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -60,19 +60,19 @@ num = ProcessSU3File (su3FileName.c_str ()); } if (num == 0) - LogPrint (eLogWarning, "Reseed: failed to reseed from ", su3FileName); + LogPrint (eLogWarning, "Reseed: Failed to reseed from ", su3FileName); } else if (zipFileName.length() > 0) // bootstrap from ZIP file { int num = ProcessZIPFile (zipFileName.c_str ()); if (num == 0) - LogPrint (eLogWarning, "Reseed: failed to reseed from ", zipFileName); + LogPrint (eLogWarning, "Reseed: Failed to reseed from ", zipFileName); } else // bootstrap from reseed servers { int num = ReseedFromServers (); if (num == 0) - LogPrint (eLogWarning, "Reseed: failed to reseed from servers"); + LogPrint (eLogWarning, "Reseed: Failed to reseed from servers"); } } @@ -82,25 +82,26 @@ */ int Reseeder::ReseedFromServers () { - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool ipv4; i2p::config::GetOption("ipv4", ipv4); - + bool ipv6; i2p::config::GetOption("ipv6", ipv6); + bool ipv4; i2p::config::GetOption("ipv4", ipv4); + bool yggdrasil; i2p::config::GetOption("meshnets.yggdrasil", yggdrasil); + std::vector httpsReseedHostList; if (ipv4 || ipv6) - { + { std::string reseedURLs; i2p::config::GetOption("reseed.urls", reseedURLs); if (!reseedURLs.empty ()) boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on); } - + std::vector yggReseedHostList; - if (!i2p::util::net::GetYggdrasilAddress ().is_unspecified ()) + if (yggdrasil && !i2p::util::net::GetYggdrasilAddress ().is_unspecified ()) { - LogPrint (eLogInfo, "Reseed: yggdrasil is supported"); + LogPrint (eLogInfo, "Reseed: Yggdrasil is supported"); std::string yggReseedURLs; i2p::config::GetOption("reseed.yggurls", yggReseedURLs); if (!yggReseedURLs.empty ()) boost::split(yggReseedHostList, yggReseedURLs, boost::is_any_of(","), boost::token_compress_on); - } + } if (httpsReseedHostList.empty () && yggReseedHostList.empty()) { @@ -113,14 +114,14 @@ { auto ind = rand () % (httpsReseedHostList.size () + yggReseedHostList.size ()); bool isHttps = ind < httpsReseedHostList.size (); - std::string reseedUrl = isHttps ? httpsReseedHostList[ind] : + std::string reseedUrl = isHttps ? httpsReseedHostList[ind] : yggReseedHostList[ind - httpsReseedHostList.size ()]; reseedUrl += "i2pseeds.su3"; auto num = ReseedFromSU3Url (reseedUrl, isHttps); if (num > 0) return num; // success reseedRetries++; } - LogPrint (eLogWarning, "Reseed: failed to reseed from servers after 10 attempts"); + LogPrint (eLogWarning, "Reseed: Failed to reseed from servers after 10 attempts"); return 0; } @@ -186,31 +187,31 @@ } s.seekg (1, std::ios::cur); // su3 file format version SigningKeyType signatureType; - s.read ((char *)&signatureType, 2); // signature type + s.read ((char *)&signatureType, 2); // signature type signatureType = be16toh (signatureType); uint16_t signatureLength; - s.read ((char *)&signatureLength, 2); // signature length + s.read ((char *)&signatureLength, 2); // signature length signatureLength = be16toh (signatureLength); s.seekg (1, std::ios::cur); // unused uint8_t versionLength; - s.read ((char *)&versionLength, 1); // version length + s.read ((char *)&versionLength, 1); // version length s.seekg (1, std::ios::cur); // unused uint8_t signerIDLength; - s.read ((char *)&signerIDLength, 1); // signer ID length + s.read ((char *)&signerIDLength, 1); // signer ID length uint64_t contentLength; - s.read ((char *)&contentLength, 8); // content length + s.read ((char *)&contentLength, 8); // content length contentLength = be64toh (contentLength); s.seekg (1, std::ios::cur); // unused uint8_t fileType; - s.read ((char *)&fileType, 1); // file type - if (fileType != 0x00) // zip file + s.read ((char *)&fileType, 1); // file type + if (fileType != 0x00) // zip file { LogPrint (eLogError, "Reseed: Can't handle file type ", (int)fileType); return 0; } s.seekg (1, std::ios::cur); // unused uint8_t contentType; - s.read ((char *)&contentType, 1); // content type + s.read ((char *)&contentType, 1); // content type if (contentType != 0x03) // reseed data { LogPrint (eLogError, "Reseed: Unexpected content type ", (int)contentType); @@ -414,13 +415,13 @@ { if (r && ts > r->GetTimestamp () + 10*i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT*1000LL) // 270 hours { - LogPrint (eLogError, "Reseed: router ", r->GetIdentHash().ToBase64 (), " is outdated by ", (ts - r->GetTimestamp ())/1000LL/3600LL, " hours"); + LogPrint (eLogError, "Reseed: Router ", r->GetIdentHash().ToBase64 (), " is outdated by ", (ts - r->GetTimestamp ())/1000LL/3600LL, " hours"); numOutdated++; } }); if (numOutdated > numFiles/2) // more than half { - LogPrint (eLogError, "Reseed: mammoth's shit\n" + LogPrint (eLogError, "Reseed: Mammoth's shit\n" " *_____*\n" " *_*****_*\n" " *_(O)_(O)_*\n" @@ -478,7 +479,7 @@ if (terminator) terminator[0] = 0; } // extract RSA key (we need n only, e = 65537) - RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert)); + const RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert)); const BIGNUM * n, * e, * d; RSA_get0_key(key, &n, &e, &d); PublicKey value; @@ -509,7 +510,7 @@ for (const std::string & file : files) { if (file.compare(file.size() - 4, 4, ".crt") != 0) { - LogPrint(eLogWarning, "Reseed: ignoring file ", file); + LogPrint(eLogWarning, "Reseed: Ignoring file ", file); continue; } LoadCertificate (file); @@ -533,17 +534,17 @@ } // check for valid proxy url schema if (proxyUrl.schema != "http" && proxyUrl.schema != "socks") { - LogPrint(eLogError, "Reseed: bad proxy url: ", proxy); + LogPrint(eLogError, "Reseed: Bad proxy url: ", proxy); return ""; } } else { - LogPrint(eLogError, "Reseed: bad proxy url: ", proxy); + LogPrint(eLogError, "Reseed: Bad proxy url: ", proxy); return ""; } } i2p::http::URL url; if (!url.parse(address)) { - LogPrint(eLogError, "Reseed: failed to parse url: ", address); + LogPrint(eLogError, "Reseed: Failed to parse url: ", address); return ""; } url.schema = "https"; @@ -680,30 +681,30 @@ auto it = boost::asio::ip::tcp::resolver(service).resolve ( boost::asio::ip::tcp::resolver::query (url.host, std::to_string(url.port)), ecode); if (!ecode) - { + { bool connected = false; boost::asio::ip::tcp::resolver::iterator end; while (it != end) - { + { boost::asio::ip::tcp::endpoint ep = *it; if ((ep.address ().is_v4 () && i2p::context.SupportsV4 ()) || - (ep.address ().is_v6 () && i2p::context.SupportsV6 ())) - { + (ep.address ().is_v6 () && i2p::context.SupportsV6 ())) + { s.lowest_layer().connect (ep, ecode); if (!ecode) { connected = true; break; - } - } + } + } it++; } if (!connected) { LogPrint(eLogError, "Reseed: Failed to connect to ", url.host); return ""; - } - } + } + } } if (!ecode) { @@ -743,55 +744,55 @@ i2p::http::HTTPRes res; int len = res.parse(data); if (len <= 0) { - LogPrint(eLogWarning, "Reseed: incomplete/broken response from ", uri); + LogPrint(eLogWarning, "Reseed: Incomplete/broken response from ", uri); return ""; } if (res.code != 200) { - LogPrint(eLogError, "Reseed: failed to reseed from ", uri, ", http code ", res.code); + LogPrint(eLogError, "Reseed: Failed to reseed from ", uri, ", http code ", res.code); return ""; } data.erase(0, len); /* drop http headers from response */ - LogPrint(eLogDebug, "Reseed: got ", data.length(), " bytes of data from ", uri); + LogPrint(eLogDebug, "Reseed: Got ", data.length(), " bytes of data from ", uri); if (res.is_chunked()) { std::stringstream in(data), out; if (!i2p::http::MergeChunkedResponse(in, out)) { - LogPrint(eLogWarning, "Reseed: failed to merge chunked response from ", uri); + LogPrint(eLogWarning, "Reseed: Failed to merge chunked response from ", uri); return ""; } - LogPrint(eLogDebug, "Reseed: got ", data.length(), "(", out.tellg(), ") bytes of data from ", uri); + LogPrint(eLogDebug, "Reseed: Got ", data.length(), "(", out.tellg(), ") bytes of data from ", uri); data = out.str(); } return data; - } + } std::string Reseeder::YggdrasilRequest (const std::string& address) { i2p::http::URL url; - if (!url.parse(address)) + if (!url.parse(address)) { - LogPrint(eLogError, "Reseed: failed to parse url: ", address); + LogPrint(eLogError, "Reseed: Failed to parse url: ", address); return ""; } url.schema = "http"; if (!url.port) url.port = 80; boost::system::error_code ecode; - boost::asio::io_service service; + boost::asio::io_service service; boost::asio::ip::tcp::socket s(service, boost::asio::ip::tcp::v6()); if (url.host.length () < 2) return ""; // assume [] auto host = url.host.substr (1, url.host.length () - 2); - LogPrint (eLogDebug, "Reseed: Connecting to yggdrasil ", url.host, ":", url.port); + LogPrint (eLogDebug, "Reseed: Connecting to Yggdrasil ", url.host, ":", url.port); s.connect (boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::from_string (host), url.port), ecode); if (!ecode) { - LogPrint (eLogDebug, "Reseed: Connected to yggdrasil ", url.host, ":", url.port); + LogPrint (eLogDebug, "Reseed: Connected to Yggdrasil ", url.host, ":", url.port); return ReseedRequest (s, url.to_string()); } else - LogPrint (eLogError, "Reseed: Couldn't connect to yggdrasil ", url.host, ": ", ecode.message ()); - + LogPrint (eLogError, "Reseed: Couldn't connect to Yggdrasil ", url.host, ": ", ecode.message ()); + return ""; - } + } } } diff -Nru i2pd-2.39.0/libi2pd/Reseed.h i2pd-2.43.0/libi2pd/Reseed.h --- i2pd-2.39.0/libi2pd/Reseed.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Reseed.h 2022-08-21 19:40:41.000000000 +0000 @@ -49,8 +49,8 @@ std::string HttpsRequest (const std::string& address); std::string YggdrasilRequest (const std::string& address); template - std::string ReseedRequest (Stream& s, const std::string& uri); - + std::string ReseedRequest (Stream& s, const std::string& uri); + private: std::map m_SigningKeys; diff -Nru i2pd-2.39.0/libi2pd/RouterContext.cpp i2pd-2.43.0/libi2pd/RouterContext.cpp --- i2pd-2.39.0/libi2pd/RouterContext.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/RouterContext.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,7 +29,7 @@ RouterContext::RouterContext (): m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false), m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown), - m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID) + m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone), m_NetID (I2PD_NET_ID) { } @@ -43,11 +43,8 @@ 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 () @@ -60,23 +57,22 @@ void RouterContext::NewRouterInfo () { - i2p::data::RouterInfo routerInfo; + i2p::data::LocalRouterInfo routerInfo; routerInfo.SetRouterIdentity (GetIdentity ()); uint16_t port; i2p::config::GetOption("port", port); - if (!port) - { - port = rand () % (30777 - 9111) + 9111; // I2P network ports range - if (port == 9150) port = 9151; // Tor browser - } - bool ipv4; i2p::config::GetOption("ipv4", ipv4); - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool ssu; i2p::config::GetOption("ssu", ssu); - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); - bool nat; i2p::config::GetOption("nat", nat); + if (!port) port = SelectRandomPort (); + bool ipv4; i2p::config::GetOption("ipv4", ipv4); + bool ipv6; i2p::config::GetOption("ipv6", ipv6); + bool ssu; i2p::config::GetOption("ssu", ssu); + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); + bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); + bool nat; i2p::config::GetOption("nat", nat); if ((ntcp2 || ygg) && !m_NTCP2Keys) NewNTCP2Keys (); + if (ssu2 && !m_SSU2Keys) + NewSSU2Keys (); bool ntcp2Published = false; if (ntcp2) { @@ -87,6 +83,9 @@ if (!ntcp2proxy.empty ()) ntcp2Published = false; } } + bool ssu2Published = false; + if (ssu2) + i2p::config::GetOption("ssu2.published", ssu2Published); uint8_t caps = 0, addressCaps = 0; if (ipv4) { @@ -115,6 +114,20 @@ routerInfo.AddSSUAddress (host.c_str(), port, nullptr); caps |= i2p::data::RouterInfo::eReachable; // R } + if (ssu2) + { + if (ssu2Published) + { + uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); + if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), ssu2Port); + } + else + { + addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4; + routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro); + } + } } if (ipv6) { @@ -150,6 +163,21 @@ routerInfo.AddSSUAddress (host.c_str(), port, nullptr); caps |= i2p::data::RouterInfo::eReachable; // R } + if (ssu2) + { + if (ssu2Published) + { + uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); + if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), ssu2Port); + } + else + { + if (!ipv4) // no other ssu2 addresses yet + routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro); + addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; + } + } } if (ygg) { @@ -160,7 +188,7 @@ if (addressCaps) routerInfo.SetUnreachableAddressesTransportCaps (addressCaps); - routerInfo.SetCaps (caps); // caps + L + routerInfo.UpdateCaps (caps); // caps + L routerInfo.SetProperty ("netId", std::to_string (m_NetID)); routerInfo.SetProperty ("router.version", I2P_VERSION); routerInfo.CreateBuffer (m_Keys); @@ -168,6 +196,13 @@ m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); } + uint16_t RouterContext::SelectRandomPort () const + { + uint16_t port = rand () % (30777 - 9111) + 9111; // I2P network ports range + if (port == 9150) port = 9151; // Tor browser + return port; + } + void RouterContext::UpdateRouterInfo () { m_RouterInfo.CreateBuffer (m_Keys); @@ -177,17 +212,37 @@ void RouterContext::NewNTCP2Keys () { - m_StaticKeys.reset (new i2p::crypto::X25519Keys ()); - m_StaticKeys->GenerateKeys (); + m_NTCP2StaticKeys.reset (new i2p::crypto::X25519Keys ()); + m_NTCP2StaticKeys->GenerateKeys (); m_NTCP2Keys.reset (new NTCP2PrivateKeys ()); - m_StaticKeys->GetPrivateKey (m_NTCP2Keys->staticPrivateKey); - memcpy (m_NTCP2Keys->staticPublicKey, m_StaticKeys->GetPublicKey (), 32); + m_NTCP2StaticKeys->GetPrivateKey (m_NTCP2Keys->staticPrivateKey); + memcpy (m_NTCP2Keys->staticPublicKey, m_NTCP2StaticKeys->GetPublicKey (), 32); RAND_bytes (m_NTCP2Keys->iv, 16); // save std::ofstream fk (i2p::fs::DataDirPath (NTCP2_KEYS), std::ofstream::binary | std::ofstream::out); fk.write ((char *)m_NTCP2Keys.get (), sizeof (NTCP2PrivateKeys)); } + void RouterContext::NewSSU2Keys () + { + m_SSU2StaticKeys.reset (new i2p::crypto::X25519Keys ()); + m_SSU2StaticKeys->GenerateKeys (); + m_SSU2Keys.reset (new SSU2PrivateKeys ()); + m_SSU2StaticKeys->GetPrivateKey (m_SSU2Keys->staticPrivateKey); + memcpy (m_SSU2Keys->staticPublicKey, m_SSU2StaticKeys->GetPublicKey (), 32); + RAND_bytes (m_SSU2Keys->intro, 32); + // save + std::ofstream fk (i2p::fs::DataDirPath (SSU2_KEYS), std::ofstream::binary | std::ofstream::out); + fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys)); + } + + bool RouterContext::IsSSU2Only () const + { + auto transports = m_RouterInfo.GetCompatibleTransports (false); + return (transports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) && + !(transports & (i2p::data::RouterInfo::eSSUV4 | i2p::data::RouterInfo::eSSUV6)); + } + void RouterContext::SetStatus (RouterStatus status) { if (status != m_Status) @@ -208,11 +263,18 @@ } } + void RouterContext::SetStatusSSU2 (RouterStatus status) + { + if (IsSSU2Only ()) + SetStatus (status); + } + void RouterContext::SetStatusV6 (RouterStatus status) { if (status != m_StatusV6) { m_StatusV6 = status; + m_ErrorV6 = eRouterErrorNone; switch (m_StatusV6) { case eRouterStatusOK: @@ -227,12 +289,18 @@ } } + void RouterContext::SetStatusV6SSU2 (RouterStatus status) + { + if (IsSSU2Only ()) + SetStatusV6 (status); + } + void RouterContext::UpdatePort (int port) { bool updated = false; for (auto& address : m_RouterInfo.GetAddresses ()) { - if (!address->IsNTCP2 () && address->port != port) + if (address->port != port && (address->transportStyle == i2p::data::RouterInfo::eTransportSSU || IsSSU2Only ())) { address->port = port; updated = true; @@ -260,15 +328,10 @@ } if (isAddr) { - if (!port && !address->port) - { - // select random port only if address's port is not set - port = rand () % (30777 - 9111) + 9111; // I2P network ports range - if (port == 9150) port = 9151; // Tor browser - } + if (!port && !address->port) port = SelectRandomPort (); if (port) address->port = port; address->published = publish; - address->ntcp2->iv = m_NTCP2Keys->iv; + memcpy (address->i, m_NTCP2Keys->iv, 16); updated = true; } } @@ -281,18 +344,23 @@ { auto& addresses = m_RouterInfo.GetAddresses (); bool found = false, updated = false; - for (auto it = addresses.begin (); it != addresses.end (); ++it) + for (auto it = addresses.begin (); it != addresses.end ();) { if ((*it)->IsNTCP2 ()) { found = true; - if (!enable) + if (enable) { - addresses.erase (it); - updated= true; - } - break; + (*it)->s = m_NTCP2Keys->staticPublicKey; + memcpy ((*it)->i, m_NTCP2Keys->iv, 16); + it++; + } + else + it = addresses.erase (it); + updated = true; } + else + it++; } if (enable && !found) { @@ -303,30 +371,116 @@ UpdateRouterInfo (); } + void RouterContext::PublishSSU2Address (int port, bool publish, bool v4, bool v6) + { + if (!m_SSU2Keys) return; + int newPort = 0; + if (!port) + { + for (const auto& address : m_RouterInfo.GetAddresses ()) + if (address->port) + { + newPort = address->port; + break; + } + if (!newPort) newPort = SelectRandomPort (); + } + bool updated = false; + for (auto& address : m_RouterInfo.GetAddresses ()) + { + if (address->IsSSU2 () && (!address->port || address->port != port || address->published != publish) && + ((v4 && address->IsV4 ()) || (v6 && address->IsV6 ()))) + { + if (port) address->port = port; + else if (!address->port) address->port = newPort; + address->published = publish; + if (publish) + address->caps |= (i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting); + else + address->caps &= ~(i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting); + updated = true; + } + } + if (updated) + UpdateRouterInfo (); + } + + void RouterContext::UpdateSSU2Address (bool enable) + { + auto& addresses = m_RouterInfo.GetAddresses (); + bool found = false, updated = false; + for (auto it = addresses.begin (); it != addresses.end ();) + { + if ((*it)->IsSSU2 ()) + { + found = true; + if (enable) + { + (*it)->s = m_SSU2Keys->staticPublicKey; + (*it)->i = m_SSU2Keys->intro; + it++; + } + else + it = addresses.erase (it); + updated = true; + } + else + it++; + } + if (enable && !found) + { + bool ipv4; i2p::config::GetOption("ipv4", ipv4); + bool ipv6; i2p::config::GetOption("ipv6", ipv6); + bool published; i2p::config::GetOption("ntcp2.published", published); + if (published) + { + if (ipv4) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV4); + if (ipv6) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV6); + } + else + { + uint8_t addressCaps = 0; + if (ipv4) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4; + if (ipv6) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; + m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addressCaps); + } + updated = true; + } + if (updated) + UpdateRouterInfo (); + } + void RouterContext::UpdateAddress (const boost::asio::ip::address& host) { bool updated = false; for (auto& address : m_RouterInfo.GetAddresses ()) { if (address->host != host && address->IsCompatible (host) && - !i2p::util::net::IsYggdrasilAddress (address->host)) + !i2p::util::net::IsYggdrasilAddress (address->host)) { + // update host address->host = host; - if (host.is_v6 () && address->transportStyle == i2p::data::RouterInfo::eTransportSSU) + updated = true; + } + if (host.is_v6 () && address->IsV6 () && address->ssu && + (!address->ssu->mtu || updated)) + { + // update MTU + auto mtu = i2p::util::net::GetMTU (host); + if (mtu) { - // update MTU - auto mtu = i2p::util::net::GetMTU (host); - if (mtu) - { - LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu); - if (mtu > 1472) { // TODO: magic constant - mtu = 1472; - LogPrint(eLogWarning, "Router: MTU dropped to upper limit of 1472 bytes"); - } - if (address->ssu) address->ssu->mtu = mtu; + LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu); + int maxMTU = i2p::util::net::GetMaxMTU (host.to_v6 ()); + if (mtu > maxMTU) + { + mtu = maxMTU; + LogPrint(eLogWarning, "Router: MTU dropped to upper limit of ", maxMTU, " bytes"); } + if (mtu && !address->IsSSU2 ()) // SSU1 + mtu = (mtu >> 4) << 4; // round to multiple of 16 + address->ssu->mtu = mtu; + updated = true; } - updated = true; } } auto ts = i2p::util::GetSecondsSinceEpoch (); @@ -348,14 +502,45 @@ UpdateRouterInfo (); } + bool RouterContext::AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4) + { + if (!IsSSU2Only ()) return false; + bool ret = m_RouterInfo.AddSSU2Introducer (introducer, v4); + if (ret) + UpdateRouterInfo (); + return ret; + } + + void RouterContext::RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4) + { + if (!IsSSU2Only ()) return; + if (m_RouterInfo.RemoveSSU2Introducer (h, v4)) + UpdateRouterInfo (); + } + + void RouterContext::ClearSSU2Introducers (bool v4) + { + bool updated = false; + auto& addresses = m_RouterInfo.GetAddresses (); + for (auto& addr : addresses) + if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())) && + addr->ssu && !addr->ssu->introducers.empty ()) + { + addr->ssu->introducers.clear (); + updated = true; + } + if (updated) + UpdateRouterInfo (); + } + void RouterContext::SetFloodfill (bool floodfill) { m_IsFloodfill = floodfill; if (floodfill) - m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill); + m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill); else { - m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill); + m_RouterInfo.UpdateCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill); // we don't publish number of routers and leaseset for non-floodfill m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS); m_RouterInfo.DeleteProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS); @@ -417,7 +602,7 @@ // no break here, extra + high means 'X' case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break; } - m_RouterInfo.SetCaps (caps); + m_RouterInfo.UpdateCaps (caps); UpdateRouterInfo (); m_BandwidthLimit = limit; } @@ -448,6 +633,7 @@ void RouterContext::RemoveNTCPAddress (bool v4only) { + bool updated = false; auto& addresses = m_RouterInfo.GetAddresses (); for (auto it = addresses.begin (); it != addresses.end ();) { @@ -455,11 +641,38 @@ (!v4only || (*it)->host.is_v4 ())) { it = addresses.erase (it); + updated = true; if (v4only) break; // otherwise might be more than one address } else ++it; } + if (updated) + m_RouterInfo.UpdateSupportedTransports (); + } + + void RouterContext::RemoveSSUAddress () + { + bool updated = false; + auto& addresses = m_RouterInfo.GetAddresses (); + for (auto it = addresses.begin (); it != addresses.end ();) + { + if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportSSU) + { + it = addresses.erase (it); + updated = true; + } + else + ++it; + } + if (updated) + m_RouterInfo.UpdateSupportedTransports (); + } + + void RouterContext::SetUnreachableSSU2 (bool v4, bool v6) + { + if (IsSSU2Only ()) + SetUnreachable (v4, v6); } void RouterContext::SetUnreachable (bool v4, bool v6) @@ -472,13 +685,14 @@ caps |= i2p::data::RouterInfo::eUnreachable; if (v6 || !SupportsV6 ()) caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill - m_RouterInfo.SetCaps (caps); + m_RouterInfo.UpdateCaps (caps); } uint16_t port = 0; // delete previous introducers auto& addresses = m_RouterInfo.GetAddresses (); for (auto& addr : addresses) - if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) + if (addr->ssu && (!addr->IsSSU2 () || IsSSU2Only ()) && + ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) { addr->published = false; addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer @@ -504,18 +718,23 @@ caps |= i2p::data::RouterInfo::eReachable; if (m_IsFloodfill) caps |= i2p::data::RouterInfo::eFloodfill; - m_RouterInfo.SetCaps (caps); + m_RouterInfo.UpdateCaps (caps); } uint16_t port = 0; // delete previous introducers + bool isSSU2Published = IsSSU2Only (); // TODO + if (isSSU2Published) + i2p::config::GetOption ("ssu2.published", isSSU2Published); auto& addresses = m_RouterInfo.GetAddresses (); for (auto& addr : addresses) - if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) + if (addr->ssu && (!addr->IsSSU2 () || isSSU2Published) && + ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) { addr->published = true; addr->caps |= i2p::data::RouterInfo::eSSUIntroducer; addr->ssu->introducers.clear (); - port = addr->port; + if (addr->port && (!addr->IsSSU2 () || IsSSU2Only ())) + port = addr->port; } // publish NTCP2 bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); @@ -539,40 +758,50 @@ if (supportsV6) { // insert v6 addresses if necessary - bool foundSSU = false, foundNTCP2 = false; + bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false; uint16_t port = 0; auto& addresses = m_RouterInfo.GetAddresses (); for (auto& addr: addresses) { if (addr->IsV6 () && !i2p::util::net::IsYggdrasilAddress (addr->host)) { - if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU) - foundSSU = true; - else if (addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP) - foundNTCP2 = true; + switch (addr->transportStyle) + { + case i2p::data::RouterInfo::eTransportSSU: + foundSSU = true; + break; + case i2p::data::RouterInfo::eTransportNTCP: + foundNTCP2 = true; + break; + case i2p::data::RouterInfo::eTransportSSU2: + foundSSU2 = true; + break; + default: ; + } } port = addr->port; } - if (!port) i2p::config::GetOption("port", port); + if (!port) + { + i2p::config::GetOption("port", port); + if (!port) port = SelectRandomPort (); + } // SSU - if (!foundSSU) + bool ssu; i2p::config::GetOption("ssu", ssu); + if (!foundSSU && ssu) { - bool ssu; i2p::config::GetOption("ssu", ssu); - if (ssu) - { - std::string host = "::1"; // TODO: read host - m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr); - } + std::string host = "::1"; // TODO: read host + m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr); } // NTCP2 if (!foundNTCP2) { bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published); - if (ntcp2) + if (ntcp2) { if (ntcp2Published) - { + { std::string ntcp2Host; if (!i2p::config::IsDefault ("ntcp2.addressv6")) i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host); @@ -581,11 +810,28 @@ 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 m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV6); } } + // SSU2 + if (!foundSSU2) + { + bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); + if (ssu2) + { + bool ssu2Published; i2p::config::GetOption("ssu2.published", ssu2Published); + if (ssu2Published) + { + uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); + if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("::1"), ssu2Port); + } + else + m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV6); + } + } m_RouterInfo.EnableV6 (); } else @@ -601,7 +847,7 @@ // update if (supportsV4) { - bool foundSSU = false, foundNTCP2 = false; + bool foundSSU = false, foundNTCP2 = false, foundSSU2 = false; std::string host = "127.0.0.1"; uint16_t port = 0; auto& addresses = m_RouterInfo.GetAddresses (); @@ -609,21 +855,32 @@ { if (addr->IsV4 ()) { - if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU) - foundSSU = true; - else if (addr->transportStyle == i2p::data::RouterInfo::eTransportNTCP) - foundNTCP2 = true; + switch (addr->transportStyle) + { + case i2p::data::RouterInfo::eTransportSSU: + foundSSU = true; + break; + case i2p::data::RouterInfo::eTransportNTCP: + foundNTCP2 = true; + break; + case i2p::data::RouterInfo::eTransportSSU2: + foundSSU2 = true; + break; + default: ; + } } if (addr->port) port = addr->port; } - if (!port) i2p::config::GetOption("port", port); + if (!port) + { + i2p::config::GetOption("port", port); + if (!port) port = SelectRandomPort (); + } // SSU - if (!foundSSU) - { - bool ssu; i2p::config::GetOption("ssu", ssu); - if (ssu) - m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr); - } + bool ssu; i2p::config::GetOption("ssu", ssu); + if (!foundSSU && ssu) + m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr); + // NTCP2 if (!foundNTCP2) { @@ -641,6 +898,23 @@ m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV4); } } + // SSU2 + if (!foundSSU2) + { + bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); + if (ssu2) + { + bool ssu2Published; i2p::config::GetOption("ssu2.published", ssu2Published); + if (ssu2Published) + { + uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); + if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port; + m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("127.0.0.1"), ssu2Port); + } + else + m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV4); + } + } m_RouterInfo.EnableV4 (); } else @@ -675,6 +949,43 @@ UpdateRouterInfo (); } + void RouterContext::SetMTU (int mtu, bool v4) + { + if (mtu < 1280 || mtu > 1500) return; + auto& addresses = m_RouterInfo.GetAddresses (); + for (auto& addr: addresses) + { + if (addr->ssu && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ()))) + { + if (!addr->IsSSU2 ()) // SSU1 + { + // round to multiple of 16 + if (v4) + { + if (mtu > 1484) mtu = 1484; + else + { + mtu -= 12; + mtu = (mtu >> 4) << 4; + mtu += 12; + } + } + else + { + if (mtu > 1488) mtu = 1488; + else + mtu = (mtu >> 4) << 4; + } + } + if (mtu) + { + addr->ssu->mtu = mtu; + LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu); + } + } + } + } + void RouterContext::UpdateNTCP2V6Address (const boost::asio::ip::address& host) { bool isYgg = i2p::util::net::IsYggdrasilAddress (host); @@ -743,7 +1054,7 @@ } std::shared_ptr oldIdentity; if (m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1 || - m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) + m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) { // update keys LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new"); @@ -795,7 +1106,31 @@ UpdateNTCP2Address (true); // enable NTCP2 } else - UpdateNTCP2Address (false); // disable NTCP2 + UpdateNTCP2Address (false); // disable NTCP2 + + // read SSU2 + bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); + if (ssu2) + { + // read SSU2 keys if available + std::ifstream s2k (i2p::fs::DataDirPath (SSU2_KEYS), std::ifstream::in | std::ifstream::binary); + if (s2k) + { + s2k.seekg (0, std::ios::end); + size_t len = s2k.tellg(); + s2k.seekg (0, std::ios::beg); + if (len == sizeof (SSU2PrivateKeys)) + { + m_SSU2Keys.reset (new SSU2PrivateKeys ()); + s2k.read ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys)); + } + s2k.close (); + } + if (!m_SSU2Keys) NewSSU2Keys (); + UpdateSSU2Address (true); // enable SSU2 + } + else + UpdateSSU2Address (false); // disable SSU2 return true; } @@ -833,27 +1168,22 @@ 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 ()) - { - 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"); - } + 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; + 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) @@ -878,32 +1208,23 @@ 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) - { + { // m_InitialNoiseState is h = SHA256(h || hepk) 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,7 +1233,7 @@ encrypted += 32; uint8_t nonce[12]; memset (nonce, 0, 12); - if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, clearTextSize, m_CurrentNoiseState.m_H, 32, + if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, clearTextSize, m_CurrentNoiseState.m_H, 32, m_CurrentNoiseState.m_CK + 32, nonce, data, clearTextSize, false)) // decrypt { LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed"); @@ -924,26 +1245,34 @@ 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; - } - } - - i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () + return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE); + } + + i2p::crypto::X25519Keys& RouterContext::GetNTCP2StaticKeys () { - if (!m_StaticKeys) + if (!m_NTCP2StaticKeys) { if (!m_NTCP2Keys) NewNTCP2Keys (); auto x = new i2p::crypto::X25519Keys (m_NTCP2Keys->staticPrivateKey, m_NTCP2Keys->staticPublicKey); - if (!m_StaticKeys) - m_StaticKeys.reset (x); + if (!m_NTCP2StaticKeys) + m_NTCP2StaticKeys.reset (x); + else + delete x; + } + return *m_NTCP2StaticKeys; + } + + i2p::crypto::X25519Keys& RouterContext::GetSSU2StaticKeys () + { + if (!m_SSU2StaticKeys) + { + if (!m_SSU2Keys) NewSSU2Keys (); + auto x = new i2p::crypto::X25519Keys (m_SSU2Keys->staticPrivateKey, m_SSU2Keys->staticPublicKey); + if (!m_SSU2StaticKeys) + m_SSU2StaticKeys.reset (x); else delete x; } - return *m_StaticKeys; + return *m_SSU2StaticKeys; } } diff -Nru i2pd-2.39.0/libi2pd/RouterContext.h i2pd-2.43.0/libi2pd/RouterContext.h --- i2pd-2.39.0/libi2pd/RouterContext.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/RouterContext.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -18,7 +18,6 @@ #include "Identity.h" #include "RouterInfo.h" #include "Garlic.h" -#include "I18N_langs.h" namespace i2p { @@ -30,6 +29,7 @@ const char ROUTER_INFO[] = "router.info"; const char ROUTER_KEYS[] = "router.keys"; const char NTCP2_KEYS[] = "ntcp2.keys"; + const char SSU2_KEYS[] = "ssu2.keys"; const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes enum RouterStatus @@ -62,13 +62,20 @@ uint8_t iv[16]; }; + struct SSU2PrivateKeys + { + uint8_t staticPublicKey[32]; + uint8_t staticPrivateKey[32]; + uint8_t intro[32]; + }; + public: RouterContext (); void Init (); const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; - i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; }; + i2p::data::LocalRouterInfo& GetRouterInfo () { return m_RouterInfo; }; std::shared_ptr GetSharedRouterInfo () { return std::shared_ptr (&m_RouterInfo, @@ -79,10 +86,16 @@ return std::shared_ptr (this, [](i2p::garlic::GarlicDestination *) {}); } + const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; }; const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; }; const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; }; - i2p::crypto::X25519Keys& GetStaticKeys (); + i2p::crypto::X25519Keys& GetNTCP2StaticKeys (); + + const uint8_t * GetSSU2StaticPublicKey () const { return m_SSU2Keys ? m_SSU2Keys->staticPublicKey : nullptr; }; + const uint8_t * GetSSU2StaticPrivateKey () const { return m_SSU2Keys ? m_SSU2Keys->staticPrivateKey : nullptr; }; + const uint8_t * GetSSU2IntroKey () const { return m_SSU2Keys ? m_SSU2Keys->intro : nullptr; }; + i2p::crypto::X25519Keys& GetSSU2StaticKeys (); uint32_t GetUptime () const; // in seconds uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; }; @@ -90,24 +103,35 @@ uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; }; RouterStatus GetStatus () const { return m_Status; }; void SetStatus (RouterStatus status); + void SetStatusSSU2 (RouterStatus status); RouterError GetError () const { return m_Error; }; void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; }; RouterStatus GetStatusV6 () const { return m_StatusV6; }; void SetStatusV6 (RouterStatus status); + void SetStatusV6SSU2 (RouterStatus status); + RouterError GetErrorV6 () const { return m_ErrorV6; }; + void SetErrorV6 (RouterError error) { m_StatusV6 = eRouterStatusError; m_ErrorV6 = error; }; int GetNetID () const { return m_NetID; }; void SetNetID (int netID) { m_NetID = netID; }; bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data); bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data); - + void UpdatePort (int port); // called from Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg); void UpdateNTCP2Address (bool enable); + void PublishSSU2Address (int port, bool publish, bool v4, bool v6); + void UpdateSSU2Address (bool enable); void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later + void RemoveSSUAddress (); // delete SSU address for older routers bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer); void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); + bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4); + void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4); + void ClearSSU2Introducers (bool v4); bool IsUnreachable () const; void SetUnreachable (bool v4, bool v6); + void SetUnreachableSSU2 (bool v4, bool v6); void SetReachable (bool v4, bool v6); bool IsFloodfill () const { return m_IsFloodfill; }; void SetFloodfill (bool floodfill); @@ -124,7 +148,7 @@ 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; }; + void SetMTU (int mtu, bool v4); i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove @@ -134,7 +158,7 @@ // 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,10 +170,6 @@ 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 @@ -162,14 +182,17 @@ void NewRouterInfo (); void UpdateRouterInfo (); void NewNTCP2Keys (); + void NewSSU2Keys (); + bool IsSSU2Only () const; // SSU2 and no SSU bool Load (); void SaveKeys (); + uint16_t SelectRandomPort () const; bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize); - + private: - i2p::data::RouterInfo m_RouterInfo; + i2p::data::LocalRouterInfo m_RouterInfo; i2p::data::PrivateKeys m_Keys; std::shared_ptr m_Decryptor, m_TunnelDecryptor; std::shared_ptr m_ECIESSession; @@ -179,16 +202,14 @@ uint64_t m_BandwidthLimit; // allowed bandwidth int m_ShareRatio; RouterStatus m_Status, m_StatusV6; - RouterError m_Error; + RouterError m_Error, m_ErrorV6; int m_NetID; std::mutex m_GarlicMutex; std::unique_ptr m_NTCP2Keys; - std::unique_ptr m_StaticKeys; + std::unique_ptr m_SSU2Keys; + std::unique_ptr m_NTCP2StaticKeys, m_SSU2StaticKeys; // for ECIESx25519 i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState; - - // i18n - std::shared_ptr m_Language; }; extern RouterContext context; diff -Nru i2pd-2.39.0/libi2pd/RouterInfo.cpp i2pd-2.43.0/libi2pd/RouterInfo.cpp --- i2pd-2.39.0/libi2pd/RouterInfo.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/RouterInfo.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -29,29 +29,36 @@ { namespace data { + RouterInfo::Buffer::Buffer (const uint8_t * buf, size_t len) + { + if (len > size ()) len = size (); + memcpy (data (), buf, len); + } + RouterInfo::RouterInfo (): m_Buffer (nullptr) { m_Addresses = boost::make_shared(); // create empty list } 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_FamilyID (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 (); + m_Buffer = NewBuffer (); // always RouterInfo's + ReadFromFile (fullPath); } - RouterInfo::RouterInfo (const uint8_t * buf, int len): - m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), - m_ReachableTransports (0), m_Caps (0), m_Version (0) + RouterInfo::RouterInfo (std::shared_ptr&& buf, size_t len): + m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false), + m_SupportedTransports (0), m_ReachableTransports (0), + m_Caps (0), m_Version (0) { - m_Addresses = boost::make_shared(); // create empty list if (len <= MAX_RI_BUFFER_SIZE) { - m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; - memcpy (m_Buffer, buf, len); + m_Addresses = boost::make_shared(); // create empty list + m_Buffer = buf; m_BufferLen = len; ReadFromBuffer (true); } @@ -63,9 +70,13 @@ } } + RouterInfo::RouterInfo (const uint8_t * buf, size_t len): + RouterInfo (std::make_shared (buf, len), len) + { + } + RouterInfo::~RouterInfo () { - delete[] m_Buffer; } void RouterInfo::Update (const uint8_t * buf, size_t len) @@ -87,22 +98,19 @@ m_ReachableTransports = 0; m_Caps = 0; // don't clean up m_Addresses, it will be replaced in ReadFromStream - m_Properties.clear (); + ClearProperties (); // copy buffer - if (!m_Buffer) - m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; - memcpy (m_Buffer, buf, len); - m_BufferLen = len; + UpdateBuffer (buf, len); // skip identity size_t identityLen = m_RouterIdentity->GetFullLen (); // read new RI - std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen)); + std::stringstream str (std::string ((char *)m_Buffer->data () + identityLen, m_BufferLen - identityLen)); ReadFromStream (str); // don't delete buffer until saved to the file } else { - LogPrint (eLogError, "RouterInfo: signature verification failed"); + LogPrint (eLogError, "RouterInfo: Signature verification failed"); m_IsUnreachable = true; } } @@ -113,33 +121,34 @@ 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); - if (!m_Buffer) m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; - s.read((char *)m_Buffer, m_BufferLen); + if (!m_Buffer) + m_Buffer = NewBuffer (); + s.read((char *)m_Buffer->data (), m_BufferLen); } 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; @@ -147,11 +156,16 @@ void RouterInfo::ReadFromBuffer (bool verifySignature) { - m_RouterIdentity = std::make_shared(m_Buffer, m_BufferLen); + if (!m_Buffer) + { + m_IsUnreachable = true; + return; + } + m_RouterIdentity = std::make_shared(m_Buffer->data (), m_BufferLen); size_t identityLen = m_RouterIdentity->GetFullLen (); if (identityLen >= m_BufferLen) { - LogPrint (eLogError, "RouterInfo: identity length ", identityLen, " exceeds buffer size ", m_BufferLen); + LogPrint (eLogError, "RouterInfo: Identity length ", identityLen, " exceeds buffer size ", m_BufferLen); m_IsUnreachable = true; return; } @@ -166,9 +180,9 @@ } // verify signature int l = m_BufferLen - m_RouterIdentity->GetSignatureLen (); - if (l < 0 || !m_RouterIdentity->Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l)) + if (l < 0 || !m_RouterIdentity->Verify ((uint8_t *)m_Buffer->data (), l, (uint8_t *)m_Buffer->data () + l)) { - LogPrint (eLogError, "RouterInfo: signature verification failed"); + LogPrint (eLogError, "RouterInfo: Signature verification failed"); m_IsUnreachable = true; return; } @@ -176,42 +190,42 @@ } // parse RI std::stringstream str; - str.write ((const char *)m_Buffer + identityLen, m_BufferLen - identityLen); + str.write ((const char *)m_Buffer->data () + identityLen, m_BufferLen - identityLen); ReadFromStream (str); if (!str) { - LogPrint (eLogError, "RouterInfo: malformed message"); + LogPrint (eLogError, "RouterInfo: Malformed message"); m_IsUnreachable = true; } } 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; - auto address = std::make_shared
(); + auto address = std::make_shared
(); uint8_t cost; // ignore s.read ((char *)&cost, sizeof (cost)); s.read ((char *)&address->date, sizeof (address->date)); - bool isHost = false, isIntroKey = false, isStaticKey = false; + bool isHost = false, isIntroKey = false, isStaticKey = false, isV2 = false; + Tag<32> iV2; // for 'i' field in SSU, TODO: remove later char transportStyle[6]; ReadString (transportStyle, 6, s); if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2 - { address->transportStyle = eTransportNTCP; - address->ntcp2.reset (new NTCP2Ext ()); - } - else if (!strcmp (transportStyle, "SSU")) + else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2 { - address->transportStyle = eTransportSSU; + address->transportStyle = (transportStyle[3] == '2') ? eTransportSSU2 : eTransportSSU; address->ssu.reset (new SSUExt ()); address->ssu->mtu = 0; } @@ -222,6 +236,12 @@ uint16_t size, r = 0; s.read ((char *)&size, sizeof (size)); if (!s) return; size = be16toh (size); + if (address->transportStyle == eTransportUnknown) + { + // skip unknown address + s.seekg (size, std::ios_base::cur); + if (s) continue; else return; + } while (r < size) { char key[255], value[255]; @@ -248,21 +268,35 @@ else if (!strcmp (key, "key")) { if (address->ssu) - isIntroKey = (Base64ToByteStream (value, strlen (value), address->ssu->key, 32) == 32); + isIntroKey = (Base64ToByteStream (value, strlen (value), address->i, 32) == 32); else LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP"); } else if (!strcmp (key, "caps")) address->caps = ExtractAddressCaps (value); - else if (!strcmp (key, "s")) // ntcp2 static key + else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key { - Base64ToByteStream (value, strlen (value), address->ntcp2->staticKey, 32); + Base64ToByteStream (value, strlen (value), address->s, 32); isStaticKey = true; } - else if (!strcmp (key, "i")) // ntcp2 iv + else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro + { + if (address->IsNTCP2 ()) + { + Base64ToByteStream (value, strlen (value), address->i, 16); + address->published = true; // presence of "i" means "published" NTCP2 + } + else if (address->IsSSU2 ()) + Base64ToByteStream (value, strlen (value), address->i, 32); + else + Base64ToByteStream (value, strlen (value), iV2, 32); + } + else if (!strcmp (key, "v")) { - Base64ToByteStream (value, strlen (value), address->ntcp2->iv, 16); - address->published = true; // presence if "i" means "published" + if (!strcmp (value, "2")) + isV2 = true; + else + LogPrint (eLogWarning, "RouterInfo: Unexpected value ", value, " for v"); } else if (key[0] == 'i') { @@ -281,7 +315,11 @@ 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")) { @@ -292,7 +330,7 @@ introducer.iPort = boost::lexical_cast(value); else if (!strcmp (key, "itag")) introducer.iTag = boost::lexical_cast(value); - else if (!strcmp (key, "ikey")) + else if (!strcmp (key, "ikey") || !strcmp (key, "ih")) Base64ToByteStream (value, strlen (value), introducer.iKey, 32); else if (!strcmp (key, "iexp")) introducer.iExp = boost::lexical_cast(value); @@ -302,39 +340,39 @@ if (address->transportStyle == eTransportNTCP) { if (isStaticKey) - { + { if (isHost) { if (address->host.is_v6 ()) - supportedTransports |= (i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6); + supportedTransports |= (i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6); else - supportedTransports |= eNTCP2V4; + supportedTransports |= eNTCP2V4; m_ReachableTransports |= supportedTransports; - } + } else if (!address->published) { if (address->caps) - { + { if (address->caps & AddressCaps::eV4) supportedTransports |= eNTCP2V4; if (address->caps & AddressCaps::eV6) supportedTransports |= eNTCP2V6; } else supportedTransports |= eNTCP2V4; // most likely, since we don't have host - } + } } - } + } else if (address->transportStyle == eTransportSSU) { if (isIntroKey) { if (isHost) - supportedTransports |= address->host.is_v4 () ? eSSUV4 : eSSUV6; - else if (address->caps & AddressCaps::eV6) - { + supportedTransports |= address->host.is_v4 () ? eSSUV4 : eSSUV6; + else if (address->caps & AddressCaps::eV6) + { supportedTransports |= eSSUV6; if (address->caps & AddressCaps::eV4) supportedTransports |= eSSUV4; // in additional to v6 - } - else + } + else supportedTransports |= eSSUV4; // in case if host or 6 caps is not preasented, we assume 4 if (address->ssu && !address->ssu->introducers.empty ()) { @@ -344,27 +382,83 @@ for (auto& it: address->ssu->introducers) { 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 ()))) + if (ts <= it.iExp && it.iPort > 0 && + ((it.iHost.is_v4 () && address->IsV4 ()) || (it.iHost.is_v6 () && address->IsV6 ()))) numValid++; else + { it.iPort = 0; - } + if (isV2) numValid++; + } + } if (numValid) m_ReachableTransports |= supportedTransports; - else + else address->ssu->introducers.resize (0); - } + } else if (isHost && address->port) - { + { address->published = true; m_ReachableTransports |= supportedTransports; + } + } + } + if (address->transportStyle == eTransportSSU2 || (isV2 && address->transportStyle == eTransportSSU)) + { + if (address->IsV4 ()) supportedTransports |= eSSU2V4; + if (address->IsV6 ()) supportedTransports |= eSSU2V6; + if (address->port) + { + if (address->host.is_v4 ()) m_ReachableTransports |= eSSU2V4; + if (address->host.is_v6 ()) m_ReachableTransports |= eSSU2V6; + } + if (address->transportStyle == eTransportSSU2) + { + if (address->port) address->published = true; + if (address->ssu && !address->ssu->introducers.empty ()) + { + // exclude invalid introducers + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + int numValid = 0; + for (auto& it: address->ssu->introducers) + { + if (it.iTag && ts <= it.iExp) + numValid++; + else + it.iTag = 0; + } + if (numValid) + m_ReachableTransports |= supportedTransports; + else + address->ssu->introducers.resize (0); + } + } + else + { + // create additional SSU2 address. TODO: remove later + auto ssu2addr = std::make_shared
(); + ssu2addr->transportStyle = eTransportSSU2; + ssu2addr->host = address->host; ssu2addr->port = address->port; + ssu2addr->s = address->s; ssu2addr->i = iV2; + ssu2addr->date = address->date; ssu2addr->caps = address->caps; + ssu2addr->published = address->published; + ssu2addr->ssu.reset (new SSUExt ()); ssu2addr->ssu->mtu = address->ssu->mtu; + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + if (!address->ssu->introducers.empty ()) + { + for (const auto& introducer: address->ssu->introducers) + if (!introducer.iPort && introducer.iHost.is_unspecified () && ts < introducer.iExp) // SSU2 + ssu2addr->ssu->introducers.push_back (introducer); + if (!ssu2addr->ssu->introducers.empty ()) + m_ReachableTransports |= supportedTransports; } + addresses->push_back(ssu2addr); } - } + } if (supportedTransports) { - addresses->push_back(address); + if (!(m_SupportedTransports & supportedTransports)) // avoid duplicates + addresses->push_back(address); m_SupportedTransports |= supportedTransports; } } @@ -378,6 +472,9 @@ s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return; s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers // read properties + m_Version = 0; + bool isNetId = false; + std::string family; uint16_t size, r = 0; s.read ((char *)&size, sizeof (size)); if (!s) return; size = be16toh (size); @@ -389,7 +486,7 @@ r += ReadString (value, 255, s); s.seekg (1, std::ios_base::cur); r++; // ; if (!s) return; - m_Properties[key] = value; + SetProperty (key, value); // extract caps if (!strcmp (key, "caps")) @@ -410,36 +507,39 @@ } } // check netId - else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID) && atoi (value) != i2p::context.GetNetID ()) + else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID)) { - LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value); - m_IsUnreachable = true; + isNetId = true; + if (atoi (value) != i2p::context.GetNetID ()) + { + LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value); + m_IsUnreachable = true; + } } // family else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY)) { - m_Family = value; - boost::to_lower (m_Family); + family = value; + boost::to_lower (family); } else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG)) { - if (!netdb.GetFamilies ().VerifyFamily (m_Family, GetIdentHash (), value)) - { - LogPrint (eLogWarning, "RouterInfo: family signature verification failed"); - m_Family.clear (); - } + if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value)) + m_FamilyID = netdb.GetFamilies ().GetFamilyID (family); + else + LogPrint (eLogWarning, "RouterInfo: Family ", family, " signature verification failed"); } if (!s) return; } - if (!m_SupportedTransports) + if (!m_SupportedTransports || !isNetId || !m_Version) SetUnreachable (true); } - bool RouterInfo::IsFamily(const std::string & fam) const + bool RouterInfo::IsFamily (FamilyID famid) const { - return m_Family == fam; + return m_FamilyID == famid; } void RouterInfo::ExtractCaps (const char * value) @@ -486,10 +586,10 @@ { case CAPS_FLAG_V4: caps |= AddressCaps::eV4; - break; + break; case CAPS_FLAG_V6: caps |= AddressCaps::eV6; - break; + break; case CAPS_FLAG_SSU_TESTING: caps |= AddressCaps::eSSUTesting; break; @@ -502,732 +602,918 @@ } return caps; } - - void RouterInfo::UpdateCapsProperty () + + bool RouterInfo::IsNewer (const uint8_t * buf, size_t len) const { - std::string caps; - if (m_Caps & eFloodfill) + if (!m_RouterIdentity) return false; + size_t size = m_RouterIdentity->GetFullLen (); + if (size + 8 > len) return false; + return bufbe64toh (buf + size) > m_Timestamp; + } + + const uint8_t * RouterInfo::LoadBuffer (const std::string& fullPath) + { + if (!m_Buffer) { - if (m_Caps & eExtraBandwidth) caps += (m_Caps & eHighBandwidth) ? - CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X' - CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' - else - caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O' - caps += CAPS_FLAG_FLOODFILL; // floodfill + if (LoadFile (fullPath)) + LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file"); + } + return m_Buffer->data (); + } + + bool RouterInfo::SaveToFile (const std::string& fullPath) + { + if (!m_Buffer) + { + LogPrint (eLogError, "RouterInfo: Can't save, m_Buffer == NULL"); + return false; + } + std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); + if (!f.is_open ()) { + LogPrint(eLogError, "RouterInfo: Can't save to ", fullPath); + return false; + } + f.write ((char *)m_Buffer->data (), m_BufferLen); + return true; + } + + size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const + { + uint8_t l; + s.read ((char *)&l, 1); + if (l < len) + { + s.read (str, l); + if (!s) l = 0; // failed, return empty string + str[l] = 0; } else { - if (m_Caps & eExtraBandwidth) - caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */ - else - caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth + LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len); + s.seekg (l, std::ios::cur); // skip + str[0] = 0; } - if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden - if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable - if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable + return l+1; + } - SetProperty ("caps", caps); + + void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu) + { + auto addr = std::make_shared
(); + addr->host = boost::asio::ip::address::from_string (host); + addr->port = port; + addr->transportStyle = eTransportSSU; + addr->published = true; + addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC; + addr->date = 0; + addr->ssu.reset (new SSUExt ()); + addr->ssu->mtu = mtu; + if (key) + memcpy (addr->i, key, 32); + else + RAND_bytes (addr->i, 32); + 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::WriteToStream (std::ostream& s) const + void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, + const boost::asio::ip::address& host, int port, uint8_t caps) { - uint64_t ts = htobe64 (m_Timestamp); - s.write ((const char *)&ts, sizeof (ts)); + auto addr = std::make_shared
(); + addr->host = host; + addr->port = port; + addr->transportStyle = eTransportNTCP; + addr->caps = caps; + addr->date = 0; + if (port) addr->published = true; + memcpy (addr->s, staticKey, 32); + memcpy (addr->i, 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)); + } - // addresses - uint8_t numAddresses = m_Addresses->size (); - s.write ((char *)&numAddresses, sizeof (numAddresses)); - for (const auto& addr_ptr : *m_Addresses) + void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps) + { + auto addr = std::make_shared
(); + addr->transportStyle = eTransportSSU2; + addr->port = 0; + addr->caps = caps; + addr->date = 0; + addr->ssu.reset (new SSUExt ()); + addr->ssu->mtu = 0; + memcpy (addr->s, staticKey, 32); + memcpy (addr->i, introKey, 32); + if (addr->IsV4 ()) m_SupportedTransports |= eSSU2V4; + if (addr->IsV6 ()) m_SupportedTransports |= eSSU2V6; + m_Addresses->push_back(std::move(addr)); + } + + void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, + const boost::asio::ip::address& host, int port) + { + auto addr = std::make_shared
(); + addr->transportStyle = eTransportSSU2; + addr->host = host; + addr->port = port; + addr->published = true; + addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC; + addr->date = 0; + addr->ssu.reset (new SSUExt ()); + addr->ssu->mtu = 0; + memcpy (addr->s, staticKey, 32); + memcpy (addr->i, introKey, 32); + if (addr->IsV4 ()) { - const Address& address = *addr_ptr; - // calculate cost - uint8_t cost = 0x7f; - if (address.transportStyle == eTransportNTCP) - cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED; - else if (address.transportStyle == eTransportSSU) - cost = address.published ? COST_SSU_DIRECT : COST_SSU_THROUGH_INTRODUCERS; - s.write ((const char *)&cost, sizeof (cost)); - s.write ((const char *)&address.date, sizeof (address.date)); - std::stringstream properties; - bool isPublished = false; - if (address.transportStyle == eTransportNTCP) + m_SupportedTransports |= eSSU2V4; + m_ReachableTransports |= eSSU2V4; + } + if (addr->IsV6 ()) + { + m_SupportedTransports |= eSSU2V6; + m_ReachableTransports |= eSSU2V6; + } + m_Addresses->push_back(std::move(addr)); + } + + bool RouterInfo::AddIntroducer (const Introducer& introducer) + { + for (auto& addr : *m_Addresses) + { + if (addr->transportStyle == eTransportSSU && + ((addr->IsV4 () && introducer.iHost.is_v4 ()) || (addr->IsV6 () && introducer.iHost.is_v6 ()))) { - if (address.IsNTCP2 ()) - { - WriteString ("NTCP2", s); - if (address.IsPublishedNTCP2 () && !address.host.is_unspecified () && address.port) - isPublished = true; - else - { - WriteString ("caps", properties); - properties << '='; - std::string caps; - if (address.IsV4 ()) caps += CAPS_FLAG_V4; - if (address.IsV6 ()) caps += CAPS_FLAG_V6; - if (caps.empty ()) caps += CAPS_FLAG_V4; - WriteString (caps, properties); - properties << ';'; - } - } - else - continue; // don't write NTCP address - } - else if (address.transportStyle == eTransportSSU) + for (auto& intro: addr->ssu->introducers) + if (intro.iTag == introducer.iTag) return false; // already presented + addr->ssu->introducers.push_back (introducer); + m_ReachableTransports |= (addr->IsV4 () ? eSSUV4 : eSSUV6); + return true; + } + } + return false; + } + + bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) + { + for (auto& addr: *m_Addresses) + { + if (addr->transportStyle == eTransportSSU && + ((addr->IsV4 () && e.address ().is_v4 ()) || (addr->IsV6 () && e.address ().is_v6 ()))) { - WriteString ("SSU", s); - // caps - WriteString ("caps", properties); - properties << '='; - std::string caps; - if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; - if (address.host.is_v4 ()) - { - if (address.published) - { - isPublished = true; - if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; - } - else - caps += CAPS_FLAG_V4; - } - else if (address.host.is_v6 ()) - { - if (address.published) - { - isPublished = true; - if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; - } - else - caps += CAPS_FLAG_V6; - } - else - { - if (address.IsV4 ()) caps += CAPS_FLAG_V4; - if (address.IsV6 ()) caps += CAPS_FLAG_V6; - if (caps.empty ()) caps += CAPS_FLAG_V4; - } - WriteString (caps, properties); - properties << ';'; - } - else - WriteString ("", s); - - if (isPublished) - { - WriteString ("host", properties); - properties << '='; - WriteString (address.host.to_string (), properties); - properties << ';'; - } - if (address.transportStyle == eTransportSSU) - { - // write introducers if any - if (!address.ssu->introducers.empty()) - { - int i = 0; - for (const auto& introducer: address.ssu->introducers) - { - if (introducer.iExp) // expiration is specified - { - WriteString ("iexp" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (boost::lexical_cast(introducer.iExp), properties); - properties << ';'; - } - i++; - } - i = 0; - for (const auto& introducer: address.ssu->introducers) - { - WriteString ("ihost" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (introducer.iHost.to_string (), properties); - properties << ';'; - i++; - } - i = 0; - for (const auto& introducer: address.ssu->introducers) - { - WriteString ("ikey" + boost::lexical_cast(i), properties); - properties << '='; - char value[64]; - size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64); - value[l] = 0; - WriteString (value, properties); - properties << ';'; - i++; - } - i = 0; - for (const auto& introducer: address.ssu->introducers) - { - WriteString ("iport" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (boost::lexical_cast(introducer.iPort), properties); - properties << ';'; - i++; - } - i = 0; - for (const auto& introducer: address.ssu->introducers) + for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it) + if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) { - WriteString ("itag" + boost::lexical_cast(i), properties); - properties << '='; - WriteString (boost::lexical_cast(introducer.iTag), properties); - properties << ';'; - i++; + addr->ssu->introducers.erase (it); + if (addr->ssu->introducers.empty ()) + m_ReachableTransports &= ~(addr->IsV4 () ? eSSUV4 : eSSUV6); + return true; } - } - // write intro key - WriteString ("key", properties); - properties << '='; - char value[64]; - size_t l = ByteStreamToBase64 (address.ssu->key, 32, value, 64); - value[l] = 0; - WriteString (value, properties); - properties << ';'; - // write mtu - if (address.ssu->mtu) - { - WriteString ("mtu", properties); - properties << '='; - WriteString (boost::lexical_cast(address.ssu->mtu), properties); - properties << ';'; - } - } - - if (address.IsNTCP2 () && isPublished) - { - // publish i for NTCP2 - WriteString ("i", properties); properties << '='; - WriteString (address.ntcp2->iv.ToBase64 (), properties); properties << ';'; } + } + return false; + } - if (isPublished || address.ssu) - { - WriteString ("port", properties); - properties << '='; - WriteString (boost::lexical_cast(address.port), properties); - properties << ';'; - } - if (address.IsNTCP2 ()) - { - // publish s and v for NTCP2 - WriteString ("s", properties); properties << '='; - WriteString (address.ntcp2->staticKey.ToBase64 (), properties); properties << ';'; - WriteString ("v", properties); properties << '='; - WriteString ("2", properties); properties << ';'; - } + bool RouterInfo::IsSSU (bool v4only) const + { + if (v4only) + return m_SupportedTransports & eSSUV4; + else + return m_SupportedTransports & (eSSUV4 | eSSUV6); + } - uint16_t size = htobe16 (properties.str ().size ()); - s.write ((char *)&size, sizeof (size)); - s.write (properties.str ().c_str (), properties.str ().size ()); - } + bool RouterInfo::IsNTCP2 (bool v4only) const + { + if (v4only) + return m_SupportedTransports & eNTCP2V4; + else + return m_SupportedTransports & (eNTCP2V4 | eNTCP2V6); + } - // peers - uint8_t numPeers = 0; - s.write ((char *)&numPeers, sizeof (numPeers)); - // properties - std::stringstream properties; - for (const auto& p : m_Properties) + void RouterInfo::EnableV6 () + { + if (!IsV6 ()) { - WriteString (p.first, properties); - properties << '='; - WriteString (p.second, properties); - properties << ';'; + uint8_t addressCaps = AddressCaps::eV6; + if (IsV4 ()) addressCaps |= AddressCaps::eV4; + SetUnreachableAddressesTransportCaps (addressCaps); + UpdateSupportedTransports (); } - uint16_t size = htobe16 (properties.str ().size ()); - s.write ((char *)&size, sizeof (size)); - s.write (properties.str ().c_str (), properties.str ().size ()); } - bool RouterInfo::IsNewer (const uint8_t * buf, size_t len) const + void RouterInfo::EnableV4 () { - if (!m_RouterIdentity) return false; - size_t size = m_RouterIdentity->GetFullLen (); - if (size + 8 > len) return false; - return bufbe64toh (buf + size) > m_Timestamp; + if (!IsV4 ()) + { + uint8_t addressCaps = AddressCaps::eV4; + if (IsV6 ()) addressCaps |= AddressCaps::eV6; + SetUnreachableAddressesTransportCaps (addressCaps); + UpdateSupportedTransports (); + } } - const uint8_t * RouterInfo::LoadBuffer () + + void RouterInfo::DisableV6 () { - if (!m_Buffer) + if (IsV6 ()) { - if (LoadFile ()) - LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file"); + for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) + { + auto addr = *it; + if (addr->IsV6 ()) + { + if (addr->IsV4 ()) + { + addr->caps &= ~AddressCaps::eV6; + ++it; + } + else + it = m_Addresses->erase (it); + } + else + ++it; + } + UpdateSupportedTransports (); } - return m_Buffer; } - void RouterInfo::CreateBuffer (const PrivateKeys& privateKeys) + void RouterInfo::DisableV4 () { - m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp - std::stringstream s; - uint8_t ident[1024]; - auto identLen = privateKeys.GetPublic ()->ToBuffer (ident, 1024); - auto signatureLen = privateKeys.GetPublic ()->GetSignatureLen (); - s.write ((char *)ident, identLen); - WriteToStream (s); - m_BufferLen = s.str ().size (); - if (!m_Buffer) - m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; - if (m_BufferLen + signatureLen < MAX_RI_BUFFER_SIZE) + if (IsV4 ()) { - memcpy (m_Buffer, s.str ().c_str (), m_BufferLen); - // signature - privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen); - m_BufferLen += signatureLen; + for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) + { + auto addr = *it; + if (addr->IsV4 ()) + { + if (addr->IsV6 ()) + { + addr->caps &= ~AddressCaps::eV4; + ++it; + } + else + it = m_Addresses->erase (it); + } + else + ++it; + } + UpdateSupportedTransports (); } - else - LogPrint (eLogError, "RouterInfo: Our RouterInfo is too long ", m_BufferLen + signatureLen); } - bool RouterInfo::SaveToFile (const std::string& fullPath) + void RouterInfo::EnableMesh () { - m_FullPath = fullPath; - if (!m_Buffer) { - LogPrint (eLogError, "RouterInfo: Can't save, m_Buffer == NULL"); - return false; - } - std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); - if (!f.is_open ()) { - LogPrint(eLogError, "RouterInfo: Can't save to ", fullPath); - return false; + if (!IsMesh ()) + { + m_SupportedTransports |= eNTCP2V6Mesh; + m_ReachableTransports |= eNTCP2V6Mesh; } - f.write ((char *)m_Buffer, m_BufferLen); - return true; } - size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const + void RouterInfo::DisableMesh () { - uint8_t l; - s.read ((char *)&l, 1); - if (l < len) - { - s.read (str, l); - if (!s) l = 0; // failed, return empty string - str[l] = 0; - } - else + if (IsMesh ()) { - LogPrint (eLogWarning, "RouterInfo: string length ", (int)l, " exceeds buffer size ", len); - s.seekg (l, std::ios::cur); // skip - str[0] = 0; + m_SupportedTransports &= ~eNTCP2V6Mesh; + m_ReachableTransports &= ~eNTCP2V6Mesh; + for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) + { + auto addr = *it; + if (i2p::util::net::IsYggdrasilAddress (addr->host)) + it = m_Addresses->erase (it); + else + ++it; + } } - return l+1; } - void RouterInfo::WriteString (const std::string& str, std::ostream& s) const + std::shared_ptr RouterInfo::GetSSUAddress (bool v4only) const { - uint8_t len = str.size (); - s.write ((char *)&len, 1); - s.write (str.c_str (), len); + return GetAddress ( + [v4only](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU) && (!v4only || address->IsV4 ()); + }); } - void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu) + std::shared_ptr RouterInfo::GetSSUV6Address () const { - auto addr = std::make_shared
(); - addr->host = boost::asio::ip::address::from_string (host); - addr->port = port; - addr->transportStyle = eTransportSSU; - addr->published = true; - addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC; - addr->date = 0; - addr->ssu.reset (new SSUExt ()); - addr->ssu->mtu = mtu; - if (key) - memcpy (addr->ssu->key, key, 32); - else - RAND_bytes (addr->ssu->key, 32); - for (const auto& it: *m_Addresses) // don't insert same address twice - 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)); + return GetAddress ( + [](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU) && address->IsV6(); + }); } - void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, - const boost::asio::ip::address& host, int port, uint8_t caps) + std::shared_ptr RouterInfo::GetSSU2V4Address () const { - auto addr = std::make_shared
(); - addr->host = host; - addr->port = port; - addr->transportStyle = eTransportNTCP; - 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 ()) + return GetAddress ( + [](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU2) && address->IsV4(); + }); + } + + std::shared_ptr RouterInfo::GetSSU2V6Address () const + { + return GetAddress ( + [](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU2) && address->IsV6(); + }); + } + + std::shared_ptr RouterInfo::GetSSU2Address (bool v4) const + { + if (v4) + { + if (m_SupportedTransports & eSSU2V4) + return GetSSU2V4Address (); + } + else { - m_SupportedTransports |= eNTCP2V6; - if (addr->published) m_ReachableTransports |= eNTCP2V6; + if (m_SupportedTransports & eSSU2V6) + return GetSSU2V6Address (); } - m_Addresses->push_back(std::move(addr)); + return nullptr; + } + + template + std::shared_ptr RouterInfo::GetAddress (Filter filter) const + { + // TODO: make it more generic using comparator +#if (BOOST_VERSION >= 105300) + auto addresses = boost::atomic_load (&m_Addresses); +#else + auto addresses = m_Addresses; +#endif + for (const auto& address : *addresses) + if (filter (address)) return address; + + return nullptr; } - bool RouterInfo::AddIntroducer (const Introducer& introducer) + std::shared_ptr RouterInfo::GetNTCP2AddressWithStaticKey (const uint8_t * key) const { - for (auto& addr : *m_Addresses) - { - if (addr->transportStyle == eTransportSSU && - ((addr->IsV4 () && introducer.iHost.is_v4 ()) || (addr->IsV6 () && introducer.iHost.is_v6 ()))) + if (!key) return nullptr; + return GetAddress ( + [key](std::shared_ptr address)->bool { - for (auto& intro: addr->ssu->introducers) - if (intro.iTag == introducer.iTag) return false; // already presented - addr->ssu->introducers.push_back (introducer); - m_ReachableTransports |= (addr->IsV4 () ? eSSUV4 : eSSUV6); - return true; - } - } - return false; + return address->IsNTCP2 () && !memcmp (address->s, key, 32); + }); } - bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) + std::shared_ptr RouterInfo::GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const { - for (auto& addr: *m_Addresses) - { - if (addr->transportStyle == eTransportSSU && - ((addr->IsV4 () && e.address ().is_v4 ()) || (addr->IsV6 () && e.address ().is_v6 ()))) + if (!key) return nullptr; + return GetAddress ( + [key, isV6](std::shared_ptr address)->bool { - for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it) - if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) - { - addr->ssu->introducers.erase (it); - if (addr->ssu->introducers.empty ()) - m_ReachableTransports &= ~(addr->IsV4 () ? eSSUV4 : eSSUV6); - return true; - } - } - } - return false; + return address->IsSSU2 () && !memcmp (address->s, key, 32) && + ((isV6 && address->IsV6 ()) || (!isV6 && address->IsV4 ())); + }); + } + + std::shared_ptr RouterInfo::GetPublishedNTCP2V4Address () const + { + return GetAddress ( + [](std::shared_ptr address)->bool + { + return address->IsPublishedNTCP2 () && address->host.is_v4 (); + }); + } + + std::shared_ptr RouterInfo::GetPublishedNTCP2V6Address () const + { + return GetAddress ( + [](std::shared_ptr address)->bool + { + return address->IsPublishedNTCP2 () && address->host.is_v6 () && + !i2p::util::net::IsYggdrasilAddress (address->host); + }); } - void RouterInfo::SetCaps (uint8_t caps) + std::shared_ptr RouterInfo::GetYggdrasilAddress () const { - m_Caps = caps; - UpdateCapsProperty (); + return GetAddress ( + [](std::shared_ptr address)->bool + { + return address->IsPublishedNTCP2 () && i2p::util::net::IsYggdrasilAddress (address->host); + }); } - void RouterInfo::SetCaps (const char * caps) + std::shared_ptr RouterInfo::GetProfile () const { - SetProperty ("caps", caps); - m_Caps = 0; - ExtractCaps (caps); + if (!m_Profile) + m_Profile = GetRouterProfile (GetIdentHash ()); + return m_Profile; } - void RouterInfo::SetProperty (const std::string& key, const std::string& value) + void RouterInfo::Encrypt (const uint8_t * data, uint8_t * encrypted) const { - m_Properties[key] = value; + auto encryptor = m_RouterIdentity->CreateEncryptor (nullptr); + if (encryptor) + encryptor->Encrypt (data, encrypted); } - void RouterInfo::DeleteProperty (const std::string& key) + bool RouterInfo::IsEligibleFloodfill () const { - m_Properties.erase (key); + // 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; } - std::string RouterInfo::GetProperty (const std::string& key) const + bool RouterInfo::IsPeerTesting (bool v4) const { - auto it = m_Properties.find (key); - if (it != m_Properties.end ()) - return it->second; - return ""; + if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false; + return (bool)GetAddress ( + [v4](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU) && address->IsPeerTesting () && + ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU (); + }); } - bool RouterInfo::IsSSU (bool v4only) const + bool RouterInfo::IsSSU2PeerTesting (bool v4) const { - if (v4only) - return m_SupportedTransports & eSSUV4; - else - return m_SupportedTransports & (eSSUV4 | eSSUV6); + if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; + return (bool)GetAddress ( + [v4](std::shared_ptr address)->bool + { + return (address->IsSSU2 ()) && address->IsPeerTesting () && + ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU (); + }); + } + + bool RouterInfo::IsIntroducer (bool v4) const + { + if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false; + return (bool)GetAddress ( + [v4](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU) && address->IsIntroducer () && + ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified (); + }); } - bool RouterInfo::IsSSUV6 () const + bool RouterInfo::IsSSU2Introducer (bool v4) const { - return m_SupportedTransports & eSSUV6; + if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; + return (bool)GetAddress ( + [v4](std::shared_ptr address)->bool + { + return (address->IsSSU2 ()) && address->IsIntroducer () && + ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified (); + }); } - - bool RouterInfo::IsNTCP2 (bool v4only) const + + void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) { - if (v4only) - return m_SupportedTransports & eNTCP2V4; - else - return m_SupportedTransports & (eNTCP2V4 | eNTCP2V6); + for (auto& addr: *m_Addresses) + { + // TODO: implement SSU + if (!addr->published && (addr->transportStyle == eTransportNTCP || addr->transportStyle == eTransportSSU2)) + { + addr->caps &= ~(eV4 | eV6); + addr->caps |= transports; + } + } } - bool RouterInfo::IsNTCP2V6 () const + void RouterInfo::UpdateSupportedTransports () { - return m_SupportedTransports & eNTCP2V6; - } - - bool RouterInfo::IsV6 () const + m_SupportedTransports = 0; + m_ReachableTransports = 0; + for (const auto& addr: *m_Addresses) + { + uint8_t transports = 0; + switch (addr->transportStyle) + { + case eTransportNTCP: + if (addr->IsV4 ()) transports |= eNTCP2V4; + if (addr->IsV6 ()) + transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6); + if (addr->IsPublishedNTCP2 ()) + m_ReachableTransports |= transports; + break; + case eTransportSSU: + if (addr->IsV4 ()) transports |= eSSUV4; + if (addr->IsV6 ()) transports |= eSSUV6; + if (addr->IsReachableSSU ()) + m_ReachableTransports |= transports; + break; + case eTransportSSU2: + if (addr->IsV4 ()) transports |= eSSU2V4; + if (addr->IsV6 ()) transports |= eSSU2V6; + if (addr->IsReachableSSU ()) + m_ReachableTransports |= transports; + break; + default: ; + } + m_SupportedTransports |= transports; + } + } + + void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len) { - return m_SupportedTransports & (eSSUV6 | eNTCP2V6); + if (!m_Buffer) + m_Buffer = NewBuffer (); + if (len > m_Buffer->size ()) len = m_Buffer->size (); + memcpy (m_Buffer->data (), buf, len); + m_BufferLen = len; } - bool RouterInfo::IsV4 () const + std::shared_ptr RouterInfo::NewBuffer () const { - return m_SupportedTransports & (eSSUV4 | eNTCP2V4); + return netdb.NewRouterInfoBuffer (); } - bool RouterInfo::IsMesh () const + void RouterInfo::RefreshTimestamp () { - return m_SupportedTransports & eNTCP2V6Mesh; - } - - void RouterInfo::EnableV6 () + m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); + } + + void LocalRouterInfo::CreateBuffer (const PrivateKeys& privateKeys) { - if (!IsV6 ()) - { - uint8_t addressCaps = AddressCaps::eV6; - if (IsV4 ()) addressCaps |= AddressCaps::eV4; - SetUnreachableAddressesTransportCaps (addressCaps); - UpdateSupportedTransports (); - } + RefreshTimestamp (); + std::stringstream s; + uint8_t ident[1024]; + auto identLen = privateKeys.GetPublic ()->ToBuffer (ident, 1024); + auto signatureLen = privateKeys.GetPublic ()->GetSignatureLen (); + s.write ((char *)ident, identLen); + WriteToStream (s); + size_t len = s.str ().size (); + if (len + signatureLen < MAX_RI_BUFFER_SIZE) + { + UpdateBuffer ((const uint8_t *)s.str ().c_str (), len); + // signature + privateKeys.Sign (GetBuffer (), len, GetBufferPointer (len)); + SetBufferLen (len + signatureLen); + } + else + LogPrint (eLogError, "RouterInfo: Our RouterInfo is too long ", len + signatureLen); } - void RouterInfo::EnableV4 () + void LocalRouterInfo::UpdateCaps (uint8_t caps) { - if (!IsV4 ()) - { - uint8_t addressCaps = AddressCaps::eV4; - if (IsV6 ()) addressCaps |= AddressCaps::eV6; - SetUnreachableAddressesTransportCaps (addressCaps); - UpdateSupportedTransports (); - } + SetCaps (caps); + UpdateCapsProperty (); } + void LocalRouterInfo::UpdateCapsProperty () + { + std::string caps; + uint8_t c = GetCaps (); + if (c & eFloodfill) + { + if (c & eExtraBandwidth) caps += (c & eHighBandwidth) ? + CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X' + CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' + else + caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O' + caps += CAPS_FLAG_FLOODFILL; // floodfill + } + else + { + if (c & eExtraBandwidth) + caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */ + else + caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth + } + if (c & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden + if (c & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable + if (c & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable + + SetProperty ("caps", caps); + } - void RouterInfo::DisableV6 () + void LocalRouterInfo::WriteToStream (std::ostream& s) const { - if (IsV6 ()) + uint64_t ts = htobe64 (GetTimestamp ()); + s.write ((const char *)&ts, sizeof (ts)); + + // addresses + const Addresses& addresses = GetAddresses (); + uint8_t numAddresses = addresses.size (); + s.write ((char *)&numAddresses, sizeof (numAddresses)); + for (const auto& addr_ptr : addresses) { - for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) + const Address& address = *addr_ptr; + // calculate cost + uint8_t cost = 0x7f; + if (address.transportStyle == eTransportNTCP) + cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED; + else if (address.transportStyle == eTransportSSU) + cost = address.published ? COST_SSU_DIRECT : COST_SSU_THROUGH_INTRODUCERS; + else if (address.transportStyle == eTransportSSU2) + cost = address.published ? COST_SSU2_DIRECT : COST_SSU2_NON_PUBLISHED; + s.write ((const char *)&cost, sizeof (cost)); + s.write ((const char *)&address.date, sizeof (address.date)); + std::stringstream properties; + bool isPublished = false; + if (address.transportStyle == eTransportNTCP) + { + if (address.IsNTCP2 ()) + { + WriteString ("NTCP2", s); + if (address.IsPublishedNTCP2 () && !address.host.is_unspecified () && address.port) + isPublished = true; + else + { + WriteString ("caps", properties); + properties << '='; + std::string caps; + if (address.IsV4 ()) caps += CAPS_FLAG_V4; + if (address.IsV6 ()) caps += CAPS_FLAG_V6; + if (caps.empty ()) caps += CAPS_FLAG_V4; + WriteString (caps, properties); + properties << ';'; + } + } + else + continue; // don't write NTCP address + } + else if (address.transportStyle == eTransportSSU) + { + WriteString ("SSU", s); + // caps + WriteString ("caps", properties); + properties << '='; + std::string caps; + if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; + if (address.host.is_v4 ()) + { + if (address.published) + { + isPublished = true; + if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; + } + else + caps += CAPS_FLAG_V4; + } + else if (address.host.is_v6 ()) + { + if (address.published) + { + isPublished = true; + if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; + } + else + caps += CAPS_FLAG_V6; + } + else + { + if (address.IsV4 ()) caps += CAPS_FLAG_V4; + if (address.IsV6 ()) caps += CAPS_FLAG_V6; + if (caps.empty ()) caps += CAPS_FLAG_V4; + } + WriteString (caps, properties); + properties << ';'; + } + else if (address.transportStyle == eTransportSSU2) + { + WriteString ("SSU2", s); + // caps + std::string caps; + if (address.published) + { + isPublished = true; + if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; + if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; + } + else + { + if (address.IsV4 ()) caps += CAPS_FLAG_V4; + if (address.IsV6 ()) caps += CAPS_FLAG_V6; + if (caps.empty ()) caps += CAPS_FLAG_V4; + } + if (!caps.empty ()) + { + WriteString ("caps", properties); + properties << '='; + WriteString (caps, properties); + properties << ';'; + } + } + else + WriteString ("", s); + + if (isPublished && !address.host.is_unspecified ()) + { + WriteString ("host", properties); + properties << '='; + WriteString (address.host.to_string (), properties); + properties << ';'; + } + if ((address.IsNTCP2 () && isPublished) || address.IsSSU2 ()) + { + // publish i for NTCP2 or SSU2 + WriteString ("i", properties); properties << '='; + size_t len = address.IsSSU2 () ? 32 : 16; + WriteString (address.i.ToBase64 (len), properties); properties << ';'; + } + if (address.transportStyle == eTransportSSU || address.IsSSU2 ()) + { + // write introducers if any + if (address.ssu && !address.ssu->introducers.empty()) + { + int i = 0; + for (const auto& introducer: address.ssu->introducers) + { + if (introducer.iExp) // expiration is specified + { + WriteString ("iexp" + boost::lexical_cast(i), properties); + properties << '='; + WriteString (boost::lexical_cast(introducer.iExp), properties); + properties << ';'; + } + i++; + } + if (address.transportStyle == eTransportSSU) + { + i = 0; + for (const auto& introducer: address.ssu->introducers) + { + WriteString ("ihost" + boost::lexical_cast(i), properties); + properties << '='; + WriteString (introducer.iHost.to_string (), properties); + properties << ';'; + i++; + } + } + i = 0; + for (const auto& introducer: address.ssu->introducers) + { + if (address.IsSSU2 ()) + WriteString ("ih" + boost::lexical_cast(i), properties); + else + WriteString ("ikey" + boost::lexical_cast(i), properties); + properties << '='; + char value[64]; + size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64); + value[l] = 0; + WriteString (value, properties); + properties << ';'; + i++; + } + if (address.transportStyle == eTransportSSU) + { + i = 0; + for (const auto& introducer: address.ssu->introducers) + { + WriteString ("iport" + boost::lexical_cast(i), properties); + properties << '='; + WriteString (boost::lexical_cast(introducer.iPort), properties); + properties << ';'; + i++; + } + } + i = 0; + for (const auto& introducer: address.ssu->introducers) + { + WriteString ("itag" + boost::lexical_cast(i), properties); + properties << '='; + WriteString (boost::lexical_cast(introducer.iTag), properties); + properties << ';'; + i++; + } + } + } + if (address.transportStyle == eTransportSSU) { - auto addr = *it; - if (addr->IsV6 ()) - { - if (addr->IsV4 ()) - { - addr->caps &= ~AddressCaps::eV6; - ++it; - } - else - it = m_Addresses->erase (it); - } - else - ++it; + // write intro key + WriteString ("key", properties); + properties << '='; + char value[64]; + size_t l = ByteStreamToBase64 (address.i, 32, value, 64); + value[l] = 0; + WriteString (value, properties); + properties << ';'; } - UpdateSupportedTransports (); - } - } - - void RouterInfo::DisableV4 () - { - if (IsV4 ()) - { - for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) + if (address.transportStyle == eTransportSSU || address.IsSSU2 ()) { - auto addr = *it; - if (addr->IsV4 ()) - { - if (addr->IsV6 ()) - { - addr->caps &= ~AddressCaps::eV4; - ++it; - } - else - it = m_Addresses->erase (it); - } - else - ++it; + // write mtu + if (address.ssu && address.ssu->mtu) + { + WriteString ("mtu", properties); + properties << '='; + WriteString (boost::lexical_cast(address.ssu->mtu), properties); + properties << ';'; + } } - UpdateSupportedTransports (); - } - } - - void RouterInfo::EnableMesh () - { - if (!IsMesh ()) - { - m_SupportedTransports |= eNTCP2V6Mesh; - m_ReachableTransports |= eNTCP2V6Mesh; - } - } - - void RouterInfo::DisableMesh () - { - if (IsMesh ()) - { - m_SupportedTransports &= ~eNTCP2V6Mesh; - m_ReachableTransports &= ~eNTCP2V6Mesh; - for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) + if ((isPublished || (address.ssu && !address.IsSSU2 ())) && address.port) { - auto addr = *it; - if (i2p::util::net::IsYggdrasilAddress (addr->host)) - it = m_Addresses->erase (it); - else - ++it; + WriteString ("port", properties); + properties << '='; + WriteString (boost::lexical_cast(address.port), properties); + properties << ';'; } - } - } - - std::shared_ptr RouterInfo::GetSSUAddress (bool v4only) const - { - return GetAddress ( - [v4only](std::shared_ptr address)->bool + if (address.IsNTCP2 () || address.IsSSU2 ()) { - return (address->transportStyle == eTransportSSU) && (!v4only || address->IsV4 ()); - }); - } + // publish s and v for NTCP2 or SSU2 + WriteString ("s", properties); properties << '='; + WriteString (address.s.ToBase64 (), properties); properties << ';'; + WriteString ("v", properties); properties << '='; + WriteString ("2", properties); properties << ';'; + } - std::shared_ptr RouterInfo::GetSSUV6Address () const - { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && address->IsV6(); - }); - } + uint16_t size = htobe16 (properties.str ().size ()); + s.write ((char *)&size, sizeof (size)); + s.write (properties.str ().c_str (), properties.str ().size ()); + } - template - std::shared_ptr RouterInfo::GetAddress (Filter filter) const - { - // TODO: make it more generic using comparator -#if (BOOST_VERSION >= 105300) - auto addresses = boost::atomic_load (&m_Addresses); -#else - auto addresses = m_Addresses; -#endif - for (const auto& address : *addresses) - if (filter (address)) return address; + // peers + uint8_t numPeers = 0; + s.write ((char *)&numPeers, sizeof (numPeers)); - return nullptr; + // properties + std::stringstream properties; + for (const auto& p : m_Properties) + { + WriteString (p.first, properties); + properties << '='; + WriteString (p.second, properties); + properties << ';'; + } + uint16_t size = htobe16 (properties.str ().size ()); + s.write ((char *)&size, sizeof (size)); + s.write (properties.str ().c_str (), properties.str ().size ()); } - std::shared_ptr RouterInfo::GetNTCP2AddressWithStaticKey (const uint8_t * key) const + void LocalRouterInfo::SetProperty (const std::string& key, const std::string& value) { - if (!key) return nullptr; - return GetAddress ( - [key](std::shared_ptr address)->bool - { - return address->IsNTCP2 () && !memcmp (address->ntcp2->staticKey, key, 32); - }); + m_Properties[key] = value; } - std::shared_ptr RouterInfo::GetPublishedNTCP2V4Address () const + void LocalRouterInfo::DeleteProperty (const std::string& key) { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return address->IsPublishedNTCP2 () && address->host.is_v4 (); - }); + m_Properties.erase (key); } - - std::shared_ptr RouterInfo::GetPublishedNTCP2V6Address () const - { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return address->IsPublishedNTCP2 () && address->host.is_v6 () && - !i2p::util::net::IsYggdrasilAddress (address->host); - }); - } - std::shared_ptr RouterInfo::GetYggdrasilAddress () const - { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return address->IsPublishedNTCP2 () && i2p::util::net::IsYggdrasilAddress (address->host); - }); - } - - std::shared_ptr RouterInfo::GetProfile () const + std::string LocalRouterInfo::GetProperty (const std::string& key) const { - if (!m_Profile) - m_Profile = GetRouterProfile (GetIdentHash ()); - return m_Profile; + auto it = m_Properties.find (key); + if (it != m_Properties.end ()) + return it->second; + return ""; } - void RouterInfo::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) const + void LocalRouterInfo::WriteString (const std::string& str, std::ostream& s) const { - auto encryptor = m_RouterIdentity->CreateEncryptor (nullptr); - if (encryptor) - encryptor->Encrypt (data, encrypted, ctx, true); + uint8_t len = str.size (); + s.write ((char *)&len, 1); + s.write (str.c_str (), len); } - bool RouterInfo::IsEligibleFloodfill () const - { - // 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; - } - - bool RouterInfo::IsPeerTesting (bool v4) const + std::shared_ptr LocalRouterInfo::NewBuffer () const { - if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false; - return (bool)GetAddress ( - [v4](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && address->IsPeerTesting () && - ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU (); - }); + return std::make_shared (); } - bool RouterInfo::IsIntroducer (bool v4) const - { - if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false; - return (bool)GetAddress ( - [v4](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && address->IsIntroducer () && - ((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified (); - }); - } - - void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) + bool LocalRouterInfo::AddSSU2Introducer (const Introducer& introducer, bool v4) { - for (auto& addr: *m_Addresses) + for (auto& addr : GetAddresses ()) { - // TODO: implement SSU - if (addr->transportStyle == eTransportNTCP && !addr->IsPublishedNTCP2 ()) + if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ()))) { - addr->caps &= ~(eV4 | eV6); - addr->caps |= transports; + for (auto& intro: addr->ssu->introducers) + if (intro.iTag == introducer.iTag) return false; // already presented + addr->ssu->introducers.push_back (introducer); + SetReachableTransports (GetReachableTransports () | ((addr->IsV4 () ? eSSU2V4 : eSSU2V6))); + return true; } } - } + return false; + } - void RouterInfo::UpdateSupportedTransports () + bool LocalRouterInfo::RemoveSSU2Introducer (const IdentHash& h, bool v4) { - m_SupportedTransports = 0; - m_ReachableTransports = 0; - for (const auto& addr: *m_Addresses) + for (auto& addr: GetAddresses ()) { - uint8_t transports = 0; - if (addr->transportStyle == eTransportNTCP) + if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ()))) { - if (addr->IsV4 ()) transports |= eNTCP2V4; - if (addr->IsV6 ()) - transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6); - if (addr->IsPublishedNTCP2 ()) - m_ReachableTransports |= transports; - } - else if (addr->transportStyle == eTransportSSU) - { - if (addr->IsV4 ()) transports |= eSSUV4; - if (addr->IsV6 ()) transports |= eSSUV6; - if (addr->IsReachableSSU ()) - m_ReachableTransports |= transports; - } - m_SupportedTransports |= transports; + for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it) + if (h == it->iKey) + { + addr->ssu->introducers.erase (it); + if (addr->ssu->introducers.empty ()) + SetReachableTransports (GetReachableTransports () & ~(addr->IsV4 () ? eSSU2V4 : eSSU2V6)); + return true; + } + } } + return false; } } } diff -Nru i2pd-2.39.0/libi2pd/RouterInfo.h i2pd-2.43.0/libi2pd/RouterInfo.h --- i2pd-2.39.0/libi2pd/RouterInfo.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/RouterInfo.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -13,12 +13,13 @@ #include #include #include -#include +#include #include #include #include #include "Identity.h" #include "Profiling.h" +#include "Family.h" namespace i2p { @@ -51,10 +52,12 @@ const uint8_t COST_NTCP2_PUBLISHED = 3; const uint8_t COST_NTCP2_NON_PUBLISHED = 14; + const uint8_t COST_SSU2_DIRECT = 8; const uint8_t COST_SSU_DIRECT = 9; const uint8_t COST_SSU_THROUGH_INTRODUCERS = 11; - - const int MAX_RI_BUFFER_SIZE = 2048; // if RouterInfo exceeds 2048 we consider it as malformed, might be changed later + const uint8_t COST_SSU2_NON_PUBLISHED = 15; + + const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later class RouterInfo: public RoutingDestination { public: @@ -65,8 +68,12 @@ eNTCP2V6 = 0x02, eSSUV4 = 0x04, eSSUV6 = 0x08, - eNTCP2V6Mesh = 0x10 + eNTCP2V6Mesh = 0x10, + eSSU2V4 = 0x20, + eSSU2V6 = 0x40, + eAllTransports = 0xFF }; + typedef uint8_t CompatibleTransports; enum Caps { @@ -85,12 +92,13 @@ eSSUTesting = 0x04, eSSUIntroducer = 0x08 }; - + enum TransportStyle { eTransportUnknown = 0, eTransportNTCP, - eTransportSSU + eTransportSSU, + eTransportSSU2 }; typedef Tag<32> IntroKey; // should be castable to MacKey and AESKey @@ -99,7 +107,7 @@ Introducer (): iPort (0), iExp (0) {}; boost::asio::ip::address iHost; int iPort; - IntroKey iKey; + IntroKey iKey; // or ih for SSU2 uint32_t iTag; uint32_t iExp; }; @@ -107,26 +115,19 @@ struct SSUExt { int mtu; - IntroKey key; // intro key for SSU std::vector introducers; }; - struct NTCP2Ext - { - Tag<32> staticKey; - Tag<16> iv; - }; - struct Address { TransportStyle transportStyle; boost::asio::ip::address host; + Tag<32> s, i; // keys, i is first 16 bytes for NTCP2 and 32 bytes intro key for SSU int port; uint64_t date; uint8_t caps; bool published = false; std::unique_ptr ssu; // not null for SSU - std::unique_ptr ntcp2; // not null for NTCP2 bool IsCompatible (const boost::asio::ip::address& other) const { @@ -136,7 +137,7 @@ bool operator==(const Address& other) const { - return transportStyle == other.transportStyle && IsNTCP2 () == other.IsNTCP2 () && + return transportStyle == other.transportStyle && host == other.host && port == other.port; } @@ -145,87 +146,106 @@ return !(*this == other); } - bool IsNTCP2 () const { return (bool)ntcp2; }; + bool IsNTCP2 () const { return transportStyle == eTransportNTCP; }; + bool IsSSU2 () const { return transportStyle == eTransportSSU2; }; bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; }; - bool IsReachableSSU () const { return (bool)ssu && (published || !ssu->introducers.empty ()); }; - bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); }; - + bool IsReachableSSU () const { return (bool)ssu && (published || UsesIntroducer ()); }; + bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); }; + bool IsIntroducer () const { return caps & eSSUIntroducer; }; bool IsPeerTesting () const { return caps & eSSUTesting; }; 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; - RouterInfo (); + class Buffer: public std::array + { + public: + + Buffer () = default; + Buffer (const uint8_t * buf, size_t len); + }; + + typedef std::vector > Addresses; + RouterInfo (const std::string& fullPath); RouterInfo (const RouterInfo& ) = default; RouterInfo& operator=(const RouterInfo& ) = default; - RouterInfo (const uint8_t * buf, int len); - ~RouterInfo (); + RouterInfo (std::shared_ptr&& buf, size_t len); + RouterInfo (const uint8_t * buf, size_t len); + virtual ~RouterInfo (); std::shared_ptr GetRouterIdentity () const { return m_RouterIdentity; }; void SetRouterIdentity (std::shared_ptr identity); std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; uint64_t GetTimestamp () const { return m_Timestamp; }; int GetVersion () const { return m_Version; }; + virtual void SetProperty (const std::string& key, const std::string& value) {}; + virtual void ClearProperties () {}; Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr std::shared_ptr GetNTCP2AddressWithStaticKey (const uint8_t * key) const; - std::shared_ptr GetPublishedNTCP2V4Address () const; - std::shared_ptr GetPublishedNTCP2V6Address () const; + std::shared_ptr GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const; + std::shared_ptr GetPublishedNTCP2V4Address () const; + std::shared_ptr GetPublishedNTCP2V6Address () const; std::shared_ptr GetSSUAddress (bool v4only = true) const; std::shared_ptr GetSSUV6Address () const; std::shared_ptr GetYggdrasilAddress () const; + std::shared_ptr GetSSU2V4Address () const; + std::shared_ptr GetSSU2V6Address () const; + std::shared_ptr GetSSU2Address (bool v4) const; void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0); - void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, + 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); + void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps = 0); // non published + void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, + const boost::asio::ip::address& host, int port); // published bool AddIntroducer (const Introducer& introducer); bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); - void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only - void DeleteProperty (const std::string& key); // called from RouterContext only - std::string GetProperty (const std::string& key) const; // called from RouterContext only - void ClearProperties () { m_Properties.clear (); }; void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps 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 IsSSUV6 () const { return m_SupportedTransports & eSSUV6; }; bool IsNTCP2 (bool v4only = true) const; - bool IsNTCP2V6 () const; - bool IsV6 () const; - bool IsV4 () const; - bool IsMesh () const; + bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; }; + bool IsSSU2V4 () const { return m_SupportedTransports & eSSU2V4; }; + bool IsSSU2V6 () const { return m_SupportedTransports & eSSU2V6; }; + bool IsV6 () const { return m_SupportedTransports & (eSSUV6 | eNTCP2V6 | eSSU2V6); }; + bool IsV4 () const { return m_SupportedTransports & (eSSUV4 | eNTCP2V4 | eSSU2V4); }; + bool IsMesh () const { return m_SupportedTransports & eNTCP2V6Mesh; }; void EnableV6 (); void DisableV6 (); void EnableV4 (); void DisableV4 (); void EnableMesh (); - void DisableMesh (); - bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; + void DisableMesh (); + bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; bool IsReachableFrom (const RouterInfo& other) const { return m_ReachableTransports & other.m_SupportedTransports; }; - bool IsReachableBy (uint8_t transports) const { return m_ReachableTransports & transports; }; + 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; }; bool IsEligibleFloodfill () const; - bool IsPeerTesting (bool v4) const; - bool IsIntroducer (bool v4) const; - + bool IsPeerTesting (bool v4) const; + bool IsSSU2PeerTesting (bool v4) const; + bool IsIntroducer (bool v4) const; + bool IsSSU2Introducer (bool v4) const; + uint8_t GetCaps () const { return m_Caps; }; - void SetCaps (uint8_t caps); - void SetCaps (const char * caps); + void SetCaps (uint8_t caps) { m_Caps = caps; }; void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; bool IsUnreachable () const { return m_IsUnreachable; }; - const uint8_t * GetBuffer () const { return m_Buffer; }; - const uint8_t * LoadBuffer (); // load if necessary - int GetBufferLen () const { return m_BufferLen; }; - void CreateBuffer (const PrivateKeys& privateKeys); + const uint8_t * GetBuffer () const { return m_Buffer->data (); }; + const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary + size_t GetBufferLen () const { return m_BufferLen; }; bool IsUpdated () const { return m_IsUpdated; }; void SetUpdated (bool updated) { m_IsUpdated = updated; }; @@ -235,47 +255,84 @@ void SaveProfile () { if (m_Profile) m_Profile->Save (GetIdentHash ()); }; void Update (const uint8_t * buf, size_t len); - void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; }; + void DeleteBuffer () { m_Buffer = nullptr; }; bool IsNewer (const uint8_t * buf, size_t len) const; /** return true if we are in a router family and the signature is valid */ - bool IsFamily(const std::string & fam) const; + bool IsFamily (FamilyID famid) const; // implements RoutingDestination 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; }; + protected: + + RouterInfo (); + uint8_t * GetBufferPointer (size_t offset = 0 ) { return m_Buffer->data () + offset; }; + void UpdateBuffer (const uint8_t * buf, size_t len); + void SetBufferLen (size_t len) { m_BufferLen = len; }; + void RefreshTimestamp (); + const Addresses& GetAddresses () const { return *m_Addresses; }; + CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; }; + void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; }; + private: - 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; size_t ReadString (char* str, size_t len, std::istream& s) const; - void WriteString (const std::string& str, std::ostream& s) const; void ExtractCaps (const char * value); uint8_t ExtractAddressCaps (const char * value) const; template std::shared_ptr GetAddress (Filter filter) const; - void UpdateCapsProperty (); + virtual std::shared_ptr NewBuffer () const; private: - std::string m_FullPath, m_Family; + FamilyID m_FamilyID; std::shared_ptr m_RouterIdentity; - uint8_t * m_Buffer; + std::shared_ptr m_Buffer; size_t m_BufferLen; uint64_t m_Timestamp; 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; }; + + class LocalRouterInfo: public RouterInfo + { + public: + + LocalRouterInfo () = default; + void CreateBuffer (const PrivateKeys& privateKeys); + void UpdateCaps (uint8_t caps); + + void SetProperty (const std::string& key, const std::string& value) override; + void DeleteProperty (const std::string& key); + std::string GetProperty (const std::string& key) const; + void ClearProperties () override { m_Properties.clear (); }; + + bool AddSSU2Introducer (const Introducer& introducer, bool v4); + bool RemoveSSU2Introducer (const IdentHash& h, bool v4); + + private: + + void WriteToStream (std::ostream& s) const; + void UpdateCapsProperty (); + void WriteString (const std::string& str, std::ostream& s) const; + std::shared_ptr NewBuffer () const override; + + private: + + std::map m_Properties; + }; } } diff -Nru i2pd-2.39.0/libi2pd/Signature.cpp i2pd-2.43.0/libi2pd/Signature.cpp --- i2pd-2.39.0/libi2pd/Signature.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Signature.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -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 * @@ -15,8 +15,7 @@ namespace crypto { #if OPENSSL_EDDSA - EDDSA25519Verifier::EDDSA25519Verifier (): - m_Pkey (nullptr) + EDDSA25519Verifier::EDDSA25519Verifier () { m_MDCtx = EVP_MD_CTX_create (); } @@ -24,13 +23,13 @@ EDDSA25519Verifier::~EDDSA25519Verifier () { EVP_MD_CTX_destroy (m_MDCtx); - if (m_Pkey) EVP_PKEY_free (m_Pkey); } void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) { - m_Pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32); - EVP_DigestVerifyInit (m_MDCtx, NULL, NULL, NULL, m_Pkey); + EVP_PKEY * pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32); + EVP_DigestVerifyInit (m_MDCtx, NULL, NULL, NULL, pkey); + EVP_PKEY_free (pkey); } bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const @@ -100,33 +99,29 @@ #if OPENSSL_EDDSA EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey): - m_Fallback (nullptr) + m_MDCtx (nullptr), m_Fallback (nullptr) { - m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_ED25519, NULL, signingPrivateKey, 32); + EVP_PKEY * pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_ED25519, NULL, signingPrivateKey, 32); uint8_t publicKey[EDDSA25519_PUBLIC_KEY_LENGTH]; size_t len = EDDSA25519_PUBLIC_KEY_LENGTH; - EVP_PKEY_get_raw_public_key (m_Pkey, publicKey, &len); + EVP_PKEY_get_raw_public_key (pkey, publicKey, &len); if (signingPublicKey && memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) { LogPrint (eLogWarning, "EdDSA public key mismatch. Fallback"); - EVP_PKEY_free (m_Pkey); m_Fallback = new EDDSA25519SignerCompat (signingPrivateKey, signingPublicKey); } else { m_MDCtx = EVP_MD_CTX_create (); - EVP_DigestSignInit (m_MDCtx, NULL, NULL, NULL, m_Pkey); + EVP_DigestSignInit (m_MDCtx, NULL, NULL, NULL, pkey); } + EVP_PKEY_free (pkey); } EDDSA25519Signer::~EDDSA25519Signer () { if (m_Fallback) delete m_Fallback; - else - { - EVP_MD_CTX_destroy (m_MDCtx); - EVP_PKEY_free (m_Pkey); - } + EVP_MD_CTX_destroy (m_MDCtx); } void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const @@ -135,7 +130,7 @@ else { size_t l = 64; - uint8_t sig[64]; // temporary buffer for signature. openssl issue #7232 + uint8_t sig[64]; // temporary buffer for signature. openssl issue #7232 EVP_DigestSign (m_MDCtx, sig, &l, buf, len); memcpy (signature, sig, 64); } diff -Nru i2pd-2.39.0/libi2pd/Signature.h i2pd-2.43.0/libi2pd/Signature.h --- i2pd-2.39.0/libi2pd/Signature.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Signature.h 2022-08-21 19:40:41.000000000 +0000 @@ -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 * @@ -304,7 +304,6 @@ private: #if OPENSSL_EDDSA - EVP_PKEY * m_Pkey; EVP_MD_CTX * m_MDCtx; #else EDDSAPoint m_PublicKey; @@ -341,7 +340,7 @@ void Sign (const uint8_t * buf, int len, uint8_t * signature) const; private: - EVP_PKEY * m_Pkey; + EVP_MD_CTX * m_MDCtx; EDDSA25519SignerCompat * m_Fallback; }; diff -Nru i2pd-2.39.0/libi2pd/SSU2.cpp i2pd-2.43.0/libi2pd/SSU2.cpp --- i2pd-2.39.0/libi2pd/SSU2.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/libi2pd/SSU2.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,1031 @@ +/* +* Copyright (c) 2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include "Log.h" +#include "RouterContext.h" +#include "Transports.h" +#include "NetDb.hpp" +#include "Config.h" +#include "SSU2.h" + +namespace i2p +{ +namespace transport +{ + SSU2Server::SSU2Server (): + RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"), + m_SocketV4 (m_ReceiveService.GetService ()), m_SocketV6 (m_ReceiveService.GetService ()), + m_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()), + m_TerminationTimer (GetService ()), m_ResendTimer (GetService ()), + m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()), + m_IsPublished (true), m_IsSyncClockFromPeers (true) + { + } + + void SSU2Server::Start () + { + if (!IsRunning ()) + { + StartIOService (); + i2p::config::GetOption ("ssu2.published", m_IsPublished); + i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers); + bool found = false; + auto& addresses = i2p::context.GetRouterInfo ().GetAddresses (); + for (const auto& address: addresses) + { + if (!address) continue; + if (address->transportStyle == i2p::data::RouterInfo::eTransportSSU2) + { + auto port = address->port; + if (!port) + { + uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); + if (ssu2Port) port = ssu2Port; + else + { + bool ssu; i2p::config::GetOption("ssu", ssu); + uint16_t p; i2p::config::GetOption ("port", p); + if (p) port = ssu ? (p + 1) : p; + } + } + if (port) + { + if (address->IsV4 ()) + { + found = true; + OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV4, port)); + m_ReceiveService.GetService ().post( + [this]() + { + Receive (m_SocketV4); + }); + ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers + } + if (address->IsV6 ()) + { + found = true; + OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV6, port)); + m_ReceiveService.GetService ().post( + [this]() + { + Receive (m_SocketV6); + }); + ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers + } + } + else + LogPrint (eLogError, "SSU2: Can't start server because port not specified"); + } + } + if (found) + m_ReceiveService.Start (); + ScheduleTermination (); + } + } + + void SSU2Server::Stop () + { + if (IsRunning ()) + { + m_TerminationTimer.cancel (); + m_ResendTimer.cancel (); + m_IntroducersUpdateTimer.cancel (); + m_IntroducersUpdateTimerV6.cancel (); + } + + auto sessions = m_Sessions; + for (auto& it: sessions) + { + it.second->RequestTermination (eSSU2TerminationReasonRouterShutdown); + it.second->Done (); + } + + if (context.SupportsV4 () || context.SupportsV6 ()) + m_ReceiveService.Stop (); + m_SocketV4.close (); + m_SocketV6.close (); + + StopIOService (); + + m_Sessions.clear (); + m_SessionsByRouterHash.clear (); + m_PendingOutgoingSessions.clear (); + m_Relays.clear (); + m_Introducers.clear (); + m_IntroducersV6.clear (); + } + + void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) + { + if (localAddress.is_unspecified ()) return; + if (localAddress.is_v4 ()) + { + m_AddressV4 = localAddress; + int mtu = i2p::util::net::GetMTU (localAddress); + if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; + if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE; + i2p::context.SetMTU (mtu, true); + } + else if (localAddress.is_v6 ()) + { + m_AddressV6 = localAddress; + int maxMTU = i2p::util::net::GetMaxMTU (localAddress.to_v6 ()); + int mtu = i2p::util::net::GetMTU (localAddress); + if (mtu > maxMTU) mtu = maxMTU; + if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; + i2p::context.SetMTU (mtu, false); + } + } + + bool SSU2Server::IsSupported (const boost::asio::ip::address& addr) const + { + if (addr.is_v4 ()) + { + if (m_SocketV4.is_open ()) + return true; + } + else if (addr.is_v6 ()) + { + if (m_SocketV6.is_open ()) + return true; + } + return false; + } + + uint16_t SSU2Server::GetPort (bool v4) const + { + boost::system::error_code ec; + boost::asio::ip::udp::endpoint ep = v4 ? m_SocketV4.local_endpoint (ec) : m_SocketV6.local_endpoint (ec); + if (ec) return 0; + return ep.port (); + } + + boost::asio::ip::udp::socket& SSU2Server::OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint) + { + boost::asio::ip::udp::socket& socket = localEndpoint.address ().is_v6 () ? m_SocketV6 : m_SocketV4; + try + { + socket.open (localEndpoint.protocol ()); + if (localEndpoint.address ().is_v6 ()) + socket.set_option (boost::asio::ip::v6_only (true)); + socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU2_SOCKET_RECEIVE_BUFFER_SIZE)); + socket.set_option (boost::asio::socket_base::send_buffer_size (SSU2_SOCKET_SEND_BUFFER_SIZE)); + socket.bind (localEndpoint); + LogPrint (eLogInfo, "SSU2: Start listening on ", localEndpoint); + } + catch (std::exception& ex ) + { + LogPrint (eLogError, "SSU2: Failed to bind to ", localEndpoint, ": ", ex.what()); + ThrowFatal ("Unable to start SSU2 transport on ", localEndpoint, ": ", ex.what ()); + } + return socket; + } + + void SSU2Server::Receive (boost::asio::ip::udp::socket& socket) + { + Packet * packet = m_PacketsPool.AcquireMt (); + socket.async_receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, + std::bind (&SSU2Server::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet, std::ref (socket))); + } + + void SSU2Server::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, + Packet * packet, boost::asio::ip::udp::socket& socket) + { + if (!ecode) + { + i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); + packet->len = bytes_transferred; + + boost::system::error_code ec; + size_t moreBytes = socket.available (ec); + if (!ec && moreBytes) + { + std::vector packets; + packets.push_back (packet); + while (moreBytes && packets.size () < 32) + { + packet = m_PacketsPool.AcquireMt (); + packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec); + if (!ec) + { + i2p::transport::transports.UpdateReceivedBytes (packet->len); + packets.push_back (packet); + moreBytes = socket.available(ec); + if (ec) break; + } + else + { + LogPrint (eLogError, "SSU2: receive_from error: code ", ec.value(), ": ", ec.message ()); + m_PacketsPool.ReleaseMt (packet); + break; + } + } + GetService ().post (std::bind (&SSU2Server::HandleReceivedPackets, this, packets)); + } + else + GetService ().post (std::bind (&SSU2Server::HandleReceivedPacket, this, packet)); + Receive (socket); + } + else + { + m_PacketsPool.ReleaseMt (packet); + if (ecode != boost::asio::error::operation_aborted) + { + LogPrint (eLogError, "SSU2: Receive error: code ", ecode.value(), ": ", ecode.message ()); + auto ep = socket.local_endpoint (); + socket.close (); + OpenSocket (ep); + Receive (socket); + } + } + } + + void SSU2Server::HandleReceivedPacket (Packet * packet) + { + if (packet) + { + ProcessNextPacket (packet->buf, packet->len, packet->from); + m_PacketsPool.ReleaseMt (packet); + if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) + m_LastSession->FlushData (); + } + } + + void SSU2Server::HandleReceivedPackets (std::vector packets) + { + for (auto& packet: packets) + ProcessNextPacket (packet->buf, packet->len, packet->from); + m_PacketsPool.ReleaseMt (packets); + if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) + m_LastSession->FlushData (); + } + + void SSU2Server::AddSession (std::shared_ptr session) + { + if (session) + { + m_Sessions.emplace (session->GetConnID (), session); + AddSessionByRouterHash (session); + } + } + + void SSU2Server::RemoveSession (uint64_t connID) + { + auto it = m_Sessions.find (connID); + if (it != m_Sessions.end ()) + { + auto ident = it->second->GetRemoteIdentity (); + if (ident) + m_SessionsByRouterHash.erase (ident->GetIdentHash ()); + if (m_LastSession == it->second) + m_LastSession = nullptr; + m_Sessions.erase (it); + } + } + + void SSU2Server::AddSessionByRouterHash (std::shared_ptr session) + { + if (session) + { + auto ident = session->GetRemoteIdentity (); + if (ident) + { + auto ret = m_SessionsByRouterHash.emplace (ident->GetIdentHash (), session); + if (!ret.second) + { + // session already exists + LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " already exists"); + // terminate existing + GetService ().post (std::bind (&SSU2Session::RequestTermination, ret.first->second, eSSU2TerminationReasonReplacedByNewSession)); + // update session + ret.first->second = session; + } + } + } + } + + bool SSU2Server::AddPendingOutgoingSession (std::shared_ptr session) + { + if (!session) return false; + return m_PendingOutgoingSessions.emplace (session->GetRemoteEndpoint (), session).second; + } + + std::shared_ptr SSU2Server::FindSession (const i2p::data::IdentHash& ident) const + { + auto it = m_SessionsByRouterHash.find (ident); + if (it != m_SessionsByRouterHash.end ()) + return it->second; + return nullptr; + } + + std::shared_ptr SSU2Server::FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const + { + auto it = m_PendingOutgoingSessions.find (ep); + if (it != m_PendingOutgoingSessions.end ()) + return it->second; + return nullptr; + } + + void SSU2Server::RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) + { + m_PendingOutgoingSessions.erase (ep); + } + + std::shared_ptr SSU2Server::GetRandomSession ( + i2p::data::RouterInfo::CompatibleTransports remoteTransports, const i2p::data::IdentHash& excluded) const + { + if (m_Sessions.empty ()) return nullptr; + uint16_t ind; + RAND_bytes ((uint8_t *)&ind, sizeof (ind)); + ind %= m_Sessions.size (); + auto it = m_Sessions.begin (); + std::advance (it, ind); + while (it != m_Sessions.end ()) + { + if ((it->second->GetRemoteTransports () & remoteTransports) && + it->second->GetRemoteIdentity ()->GetIdentHash () != excluded) + return it->second; + it++; + } + // not found, try from begining + it = m_Sessions.begin (); + while (it != m_Sessions.end () && ind) + { + if ((it->second->GetRemoteTransports () & remoteTransports) && + it->second->GetRemoteIdentity ()->GetIdentHash () != excluded) + return it->second; + it++; ind--; + } + return nullptr; + } + + void SSU2Server::AddRelay (uint32_t tag, std::shared_ptr relay) + { + m_Relays.emplace (tag, relay); + } + + void SSU2Server::RemoveRelay (uint32_t tag) + { + m_Relays.erase (tag); + } + + std::shared_ptr SSU2Server::FindRelaySession (uint32_t tag) + { + auto it = m_Relays.find (tag); + if (it != m_Relays.end ()) + { + if (it->second->IsEstablished ()) + return it->second; + else + m_Relays.erase (it); + } + return nullptr; + } + + void SSU2Server::ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) + { + if (len < 24) return; + uint64_t connID; + memcpy (&connID, buf, 8); + connID ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); + if (!m_LastSession || m_LastSession->GetConnID () != connID) + { + if (m_LastSession) m_LastSession->FlushData (); + auto it = m_Sessions.find (connID); + if (it != m_Sessions.end ()) + m_LastSession = it->second; + else + m_LastSession = nullptr; + } + if (m_LastSession) + { + switch (m_LastSession->GetState ()) + { + case eSSU2SessionStateEstablished: + case eSSU2SessionStateSessionConfirmedSent: + m_LastSession->ProcessData (buf, len); + break; + case eSSU2SessionStateSessionCreatedSent: + if (!m_LastSession->ProcessSessionConfirmed (buf, len)) + { + m_LastSession->Done (); + m_LastSession = nullptr; + } + break; + case eSSU2SessionStateIntroduced: + if (m_LastSession->GetRemoteEndpoint ().address ().is_unspecified ()) + m_LastSession->SetRemoteEndpoint (senderEndpoint); + if (m_LastSession->GetRemoteEndpoint () == senderEndpoint) + m_LastSession->ProcessHolePunch (buf, len); + else + { + LogPrint (eLogWarning, "SSU2: HolePunch endpoint ", senderEndpoint, + " doesn't match RelayResponse ", m_LastSession->GetRemoteEndpoint ()); + m_LastSession->Done (); + m_LastSession = nullptr; + } + break; + case eSSU2SessionStatePeerTest: + m_LastSession->SetRemoteEndpoint (senderEndpoint); + m_LastSession->ProcessPeerTest (buf, len); + break; + case eSSU2SessionStateClosing: + m_LastSession->ProcessData (buf, len); // we might receive termintaion block + if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) + m_LastSession->RequestTermination (eSSU2TerminationReasonIdleTimeout); // send termination again + break; + case eSSU2SessionStateTerminated: + m_LastSession = nullptr; + break; + default: + LogPrint (eLogWarning, "SSU2: Invalid session state ", (int)m_LastSession->GetState ()); + } + } + else + { + // check pending sessions if it's SessionCreated or Retry + auto it1 = m_PendingOutgoingSessions.find (senderEndpoint); + if (it1 != m_PendingOutgoingSessions.end ()) + { + if (it1->second->GetState () == eSSU2SessionStateSessionRequestSent && + it1->second->ProcessSessionCreated (buf, len)) + m_PendingOutgoingSessions.erase (it1); // we are done with that endpoint + else + it1->second->ProcessRetry (buf, len); + } + else + { + // assume new incoming session + auto session = std::make_shared (*this); + session->SetRemoteEndpoint (senderEndpoint); + session->ProcessFirstIncomingMessage (connID, buf, len); + } + } + } + + void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen, + const boost::asio::ip::udp::endpoint& to) + { + std::vector bufs + { + boost::asio::buffer (header, headerLen), + boost::asio::buffer (payload, payloadLen) + }; + boost::system::error_code ec; + if (to.address ().is_v6 ()) + m_SocketV6.send_to (bufs, to, 0, ec); + else + m_SocketV4.send_to (bufs, to, 0, ec); + if (!ec) + i2p::transport::transports.UpdateSentBytes (headerLen + payloadLen); + else + LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to); + } + + void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, + const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to) + { + std::vector bufs + { + boost::asio::buffer (header, headerLen), + boost::asio::buffer (headerX, headerXLen), + boost::asio::buffer (payload, payloadLen) + }; + boost::system::error_code ec; + if (to.address ().is_v6 ()) + m_SocketV6.send_to (bufs, to, 0, ec); + else + m_SocketV4.send_to (bufs, to, 0, ec); + + if (!ec) + i2p::transport::transports.UpdateSentBytes (headerLen + headerXLen + payloadLen); + else + LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to); + } + + bool SSU2Server::CreateSession (std::shared_ptr router, + std::shared_ptr address, bool peerTest) + { + if (router && address) + { + // check if no session + auto it = m_SessionsByRouterHash.find (router->GetIdentHash ()); + if (it != m_SessionsByRouterHash.end ()) + { + // session with router found, trying to send peer test if requested + if (peerTest && it->second->IsEstablished ()) + { + auto session = it->second; + GetService ().post ([session]() { session->SendPeerTest (); }); + } + return false; + } + // check is no pending session + bool isValidEndpoint = !address->host.is_unspecified () && address->port; + if (isValidEndpoint) + { + if (i2p::util::net::IsInReservedRange(address->host)) return false; + auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port)); + if (s) + { + if (peerTest) + { + // if peer test requested add it to the list for pending session + auto onEstablished = s->GetOnEstablished (); + if (onEstablished) + s->SetOnEstablished ([s, onEstablished]() + { + onEstablished (); + s->SendPeerTest (); + }); + else + s->SetOnEstablished ([s]() { s->SendPeerTest (); }); + } + return false; + } + } + + auto session = std::make_shared (*this, router, address); + if (peerTest) + session->SetOnEstablished ([session]() {session->SendPeerTest (); }); + + if (address->UsesIntroducer ()) + GetService ().post (std::bind (&SSU2Server::ConnectThroughIntroducer, this, session)); + else if (isValidEndpoint) // we can't connect without endpoint + GetService ().post ([session]() { session->Connect (); }); + else + return false; + } + else + return false; + return true; + } + + void SSU2Server::ConnectThroughIntroducer (std::shared_ptr session) + { + if (!session) return; + auto address = session->GetAddress (); + if (!address) return; + session->WaitForIntroduction (); + // try to find existing session first + for (auto& it: address->ssu->introducers) + { + auto it1 = m_SessionsByRouterHash.find (it.iKey); + if (it1 != m_SessionsByRouterHash.end ()) + { + it1->second->Introduce (session, it.iTag); + return; + } + } + // we have to start a new session to an introducer + auto ts = i2p::util::GetSecondsSinceEpoch (); + std::shared_ptr r; + uint32_t relayTag = 0; + if (!address->ssu->introducers.empty ()) + { + std::vector indicies; + for (int i = 0; i < (int)address->ssu->introducers.size (); i++) indicies.push_back(i); + if (indicies.size () > 1) + std::shuffle (indicies.begin(), indicies.end(), std::mt19937(std::random_device()())); + + for (auto i: indicies) + { + const auto& introducer = address->ssu->introducers[indicies[i]]; + if (introducer.iTag && ts < introducer.iExp) + { + r = i2p::data::netdb.FindRouter (introducer.iKey); + if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ())) + { + relayTag = introducer.iTag; + if (relayTag) break; + } + } + } + } + if (r) + { + if (relayTag) + { + // introducer and tag found connect to it through SSU2 + auto addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); + if (addr) + { + bool isValidEndpoint = !addr->host.is_unspecified () && addr->port && + !i2p::util::net::IsInReservedRange(addr->host); + if (isValidEndpoint) + { + auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port)); + if (!s) + { + s = std::make_shared (*this, r, addr); + s->SetOnEstablished ([session, s, relayTag]() { s->Introduce (session, relayTag); }); + s->Connect (); + } + else + { + auto onEstablished = s->GetOnEstablished (); + if (onEstablished) + s->SetOnEstablished ([session, s, relayTag, onEstablished]() + { + onEstablished (); + s->Introduce (session, relayTag); + }); + else + s->SetOnEstablished ([session, s, relayTag]() {s->Introduce (session, relayTag); }); + } + } + } + } + } + else + { + // introducers not found, try to request them + for (auto& it: address->ssu->introducers) + if (it.iTag && ts < it.iExp) + i2p::data::netdb.RequestDestination (it.iKey); + } + } + + bool SSU2Server::StartPeerTest (std::shared_ptr router, bool v4) + { + if (!router) return false; + auto addr = v4 ? router->GetSSU2V4Address () : router->GetSSU2V6Address (); + if (!addr) return false; + auto it = m_SessionsByRouterHash.find (router->GetIdentHash ()); + if (it != m_SessionsByRouterHash.end ()) + { + auto s = it->second; + if (it->second->IsEstablished ()) + GetService ().post ([s]() { s->SendPeerTest (); }); + else + s->SetOnEstablished ([s]() { s->SendPeerTest (); }); + return true; + } + else + CreateSession (router, addr, true); + return true; + } + + void SSU2Server::ScheduleTermination () + { + m_TerminationTimer.expires_from_now (boost::posix_time::seconds(SSU2_TERMINATION_CHECK_TIMEOUT)); + m_TerminationTimer.async_wait (std::bind (&SSU2Server::HandleTerminationTimer, + this, std::placeholders::_1)); + } + + void SSU2Server::HandleTerminationTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_PendingOutgoingSessions.begin (); it != m_PendingOutgoingSessions.end ();) + { + if (it->second->IsTerminationTimeoutExpired (ts)) + { + //it->second->Terminate (); + it = m_PendingOutgoingSessions.erase (it); + } + else + it++; + } + + for (auto it: m_Sessions) + { + auto state = it.second->GetState (); + if (state == eSSU2SessionStateTerminated || state == eSSU2SessionStateClosing) + it.second->Done (); + else if (it.second->IsTerminationTimeoutExpired (ts)) + { + if (it.second->IsEstablished ()) + it.second->RequestTermination (eSSU2TerminationReasonIdleTimeout); + else + it.second->Done (); + } + else + it.second->CleanUp (ts); + } + + for (auto it = m_SessionsByRouterHash.begin (); it != m_SessionsByRouterHash.begin ();) + { + if (it->second && it->second->GetState () == eSSU2SessionStateTerminated) + it = m_SessionsByRouterHash.erase (it); + else + it++; + } + + for (auto it = m_Relays.begin (); it != m_Relays.begin ();) + { + if (it->second && it->second->GetState () == eSSU2SessionStateTerminated) + it = m_Relays.erase (it); + else + it++; + } + + for (auto it = m_IncomingTokens.begin (); it != m_IncomingTokens.end (); ) + { + if (ts > it->second.second) + it = m_IncomingTokens.erase (it); + else + it++; + } + + for (auto it = m_OutgoingTokens.begin (); it != m_OutgoingTokens.end (); ) + { + if (ts > it->second.second) + it = m_OutgoingTokens.erase (it); + else + it++; + } + + m_PacketsPool.CleanUpMt (); + m_SentPacketsPool.CleanUp (); + ScheduleTermination (); + } + } + + void SSU2Server::ScheduleResend () + { + m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(SSU2_RESEND_CHECK_TIMEOUT)); + m_ResendTimer.async_wait (std::bind (&SSU2Server::HandleResendTimer, + this, std::placeholders::_1)); + } + + void SSU2Server::HandleResendTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + for (auto it: m_Sessions) + it.second->Resend (ts); + for (auto it: m_PendingOutgoingSessions) + it.second->Resend (ts); + ScheduleResend (); + } + } + + void SSU2Server::UpdateOutgoingToken (const boost::asio::ip::udp::endpoint& ep, uint64_t token, uint32_t exp) + { + m_OutgoingTokens[ep] = {token, exp}; + } + + uint64_t SSU2Server::FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) const + { + auto it = m_OutgoingTokens.find (ep); + if (it != m_OutgoingTokens.end ()) + { + if (i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_THRESHOLD > it->second.second) + return 0; // token expired + return it->second.first; + } + return 0; + } + + uint64_t SSU2Server::GetIncomingToken (const boost::asio::ip::udp::endpoint& ep) + { + auto it = m_IncomingTokens.find (ep); + if (it != m_IncomingTokens.end ()) + return it->second.first; + uint64_t token; + RAND_bytes ((uint8_t *)&token, 8); + m_IncomingTokens.emplace (ep, std::make_pair (token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT)); + return token; + } + + std::pair SSU2Server::NewIncomingToken (const boost::asio::ip::udp::endpoint& ep) + { + m_IncomingTokens.erase (ep); // drop previous + uint64_t token; + RAND_bytes ((uint8_t *)&token, 8); + auto ret = std::make_pair (token, i2p::util::GetSecondsSinceEpoch () + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT); + m_IncomingTokens.emplace (ep, ret); + return ret; + } + + std::list > SSU2Server::FindIntroducers (int maxNumIntroducers, + bool v4, const std::set& excluded) const + { + std::list > ret; + for (const auto& s : m_Sessions) + { + if (s.second->IsEstablished () && (s.second->GetRelayTag () && s.second->IsOutgoing ()) && + !excluded.count (s.second->GetRemoteIdentity ()->GetIdentHash ()) && + ((v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V4)) || + (!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6)))) + ret.push_back (s.second); + } + if ((int)ret.size () > maxNumIntroducers) + { + // shink ret randomly + int sz = ret.size () - maxNumIntroducers; + for (int i = 0; i < sz; i++) + { + auto ind = rand () % ret.size (); + auto it = ret.begin (); + std::advance (it, ind); + ret.erase (it); + } + } + return ret; + } + + void SSU2Server::UpdateIntroducers (bool v4) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + std::list newList; + auto& introducers = v4 ? m_Introducers : m_IntroducersV6; + std::set excluded; + for (const auto& it : introducers) + { + std::shared_ptr session; + auto it1 = m_SessionsByRouterHash.find (it); + if (it1 != m_SessionsByRouterHash.end ()) + { + session = it1->second; + excluded.insert (it); + } + if (session && session->IsEstablished ()) + { + if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION) + session->SendKeepAlive (); + if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION) + newList.push_back (it); + else + session = nullptr; + } + if (!session) + i2p::context.RemoveSSU2Introducer (it, v4); + } + if (newList.size () < SSU2_MAX_NUM_INTRODUCERS) + { + auto sessions = FindIntroducers (SSU2_MAX_NUM_INTRODUCERS - newList.size (), v4, excluded); + if (sessions.empty () && !introducers.empty ()) + { + // bump creation time for previous introducers if no new sessions found + LogPrint (eLogDebug, "SSU2: No new introducers found. Trying to reuse existing"); + for (auto& it : introducers) + { + auto it1 = m_SessionsByRouterHash.find (it); + if (it1 != m_SessionsByRouterHash.end ()) + { + auto session = it1->second; + if (session->IsEstablished ()) + { + session->SetCreationTime (session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION); + if (std::find (newList.begin (), newList.end (), it) == newList.end ()) + { + newList.push_back (it); + sessions.push_back (session); + } + } + } + } + } + + for (const auto& it : sessions) + { + i2p::data::RouterInfo::Introducer introducer; + introducer.iTag = it->GetRelayTag (); + introducer.iKey = it->GetRemoteIdentity ()->GetIdentHash (); + introducer.iExp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION; + excluded.insert (it->GetRemoteIdentity ()->GetIdentHash ()); + if (i2p::context.AddSSU2Introducer (introducer, v4)) + { + LogPrint (eLogDebug, "SSU2: Introducer added ", it->GetRelayTag (), " at ", + i2p::data::GetIdentHashAbbreviation (it->GetRemoteIdentity ()->GetIdentHash ())); + newList.push_back (it->GetRemoteIdentity ()->GetIdentHash ()); + if (newList.size () >= SSU2_MAX_NUM_INTRODUCERS) break; + } + } + } + introducers = newList; + + if (introducers.size () < SSU2_MAX_NUM_INTRODUCERS) + { + for (auto i = introducers.size (); i < SSU2_MAX_NUM_INTRODUCERS; i++) + { + auto introducer = i2p::data::netdb.GetRandomSSU2Introducer (v4, excluded); + if (introducer) + { + auto address = v4 ? introducer->GetSSU2V4Address () : introducer->GetSSU2V6Address (); + if (address) + { + CreateSession (introducer, address); + excluded.insert (introducer->GetIdentHash ()); + } + } + else + { + LogPrint (eLogDebug, "SSU2: Can't find more introducers"); + break; + } + } + } + } + + void SSU2Server::ScheduleIntroducersUpdateTimer () + { + if (m_IsPublished) + { + m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL)); + m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, true)); + } + } + + void SSU2Server::RescheduleIntroducersUpdateTimer () + { + if (m_IsPublished) + { + m_IntroducersUpdateTimer.cancel (); + i2p::context.ClearSSU2Introducers (true); + m_Introducers.clear (); + m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2)); + m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, true)); + } + } + + void SSU2Server::ScheduleIntroducersUpdateTimerV6 () + { + if (m_IsPublished) + { + m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL)); + m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, false)); + } + } + + void SSU2Server::RescheduleIntroducersUpdateTimerV6 () + { + if (m_IsPublished) + { + m_IntroducersUpdateTimerV6.cancel (); + i2p::context.ClearSSU2Introducers (false); + m_IntroducersV6.clear (); + m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2)); + m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, + this, std::placeholders::_1, false)); + } + } + + void SSU2Server::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4) + { + if (ecode != boost::asio::error::operation_aborted) + { + // timeout expired + if (v4) + { + if (i2p::context.GetStatus () == eRouterStatusTesting) + { + // we still don't know if we need introducers + ScheduleIntroducersUpdateTimer (); + return; + } + if (i2p::context.GetStatus () != eRouterStatusFirewalled) + { + // we don't need introducers + i2p::context.ClearSSU2Introducers (true); + m_Introducers.clear (); + return; + } + // we are firewalled + auto addr = i2p::context.GetRouterInfo ().GetSSU2V4Address (); + if (addr && addr->ssu && addr->ssu->introducers.empty ()) + i2p::context.SetUnreachableSSU2 (true, false); // v4 + + UpdateIntroducers (true); + ScheduleIntroducersUpdateTimer (); + } + else + { + if (i2p::context.GetStatusV6 () == eRouterStatusTesting) + { + // we still don't know if we need introducers + ScheduleIntroducersUpdateTimerV6 (); + return; + } + if (i2p::context.GetStatusV6 () != eRouterStatusFirewalled) + { + // we don't need introducers + i2p::context.ClearSSU2Introducers (false); + m_IntroducersV6.clear (); + return; + } + // we are firewalled + auto addr = i2p::context.GetRouterInfo ().GetSSU2V6Address (); + if (addr && addr->ssu && addr->ssu->introducers.empty ()) + i2p::context.SetUnreachableSSU2 (false, true); // v6 + + UpdateIntroducers (false); + ScheduleIntroducersUpdateTimerV6 (); + } + } + } +} +} diff -Nru i2pd-2.39.0/libi2pd/SSU2.h i2pd-2.43.0/libi2pd/SSU2.h --- i2pd-2.39.0/libi2pd/SSU2.h 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/libi2pd/SSU2.h 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,145 @@ +/* +* Copyright (c) 2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef SSU2_H__ +#define SSU2_H__ + +#include +#include "util.h" +#include "SSU2Session.h" + +namespace i2p +{ +namespace transport +{ + const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds + const int SSU2_RESEND_CHECK_TIMEOUT = 500; // in milliseconds + const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K + const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K + const size_t SSU2_MAX_NUM_INTRODUCERS = 3; + const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour + const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes + const int SSU2_KEEP_ALIVE_INTERVAL = 30; // 30 seconds + + class SSU2Server: private i2p::util::RunnableServiceWithWork + { + struct Packet + { + uint8_t buf[SSU2_MAX_PACKET_SIZE]; + size_t len; + boost::asio::ip::udp::endpoint from; + }; + + class ReceiveService: public i2p::util::RunnableService + { + public: + + ReceiveService (const std::string& name): RunnableService (name) {}; + boost::asio::io_service& GetService () { return GetIOService (); }; + void Start () { StartIOService (); }; + void Stop () { StopIOService (); }; + }; + + public: + + SSU2Server (); + ~SSU2Server () {}; + + void Start (); + void Stop (); + boost::asio::io_service& GetService () { return GetIOService (); }; + void SetLocalAddress (const boost::asio::ip::address& localAddress); + bool IsSupported (const boost::asio::ip::address& addr) const; + uint16_t GetPort (bool v4) const; + bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; }; + + void AddSession (std::shared_ptr session); + void RemoveSession (uint64_t connID); + void AddSessionByRouterHash (std::shared_ptr session); + bool AddPendingOutgoingSession (std::shared_ptr session); + void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep); + std::shared_ptr FindSession (const i2p::data::IdentHash& ident) const; + std::shared_ptr FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const; + std::shared_ptr GetRandomSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports, + const i2p::data::IdentHash& excluded) const; + + void AddRelay (uint32_t tag, std::shared_ptr relay); + void RemoveRelay (uint32_t tag); + std::shared_ptr FindRelaySession (uint32_t tag); + + void Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen, + const boost::asio::ip::udp::endpoint& to); + void Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, + const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to); + + bool CreateSession (std::shared_ptr router, + std::shared_ptr address, bool peerTest = false); + bool StartPeerTest (std::shared_ptr router, bool v4); + + void UpdateOutgoingToken (const boost::asio::ip::udp::endpoint& ep, uint64_t token, uint32_t exp); + uint64_t FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) const; + uint64_t GetIncomingToken (const boost::asio::ip::udp::endpoint& ep); + std::pair NewIncomingToken (const boost::asio::ip::udp::endpoint& ep); + + void RescheduleIntroducersUpdateTimer (); + void RescheduleIntroducersUpdateTimerV6 (); + + i2p::util::MemoryPool& GetSentPacketsPool () { return m_SentPacketsPool; }; + + private: + + boost::asio::ip::udp::socket& OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint); + void Receive (boost::asio::ip::udp::socket& socket); + void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, + Packet * packet, boost::asio::ip::udp::socket& socket); + void HandleReceivedPacket (Packet * packet); + void HandleReceivedPackets (std::vector packets); + void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); + + void ScheduleTermination (); + void HandleTerminationTimer (const boost::system::error_code& ecode); + + void ScheduleResend (); + void HandleResendTimer (const boost::system::error_code& ecode); + + void ConnectThroughIntroducer (std::shared_ptr session); + std::list > FindIntroducers (int maxNumIntroducers, + bool v4, const std::set& excluded) const; + void UpdateIntroducers (bool v4); + void ScheduleIntroducersUpdateTimer (); + void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4); + void ScheduleIntroducersUpdateTimerV6 (); + + private: + + ReceiveService m_ReceiveService; + boost::asio::ip::udp::socket m_SocketV4, m_SocketV6; + boost::asio::ip::address m_AddressV4, m_AddressV6; + std::unordered_map > m_Sessions; + std::unordered_map > m_SessionsByRouterHash; + std::map > m_PendingOutgoingSessions; + std::map > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds) + std::map > m_Relays; // we are introducer, relay tag -> session + std::list m_Introducers, m_IntroducersV6; // introducers we are connected to + i2p::util::MemoryPoolMt m_PacketsPool; + i2p::util::MemoryPool m_SentPacketsPool; + boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer, + m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6; + std::shared_ptr m_LastSession; + bool m_IsPublished; // if we maintain introducers + bool m_IsSyncClockFromPeers; + + public: + + // for HTTP/I2PControl + const decltype(m_Sessions)& GetSSU2Sessions () const { return m_Sessions; }; + }; +} +} + +#endif diff -Nru i2pd-2.39.0/libi2pd/SSU2Session.cpp i2pd-2.43.0/libi2pd/SSU2Session.cpp --- i2pd-2.39.0/libi2pd/SSU2Session.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/libi2pd/SSU2Session.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,2669 @@ +/* +* Copyright (c) 2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include +#include "Log.h" +#include "Transports.h" +#include "Gzip.h" +#include "NetDb.hpp" +#include "SSU2.h" + +namespace i2p +{ +namespace transport +{ + void SSU2IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize) + { + if (msg->len + fragmentSize > msg->maxLen) + { + LogPrint (eLogInfo, "SSU2: I2NP message size ", msg->maxLen, " is not enough"); + auto newMsg = NewI2NPMessage (); + *newMsg = *msg; + msg = newMsg; + } + if (msg->Concat (fragment, fragmentSize) < fragmentSize) + LogPrint (eLogError, "SSU2: I2NP buffer overflow ", msg->maxLen); + nextFragmentNum++; + } + + + SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr in_RemoteRouter, + std::shared_ptr addr): + TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT), + m_Server (server), m_Address (addr), m_RemoteTransports (0), + m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown), + m_SendPacketNum (0), m_ReceivePacketNum (0), m_IsDataReceived (false), + m_WindowSize (SSU2_MIN_WINDOW_SIZE), m_RTT (SSU2_RESEND_INTERVAL), + m_RTO (SSU2_RESEND_INTERVAL*SSU2_kAPPA), m_RelayTag (0), + m_ConnectTimer (server.GetService ()), m_TerminationReason (eSSU2TerminationReasonNormalClose), + m_MaxPayloadSize (SSU2_MIN_PACKET_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - 32) // min size + { + m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState); + if (in_RemoteRouter && m_Address) + { + // outgoing + InitNoiseXKState1 (*m_NoiseState, m_Address->s); + m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port); + m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false); + RAND_bytes ((uint8_t *)&m_DestConnID, 8); + RAND_bytes ((uint8_t *)&m_SourceConnID, 8); + } + else + { + // incoming + InitNoiseXKState1 (*m_NoiseState, i2p::context.GetSSU2StaticPublicKey ()); + } + } + + SSU2Session::~SSU2Session () + { + } + + void SSU2Session::Connect () + { + if (m_State == eSSU2SessionStateUnknown || m_State == eSSU2SessionStateTokenReceived) + { + ScheduleConnectTimer (); + auto token = m_Server.FindOutgoingToken (m_RemoteEndpoint); + if (token) + SendSessionRequest (token); + else + { + m_State = eSSU2SessionStateUnknown; + SendTokenRequest (); + } + } + } + + void SSU2Session::ScheduleConnectTimer () + { + m_ConnectTimer.cancel (); + m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU2_CONNECT_TIMEOUT)); + m_ConnectTimer.async_wait (std::bind (&SSU2Session::HandleConnectTimer, + shared_from_this (), std::placeholders::_1)); + } + + void SSU2Session::HandleConnectTimer (const boost::system::error_code& ecode) + { + if (!ecode) + { + // timeout expired + LogPrint (eLogWarning, "SSU2: Session with ", m_RemoteEndpoint, " was not established after ", SSU2_CONNECT_TIMEOUT, " seconds"); + Terminate (); + } + } + + bool SSU2Session::Introduce (std::shared_ptr session, uint32_t relayTag) + { + // we are Alice + if (!session || !relayTag) return false; + // find local adddress to introduce + auto localAddress = session->FindLocalAddress (); + if (!localAddress) return false; + // create nonce + uint32_t nonce; + RAND_bytes ((uint8_t *)&nonce, 4); + auto ts = i2p::util::GetSecondsSinceEpoch (); + // payload + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = 0; + payload[0] = eSSU2BlkRelayRequest; + payload[3] = 0; // flag + htobe32buf (payload + 4, nonce); + htobe32buf (payload + 8, relayTag); + htobe32buf (payload + 12, ts); + payload[16] = 2; // ver + size_t asz = CreateEndpoint (payload + 18, m_MaxPayloadSize - 18, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port)); + if (!asz) return false; + payload[17] = asz; + payloadSize += asz + 18; + SignedData s; + s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue + s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash + s.Insert (session->GetRemoteIdentity ()->GetIdentHash (), 32); // chash + s.Insert (payload + 4, 14 + asz); // nonce, relay tag, timestamp, ver, asz and Alice's endpoint + s.Sign (i2p::context.GetPrivateKeys (), payload + payloadSize); + payloadSize += i2p::context.GetIdentity ()->GetSignatureLen (); + htobe16buf (payload + 1, payloadSize - 3); // size + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + // send + m_RelaySessions.emplace (nonce, std::make_pair (session, ts)); + session->m_SourceConnID = htobe64 (((uint64_t)nonce << 32) | nonce); + session->m_DestConnID = ~session->m_SourceConnID; + m_Server.AddSession (session); + SendData (payload, payloadSize); + + return true; + } + + void SSU2Session::WaitForIntroduction () + { + m_State = eSSU2SessionStateIntroduced; + ScheduleConnectTimer (); + } + + void SSU2Session::ConnectAfterIntroduction () + { + if (m_State == eSSU2SessionStateIntroduced) + { + // create new connID + uint64_t oldConnID = GetConnID (); + RAND_bytes ((uint8_t *)&m_DestConnID, 8); + RAND_bytes ((uint8_t *)&m_SourceConnID, 8); + // connect + m_State = eSSU2SessionStateTokenReceived; + m_Server.AddPendingOutgoingSession (shared_from_this ()); + m_Server.RemoveSession (oldConnID); + Connect (); + } + } + + void SSU2Session::SendPeerTest () + { + // we are Alice + uint32_t nonce; + RAND_bytes ((uint8_t *)&nonce, 4); + auto ts = i2p::util::GetSecondsSinceEpoch (); + // session for message 5 + auto session = std::make_shared (m_Server); + session->SetState (eSSU2SessionStatePeerTest); + m_PeerTests.emplace (nonce, std::make_pair (session, ts)); + session->m_SourceConnID = htobe64 (((uint64_t)nonce << 32) | nonce); + session->m_DestConnID = ~session->m_SourceConnID; + m_Server.AddSession (session); + // peer test block + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = CreatePeerTestBlock (payload, m_MaxPayloadSize, nonce); + if (payloadSize > 0) + { + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData (payload, payloadSize); + } + } + + void SSU2Session::SendKeepAlive () + { + if (IsEstablished ()) + { + uint8_t payload[20]; + size_t payloadSize = CreatePaddingBlock (payload, 20, 5); + SendData (payload, payloadSize); + } + } + + void SSU2Session::Terminate () + { + if (m_State != eSSU2SessionStateTerminated) + { + m_State = eSSU2SessionStateTerminated; + m_ConnectTimer.cancel (); + m_OnEstablished = nullptr; + if (m_RelayTag) + m_Server.RemoveRelay (m_RelayTag); + m_SentHandshakePacket.reset (nullptr); + m_SendQueue.clear (); + m_SentPackets.clear (); + m_IncompleteMessages.clear (); + m_RelaySessions.clear (); + m_PeerTests.clear (); + m_Server.RemoveSession (m_SourceConnID); + transports.PeerDisconnected (shared_from_this ()); + LogPrint (eLogDebug, "SSU2: Session terminated"); + } + } + + void SSU2Session::RequestTermination (SSU2TerminationReason reason) + { + if (m_State == eSSU2SessionStateEstablished || m_State == eSSU2SessionStateClosing) + { + m_TerminationReason = reason; + SendTermination (); + } + m_State = eSSU2SessionStateClosing; + } + + void SSU2Session::Established () + { + m_State = eSSU2SessionStateEstablished; + m_EphemeralKeys = nullptr; + m_NoiseState.reset (nullptr); + m_SessionConfirmedFragment.reset (nullptr); + m_SentHandshakePacket.reset (nullptr); + m_ConnectTimer.cancel (); + SetTerminationTimeout (SSU2_TERMINATION_TIMEOUT); + transports.PeerConnected (shared_from_this ()); + if (m_OnEstablished) + { + m_OnEstablished (); + m_OnEstablished = nullptr; + } + } + + void SSU2Session::Done () + { + m_Server.GetService ().post (std::bind (&SSU2Session::Terminate, shared_from_this ())); + } + + void SSU2Session::SendLocalRouterInfo (bool update) + { + if (update || !IsOutgoing ()) + { + auto s = shared_from_this (); + m_Server.GetService ().post ([s]() + { + if (!s->IsEstablished ()) return; + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = s->CreateRouterInfoBlock (payload, s->m_MaxPayloadSize - 32, i2p::context.GetSharedRouterInfo ()); + if (payloadSize) + { + if (payloadSize < s->m_MaxPayloadSize) + payloadSize += s->CreatePaddingBlock (payload + payloadSize, s->m_MaxPayloadSize - payloadSize); + s->SendData (payload, payloadSize); + } + else + s->SendFragmentedMessage (CreateDatabaseStoreMsg ()); + }); + } + + } + + void SSU2Session::SendI2NPMessages (const std::vector >& msgs) + { + m_Server.GetService ().post (std::bind (&SSU2Session::PostI2NPMessages, shared_from_this (), msgs)); + } + + void SSU2Session::PostI2NPMessages (std::vector > msgs) + { + if (m_State == eSSU2SessionStateTerminated) return; + for (auto it: msgs) + m_SendQueue.push_back (it); + SendQueue (); + + if (m_SendQueue.size () > 0) // windows is full + { + if (m_SendQueue.size () <= SSU2_MAX_OUTGOING_QUEUE_SIZE) + Resend (i2p::util::GetMillisecondsSinceEpoch ()); + else + { + LogPrint (eLogWarning, "SSU2: Outgoing messages queue size to ", + GetIdentHashBase64(), " exceeds ", SSU2_MAX_OUTGOING_QUEUE_SIZE); + RequestTermination (eSSU2TerminationReasonTimeout); + } + } + } + + bool SSU2Session::SendQueue () + { + if (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize) + { + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); + size_t ackBlockSize = CreateAckBlock (packet->payload, m_MaxPayloadSize); + bool ackBlockSent = false; + packet->payloadSize += ackBlockSize; + while (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize) + { + auto msg = m_SendQueue.front (); + size_t len = msg->GetNTCP2Length () + 3; + if (len > m_MaxPayloadSize) // message too long + { + m_SendQueue.pop_front (); + if (SendFragmentedMessage (msg)) + ackBlockSent = true; + } + else if (packet->payloadSize + len <= m_MaxPayloadSize) + { + m_SendQueue.pop_front (); + packet->payloadSize += CreateI2NPBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, std::move (msg)); + } + else + { + // create new packet and copy ack block + auto newPacket = m_Server.GetSentPacketsPool ().AcquireShared (); + memcpy (newPacket->payload, packet->payload, ackBlockSize); + newPacket->payloadSize = ackBlockSize; + // complete current packet + if (packet->payloadSize > ackBlockSize) // more than just ack block + { + ackBlockSent = true; + // try to add padding + if (packet->payloadSize + 16 < m_MaxPayloadSize) + packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); + } + else + { + // reduce ack block + if (len + 8 < m_MaxPayloadSize) + { + // keep Ack block and drop some ranges + ackBlockSent = true; + packet->payloadSize = m_MaxPayloadSize - len; + if (packet->payloadSize & 0x01) packet->payloadSize--; // make it even + htobe16buf (packet->payload + 1, packet->payloadSize - 3); // new block size + } + else // drop Ack block completely + packet->payloadSize = 0; + // msg fits single packet + m_SendQueue.pop_front (); + packet->payloadSize += CreateI2NPBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, std::move (msg)); + } + // send right a way + uint32_t packetNum = SendData (packet->payload, packet->payloadSize); + packet->sendTime = ts; + m_SentPackets.emplace (packetNum, packet); + packet = newPacket; // just ack block + } + }; + if (packet->payloadSize > ackBlockSize) + { + ackBlockSent = true; + if (packet->payloadSize + 16 < m_MaxPayloadSize) + packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); + uint32_t packetNum = SendData (packet->payload, packet->payloadSize); + packet->sendTime = ts; + m_SentPackets.emplace (packetNum, packet); + } + return ackBlockSent; + } + return false; + } + + bool SSU2Session::SendFragmentedMessage (std::shared_ptr msg) + { + size_t lastFragmentSize = (msg->GetNTCP2Length () + 3 - m_MaxPayloadSize) % (m_MaxPayloadSize - 8); + size_t extraSize = m_MaxPayloadSize - lastFragmentSize; + bool ackBlockSent = false; + uint32_t msgID; + memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); + if (extraSize >= 8) + { + packet->payloadSize = CreateAckBlock (packet->payload, extraSize); + ackBlockSent = true; + if (packet->payloadSize + 12 < m_MaxPayloadSize) + { + uint32_t packetNum = SendData (packet->payload, packet->payloadSize); + packet->sendTime = ts; + m_SentPackets.emplace (packetNum, packet); + packet = m_Server.GetSentPacketsPool ().AcquireShared (); + } + else + extraSize -= packet->payloadSize; + } + size_t offset = extraSize > 0 ? (rand () % extraSize) : 0; + if (offset + packet->payloadSize >= m_MaxPayloadSize) offset = 0; + auto size = CreateFirstFragmentBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - offset - packet->payloadSize, msg); + if (!size) return false; + extraSize -= offset; + packet->payloadSize += size; + uint32_t firstPacketNum = SendData (packet->payload, packet->payloadSize); + packet->sendTime = ts; + m_SentPackets.emplace (firstPacketNum, packet); + uint8_t fragmentNum = 0; + while (msg->offset < msg->len) + { + offset = extraSize > 0 ? (rand () % extraSize) : 0; + packet = m_Server.GetSentPacketsPool ().AcquireShared (); + packet->payloadSize = CreateFollowOnFragmentBlock (packet->payload, m_MaxPayloadSize - offset, msg, fragmentNum, msgID); + extraSize -= offset; + if (msg->offset >= msg->len && packet->payloadSize + 16 < m_MaxPayloadSize) // last fragment + packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); + uint32_t followonPacketNum = SendData (packet->payload, packet->payloadSize); + packet->sendTime = ts; + m_SentPackets.emplace (followonPacketNum, packet); + } + return ackBlockSent; + } + + void SSU2Session::Resend (uint64_t ts) + { + // resend handshake packet + if (m_SentHandshakePacket && ts >= m_SentHandshakePacket->sendTime + SSU2_HANDSHAKE_RESEND_INTERVAL) + { + LogPrint (eLogDebug, "SSU2: Resending ", (int)m_State); + ResendHandshakePacket (); + m_SentHandshakePacket->sendTime = ts; + return; + } + // resend data packets + if (m_SentPackets.empty ()) return; + std::map > resentPackets; + for (auto it = m_SentPackets.begin (); it != m_SentPackets.end (); ) + if (ts >= it->second->sendTime + it->second->numResends*m_RTO) + { + if (it->second->numResends > SSU2_MAX_NUM_RESENDS) + { + LogPrint (eLogInfo, "SSU2: Packet was not Acked after ", it->second->numResends, " attempts. Terminate session"); + m_SentPackets.clear (); + m_SendQueue.clear (); + RequestTermination (eSSU2TerminationReasonTimeout); + return; + } + else + { + uint32_t packetNum = SendData (it->second->payload, it->second->payloadSize); + it->second->numResends++; + it->second->sendTime = ts; + resentPackets.emplace (packetNum, it->second); + it = m_SentPackets.erase (it); + } + } + else + it++; + if (!resentPackets.empty ()) + { +#if (__cplusplus >= 201703L) // C++ 17 or higher + m_SentPackets.merge (resentPackets); +#else + m_SentPackets.insert (resentPackets.begin (), resentPackets.end ()); +#endif + m_WindowSize >>= 1; // /2 + if (m_WindowSize < SSU2_MIN_WINDOW_SIZE) m_WindowSize = SSU2_MIN_WINDOW_SIZE; + } + } + + void SSU2Session::ResendHandshakePacket () + { + if (m_SentHandshakePacket) + { + m_Server.Send (m_SentHandshakePacket->header.buf, 16, m_SentHandshakePacket->headerX, 48, + m_SentHandshakePacket->payload, m_SentHandshakePacket->payloadSize, m_RemoteEndpoint); + if (m_SessionConfirmedFragment && m_State == eSSU2SessionStateSessionConfirmedSent) + // resend second fragment of SessionConfirmed + m_Server.Send (m_SessionConfirmedFragment->header.buf, 16, + m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize, m_RemoteEndpoint); + } + } + + bool SSU2Session::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) + { + // we are Bob + m_SourceConnID = connID; + Header header; + header.h.connID = connID; + memcpy (header.buf + 8, buf + 8, 8); + header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); + switch (header.h.type) + { + case eSSU2SessionRequest: + ProcessSessionRequest (header, buf, len); + break; + case eSSU2TokenRequest: + ProcessTokenRequest (header, buf, len); + break; + case eSSU2PeerTest: + { + // TODO: remove later + const uint8_t nonce[12] = {0}; + uint64_t headerX[2]; + i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); + LogPrint (eLogWarning, "SSU2: Unexpected PeerTest message SourceConnID=", connID, " DestConnID=", headerX[0]); + break; + } + case eSSU2HolePunch: + LogPrint (eLogDebug, "SSU2: Late HolePunch for ", connID); + break; + default: + { + LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " from ", m_RemoteEndpoint, " of ", len, " bytes"); + return false; + } + } + return true; + } + + void SSU2Session::SendSessionRequest (uint64_t token) + { + // we are Alice + m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); + m_SentHandshakePacket.reset (new HandshakePacket); + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + m_SentHandshakePacket->sendTime = ts; + + Header& header = m_SentHandshakePacket->header; + uint8_t * headerX = m_SentHandshakePacket->headerX, + * payload = m_SentHandshakePacket->payload; + // fill packet + header.h.connID = m_DestConnID; // dest id + header.h.packetNum = 0; + header.h.type = eSSU2SessionRequest; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (headerX, &m_SourceConnID, 8); // source id + memcpy (headerX + 8, &token, 8); // token + memcpy (headerX + 16, m_EphemeralKeys->GetPublicKey (), 32); // X + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, ts/1000); + size_t payloadSize = 7; + if (GetRouterStatus () == eRouterStatusFirewalled && m_Address->IsIntroducer ()) + { + // relay tag request + payload[payloadSize] = eSSU2BlkRelayTagRequest; + memset (payload + payloadSize + 1, 0, 2); // size = 0 + payloadSize += 3; + } + payloadSize += CreatePaddingBlock (payload + payloadSize, 40 - payloadSize, 1); + // KDF for session request + m_NoiseState->MixHash ({ {header.buf, 16}, {headerX, 16} }); // h = SHA256(h || header) + m_NoiseState->MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk); + uint8_t sharedSecret[32]; + m_EphemeralKeys->Agree (m_Address->s, sharedSecret); + m_NoiseState->MixKey (sharedSecret); + // encrypt + const uint8_t nonce[12] = {0}; + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 12)); + i2p::crypto::ChaCha20 (headerX, 48, m_Address->i, nonce, headerX); + m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || encrypted payload from Session Request) for SessionCreated + m_SentHandshakePacket->payloadSize = payloadSize; + // send + if (m_State == eSSU2SessionStateTokenReceived || m_Server.AddPendingOutgoingSession (shared_from_this ())) + { + m_State = eSSU2SessionStateSessionRequestSent; + m_Server.Send (header.buf, 16, headerX, 48, payload, payloadSize, m_RemoteEndpoint); + } + else + { + LogPrint (eLogWarning, "SSU2: SessionRequest request to ", m_RemoteEndpoint, " already pending"); + Terminate (); + } + } + + void SSU2Session::ProcessSessionRequest (Header& header, uint8_t * buf, size_t len) + { + // we are Bob + const uint8_t nonce[12] = {0}; + uint8_t headerX[48]; + i2p::crypto::ChaCha20 (buf + 16, 48, i2p::context.GetSSU2IntroKey (), nonce, headerX); + memcpy (&m_DestConnID, headerX, 8); + uint64_t token; + memcpy (&token, headerX + 8, 8); + if (!token || token != m_Server.GetIncomingToken (m_RemoteEndpoint)) + { + LogPrint (eLogDebug, "SSU2: SessionRequest token mismatch. Retry"); + SendRetry (); + return; + } + // KDF for session request + m_NoiseState->MixHash ( { {header.buf, 16}, {headerX, 16} } ); // h = SHA256(h || header) + m_NoiseState->MixHash (headerX + 16, 32); // h = SHA256(h || aepk); + uint8_t sharedSecret[32]; + i2p::context.GetSSU2StaticKeys ().Agree (headerX + 16, sharedSecret); + m_NoiseState->MixKey (sharedSecret); + // decrypt + uint8_t * payload = buf + 64; + std::vector decryptedPayload(len - 80); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 80, m_NoiseState->m_H, 32, + m_NoiseState->m_CK + 32, nonce, decryptedPayload.data (), decryptedPayload.size (), false)) + { + LogPrint (eLogWarning, "SSU2: SessionRequest AEAD verification failed "); + return; + } + m_NoiseState->MixHash (payload, len - 64); // h = SHA256(h || encrypted payload from Session Request) for SessionCreated + // payload + m_State = eSSU2SessionStateSessionRequestReceived; + HandlePayload (decryptedPayload.data (), decryptedPayload.size ()); + + m_Server.AddSession (shared_from_this ()); + SendSessionCreated (headerX + 16); + } + + void SSU2Session::SendSessionCreated (const uint8_t * X) + { + // we are Bob + m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); + m_SentHandshakePacket.reset (new HandshakePacket); + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + m_SentHandshakePacket->sendTime = ts; + + uint8_t kh2[32]; + i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessCreateHeader", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessCreateHeader", 32) + // fill packet + Header& header = m_SentHandshakePacket->header; + uint8_t * headerX = m_SentHandshakePacket->headerX, + * payload = m_SentHandshakePacket->payload; + header.h.connID = m_DestConnID; // dest id + header.h.packetNum = 0; + header.h.type = eSSU2SessionCreated; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (headerX, &m_SourceConnID, 8); // source id + memset (headerX + 8, 0, 8); // token = 0 + memcpy (headerX + 16, m_EphemeralKeys->GetPublicKey (), 32); // Y + // payload + size_t maxPayloadSize = m_MaxPayloadSize - 48; + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, ts/1000); + size_t payloadSize = 7; + payloadSize += CreateAddressBlock (payload + payloadSize, maxPayloadSize - payloadSize, m_RemoteEndpoint); + if (m_RelayTag) + { + payload[payloadSize] = eSSU2BlkRelayTag; + htobe16buf (payload + payloadSize + 1, 4); + htobe32buf (payload + payloadSize + 3, m_RelayTag); + payloadSize += 7; + } + auto token = m_Server.NewIncomingToken (m_RemoteEndpoint); + if (ts + SSU2_TOKEN_EXPIRATION_THRESHOLD > token.second) // not expired? + { + payload[payloadSize] = eSSU2BlkNewToken; + htobe16buf (payload + payloadSize + 1, 12); + htobe32buf (payload + payloadSize + 3, token.second - SSU2_TOKEN_EXPIRATION_THRESHOLD); // expires + memcpy (payload + payloadSize + 7, &token.first, 8); // token + payloadSize += 15; + } + if (m_TerminationReason != eSSU2TerminationReasonNormalClose) + payloadSize += CreateTerminationBlock (payload + payloadSize, maxPayloadSize - payloadSize); + payloadSize += CreatePaddingBlock (payload + payloadSize, maxPayloadSize - payloadSize); + // KDF for SessionCreated + m_NoiseState->MixHash ( { {header.buf, 16}, {headerX, 16} } ); // h = SHA256(h || header) + m_NoiseState->MixHash (headerX + 16, 32); // h = SHA256(h || bepk); + uint8_t sharedSecret[32]; + m_EphemeralKeys->Agree (X, sharedSecret); + m_NoiseState->MixKey (sharedSecret); + // encrypt + const uint8_t nonce[12] = {0}; + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true); + payloadSize += 16; + m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || encrypted Noise payload from Session Created) + header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (kh2, payload + (payloadSize - 12)); + i2p::crypto::ChaCha20 (headerX, 48, kh2, nonce, headerX); + m_State = eSSU2SessionStateSessionCreatedSent; + m_SentHandshakePacket->payloadSize = payloadSize; + // send + m_Server.Send (header.buf, 16, headerX, 48, payload, payloadSize, m_RemoteEndpoint); + // terminate if errors + if (m_TerminationReason != eSSU2TerminationReasonNormalClose) + Terminate (); + } + + bool SSU2Session::ProcessSessionCreated (uint8_t * buf, size_t len) + { + // we are Alice + Header header; + memcpy (header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask (m_Address->i, buf + (len - 24)); + uint8_t kh2[32]; + i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessCreateHeader", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessCreateHeader", 32) + header.ll[1] ^= CreateHeaderMask (kh2, buf + (len - 12)); + if (header.h.type != eSSU2SessionCreated) + // this situation is valid, because it might be Retry with different encryption + return false; + const uint8_t nonce[12] = {0}; + uint8_t headerX[48]; + i2p::crypto::ChaCha20 (buf + 16, 48, kh2, nonce, headerX); + // KDF for SessionCreated + m_NoiseState->MixHash ( { {header.buf, 16}, {headerX, 16} } ); // h = SHA256(h || header) + m_NoiseState->MixHash (headerX + 16, 32); // h = SHA256(h || bepk); + uint8_t sharedSecret[32]; + m_EphemeralKeys->Agree (headerX + 16, sharedSecret); + m_NoiseState->MixKey (sharedSecret); + // decrypt + uint8_t * payload = buf + 64; + std::vector decryptedPayload(len - 80); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 80, m_NoiseState->m_H, 32, + m_NoiseState->m_CK + 32, nonce, decryptedPayload.data (), decryptedPayload.size (), false)) + { + LogPrint (eLogWarning, "SSU2: SessionCreated AEAD verification failed "); + return false; + } + m_NoiseState->MixHash (payload, len - 64); // h = SHA256(h || encrypted payload from SessionCreated) for SessionConfirmed + // payload + m_State = eSSU2SessionStateSessionCreatedReceived; + HandlePayload (decryptedPayload.data (), decryptedPayload.size ()); + + m_Server.AddSession (shared_from_this ()); + AdjustMaxPayloadSize (); + SendSessionConfirmed (headerX + 16); + KDFDataPhase (m_KeyDataSend, m_KeyDataReceive); + + return true; + } + + void SSU2Session::SendSessionConfirmed (const uint8_t * Y) + { + // we are Alice + m_SentHandshakePacket.reset (new HandshakePacket); + m_SentHandshakePacket->sendTime = i2p::util::GetMillisecondsSinceEpoch (); + + uint8_t kh2[32]; + i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessionConfirmed", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessionConfirmed", 32) + // fill packet + Header& header = m_SentHandshakePacket->header; + header.h.connID = m_DestConnID; // dest id + header.h.packetNum = 0; + header.h.type = eSSU2SessionConfirmed; + memset (header.h.flags, 0, 3); + header.h.flags[0] = 1; // frag, total fragments always 1 + // payload + size_t maxPayloadSize = m_MaxPayloadSize - 48; // for part 2, 48 is part1 + uint8_t * payload = m_SentHandshakePacket->payload; + size_t payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ()); + if (!payloadSize) + { + // split by two fragments + maxPayloadSize += m_MaxPayloadSize; + payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ()); + header.h.flags[0] = 0x02; // frag 0, total fragments 2 + // TODO: check if we need more fragments + } + if (payloadSize < maxPayloadSize) + payloadSize += CreatePaddingBlock (payload + payloadSize, maxPayloadSize - payloadSize); + // KDF for Session Confirmed part 1 + m_NoiseState->MixHash (header.buf, 16); // h = SHA256(h || header) + // Encrypt part 1 + uint8_t * part1 = m_SentHandshakePacket->headerX; + uint8_t nonce[12]; + CreateNonce (1, nonce); + i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetSSU2StaticPublicKey (), 32, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, part1, 48, true); + m_NoiseState->MixHash (part1, 48); // h = SHA256(h || ciphertext); + // KDF for Session Confirmed part 2 + uint8_t sharedSecret[32]; + i2p::context.GetSSU2StaticKeys ().Agree (Y, sharedSecret); + m_NoiseState->MixKey (sharedSecret); + // Encrypt part2 + memset (nonce, 0, 12); + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true); + payloadSize += 16; + m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || ciphertext); + m_SentHandshakePacket->payloadSize = payloadSize; + if (header.h.flags[0] > 1) + { + if (payloadSize > m_MaxPayloadSize - 48) + { + payloadSize = m_MaxPayloadSize - 48 - (rand () % 16); + if (m_SentHandshakePacket->payloadSize - payloadSize < 24) + payloadSize -= 24; + } + else + header.h.flags[0] = 1; + } + // Encrypt header + header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (kh2, payload + (payloadSize - 12)); + m_State = eSSU2SessionStateSessionConfirmedSent; + // send + m_Server.Send (header.buf, 16, part1, 48, payload, payloadSize, m_RemoteEndpoint); + m_SendPacketNum++; + if (m_SentHandshakePacket->payloadSize > payloadSize) + { + // send second fragment + m_SessionConfirmedFragment.reset (new HandshakePacket); + Header& header = m_SessionConfirmedFragment->header; + header.h.connID = m_DestConnID; // dest id + header.h.packetNum = 0; + header.h.type = eSSU2SessionConfirmed; + memset (header.h.flags, 0, 3); + header.h.flags[0] = 0x12; // frag 1, total fragments 2 + m_SessionConfirmedFragment->payloadSize = m_SentHandshakePacket->payloadSize - payloadSize; + memcpy (m_SessionConfirmedFragment->payload, m_SentHandshakePacket->payload + payloadSize, m_SessionConfirmedFragment->payloadSize); + m_SentHandshakePacket->payloadSize = payloadSize; + header.ll[0] ^= CreateHeaderMask (m_Address->i, m_SessionConfirmedFragment->payload + (m_SessionConfirmedFragment->payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (kh2, m_SessionConfirmedFragment->payload + (m_SessionConfirmedFragment->payloadSize - 12)); + m_Server.Send (header.buf, 16, m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize, m_RemoteEndpoint); + } + } + + bool SSU2Session::ProcessSessionConfirmed (uint8_t * buf, size_t len) + { + // we are Bob + Header header; + memcpy (header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); + uint8_t kh2[32]; + i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessionConfirmed", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessionConfirmed", 32) + header.ll[1] ^= CreateHeaderMask (kh2, buf + (len - 12)); + if (header.h.type != eSSU2SessionConfirmed) + { + LogPrint (eLogInfo, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2SessionConfirmed); + // TODO: queue up + return true; + } + // check if fragmented + if ((header.h.flags[0] & 0x0F) > 1) + { + // fragmented + if (!(header.h.flags[0] & 0xF0)) + { + // first fragment + if (!m_SessionConfirmedFragment) + { + m_SessionConfirmedFragment.reset (new HandshakePacket); + m_SessionConfirmedFragment->header = header; + memcpy (m_SessionConfirmedFragment->payload, buf + 16, len - 16); + m_SessionConfirmedFragment->payloadSize = len - 16; + return true; // wait for second fragment + } + else if (m_SessionConfirmedFragment->isSecondFragment) + { + // we have second fragment + m_SessionConfirmedFragment->header = header; + memmove (m_SessionConfirmedFragment->payload + (len - 16), m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize); + memcpy (m_SessionConfirmedFragment->payload, buf + 16, len - 16); + m_SessionConfirmedFragment->payloadSize += (len - 16); + buf = m_SessionConfirmedFragment->payload - 16; + len = m_SessionConfirmedFragment->payloadSize + 16; + } + else + return true; + } + else + { + // second fragment + if (!m_SessionConfirmedFragment) + { + // out of sequence, save it + m_SessionConfirmedFragment.reset (new HandshakePacket); + memcpy (m_SessionConfirmedFragment->payload, buf + 16, len - 16); + m_SessionConfirmedFragment->payloadSize = len - 16; + m_SessionConfirmedFragment->isSecondFragment = true; + return true; + } + header = m_SessionConfirmedFragment->header; + memcpy (m_SessionConfirmedFragment->payload + m_SessionConfirmedFragment->payloadSize, buf + 16, len - 16); + m_SessionConfirmedFragment->payloadSize += (len - 16); + buf = m_SessionConfirmedFragment->payload - 16; + len = m_SessionConfirmedFragment->payloadSize + 16; + } + } + // KDF for Session Confirmed part 1 + m_NoiseState->MixHash (header.buf, 16); // h = SHA256(h || header) + // decrypt part1 + uint8_t nonce[12]; + CreateNonce (1, nonce); + uint8_t S[32]; + if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 16, 32, m_NoiseState->m_H, 32, + m_NoiseState->m_CK + 32, nonce, S, 32, false)) + { + LogPrint (eLogWarning, "SSU2: SessionConfirmed part 1 AEAD verification failed "); + return false; + } + m_NoiseState->MixHash (buf + 16, 48); // h = SHA256(h || ciphertext); + // KDF for Session Confirmed part 2 and data phase + uint8_t sharedSecret[32]; + m_EphemeralKeys->Agree (S, sharedSecret); + m_NoiseState->MixKey (sharedSecret); + KDFDataPhase (m_KeyDataReceive, m_KeyDataSend); + // decrypt part2 + memset (nonce, 0, 12); + uint8_t * payload = buf + 64; + std::vector decryptedPayload(len - 80); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 80, m_NoiseState->m_H, 32, + m_NoiseState->m_CK + 32, nonce, decryptedPayload.data (), decryptedPayload.size (), false)) + { + LogPrint (eLogWarning, "SSU2: SessionConfirmed part 2 AEAD verification failed "); + return false; + } + m_NoiseState->MixHash (payload, len - 64); // h = SHA256(h || ciphertext); + // payload + // handle RouterInfo block that must be first + if (decryptedPayload[0] != eSSU2BlkRouterInfo) + { + LogPrint (eLogError, "SSU2: SessionConfirmed unexpected first block type ", (int)decryptedPayload[0]); + return false; + } + size_t riSize = bufbe16toh (decryptedPayload.data () + 1); + if (riSize + 3 > decryptedPayload.size ()) + { + LogPrint (eLogError, "SSU2: SessionConfirmed RouterInfo block is too long ", riSize); + return false; + } + LogPrint (eLogDebug, "SSU2: RouterInfo in SessionConfirmed"); + auto ri = ExtractRouterInfo (decryptedPayload.data () + 3, riSize); + if (!ri) + { + LogPrint (eLogError, "SSU2: SessionConfirmed malformed RouterInfo block"); + return false; + } + m_Address = ri->GetSSU2AddressWithStaticKey (S, m_RemoteEndpoint.address ().is_v6 ()); + if (!m_Address) + { + LogPrint (eLogError, "SSU2: No SSU2 address with static key found in SessionConfirmed from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ())); + return false; + } + // update RouterInfo in netdb + ri = i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // ri points to one from netdb now + if (!ri) + { + LogPrint (eLogError, "SSU2: Couldn't update RouterInfo from SessionConfirmed in netdb"); + return false; + } + SetRemoteIdentity (ri->GetRouterIdentity ()); + AdjustMaxPayloadSize (); + m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now + m_RemoteTransports = ri->GetCompatibleTransports (false); + // handle other blocks + HandlePayload (decryptedPayload.data () + riSize + 3, decryptedPayload.size () - riSize - 3); + Established (); + + SendQuickAck (); + + return true; + } + + void SSU2Session::KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba) + { + uint8_t keydata[64]; + i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) + // ab + i2p::crypto::HKDF (keydata, nullptr, 0, "HKDFSSU2DataKeys", keydata_ab); // keydata_ab = HKDF(keydata, ZEROLEN, "HKDFSSU2DataKeys", 64) + // ba + i2p::crypto::HKDF (keydata + 32, nullptr, 0, "HKDFSSU2DataKeys", keydata_ba); // keydata_ba = HKDF(keydata + 32, ZEROLEN, "HKDFSSU2DataKeys", 64) + } + + void SSU2Session::SendTokenRequest () + { + // we are Alice + Header header; + uint8_t h[32], payload[41]; + // fill packet + header.h.connID = m_DestConnID; // dest id + RAND_bytes (header.buf + 8, 4); // random packet num + header.h.type = eSSU2TokenRequest; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (h, header.buf, 16); + memcpy (h + 16, &m_SourceConnID, 8); // source id + memset (h + 24, 0, 8); // zero token + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); + size_t payloadSize = 7; + payloadSize += CreatePaddingBlock (payload + payloadSize, 25 - payloadSize, 1); + // encrypt + uint8_t nonce[12]; + CreateNonce (be32toh (header.h.packetNum), nonce); + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, m_Address->i, nonce, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 12)); + memset (nonce, 0, 12); + i2p::crypto::ChaCha20 (h + 16, 16, m_Address->i, nonce, h + 16); + // send + if (m_Server.AddPendingOutgoingSession (shared_from_this ())) + m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); + else + { + LogPrint (eLogWarning, "SSU2: TokenRequest request to ", m_RemoteEndpoint, " already pending"); + Terminate (); + } + } + + void SSU2Session::ProcessTokenRequest (Header& header, uint8_t * buf, size_t len) + { + // we are Bob + if (len < 48) + { + LogPrint (eLogWarning, "SSU2: Incorrect TokenRequest len ", len); + return; + } + uint8_t nonce[12] = {0}; + uint8_t h[32]; + memcpy (h, header.buf, 16); + i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); + memcpy (&m_DestConnID, h + 16, 8); + // decrypt + CreateNonce (be32toh (header.h.packetNum), nonce); + uint8_t * payload = buf + 32; + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, + i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) + { + LogPrint (eLogWarning, "SSU2: TokenRequest AEAD verification failed "); + return; + } + // payload + HandlePayload (payload, len - 48); + SendRetry (); + } + + void SSU2Session::SendRetry () + { + // we are Bob + Header header; + uint8_t h[32], payload[64]; + // fill packet + header.h.connID = m_DestConnID; // dest id + RAND_bytes (header.buf + 8, 4); // random packet num + header.h.type = eSSU2Retry; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (h, header.buf, 16); + memcpy (h + 16, &m_SourceConnID, 8); // source id + uint64_t token = m_Server.GetIncomingToken (m_RemoteEndpoint); + memcpy (h + 24, &token, 8); // token + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); + size_t payloadSize = 7; + payloadSize += CreateAddressBlock (payload + payloadSize, 64 - payloadSize, m_RemoteEndpoint); + payloadSize += CreatePaddingBlock (payload + payloadSize, 64 - payloadSize); + // encrypt + uint8_t nonce[12]; + CreateNonce (be32toh (header.h.packetNum), nonce); + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, i2p::context.GetSSU2IntroKey (), nonce, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 12)); + memset (nonce, 0, 12); + i2p::crypto::ChaCha20 (h + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); + // send + m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); + } + + bool SSU2Session::ProcessRetry (uint8_t * buf, size_t len) + { + // we are Alice + Header header; + memcpy (header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask (m_Address->i, buf + (len - 24)); + header.ll[1] ^= CreateHeaderMask (m_Address->i, buf + (len - 12)); + if (header.h.type != eSSU2Retry) + { + LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2Retry); + return false; + } + uint8_t nonce[12] = {0}; + uint64_t headerX[2]; // sourceConnID, token + i2p::crypto::ChaCha20 (buf + 16, 16, m_Address->i, nonce, (uint8_t *)headerX); + m_Server.UpdateOutgoingToken (m_RemoteEndpoint, headerX[1], i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); + // decrypt and handle payload + uint8_t * payload = buf + 32; + CreateNonce (be32toh (header.h.packetNum), nonce); + uint8_t h[32]; + memcpy (h, header.buf, 16); + memcpy (h + 16, &headerX, 16); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, + m_Address->i, nonce, payload, len - 48, false)) + { + LogPrint (eLogWarning, "SSU2: Retry AEAD verification failed "); + return false; + } + HandlePayload (payload, len - 48); + + m_State = eSSU2SessionStateTokenReceived; + InitNoiseXKState1 (*m_NoiseState, m_Address->s); // reset Noise TODO: check state + SendSessionRequest (headerX[1]); + return true; + } + + void SSU2Session::SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, + const uint8_t * introKey, uint64_t token) + { + // we are Charlie + LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep); + Header header; + uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; + // fill packet + header.h.connID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id + RAND_bytes (header.buf + 8, 4); // random packet num + header.h.type = eSSU2HolePunch; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (h, header.buf, 16); + uint64_t c = ~header.h.connID; + memcpy (h + 16, &c, 8); // source id + RAND_bytes (h + 24, 8); // token + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); + size_t payloadSize = 7; + payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, ep); + payloadSize += CreateRelayResponseBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, + eSSU2RelayResponseCodeAccept, nonce, token, ep.address ().is_v4 ()); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + // encrypt + uint8_t n[12]; + CreateNonce (be32toh (header.h.packetNum), n); + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask (introKey, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (introKey, payload + (payloadSize - 12)); + memset (n, 0, 12); + i2p::crypto::ChaCha20 (h + 16, 16, introKey, n, h + 16); + // send + m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep); + } + + bool SSU2Session::ProcessHolePunch (uint8_t * buf, size_t len) + { + // we are Alice + LogPrint (eLogDebug, "SSU2: HolePunch"); + Header header; + memcpy (header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); + header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); + if (header.h.type != eSSU2HolePunch) + { + LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2HolePunch); + return false; + } + uint8_t nonce[12] = {0}; + uint64_t headerX[2]; // sourceConnID, token + i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); + m_DestConnID = headerX[0]; + // decrypt and handle payload + uint8_t * payload = buf + 32; + CreateNonce (be32toh (header.h.packetNum), nonce); + uint8_t h[32]; + memcpy (h, header.buf, 16); + memcpy (h + 16, &headerX, 16); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, + i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) + { + LogPrint (eLogWarning, "SSU2: HolePunch AEAD verification failed "); + return false; + } + HandlePayload (payload, len - 48); + // connect to Charlie + ConnectAfterIntroduction (); + + return true; + } + + void SSU2Session::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey) + { + Header header; + uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; + // fill packet + header.h.connID = m_DestConnID; // dest id + RAND_bytes (header.buf + 8, 4); // random packet num + header.h.type = eSSU2PeerTest; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (h, header.buf, 16); + memcpy (h + 16, &m_SourceConnID, 8); // source id + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, i2p::util::GetSecondsSinceEpoch ()); + size_t payloadSize = 7; + if (msg == 6 || msg == 7) + payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, m_RemoteEndpoint); + payloadSize += CreatePeerTestBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, + msg, eSSU2PeerTestCodeAccept, nullptr, signedData, signedDataLen); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + // encrypt + uint8_t n[12]; + CreateNonce (be32toh (header.h.packetNum), n); + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask (introKey, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (introKey, payload + (payloadSize - 12)); + memset (n, 0, 12); + i2p::crypto::ChaCha20 (h + 16, 16, introKey, n, h + 16); + // send + m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); + } + + bool SSU2Session::ProcessPeerTest (uint8_t * buf, size_t len) + { + // we are Alice or Charlie + Header header; + memcpy (header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); + header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); + if (header.h.type != eSSU2PeerTest) + { + LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest); + return false; + } + uint8_t nonce[12] = {0}; + uint64_t headerX[2]; // sourceConnID, token + i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); + m_DestConnID = headerX[0]; + // decrypt and handle payload + uint8_t * payload = buf + 32; + CreateNonce (be32toh (header.h.packetNum), nonce); + uint8_t h[32]; + memcpy (h, header.buf, 16); + memcpy (h + 16, &headerX, 16); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, + i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) + { + LogPrint (eLogWarning, "SSU2: PeerTest AEAD verification failed "); + return false; + } + HandlePayload (payload, len - 48); + return true; + } + + uint32_t SSU2Session::SendData (const uint8_t * buf, size_t len) + { + if (len < 8) + { + LogPrint (eLogWarning, "SSU2: Data message payload is too short ", (int)len); + return 0; + } + Header header; + header.h.connID = m_DestConnID; + header.h.packetNum = htobe32 (m_SendPacketNum); + header.h.type = eSSU2Data; + memset (header.h.flags, 0, 3); + uint8_t nonce[12]; + CreateNonce (m_SendPacketNum, nonce); + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + i2p::crypto::AEADChaCha20Poly1305 (buf, len, header.buf, 16, m_KeyDataSend, nonce, payload, SSU2_MAX_PACKET_SIZE, true); + header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (len - 8)); + header.ll[1] ^= CreateHeaderMask (m_KeyDataSend + 32, payload + (len + 4)); + m_Server.Send (header.buf, 16, payload, len + 16, m_RemoteEndpoint); + m_SendPacketNum++; + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + m_NumSentBytes += len + 32; + return m_SendPacketNum - 1; + } + + void SSU2Session::ProcessData (uint8_t * buf, size_t len) + { + Header header; + header.ll[0] = m_SourceConnID; + memcpy (header.buf + 8, buf + 8, 8); + header.ll[1] ^= CreateHeaderMask (m_KeyDataReceive + 32, buf + (len - 12)); + if (header.h.type != eSSU2Data) + { + LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2Data); + if (IsEstablished ()) + SendQuickAck (); // in case it was SessionConfirmed + else + ResendHandshakePacket (); // assume we receive + return; + } + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = len - 32; + uint32_t packetNum = be32toh (header.h.packetNum); + uint8_t nonce[12]; + CreateNonce (packetNum, nonce); + if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 16, payloadSize, header.buf, 16, + m_KeyDataReceive, nonce, payload, payloadSize, false)) + { + LogPrint (eLogWarning, "SSU2: Data AEAD verification failed "); + return; + } + m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); + m_NumReceivedBytes += len; + if (!packetNum || UpdateReceivePacketNum (packetNum)) + HandlePayload (payload, payloadSize); + } + + void SSU2Session::HandlePayload (const uint8_t * buf, size_t len) + { + size_t offset = 0; + while (offset < len) + { + uint8_t blk = buf[offset]; + offset++; + auto size = bufbe16toh (buf + offset); + offset += 2; + LogPrint (eLogDebug, "SSU2: Block type ", (int)blk, " of size ", size); + if (size > len) + { + LogPrint (eLogError, "SSU2: Unexpected block length ", size); + break; + } + switch (blk) + { + case eSSU2BlkDateTime: + LogPrint (eLogDebug, "SSU2: Datetime"); + HandleDateTime (buf + offset, size); + break; + case eSSU2BlkOptions: + LogPrint (eLogDebug, "SSU2: Options"); + break; + case eSSU2BlkRouterInfo: + { + // not from SessionConfirmed, we must add it instantly to use in next block + LogPrint (eLogDebug, "SSU2: RouterInfo"); + auto ri = ExtractRouterInfo (buf + offset, size); + if (ri) + i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // TODO: add ri + break; + } + case eSSU2BlkI2NPMessage: + { + LogPrint (eLogDebug, "SSU2: I2NP message"); + auto nextMsg = (buf[offset] == eI2NPTunnelData) ? NewI2NPTunnelMessage (true) : NewI2NPShortMessage (); + nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header + memcpy (nextMsg->GetNTCP2Header (), buf + offset, size); + nextMsg->FromNTCP2 (); // SSU2 has the same format as NTCP2 + m_Handler.PutNextMessage (std::move (nextMsg)); + m_IsDataReceived = true; + break; + } + case eSSU2BlkFirstFragment: + LogPrint (eLogDebug, "SSU2: First fragment"); + HandleFirstFragment (buf + offset, size); + m_IsDataReceived = true; + break; + case eSSU2BlkFollowOnFragment: + LogPrint (eLogDebug, "SSU2: Follow-on fragment"); + HandleFollowOnFragment (buf + offset, size); + m_IsDataReceived = true; + break; + case eSSU2BlkTermination: + LogPrint (eLogDebug, "SSU2: Termination reason=", (int)buf[11]); + if (IsEstablished () && buf[11] != eSSU2TerminationReasonTerminationReceived) + RequestTermination (eSSU2TerminationReasonTerminationReceived); + else + Done (); + break; + case eSSU2BlkRelayRequest: + LogPrint (eLogDebug, "SSU2: RelayRequest"); + HandleRelayRequest (buf + offset, size); + break; + case eSSU2BlkRelayResponse: + LogPrint (eLogDebug, "SSU2: RelayResponse"); + HandleRelayResponse (buf + offset, size); + break; + case eSSU2BlkRelayIntro: + LogPrint (eLogDebug, "SSU2: RelayIntro"); + HandleRelayIntro (buf + offset, size); + break; + case eSSU2BlkPeerTest: + LogPrint (eLogDebug, "SSU2: PeerTest msg=", (int)buf[offset], " code=", (int)buf[offset+1]); + HandlePeerTest (buf + offset, size); + if (buf[offset] < 5) + m_IsDataReceived = true; + break; + case eSSU2BlkNextNonce: + break; + case eSSU2BlkAck: + LogPrint (eLogDebug, "SSU2: Ack"); + HandleAck (buf + offset, size); + break; + case eSSU2BlkAddress: + LogPrint (eLogDebug, "SSU2: Address"); + HandleAddress (buf + offset, size); + break; + case eSSU2BlkIntroKey: + break; + case eSSU2BlkRelayTagRequest: + LogPrint (eLogDebug, "SSU2: RelayTagRequest"); + if (!m_RelayTag) + { + RAND_bytes ((uint8_t *)&m_RelayTag, 4); + m_Server.AddRelay (m_RelayTag, shared_from_this ()); + } + break; + case eSSU2BlkRelayTag: + LogPrint (eLogDebug, "SSU2: RelayTag"); + m_RelayTag = bufbe32toh (buf + offset); + break; + case eSSU2BlkNewToken: + { + LogPrint (eLogDebug, "SSU2: New token"); + uint64_t token; + memcpy (&token, buf + offset + 4, 8); + m_Server.UpdateOutgoingToken (m_RemoteEndpoint, token, bufbe32toh (buf + offset)); + break; + } + case eSSU2BlkPathChallenge: + LogPrint (eLogDebug, "SSU2: Path challenge"); + SendPathResponse (buf + offset, size); + break; + case eSSU2BlkPathResponse: + LogPrint (eLogDebug, "SSU2: Path response"); + break; + case eSSU2BlkFirstPacketNumber: + break; + case eSSU2BlkPadding: + LogPrint (eLogDebug, "SSU2: Padding"); + break; + default: + LogPrint (eLogWarning, "SSU2: Unknown block type ", (int)blk); + } + offset += size; + } + } + + void SSU2Session::HandleDateTime (const uint8_t * buf, size_t len) + { + int64_t offset = (int64_t)i2p::util::GetSecondsSinceEpoch () - (int64_t)bufbe32toh (buf); + switch (m_State) + { + case eSSU2SessionStateSessionRequestReceived: + if (std::abs (offset) > SSU2_CLOCK_SKEW) + m_TerminationReason = eSSU2TerminationReasonClockSkew; + break; + case eSSU2SessionStateSessionCreatedReceived: + if ((m_RemoteEndpoint.address ().is_v4 () && i2p::context.GetStatus () == eRouterStatusTesting) || + (m_RemoteEndpoint.address ().is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusTesting)) + { + if (m_Server.IsSyncClockFromPeers ()) + { + if (std::abs (offset) > SSU2_CLOCK_THRESHOLD) + { + LogPrint (eLogWarning, "SSU2: Clock adjusted by ", -offset, " seconds"); + i2p::util::AdjustTimeOffset (-offset); + } + } + else if (std::abs (offset) > SSU2_CLOCK_SKEW) + { + LogPrint (eLogError, "SSU2: Clock skew detected ", offset, ". Check your clock"); + i2p::context.SetError (eRouterErrorClockSkew); + } + } + break; + default: ; + }; + } + + void SSU2Session::HandleAck (const uint8_t * buf, size_t len) + { + if (m_State == eSSU2SessionStateSessionConfirmedSent) + { + Established (); + return; + } + if (m_SentPackets.empty ()) return; + if (len < 5) return; + // acnt + uint32_t ackThrough = bufbe32toh (buf); + uint32_t firstPacketNum = ackThrough > buf[4] ? ackThrough - buf[4] : 0; + HandleAckRange (firstPacketNum, ackThrough, i2p::util::GetMillisecondsSinceEpoch ()); // acnt + // ranges + len -= 5; + const uint8_t * ranges = buf + 5; + while (len > 0 && firstPacketNum) + { + uint32_t lastPacketNum = firstPacketNum - 1; + if (*ranges > lastPacketNum) break; + lastPacketNum -= *ranges; ranges++; // nacks + if (*ranges > lastPacketNum + 1) break; + firstPacketNum = lastPacketNum - *ranges + 1; ranges++; // acks + len -= 2; + HandleAckRange (firstPacketNum, lastPacketNum, 0); + } + } + + void SSU2Session::HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts) + { + if (firstPacketNum > lastPacketNum) return; + auto it = m_SentPackets.begin (); + while (it != m_SentPackets.end () && it->first < firstPacketNum) it++; // find first acked packet + if (it == m_SentPackets.end () || it->first > lastPacketNum) return; // not found + auto it1 = it; + int numPackets = 0; + while (it1 != m_SentPackets.end () && it1->first <= lastPacketNum) + { + if (ts && !it1->second->numResends) + { + if (ts > it1->second->sendTime) + { + auto rtt = ts - it1->second->sendTime; + m_RTT = (m_RTT*m_SendPacketNum + rtt)/(m_SendPacketNum + 1); + m_RTO = m_RTT*SSU2_kAPPA; + if (m_RTO < SSU2_MIN_RTO) m_RTO = SSU2_MIN_RTO; + if (m_RTO > SSU2_MAX_RTO) m_RTO = SSU2_MAX_RTO; + } + ts = 0; // update RTT one time per range + } + it1++; + numPackets++; + } + m_SentPackets.erase (it, it1); + if (numPackets > 0) + { + m_WindowSize += numPackets; + if (m_WindowSize > SSU2_MAX_WINDOW_SIZE) m_WindowSize = SSU2_MAX_WINDOW_SIZE; + } + } + + void SSU2Session::HandleAddress (const uint8_t * buf, size_t len) + { + boost::asio::ip::udp::endpoint ep; + if (ExtractEndpoint (buf, len, ep)) + { + LogPrint (eLogInfo, "SSU2: Our external address is ", ep); + if (!i2p::util::net::IsInReservedRange (ep.address ())) + { + i2p::context.UpdateAddress (ep.address ()); + // check our port + bool isV4 = ep.address ().is_v4 (); + if (ep.port () != m_Server.GetPort (isV4)) + { + if (isV4) + { + if (i2p::context.GetStatus () == eRouterStatusTesting) + i2p::context.SetError (eRouterErrorSymmetricNAT); + } + else + { + if (i2p::context.GetStatusV6 () == eRouterStatusTesting) + i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT); + } + } + else + { + if (isV4) + { + if (i2p::context.GetStatus () == eRouterStatusError && i2p::context.GetError () == eRouterErrorSymmetricNAT) + i2p::context.SetStatus (eRouterStatusTesting); + } + else + { + if (i2p::context.GetStatusV6 () == eRouterStatusError && i2p::context.GetErrorV6 () == eRouterErrorSymmetricNAT) + i2p::context.SetStatusV6 (eRouterStatusTesting); + } + } + } + } + } + + void SSU2Session::HandleFirstFragment (const uint8_t * buf, size_t len) + { + uint32_t msgID; memcpy (&msgID, buf + 1, 4); + auto msg = NewI2NPShortMessage (); + // same format as I2NP message block + msg->len = msg->offset + len + 7; + memcpy (msg->GetNTCP2Header (), buf, len); + std::shared_ptr m; + bool found = false; + auto it = m_IncompleteMessages.find (msgID); + if (it != m_IncompleteMessages.end ()) + { + found = true; + m = it->second; + } + else + { + m = std::make_shared(); + m_IncompleteMessages.emplace (msgID, m); + } + m->msg = msg; + m->nextFragmentNum = 1; + m->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); + if (found && ConcatOutOfSequenceFragments (m)) + { + // we have all follow-on fragments already + m->msg->FromNTCP2 (); + m_Handler.PutNextMessage (std::move (m->msg)); + m_IncompleteMessages.erase (it); + } + } + + void SSU2Session::HandleFollowOnFragment (const uint8_t * buf, size_t len) + { + if (len < 5) return; + uint8_t fragmentNum = buf[0] >> 1; + bool isLast = buf[0] & 0x01; + uint32_t msgID; memcpy (&msgID, buf + 1, 4); + auto it = m_IncompleteMessages.find (msgID); + if (it != m_IncompleteMessages.end ()) + { + if (it->second->nextFragmentNum == fragmentNum && fragmentNum < SSU2_MAX_NUM_FRAGMENTS && + it->second->msg) + { + // in sequence + it->second->AttachNextFragment (buf + 5, len - 5); + if (isLast) + { + it->second->msg->FromNTCP2 (); + m_Handler.PutNextMessage (std::move (it->second->msg)); + m_IncompleteMessages.erase (it); + } + else + { + if (ConcatOutOfSequenceFragments (it->second)) + { + m_Handler.PutNextMessage (std::move (it->second->msg)); + m_IncompleteMessages.erase (it); + } + else + it->second->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); + } + return; + } + } + else + { + // follow-on fragment before first fragment + auto msg = std::make_shared (); + msg->nextFragmentNum = 0; + it = m_IncompleteMessages.emplace (msgID, msg).first; + } + // insert out of sequence fragment + if (fragmentNum >= SSU2_MAX_NUM_FRAGMENTS) + { + LogPrint (eLogWarning, "SSU2: Fragment number ", fragmentNum, " exceeds ", SSU2_MAX_NUM_FRAGMENTS); + return; + } + auto fragment = std::make_shared (); + memcpy (fragment->buf, buf + 5, len -5); + fragment->len = len - 5; + fragment->isLast = isLast; + it->second->outOfSequenceFragments.emplace (fragmentNum, fragment); + it->second->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); + } + + bool SSU2Session::ConcatOutOfSequenceFragments (std::shared_ptr m) + { + if (!m) return false; + bool isLast = false; + for (auto it = m->outOfSequenceFragments.begin (); it != m->outOfSequenceFragments.end ();) + if (it->first == m->nextFragmentNum) + { + m->AttachNextFragment (it->second->buf, it->second->len); + isLast = it->second->isLast; + it = m->outOfSequenceFragments.erase (it); + } + else + break; + return isLast; + } + + void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len) + { + // we are Bob + uint32_t relayTag = bufbe32toh (buf + 5); // relay tag + auto session = m_Server.FindRelaySession (relayTag); + if (!session) + { + LogPrint (eLogWarning, "SSU2: RelayRequest session with relay tag ", relayTag, " not found"); + // send relay response back to Alice + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = CreateRelayResponseBlock (payload, m_MaxPayloadSize, + eSSU2RelayResponseCodeBobRelayTagNotFound, bufbe32toh (buf + 1), 0, false); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData (payload, payloadSize); + return; + } + session->m_RelaySessions.emplace (bufbe32toh (buf + 1), // nonce + std::make_pair (shared_from_this (), i2p::util::GetSecondsSinceEpoch ()) ); + + // send relay intro to Charlie + auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI + if (r) + i2p::data::netdb.PopulateRouterInfoBuffer (r); + else + LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found"); + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = r ? CreateRouterInfoBlock (payload, m_MaxPayloadSize - len - 32, r) : 0; + if (!payloadSize && r) + session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); + payloadSize += CreateRelayIntroBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, buf + 1, len -1); + if (payloadSize < m_MaxPayloadSize) + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + session->SendData (payload, payloadSize); + } + + void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len) + { + // we are Charlie + SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept; + uint64_t token = 0; + bool isV4 = false; + auto r = i2p::data::netdb.FindRouter (buf + 1); // Alice + if (r) + { + SignedData s; + s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue + s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash + s.Insert (i2p::context.GetIdentHash (), 32); // chash + s.Insert (buf + 33, 14); // nonce, relay tag, timestamp, ver, asz + uint8_t asz = buf[46]; + s.Insert (buf + 47, asz); // Alice Port, Alice IP + if (s.Verify (r->GetIdentity (), buf + 47 + asz)) + { + // send HolePunch + boost::asio::ip::udp::endpoint ep; + if (ExtractEndpoint (buf + 47, asz, ep)) + { + auto addr = ep.address ().is_v6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); + if (addr) + { + if (m_Server.IsSupported (ep.address ())) + { + token = m_Server.GetIncomingToken (ep); + isV4 = ep.address ().is_v4 (); + SendHolePunch (bufbe32toh (buf + 33), ep, addr->i, token); + } + else + { + LogPrint (eLogWarning, "SSU2: RelayIntro unsupported address"); + code = eSSU2RelayResponseCodeCharlieUnsupportedAddress; + } + } + else + { + LogPrint (eLogWarning, "SSU2: RelayIntro unknown address"); + code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; + } + } + else + { + LogPrint (eLogWarning, "SSU2: RelayIntro can't extract endpoint"); + code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; + } + } + else + { + LogPrint (eLogWarning, "SSU2: RelayIntro signature verification failed"); + code = eSSU2RelayResponseCodeCharlieSignatureFailure; + } + } + else + { + LogPrint (eLogError, "SSU2: RelayIntro unknown router to introduce"); + code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; + } + // send relay response to Bob + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = CreateRelayResponseBlock (payload, m_MaxPayloadSize, + code, bufbe32toh (buf + 33), token, isV4); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData (payload, payloadSize); + } + + void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len) + { + uint32_t nonce = bufbe32toh (buf + 2); + if (m_State == eSSU2SessionStateIntroduced) + { + // HolePunch from Charlie + // TODO: verify address and signature + // verify nonce + if (~htobe64 (((uint64_t)nonce << 32) | nonce) != m_DestConnID) + LogPrint (eLogWarning, "SSU2: Relay response nonce mismatch ", nonce, " connID=", m_DestConnID); + if (len >= 8) + { + // new token + uint64_t token; + memcpy (&token, buf + len - 8, 8); + m_Server.UpdateOutgoingToken (m_RemoteEndpoint, token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); + } + return; + } + auto it = m_RelaySessions.find (nonce); + if (it != m_RelaySessions.end ()) + { + if (it->second.first && it->second.first->IsEstablished ()) + { + // we are Bob, message from Charlie + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + payload[0] = eSSU2BlkRelayResponse; + htobe16buf (payload + 1, len); + memcpy (payload + 3, buf, len); // forward to Alice as is + size_t payloadSize = len + 3; + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + it->second.first->SendData (payload, payloadSize); + } + else + { + // we are Alice, message from Bob + if (!buf[1]) // status code accepted? + { + // verify signature + uint8_t csz = buf[11]; + SignedData s; + s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue + s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash + s.Insert (buf + 2, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint + if (s.Verify (it->second.first->GetRemoteIdentity (), buf + 12 + csz)) + { + if (it->second.first->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet + { + // update Charlie's endpoint + if (ExtractEndpoint (buf + 12, csz, it->second.first->m_RemoteEndpoint)) + { + // update token + uint64_t token; + memcpy (&token, buf + len - 8, 8); + m_Server.UpdateOutgoingToken (it->second.first->m_RemoteEndpoint, + token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); + // connect to Charlie, HolePunch will be ignored + it->second.first->ConnectAfterIntroduction (); + } + else + LogPrint (eLogWarning, "SSU2: RelayResponse can't extract endpoint"); + } + } + else + { + LogPrint (eLogWarning, "SSU2: RelayResponse signature verification failed"); + it->second.first->Done (); + } + } + else + { + LogPrint (eLogInfo, "SSU2: RelayResponse status code=", (int)buf[1]); + it->second.first->Done (); + } + } + m_RelaySessions.erase (it); + } + else + LogPrint (eLogWarning, "SSU2: RelayResponse unknown nonce ", bufbe32toh (buf + 2)); + } + + void SSU2Session::HandlePeerTest (const uint8_t * buf, size_t len) + { + if (len < 3) return; + uint8_t msg = buf[0]; + size_t offset = 3; // points to signed data + if (msg == 2 || msg == 4) offset += 32; // hash is presented for msg 2 and 4 only + if (len < offset + 5) return; + uint32_t nonce = bufbe32toh (buf + offset + 1); + switch (msg) // msg + { + case 1: // Bob from Alice + { + auto session = m_Server.GetRandomSession ((buf[12] == 6) ? i2p::data::RouterInfo::eSSU2V4 : i2p::data::RouterInfo::eSSU2V6, + GetRemoteIdentity ()->GetIdentHash ()); + if (session) // session with Charlie + { + session->m_PeerTests.emplace (nonce, std::make_pair (shared_from_this (), i2p::util::GetSecondsSinceEpoch ())); + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + // Alice's RouterInfo + auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); + if (r) i2p::data::netdb.PopulateRouterInfoBuffer (r); + size_t payloadSize = r ? CreateRouterInfoBlock (payload, m_MaxPayloadSize - len - 32, r) : 0; + if (!payloadSize && r) + session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); + if (payloadSize + len + 48 > m_MaxPayloadSize) + { + // doesn't fit one message, send RouterInfo in separate message + session->SendData (payload, payloadSize); + payloadSize = 0; + } + // PeerTest to Charlie + payloadSize += CreatePeerTestBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, 2, + eSSU2PeerTestCodeAccept, GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + session->SendData (payload, payloadSize); + } + else + { + // Charlie not found, send error back to Alice + uint8_t payload[SSU2_MAX_PACKET_SIZE], zeroHash[32] = {0}; + size_t payloadSize = CreatePeerTestBlock (payload, m_MaxPayloadSize, 4, + eSSU2PeerTestCodeBobNoCharlieAvailable, zeroHash, buf + offset, len - offset); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData (payload, payloadSize); + } + break; + } + case 2: // Charlie from Bob + { + // sign with Charlie's key + uint8_t asz = buf[offset + 9]; + std::vector newSignedData (asz + 10 + i2p::context.GetIdentity ()->GetSignatureLen ()); + memcpy (newSignedData.data (), buf + offset, asz + 10); + SignedData s; + s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue + s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash + s.Insert (buf + 3, 32); // ahash + s.Insert (newSignedData.data (), asz + 10); // ver, nonce, ts, asz, Alice's endpoint + s.Sign (i2p::context.GetPrivateKeys (), newSignedData.data () + 10 + asz); + // send response (msg 3) back and msg 5 if accepted + SSU2PeerTestCode code = eSSU2PeerTestCodeAccept; + auto r = i2p::data::netdb.FindRouter (buf + 3); // find Alice + if (r) + { + size_t signatureLen = r->GetIdentity ()->GetSignatureLen (); + if (len >= offset + asz + 10 + signatureLen) + { + s.Reset (); + s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue + s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash + s.Insert (buf + offset, asz + 10); // signed data + if (s.Verify (r->GetIdentity (), buf + offset + asz + 10)) + { + if (!m_Server.FindSession (r->GetIdentity ()->GetIdentHash ())) + { + boost::asio::ip::udp::endpoint ep; + std::shared_ptr addr; + if (ExtractEndpoint (buf + offset + 10, asz, ep)) + addr = r->GetSSU2Address (ep.address ().is_v4 ()); + if (addr && m_Server.IsSupported (ep.address ())) + { + // send msg 5 to Alice + auto session = std::make_shared (m_Server, r, addr); + session->SetState (eSSU2SessionStatePeerTest); + session->m_RemoteEndpoint = ep; // might be different + session->m_DestConnID = htobe64 (((uint64_t)nonce << 32) | nonce); + session->m_SourceConnID = ~session->m_DestConnID; + m_Server.AddSession (session); + session->SendPeerTest (5, newSignedData.data (), newSignedData.size (), addr->i); + } + else + code = eSSU2PeerTestCodeCharlieUnsupportedAddress; + } + else + code = eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected; + } + else + code = eSSU2PeerTestCodeCharlieSignatureFailure; + } + else // maformed message + code = eSSU2PeerTestCodeCharlieReasonUnspecified; + } + else + code = eSSU2PeerTestCodeCharlieAliceIsUnknown; + // send msg 3 back to Bob + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = CreatePeerTestBlock (payload, m_MaxPayloadSize, 3, + code, nullptr, newSignedData.data (), newSignedData.size ()); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData (payload, payloadSize); + break; + } + case 3: // Bob from Charlie + { + auto it = m_PeerTests.find (nonce); + if (it != m_PeerTests.end () && it->second.first) + { + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + // Charlie's RouterInfo + auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); + if (r) i2p::data::netdb.PopulateRouterInfoBuffer (r); + size_t payloadSize = r ? CreateRouterInfoBlock (payload, m_MaxPayloadSize - len - 32, r) : 0; + if (!payloadSize && r) + it->second.first->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); + if (payloadSize + len + 16 > m_MaxPayloadSize) + { + // doesn't fit one message, send RouterInfo in separate message + it->second.first->SendData (payload, payloadSize); + payloadSize = 0; + } + // PeerTest to Alice + payloadSize += CreatePeerTestBlock (payload + payloadSize, m_MaxPayloadSize, 4, + (SSU2PeerTestCode)buf[1], GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset); + if (payloadSize < m_MaxPayloadSize) + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + it->second.first->SendData (payload, payloadSize); + m_PeerTests.erase (it); + } + else + LogPrint (eLogWarning, "SSU2: Unknown peer test 3 nonce ", nonce); + break; + } + case 4: // Alice from Bob + { + auto it = m_PeerTests.find (nonce); + if (it != m_PeerTests.end ()) + { + if (buf[1] == eSSU2PeerTestCodeAccept) + { + if (GetRouterStatus () == eRouterStatusUnknown) + SetRouterStatus (eRouterStatusTesting); + auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie + if (r && it->second.first) + { + uint8_t asz = buf[offset + 9]; + SignedData s; + s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue + s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash + s.Insert (i2p::context.GetIdentity ()->GetIdentHash (), 32); // ahash + s.Insert (buf + offset, asz + 10); // ver, nonce, ts, asz, Alice's endpoint + if (s.Verify (r->GetIdentity (), buf + offset + asz + 10)) + { + it->second.first->SetRemoteIdentity (r->GetIdentity ()); + auto addr = r->GetSSU2Address (m_Address->IsV4 ()); + if (addr) + { + it->second.first->m_Address = addr; + if (it->second.first->m_State == eSSU2SessionStatePeerTestReceived) + { + // msg 5 already received. send msg 6 + SetRouterStatus (eRouterStatusOK); + it->second.first->m_State = eSSU2SessionStatePeerTest; + it->second.first->SendPeerTest (6, buf + offset, len - offset, addr->i); + } + else + { + if (GetRouterStatus () == eRouterStatusTesting) + { + SetRouterStatus (eRouterStatusFirewalled); + if (m_Address->IsV4 ()) + m_Server.RescheduleIntroducersUpdateTimer (); + else + m_Server.RescheduleIntroducersUpdateTimerV6 (); + } + } + } + else + { + LogPrint (eLogWarning, "SSU2: Peer test 4 address not found"); + it->second.first->Done (); + } + } + else + { + LogPrint (eLogWarning, "SSU2: Peer test 4 signature verification failed"); + it->second.first->Done (); + } + } + } + else + { + LogPrint (eLogInfo, "SSU2: Peer test 4 error code ", (int)buf[1], " from ", + i2p::data::GetIdentHashAbbreviation (buf[1] < 64 ? GetRemoteIdentity ()->GetIdentHash () : i2p::data::IdentHash (buf + 3))); + if (GetRouterStatus () == eRouterStatusTesting) + SetRouterStatus (eRouterStatusUnknown); + it->second.first->Done (); + } + m_PeerTests.erase (it); + } + else + LogPrint (eLogWarning, "SSU2: Unknown peer test 4 nonce ", nonce); + break; + } + case 5: // Alice from Charlie 1 + if (htobe64 (((uint64_t)nonce << 32) | nonce) == m_SourceConnID) + { + if (m_Address) + { + SetRouterStatus (eRouterStatusOK); + SendPeerTest (6, buf + offset, len - offset, m_Address->i); + } + else + // we received msg 5 before msg 4 + m_State = eSSU2SessionStatePeerTestReceived; + } + else + LogPrint (eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", m_SourceConnID); + break; + case 6: // Charlie from Alice + if (m_Address) + SendPeerTest (7, buf + offset, len - offset, m_Address->i); + else + LogPrint (eLogWarning, "SSU2: Unknown address for peer test 6"); + m_Server.RemoveSession (~htobe64 (((uint64_t)nonce << 32) | nonce)); + break; + case 7: // Alice from Charlie 2 + m_Server.RemoveSession (htobe64 (((uint64_t)nonce << 32) | nonce)); + if (m_Address->IsV6 ()) + i2p::context.SetStatusV6 (eRouterStatusOK); // set status OK for ipv6 even if from SSU2 + break; + default: + LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", buf[0]); + } + } + + bool SSU2Session::ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep) + { + if (size < 2) return false; + int port = bufbe16toh (buf); + if (size == 6) + { + boost::asio::ip::address_v4::bytes_type bytes; + memcpy (bytes.data (), buf + 2, 4); + ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v4 (bytes), port); + } + else if (size == 18) + { + boost::asio::ip::address_v6::bytes_type bytes; + memcpy (bytes.data (), buf + 2, 16); + ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v6 (bytes), port); + } + else + { + LogPrint (eLogWarning, "SSU2: Address size ", int(size), " is not supported"); + return false; + } + return true; + } + + size_t SSU2Session::CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep) + { + if (len < 6) return 0; + htobe16buf (buf, ep.port ()); + size_t size = 0; + if (ep.address ().is_v4 ()) + { + memcpy (buf + 2, ep.address ().to_v4 ().to_bytes ().data (), 4); + size = 6; + } + else if (ep.address ().is_v6 ()) + { + if (len < 18) return 0; + memcpy (buf + 2, ep.address ().to_v6 ().to_bytes ().data (), 16); + size = 18; + } + else + { + LogPrint (eLogWarning, "SSU2: Wrong address type ", ep.address ().to_string ()); + return 0; + } + return size; + } + + std::shared_ptr SSU2Session::FindLocalAddress () const + { + if (m_Address) + return i2p::context.GetRouterInfo ().GetSSU2Address (m_Address->IsV4 ()); + return nullptr; + } + + void SSU2Session::AdjustMaxPayloadSize () + { + auto addr = FindLocalAddress (); + if (addr && addr->ssu) + { + int mtu = addr->ssu->mtu; + if (!mtu && addr->IsV4 ()) mtu = SSU2_MAX_PACKET_SIZE; + if (m_Address && m_Address->ssu && (!mtu || m_Address->ssu->mtu < mtu)) + mtu = m_Address->ssu->mtu; + if (mtu) + { + m_MaxPayloadSize = mtu - (addr->IsV6 () ? IPV6_HEADER_SIZE: IPV4_HEADER_SIZE) - UDP_HEADER_SIZE - 32; + LogPrint (eLogDebug, "SSU2: Session MTU=", mtu, ", max payload size=", m_MaxPayloadSize); + } + } + } + + RouterStatus SSU2Session::GetRouterStatus () const + { + if (m_Address) + { + if (m_Address->IsV4 ()) + return i2p::context.GetStatus (); + if (m_Address->IsV6 ()) + return i2p::context.GetStatusV6 (); + } + return eRouterStatusUnknown; + } + + void SSU2Session::SetRouterStatus (RouterStatus status) const + { + if (m_Address) + { + if (m_Address->IsV4 ()) + i2p::context.SetStatusSSU2 (status); + else if (m_Address->IsV6 ()) + i2p::context.SetStatusV6SSU2 (status); + } + } + + size_t SSU2Session::CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep) + { + if (len < 9) return 0; + buf[0] = eSSU2BlkAddress; + size_t size = CreateEndpoint (buf + 3, len - 3, ep); + if (!size) return 0; + htobe16buf (buf + 1, size); + return size + 3; + } + + size_t SSU2Session::CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr r) + { + if (!r || !r->GetBuffer () || len < 5) return 0; + buf[0] = eSSU2BlkRouterInfo; + size_t size = r->GetBufferLen (); + if (size + 5 < len) + { + memcpy (buf + 5, r->GetBuffer (), size); + buf[3] = 0; // flag + } + else + { + i2p::data::GzipDeflator deflator; + deflator.SetCompressionLevel (9); + size = deflator.Deflate (r->GetBuffer (), r->GetBufferLen (), buf + 5, len - 5); + if (!size) return 0; // doesn't fit + buf[3] = SSU2_ROUTER_INFO_FLAG_GZIP; // flag + } + htobe16buf (buf + 1, size + 2); // size + buf[4] = 1; // frag + return size + 5; + } + + size_t SSU2Session::CreateAckBlock (uint8_t * buf, size_t len) + { + if (len < 8) return 0; + int maxNumRanges = (len - 8) >> 1; + if (maxNumRanges > SSU2_MAX_NUM_ACK_RANGES) maxNumRanges = SSU2_MAX_NUM_ACK_RANGES; + buf[0] = eSSU2BlkAck; + uint32_t ackThrough = m_OutOfSequencePackets.empty () ? m_ReceivePacketNum : *m_OutOfSequencePackets.rbegin (); + htobe32buf (buf + 3, ackThrough); // Ack Through + uint16_t acnt = 0; + int numRanges = 0; + if (ackThrough) + { + if (m_OutOfSequencePackets.empty ()) + acnt = std::min ((int)ackThrough, 255); // no gaps + else + { + auto it = m_OutOfSequencePackets.rbegin (); it++; // prev packet num + while (it != m_OutOfSequencePackets.rend () && *it == ackThrough - acnt - 1) + { + acnt++; + it++; + } + // ranges + uint32_t lastNum = ackThrough - acnt; + if (acnt > 255) + { + auto d = std::div (acnt - 255, 255); + acnt = 255; + if (d.quot > maxNumRanges) + { + d.quot = maxNumRanges; + d.rem = 0; + } + // Acks only ragnes for acnt + for (int i = 0; i < d.quot; i++) + { + buf[8 + numRanges*2] = 0; buf[8 + numRanges*2 + 1] = 255; // NACKs 0, Acks 255 + numRanges++; + } + if (d.rem > 0) + { + buf[8 + numRanges*2] = 0; buf[8 + numRanges*2 + 1] = d.rem; + numRanges++; + } + } + while (it != m_OutOfSequencePackets.rend () && numRanges < maxNumRanges) + { + if (lastNum - (*it) > 255) + { + // NACKs only ranges + if (lastNum > (*it) + 255*(maxNumRanges - numRanges)) break; // too many NACKs + while (lastNum - (*it) > 255) + { + buf[8 + numRanges*2] = 255; buf[8 + numRanges*2 + 1] = 0; // NACKs 255, Acks 0 + lastNum -= 255; + numRanges++; + } + } + // NACKs and Acks ranges + buf[8 + numRanges*2] = lastNum - (*it) - 1; // NACKs + lastNum = *it; it++; + int numAcks = 1; + while (it != m_OutOfSequencePackets.rend () && lastNum > 0 && *it == lastNum - 1) + { + numAcks++; lastNum--; + it++; + } + while (numAcks > 255) + { + // Acks only ranges + buf[8 + numRanges*2 + 1] = 255; // Acks 255 + numAcks -= 255; + numRanges++; + buf[8 + numRanges*2] = 0; // NACKs 0 + if (numRanges >= maxNumRanges) break; + } + if (numAcks > 255) numAcks = 255; + buf[8 + numRanges*2 + 1] = (uint8_t)numAcks; // Acks + numRanges++; + } + if (numRanges < maxNumRanges && it == m_OutOfSequencePackets.rend ()) + { + // add range between out-of-seqence and received + int nacks = *m_OutOfSequencePackets.begin () - m_ReceivePacketNum - 1; + if (nacks > 0) + { + if (nacks > 255) nacks = 255; + buf[8 + numRanges*2] = nacks; + buf[8 + numRanges*2 + 1] = std::min ((int)m_ReceivePacketNum + 1, 255); + numRanges++; + } + } + } + } + buf[7] = (uint8_t)acnt; // acnt + htobe16buf (buf + 1, 5 + numRanges*2); + return 8 + numRanges*2; + } + + size_t SSU2Session::CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize) + { + if (len < minSize) return 0; + uint8_t paddingSize = rand () & 0x0F; // 0 - 15 + if (paddingSize > len) paddingSize = len; + else if (paddingSize < minSize) paddingSize = minSize; + if (paddingSize) + { + buf[0] = eSSU2BlkPadding; + htobe16buf (buf + 1, paddingSize); + memset (buf + 3, 0, paddingSize); + } + else + return 0; + return paddingSize + 3; + } + + size_t SSU2Session::CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr&& msg) + { + msg->ToNTCP2 (); + auto msgBuf = msg->GetNTCP2Header (); + auto msgLen = msg->GetNTCP2Length (); + if (msgLen + 3 > len) msgLen = len - 3; + buf[0] = eSSU2BlkI2NPMessage; + htobe16buf (buf + 1, msgLen); // size + memcpy (buf + 3, msgBuf, msgLen); + return msgLen + 3; + } + + size_t SSU2Session::CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg) + { + if (len < 12) return 0; + msg->ToNTCP2 (); + auto msgBuf = msg->GetNTCP2Header (); + auto msgLen = msg->GetNTCP2Length (); + if (msgLen + 3 <= len) return 0; + msgLen = len - 3; + buf[0] = eSSU2BlkFirstFragment; + htobe16buf (buf + 1, msgLen); // size + memcpy (buf + 3, msgBuf, msgLen); + msg->offset = (msgBuf - msg->buf) + msgLen; + return msgLen + 3; + } + + size_t SSU2Session::CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg, uint8_t& fragmentNum, uint32_t msgID) + { + if (len < 8) return 0; + bool isLast = true; + auto msgLen = msg->len - msg->offset; + if (msgLen + 8 > len) + { + msgLen = len - 8; + isLast = false; + } + buf[0] = eSSU2BlkFollowOnFragment; + htobe16buf (buf + 1, msgLen + 5); // size + fragmentNum++; + buf[3] = fragmentNum << 1; + if (isLast) buf[3] |= 0x01; + memcpy (buf + 4, &msgID, 4); + memcpy (buf + 8, msg->buf + msg->offset, msgLen); + msg->offset += msgLen; + return msgLen + 8; + } + + size_t SSU2Session::CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen) + { + buf[0] = eSSU2BlkRelayIntro; + size_t payloadSize = 1/* flag */ + 32/* Alice router hash */ + introDataLen; + if (payloadSize + 3 > len) return 0; + htobe16buf (buf + 1, payloadSize); // size + buf[3] = 0; // flag + memcpy (buf + 4, GetRemoteIdentity ()->GetIdentHash (), 32); // Alice router hash + memcpy (buf + 36, introData, introDataLen); + return payloadSize + 3; + } + + size_t SSU2Session::CreateRelayResponseBlock (uint8_t * buf, size_t len, + SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4) + { + buf[0] = eSSU2BlkRelayResponse; + buf[3] = 0; // flag + buf[4] = code; // code + htobe32buf (buf + 5, nonce); // nonce + htobe32buf (buf + 9, i2p::util::GetSecondsSinceEpoch ()); // timestamp + buf[13] = 2; // ver + size_t csz = 0; + if (code == eSSU2RelayResponseCodeAccept) + { + auto addr = i2p::context.GetRouterInfo ().GetSSU2Address (v4); + if (!addr) + { + LogPrint (eLogError, "SSU2: Can't find local address for RelayResponse"); + return 0; + } + csz = CreateEndpoint (buf + 15, len - 15, boost::asio::ip::udp::endpoint (addr->host, addr->port)); + if (!csz) + { + LogPrint (eLogError, "SSU2: Can't create local endpoint for RelayResponse"); + return 0; + } + } + buf[14] = csz; // csz + // signature + size_t signatureLen = i2p::context.GetIdentity ()->GetSignatureLen (); + if (15 + csz + signatureLen > len) + { + LogPrint (eLogError, "SSU2: Buffer for RelayResponse signature is too small ", len); + return 0; + } + SignedData s; + s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue + if (code == eSSU2RelayResponseCodeAccept || code >= 64) // Charlie + s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash + else // Bob's reject + s.Insert (i2p::context.GetIdentity ()->GetIdentHash (), 32); // bhash + s.Insert (buf + 5, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint + s.Sign (i2p::context.GetPrivateKeys (), buf + 15 + csz); + size_t payloadSize = 12 + csz + signatureLen; + if (!code) + { + if (payloadSize + 11 > len) + { + LogPrint (eLogError, "SSU2: Buffer for RelayResponse token is too small ", len); + return 0; + } + memcpy (buf + 3 + payloadSize, &token, 8); + payloadSize += 8; + } + htobe16buf (buf + 1, payloadSize); // size + return payloadSize + 3; + } + + size_t SSU2Session::CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, + const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen) + { + buf[0] = eSSU2BlkPeerTest; + size_t payloadSize = 3/* msg, code, flag */ + signedDataLen; + if (routerHash) payloadSize += 32; // router hash + if (payloadSize + 3 > len) return 0; + htobe16buf (buf + 1, payloadSize); // size + buf[3] = msg; // msg + buf[4] = (uint8_t)code; // code + buf[5] = 0; //flag + size_t offset = 6; + if (routerHash) + { + memcpy (buf + offset, routerHash, 32); // router hash + offset += 32; + } + memcpy (buf + offset, signedData, signedDataLen); + return payloadSize + 3; + } + + size_t SSU2Session::CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce) + { + auto localAddress = FindLocalAddress (); + if (!localAddress || !localAddress->port || localAddress->host.is_unspecified () || + localAddress->host.is_v4 () != m_RemoteEndpoint.address ().is_v4 ()) + { + LogPrint (eLogWarning, "SSU2: Can't find local address for peer test"); + return 0; + } + // signed data + auto ts = i2p::util::GetSecondsSinceEpoch (); + uint8_t signedData[96]; + signedData[0] = 2; // ver + htobe32buf (signedData + 1, nonce); + htobe32buf (signedData + 5, ts); + size_t asz = CreateEndpoint (signedData + 10, 86, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port)); + signedData[9] = asz; + // signature + SignedData s; + s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue + s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash + s.Insert (signedData, 10 + asz); // ver, nonce, ts, asz, Alice's endpoint + s.Sign (i2p::context.GetPrivateKeys (), signedData + 10 + asz); + return CreatePeerTestBlock (buf, len, 1, eSSU2PeerTestCodeAccept, nullptr, + signedData, 10 + asz + i2p::context.GetIdentity ()->GetSignatureLen ()); + } + + size_t SSU2Session::CreateTerminationBlock (uint8_t * buf, size_t len) + { + buf[0] = eSSU2BlkTermination; + htobe16buf (buf + 1, 9); + htobe64buf (buf + 3, m_ReceivePacketNum); + buf[11] = (uint8_t)m_TerminationReason; + return 12; + } + + std::shared_ptr SSU2Session::ExtractRouterInfo (const uint8_t * buf, size_t size) + { + if (size < 2) return nullptr; + // TODO: handle frag + std::shared_ptr ri; + if (buf[0] & SSU2_ROUTER_INFO_FLAG_GZIP) + { + i2p::data::GzipInflator inflator; + uint8_t uncompressed[i2p::data::MAX_RI_BUFFER_SIZE]; + size_t uncompressedSize = inflator.Inflate (buf + 2, size - 2, uncompressed, i2p::data::MAX_RI_BUFFER_SIZE); + if (uncompressedSize && uncompressedSize < i2p::data::MAX_RI_BUFFER_SIZE) + ri = std::make_shared(uncompressed, uncompressedSize); + else + LogPrint (eLogInfo, "SSU2: RouterInfo decompression failed ", uncompressedSize); + } + else + ri = std::make_shared(buf + 2, size - 2); + return ri; + } + + void SSU2Session::CreateNonce (uint64_t seqn, uint8_t * nonce) + { + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); + } + + bool SSU2Session::UpdateReceivePacketNum (uint32_t packetNum) + { + if (packetNum <= m_ReceivePacketNum) return false; // duplicate + if (packetNum == m_ReceivePacketNum + 1) + { + for (auto it = m_OutOfSequencePackets.begin (); it != m_OutOfSequencePackets.end ();) + { + if (*it == packetNum + 1) + { + packetNum++; + it = m_OutOfSequencePackets.erase (it); + } + else + break; + } + m_ReceivePacketNum = packetNum; + } + else + m_OutOfSequencePackets.insert (packetNum); + return true; + } + + void SSU2Session::SendQuickAck () + { + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = CreateAckBlock (payload, m_MaxPayloadSize); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + SendData (payload, payloadSize); + } + + void SSU2Session::SendTermination () + { + uint8_t payload[32]; + size_t payloadSize = CreateTerminationBlock (payload, 32); + payloadSize += CreatePaddingBlock (payload + payloadSize, 32 - payloadSize); + SendData (payload, payloadSize); + } + + void SSU2Session::SendPathResponse (const uint8_t * data, size_t len) + { + if (len < 8 || len > m_MaxPayloadSize - 3) + { + LogPrint (eLogWarning, "SSU2: Incorrect data size for path response ", len); + return; + } + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + payload[0] = eSSU2BlkPathResponse; + htobe16buf (payload + 1, len); + memcpy (payload + 3, data, len); + SendData (payload, len + 3); + } + + void SSU2Session::CleanUp (uint64_t ts) + { + for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) + { + if (ts > it->second->lastFragmentInsertTime + SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) + { + LogPrint (eLogWarning, "SSU2: message ", it->first, " was not completed in ", SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); + it = m_IncompleteMessages.erase (it); + } + else + ++it; + } + if (!m_OutOfSequencePackets.empty ()) + { + if (m_OutOfSequencePackets.size () > 2*SSU2_MAX_NUM_ACK_RANGES || + *m_OutOfSequencePackets.rbegin () > m_ReceivePacketNum + 255*8) + { + uint32_t packet = *m_OutOfSequencePackets.begin (); + if (packet > m_ReceivePacketNum + 1) + { + // like we've just received all packets before first + packet--; + m_ReceivePacketNum = packet - 1; + UpdateReceivePacketNum (packet); + } + else + LogPrint (eLogError, "SSU2: Out of sequence packet ", packet, " is less than last received ", m_ReceivePacketNum); + } + if (m_OutOfSequencePackets.size () > 255*4) + { + // seems we have a serious network issue + m_ReceivePacketNum = *m_OutOfSequencePackets.rbegin (); + m_OutOfSequencePackets.clear (); + } + } + + for (auto it = m_RelaySessions.begin (); it != m_RelaySessions.end ();) + { + if (ts > it->second.second + SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT) + { + LogPrint (eLogWarning, "SSU2: Relay nonce ", it->first, " was not responded in ", SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT, " seconds, deleted"); + it = m_RelaySessions.erase (it); + } + else + ++it; + } + for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();) + { + if (ts > it->second.second + SSU2_PEER_TEST_EXPIRATION_TIMEOUT) + { + LogPrint (eLogWarning, "SSU2: Peer test nonce ", it->first, " was not responded in ", SSU2_PEER_TEST_EXPIRATION_TIMEOUT, " seconds, deleted"); + it = m_PeerTests.erase (it); + } + else + ++it; + } + } + + void SSU2Session::FlushData () + { + bool sent = SendQueue (); // if we have something to send + if (m_IsDataReceived) + { + if (!sent) SendQuickAck (); + m_Handler.Flush (); + m_IsDataReceived = false; + } + } + +} +} diff -Nru i2pd-2.39.0/libi2pd/SSU2Session.h i2pd-2.43.0/libi2pd/SSU2Session.h --- i2pd-2.39.0/libi2pd/SSU2Session.h 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/libi2pd/SSU2Session.h 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,358 @@ +/* +* Copyright (c) 2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef SSU2_SESSION_H__ +#define SSU2_SESSION_H__ + +#include +#include +#include +#include +#include +#include +#include "Crypto.h" +#include "RouterInfo.h" +#include "RouterContext.h" +#include "TransportSession.h" + +namespace i2p +{ +namespace transport +{ + const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds + const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes + const int SSU2_CLOCK_SKEW = 60; // in seconds + const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust + const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds + const int SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52*60; // for next token block, in seconds + const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds + const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds + const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds + const size_t SSU2_MAX_PACKET_SIZE = 1500; + const size_t SSU2_MIN_PACKET_SIZE = 1280; + const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in millseconds + const int SSU2_RESEND_INTERVAL = 300; // in milliseconds + const int SSU2_MAX_NUM_RESENDS = 5; + const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds + const size_t SSU2_MIN_WINDOW_SIZE = 16; // in packets + const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets + const size_t SSU2_MIN_RTO = 100; // in milliseconds + const size_t SSU2_MAX_RTO = 2500; // in milliseconds + const float SSU2_kAPPA = 1.8; + const size_t SSU2_MAX_OUTGOING_QUEUE_SIZE = 500; // in messages + const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send + const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64; + + enum SSU2MessageType + { + eSSU2SessionRequest = 0, + eSSU2SessionCreated = 1, + eSSU2SessionConfirmed = 2, + eSSU2Data = 6, + eSSU2PeerTest = 7, + eSSU2Retry = 9, + eSSU2TokenRequest = 10, + eSSU2HolePunch = 11 + }; + + enum SSU2BlockType + { + eSSU2BlkDateTime = 0, + eSSU2BlkOptions, // 1 + eSSU2BlkRouterInfo, // 2 + eSSU2BlkI2NPMessage, // 3 + eSSU2BlkFirstFragment, // 4 + eSSU2BlkFollowOnFragment, // 5 + eSSU2BlkTermination, // 6 + eSSU2BlkRelayRequest, // 7 + eSSU2BlkRelayResponse, // 8 + eSSU2BlkRelayIntro, // 9 + eSSU2BlkPeerTest, // 10 + eSSU2BlkNextNonce, // 11 + eSSU2BlkAck, // 12 + eSSU2BlkAddress, // 13 + eSSU2BlkIntroKey, // 14 + eSSU2BlkRelayTagRequest, // 15 + eSSU2BlkRelayTag, // 16 + eSSU2BlkNewToken, // 17 + eSSU2BlkPathChallenge, // 18 + eSSU2BlkPathResponse, // 19 + eSSU2BlkFirstPacketNumber, // 20 + eSSU2BlkPadding = 254 + }; + + enum SSU2SessionState + { + eSSU2SessionStateUnknown, + eSSU2SessionStateTokenReceived, + eSSU2SessionStateSessionRequestSent, + eSSU2SessionStateSessionRequestReceived, + eSSU2SessionStateSessionCreatedSent, + eSSU2SessionStateSessionCreatedReceived, + eSSU2SessionStateSessionConfirmedSent, + eSSU2SessionStateEstablished, + eSSU2SessionStateClosing, + eSSU2SessionStateTerminated, + eSSU2SessionStateFailed, + eSSU2SessionStateIntroduced, + eSSU2SessionStatePeerTest, + eSSU2SessionStatePeerTestReceived // 5 before 4 + }; + + enum SSU2PeerTestCode + { + eSSU2PeerTestCodeAccept = 0, + eSSU2PeerTestCodeBobReasonUnspecified = 1, + eSSU2PeerTestCodeBobNoCharlieAvailable = 2, + eSSU2PeerTestCodeBobLimitExceeded = 3, + eSSU2PeerTestCodeBobSignatureFailure = 4, + eSSU2PeerTestCodeCharlieReasonUnspecified = 64, + eSSU2PeerTestCodeCharlieUnsupportedAddress = 65, + eSSU2PeerTestCodeCharlieLimitExceeded = 66, + eSSU2PeerTestCodeCharlieSignatureFailure = 67, + eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected = 68, + eSSU2PeerTestCodeCharlieAliceIsBanned = 69, + eSSU2PeerTestCodeCharlieAliceIsUnknown = 70, + eSSU2PeerTestCodeUnspecified = 128 + }; + + enum SSU2RelayResponseCode + { + eSSU2RelayResponseCodeAccept = 0, + eSSU2RelayResponseCodeBobRelayTagNotFound = 5, + eSSU2RelayResponseCodeCharlieUnsupportedAddress = 65, + eSSU2RelayResponseCodeCharlieSignatureFailure = 67, + eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70 + }; + + enum SSU2TerminationReason + { + eSSU2TerminationReasonNormalClose = 0, + eSSU2TerminationReasonTerminationReceived = 1, + eSSU2TerminationReasonIdleTimeout = 2, + eSSU2TerminationReasonRouterShutdown = 3, + eSSU2TerminationReasonDataPhaseAEADFailure= 4, + eSSU2TerminationReasonIncompatibleOptions = 5, + eSSU2TerminationReasonTncompatibleSignatureType = 6, + eSSU2TerminationReasonClockSkew = 7, + eSSU2TerminationPaddingViolation = 8, + eSSU2TerminationReasonAEADFramingError = 9, + eSSU2TerminationReasonPayloadFormatError = 10, + eSSU2TerminationReasonSessionRequestError = 11, + eSSU2TerminationReasonSessionCreatedError = 12, + eSSU2TerminationReasonSessionConfirmedError = 13, + eSSU2TerminationReasonTimeout = 14, + eSSU2TerminationReasonRouterInfoSignatureVerificationFail = 15, + eSSU2TerminationReasonInvalidS = 16, + eSSU2TerminationReasonBanned = 17, + eSSU2TerminationReasonBadToken = 18, + eSSU2TerminationReasonConnectionLimits = 19, + eSSU2TerminationReasonIncompatibleVersion = 20, + eSSU2TerminationReasonWrongNetID = 21, + eSSU2TerminationReasonReplacedByNewSession = 22 + }; + + struct SSU2IncompleteMessage + { + struct Fragment + { + uint8_t buf[SSU2_MAX_PACKET_SIZE]; + size_t len; + bool isLast; + }; + + std::shared_ptr msg; + int nextFragmentNum; + uint32_t lastFragmentInsertTime; // in seconds + std::map > outOfSequenceFragments; + + void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); + }; + + struct SSU2SentPacket + { + uint8_t payload[SSU2_MAX_PACKET_SIZE]; + size_t payloadSize = 0; + uint64_t sendTime; // in milliseconds + int numResends = 0; + }; + + // RouterInfo flags + const uint8_t SSU2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; + const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02; + + class SSU2Server; + class SSU2Session: public TransportSession, public std::enable_shared_from_this + { + union Header + { + uint64_t ll[2]; + uint8_t buf[16]; + struct + { + uint64_t connID; + uint32_t packetNum; + uint8_t type; + uint8_t flags[3]; + } h; + }; + + struct HandshakePacket + { + Header header; + uint8_t headerX[48]; // part1 for SessionConfirmed + uint8_t payload[SSU2_MAX_PACKET_SIZE*2]; + size_t payloadSize = 0; + uint64_t sendTime = 0; // in milliseconds + bool isSecondFragment = false; // for SessionConfirmed + }; + + typedef std::function OnEstablished; + + public: + + SSU2Session (SSU2Server& server, std::shared_ptr in_RemoteRouter = nullptr, + std::shared_ptr addr = nullptr); + ~SSU2Session (); + + void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; }; + const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; }; + i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports () const { return m_RemoteTransports; }; + std::shared_ptr GetAddress () const { return m_Address; }; + void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; }; + OnEstablished GetOnEstablished () const { return m_OnEstablished; }; + + void Connect (); + bool Introduce (std::shared_ptr session, uint32_t relayTag); + void WaitForIntroduction (); + void SendPeerTest (); // Alice, Data message + void SendKeepAlive (); + void RequestTermination (SSU2TerminationReason reason); + void CleanUp (uint64_t ts); + void FlushData (); + void Done () override; + void SendLocalRouterInfo (bool update) override; + void SendI2NPMessages (const std::vector >& msgs) override; + uint32_t GetRelayTag () const override { return m_RelayTag; }; + void Resend (uint64_t ts); + bool IsEstablished () const { return m_State == eSSU2SessionStateEstablished; }; + uint64_t GetConnID () const { return m_SourceConnID; }; + SSU2SessionState GetState () const { return m_State; }; + void SetState (SSU2SessionState state) { m_State = state; }; + + bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len); + bool ProcessSessionCreated (uint8_t * buf, size_t len); + bool ProcessSessionConfirmed (uint8_t * buf, size_t len); + bool ProcessRetry (uint8_t * buf, size_t len); + bool ProcessHolePunch (uint8_t * buf, size_t len); + bool ProcessPeerTest (uint8_t * buf, size_t len); + void ProcessData (uint8_t * buf, size_t len); + + private: + + void Terminate (); + void Established (); + void ScheduleConnectTimer (); + void HandleConnectTimer (const boost::system::error_code& ecode); + void PostI2NPMessages (std::vector > msgs); + bool SendQueue (); // returns true if ack block was sent + bool SendFragmentedMessage (std::shared_ptr msg); + void ResendHandshakePacket (); + void ConnectAfterIntroduction (); + + void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len); + void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len); + + void SendSessionRequest (uint64_t token = 0); + void SendSessionCreated (const uint8_t * X); + void SendSessionConfirmed (const uint8_t * Y); + void KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba); + void SendTokenRequest (); + void SendRetry (); + uint32_t SendData (const uint8_t * buf, size_t len); // returns packet num + void SendQuickAck (); + void SendTermination (); + void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token); + void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message + void SendPathResponse (const uint8_t * data, size_t len); + + void HandlePayload (const uint8_t * buf, size_t len); + void HandleDateTime (const uint8_t * buf, size_t len); + void HandleAck (const uint8_t * buf, size_t len); + void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts); + void HandleAddress (const uint8_t * buf, size_t len); + bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep); + size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); + std::shared_ptr FindLocalAddress () const; + void AdjustMaxPayloadSize (); + RouterStatus GetRouterStatus () const; + void SetRouterStatus (RouterStatus status) const; + std::shared_ptr ExtractRouterInfo (const uint8_t * buf, size_t size); + void CreateNonce (uint64_t seqn, uint8_t * nonce); + bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate + void HandleFirstFragment (const uint8_t * buf, size_t len); + void HandleFollowOnFragment (const uint8_t * buf, size_t len); + bool ConcatOutOfSequenceFragments (std::shared_ptr m); // true if message complete + void HandleRelayRequest (const uint8_t * buf, size_t len); + void HandleRelayIntro (const uint8_t * buf, size_t len); + void HandleRelayResponse (const uint8_t * buf, size_t len); + void HandlePeerTest (const uint8_t * buf, size_t len); + + size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); + size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr r); + size_t CreateAckBlock (uint8_t * buf, size_t len); + size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0); + size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr&& msg); + size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg); + size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg, uint8_t& fragmentNum, uint32_t msgID); + size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen); + size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4); + size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen); + size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice + size_t CreateTerminationBlock (uint8_t * buf, size_t len); + + private: + + SSU2Server& m_Server; + std::shared_ptr m_EphemeralKeys; + std::unique_ptr m_NoiseState; + std::unique_ptr m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice + std::unique_ptr m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed + std::shared_ptr m_Address; + boost::asio::ip::udp::endpoint m_RemoteEndpoint; + i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests + uint64_t m_DestConnID, m_SourceConnID; + SSU2SessionState m_State; + uint8_t m_KeyDataSend[64], m_KeyDataReceive[64]; + uint32_t m_SendPacketNum, m_ReceivePacketNum; + std::set m_OutOfSequencePackets; // packet nums > receive packet num + std::map > m_SentPackets; // packetNum -> packet + std::map > m_IncompleteMessages; // I2NP + std::map, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice + std::map, uint64_t > > m_PeerTests; // same as for relay sessions + std::list > m_SendQueue; + i2p::I2NPMessagesHandler m_Handler; + bool m_IsDataReceived; + size_t m_WindowSize, m_RTT, m_RTO; + uint32_t m_RelayTag; // between Bob and Charlie + OnEstablished m_OnEstablished; // callback from Established + boost::asio::deadline_timer m_ConnectTimer; + SSU2TerminationReason m_TerminationReason; + size_t m_MaxPayloadSize; + }; + + inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce) + { + uint64_t data = 0; + i2p::crypto::ChaCha20 ((uint8_t *)&data, 8, kh, nonce, (uint8_t *)&data); + return data; + } +} +} + +#endif diff -Nru i2pd-2.39.0/libi2pd/SSU.cpp i2pd-2.43.0/libi2pd/SSU.cpp --- i2pd-2.39.0/libi2pd/SSU.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/SSU.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,10 +11,11 @@ #include "Timestamp.h" #include "RouterContext.h" #include "NetDb.hpp" -#include "SSU.h" +#include "Config.h" #include "util.h" +#include "SSU.h" -#ifdef __linux__ +#if defined(__linux__) && !defined(_NETINET_IN_H) #include #endif @@ -33,7 +34,8 @@ m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port), m_Socket (m_ReceiversService), m_SocketV6 (m_ReceiversServiceV6), m_IntroducersUpdateTimer (m_Service), m_IntroducersUpdateTimerV6 (m_Service), - m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service) + m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_Service), + m_IsSyncClockFromPeers (true) { } @@ -53,7 +55,7 @@ } catch ( std::exception & ex ) { - LogPrint (eLogError, "SSU: failed to bind to v4 port ", m_Endpoint.port(), ": ", ex.what()); + LogPrint (eLogError, "SSU: Failed to bind to v4 port ", m_Endpoint.port(), ": ", ex.what()); ThrowFatal ("Unable to start IPv4 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ()); } } @@ -66,7 +68,7 @@ m_SocketV6.set_option (boost::asio::ip::v6_only (true)); m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE)); m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE)); -#ifdef __linux__ +#if defined(__linux__) && !defined(_NETINET_IN_H) if (m_EndpointV6.address() == boost::asio::ip::address().from_string("::")) // only if not binded to address { // Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others @@ -83,13 +85,14 @@ } catch ( std::exception & ex ) { - LogPrint (eLogError, "SSU: failed to bind to v6 port ", m_EndpointV6.port(), ": ", ex.what()); + LogPrint (eLogError, "SSU: Failed to bind to v6 port ", m_EndpointV6.port(), ": ", ex.what()); ThrowFatal ("Unable to start IPv6 SSU transport at port ", m_Endpoint.port(), ": ", ex.what ()); } } void SSUServer::Start () { + i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers); m_IsRunning = true; m_Thread = new std::thread (std::bind (&SSUServer::Run, this)); if (context.SupportsV4 ()) @@ -156,7 +159,7 @@ } catch (std::exception& ex) { - LogPrint (eLogError, "SSU: server runtime exception: ", ex.what ()); + LogPrint (eLogError, "SSU: Server runtime exception: ", ex.what ()); } } } @@ -173,7 +176,7 @@ } catch (std::exception& ex) { - LogPrint (eLogError, "SSU: receivers runtime exception: ", ex.what ()); + LogPrint (eLogError, "SSU: Receivers runtime exception: ", ex.what ()); if (m_IsRunning) { // restart socket @@ -218,7 +221,7 @@ 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) @@ -249,20 +252,20 @@ if (ec) { - LogPrint (eLogError, "SSU: send exception: ", ec.message (), " while trying to send data to ", to.address (), ":", to.port (), " (length: ", len, ")"); + LogPrint (eLogError, "SSU: Send exception: ", ec.message (), " while trying to send data to ", to.address (), ":", to.port (), " (length: ", len, ")"); } } 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)); } @@ -270,14 +273,14 @@ void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) { if (!ecode - || ecode == boost::asio::error::connection_refused - || ecode == boost::asio::error::connection_reset - || ecode == boost::asio::error::network_unreachable - || ecode == boost::asio::error::host_unreachable + || ecode == boost::asio::error::connection_refused + || ecode == boost::asio::error::connection_reset + || ecode == boost::asio::error::network_unreachable + || ecode == boost::asio::error::host_unreachable #ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO - || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ - || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ - || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ + || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ + || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ + || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ #endif ) // just try continue reading when received ICMP response otherwise socket can crash, @@ -293,7 +296,7 @@ { 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 +307,7 @@ else { LogPrint (eLogError, "SSU: receive_from error: code ", ec.value(), ": ", ec.message ()); - delete packet; + m_PacketsPool.ReleaseMt (packet); break; } } @@ -315,10 +318,10 @@ } else { - delete packet; + m_PacketsPool.ReleaseMt (packet); if (ecode != boost::asio::error::operation_aborted) { - LogPrint (eLogError, "SSU: receive error: code ", ecode.value(), ": ", ecode.message ()); + LogPrint (eLogError, "SSU: Receive error: code ", ecode.value(), ": ", ecode.message ()); m_Socket.close (); OpenSocket (); Receive (); @@ -329,14 +332,14 @@ void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) { if (!ecode - || ecode == boost::asio::error::connection_refused - || ecode == boost::asio::error::connection_reset - || ecode == boost::asio::error::network_unreachable - || ecode == boost::asio::error::host_unreachable + || ecode == boost::asio::error::connection_refused + || ecode == boost::asio::error::connection_reset + || ecode == boost::asio::error::network_unreachable + || ecode == boost::asio::error::host_unreachable #ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO - || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ - || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ - || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ + || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ + || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ + || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ #endif ) // just try continue reading when received ICMP response otherwise socket can crash, @@ -352,7 +355,7 @@ { 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 +366,7 @@ else { LogPrint (eLogError, "SSU: v6 receive_from error: code ", ec.value(), ": ", ec.message ()); - delete packet; + m_PacketsPool.ReleaseMt (packet);; break; } } @@ -374,7 +377,7 @@ } 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 ()); @@ -409,7 +412,7 @@ session = std::make_shared (*this, packet->from); session->WaitForConnect (); (*sessions)[packet->from] = session; - LogPrint (eLogDebug, "SSU: new session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created"); + LogPrint (eLogDebug, "SSU: New session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created"); } } if (session) @@ -421,8 +424,8 @@ if (session) session->FlushData (); session = nullptr; } - delete packet; } + m_PacketsPool.ReleaseMt (packets); if (session) session->FlushData (); } @@ -579,7 +582,7 @@ "] through introducer ", introducer->iHost, ":", introducer->iPort); session->WaitForIntroduction (); if ((address->host.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) || - (address->host.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled)) + (address->host.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled)) { uint8_t buf[1]; Send (buf, 0, remoteEndpoint); // send HolePunch @@ -673,7 +676,7 @@ for (const auto& s : sessions) { if (s.second->GetRelayTag () && s.second->GetState () == eSessionStateEstablished && - ts < s.second->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION) + ts < s.second->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_EXPIRATION) ret.push_back (s.second); else if (s.second->GetRemoteIdentity ()) excluded.insert (s.second->GetRemoteIdentity ()->GetIdentHash ()); @@ -797,7 +800,7 @@ if (sessions.empty () && !introducers.empty ()) { // bump creation time for previous introducers if no new sessions found - LogPrint (eLogDebug, "SSU: no new introducers found. Trying to reuse existing"); + LogPrint (eLogDebug, "SSU: No new introducers found. Trying to reuse existing"); for (const auto& it : introducers) { auto session = FindSession (it); @@ -847,7 +850,7 @@ } else { - LogPrint (eLogDebug, "SSU: can't find more introducers"); + LogPrint (eLogDebug, "SSU: Can't find more introducers"); break; } } @@ -919,13 +922,19 @@ } 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)); } @@ -940,20 +949,23 @@ { auto session = it.second; if (it.first != session->GetRemoteEndpoint ()) - LogPrint (eLogWarning, "SSU: remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first, " adjusted"); + LogPrint (eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first, " adjusted"); m_Service.post ([session] { - LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); + LogPrint (eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); 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)); } @@ -968,13 +980,15 @@ { auto session = it.second; if (it.first != session->GetRemoteEndpoint ()) - LogPrint (eLogWarning, "SSU: remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first); + LogPrint (eLogWarning, "SSU: Remote endpoint ", session->GetRemoteEndpoint (), " doesn't match key ", it.first); m_Service.post ([session] { - LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); + LogPrint (eLogWarning, "SSU: No activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds"); session->Failed (); }); } + else + it.second->CleanUp (ts); ScheduleTerminationV6 (); } } diff -Nru i2pd-2.39.0/libi2pd/SSUData.cpp i2pd-2.43.0/libi2pd/SSUData.cpp --- i2pd-2.39.0/libi2pd/SSUData.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/SSUData.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -33,7 +33,6 @@ 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 @@ 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 @@ if (bitfield & mask) { if (fragment < numSentFragments) - it->second->fragments[fragment].reset (nullptr); + it->second->fragments[fragment] = nullptr; } fragment++; mask <<= 1; @@ -174,7 +171,7 @@ return; } - // find message with msgID + // find message with msgID auto it = m_IncompleteMessages.find (msgID); if (it == m_IncompleteMessages.end ()) { @@ -182,15 +179,15 @@ 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 (std::move (msg)))).first; } - std::unique_ptr& incompleteMessage = it->second; + auto& incompleteMessage = it->second; // mark fragment as received if (fragmentNum < 64) - incompleteMessage->receivedFragmentsBits |= (0x01 << fragmentNum); + incompleteMessage->receivedFragmentsBits |= (uint64_t(0x01) << fragmentNum); else LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64"); - + // handle current fragment if (fragmentNum == incompleteMessage->nextFragmentNum) { @@ -224,8 +221,8 @@ { // 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,11 +243,11 @@ { 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); + m_Handler.PutNextMessage (std::move (msg)); } else LogPrint (eLogDebug, "SSU: message expired"); @@ -313,22 +310,22 @@ 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; sentMessage->numResends = 0; } auto& fragments = sentMessage->fragments; - size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) + size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) size_t len = msg->GetLength (); uint8_t * msgBuf = msg->GetSSUHeader (); 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 @@ -352,13 +349,13 @@ size += payload - fragment->buf; uint8_t rem = size & 0x0F; if (rem) // make sure 16 bytes boundary - { + { auto padding = 16 - rem; memset (fragment->buf + size, 0, padding); 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]; @@ -411,14 +408,14 @@ // one ack *(uint32_t *)(payload) = htobe32 (msgID); // msgID payload += 4; - size_t len = 0; + size_t len = 0; while (bits) { *payload = (bits & 0x7F); // next 7 bits bits >>= 7; if (bits) *payload &= 0x80; // 0x80 means non-last payload++; len++; - } + } *payload = 0; // number of fragments len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4 // encrypt message with session key @@ -452,7 +449,7 @@ if (f) { try - { + { m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, f->buf, f->len, buf); m_Session.Send (buf, f->len); // resend numResent++; @@ -487,36 +484,32 @@ } } - 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); }); - } + for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) + { + 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); + } + else + ++it; + } - void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) + if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || ts > m_LastMessageReceivedTime + DECAY_INTERVAL) + // decay + m_ReceivedMessages.clear (); + else { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) + // delete old received messages + for (auto it = m_ReceivedMessages.begin (); it != m_ReceivedMessages.end ();) { - 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); - } + 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 -Nru i2pd-2.39.0/libi2pd/SSUData.h i2pd-2.43.0/libi2pd/SSUData.h --- i2pd-2.39.0/libi2pd/SSUData.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/SSUData.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -11,35 +11,29 @@ #include #include -#include #include -#include +#include +#include #include #include #include "I2NPProtocol.h" #include "Identity.h" #include "RouterInfo.h" +#include "TransportSession.h" namespace i2p { namespace transport { - const size_t SSU_MTU_V4 = 1484; - #ifdef MESHNET - const size_t SSU_MTU_V6 = 1286; - #else const size_t SSU_MTU_V6 = 1488; - #endif - const size_t IPV4_HEADER_SIZE = 20; - const size_t IPV6_HEADER_SIZE = 40; - const size_t UDP_HEADER_SIZE = 8; const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456 const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440 const int RESEND_INTERVAL = 3; // in seconds 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 +58,7 @@ 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,16 +70,16 @@ 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), + IncompleteMessage (std::shared_ptr&& m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0), receivedFragmentsBits (0) {}; void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); }; struct SentMessage { - std::vector > fragments; + std::vector > fragments; uint32_t nextResendTime; // in seconds int numResends; }; @@ -100,6 +94,7 @@ void Start (); void Stop (); + void CleanUp (uint64_t ts); void ProcessMessage (uint8_t * buf, size_t len); void FlushReceivedMessage (); @@ -119,17 +114,13 @@ 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 -Nru i2pd-2.39.0/libi2pd/SSU.h i2pd-2.43.0/libi2pd/SSU.h --- i2pd-2.39.0/libi2pd/SSU.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/SSU.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -18,6 +18,7 @@ #include #include #include "Crypto.h" +#include "util.h" #include "I2PEndian.h" #include "Identity.h" #include "RouterInfo.h" @@ -63,16 +64,21 @@ 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 (); }; + bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; }; void SetLocalAddress (const boost::asio::ip::address& localAddress); - + void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); void AddRelay (uint32_t tag, std::shared_ptr relay); void RemoveRelay (uint32_t tag); std::shared_ptr FindRelaySession (uint32_t tag); void RescheduleIntroducersUpdateTimer (); void RescheduleIntroducersUpdateTimerV6 (); - + void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr session = nullptr); PeerTestParticipant GetPeerTestParticipant (uint32_t nonce); std::shared_ptr GetPeerTestSession (uint32_t nonce); @@ -93,7 +99,7 @@ void HandleReceivedPackets (std::vector packets, std::map >* sessions); - void CreateSessionThroughIntroducer (std::shared_ptr router, + void CreateSessionThroughIntroducer (std::shared_ptr router, std::shared_ptr address, bool peerTest = false); template std::shared_ptr GetRandomV4Session (Filter filter); @@ -129,13 +135,19 @@ boost::asio::io_service::work m_Work, m_ReceiversWork, m_ReceiversWorkV6; boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6; boost::asio::ip::udp::socket m_Socket, m_SocketV6; - boost::asio::deadline_timer m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6, + boost::asio::deadline_timer m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6, m_PeerTestsCleanupTimer, m_TerminationTimer, m_TerminationTimerV6; + bool m_IsSyncClockFromPeers; std::list m_Introducers, m_IntroducersV6; // introducers we are connected to std::map > m_Sessions, m_SessionsV6; 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 -Nru i2pd-2.39.0/libi2pd/SSUSession.cpp i2pd-2.43.0/libi2pd/SSUSession.cpp --- i2pd-2.39.0/libi2pd/SSUSession.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/SSUSession.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -31,7 +31,7 @@ { // we are client auto address = IsV6 () ? router->GetSSUV6Address () : router->GetSSUAddress (true); - if (address) m_IntroKey = address->ssu->key; + if (address) m_IntroKey = address->i; m_Data.AdjustPacketSize (router); // mtu } else @@ -39,9 +39,8 @@ // we are server auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : i2p::context.GetRouterInfo ().GetSSUAddress (true); - if (address) m_IntroKey = address->ssu->key; + if (address) m_IntroKey = address->i; } - m_CreationTime = i2p::util::GetSecondsSinceEpoch (); } SSUSession::~SSUSession () @@ -79,7 +78,7 @@ nonZero++; if (nonZero - sharedKey > 32) { - LogPrint (eLogWarning, "SSU: first 32 bytes of shared key is all zeros. Ignored"); + LogPrint (eLogWarning, "SSU: First 32 bytes of shared key is all zeros. Ignored"); return; } } @@ -124,11 +123,11 @@ i2p::context.GetRouterInfo ().GetSSUAddress (true); if (!address) { - LogPrint (eLogInfo, "SSU is not supported"); + LogPrint (eLogInfo, "SSU: SSU is not supported"); return; } - if (Validate (buf, len, address->ssu->key)) - Decrypt (buf, len, address->ssu->key); + if (Validate (buf, len, address->i)) + Decrypt (buf, len, address->i); else { LogPrint (eLogWarning, "SSU: MAC verification failed ", len, " bytes from ", senderEndpoint); @@ -158,7 +157,7 @@ auto headerSize = GetSSUHeaderSize (buf); if (headerSize >= len) { - LogPrint (eLogError, "SSU header size ", headerSize, " exceeds packet length ", len); + LogPrint (eLogError, "SSU: SSU header size ", headerSize, " exceeds packet length ", len); return; } SSUHeader * header = (SSUHeader *)buf; @@ -177,12 +176,12 @@ ProcessSessionConfirmed (buf, len); // buf with header break; case PAYLOAD_TYPE_PEER_TEST: - LogPrint (eLogDebug, "SSU: peer test received"); + LogPrint (eLogDebug, "SSU: Peer test received"); ProcessPeerTest (buf + headerSize, len - headerSize, senderEndpoint); break; case PAYLOAD_TYPE_SESSION_DESTROYED: { - LogPrint (eLogDebug, "SSU: session destroy received"); + LogPrint (eLogDebug, "SSU: Session destroy received"); m_Server.DeleteSession (shared_from_this ()); break; } @@ -192,11 +191,11 @@ m_Server.DeleteSession (shared_from_this ()); break; case PAYLOAD_TYPE_RELAY_REQUEST: - LogPrint (eLogDebug, "SSU: relay request received"); + LogPrint (eLogDebug, "SSU: Relay request received"); ProcessRelayRequest (buf + headerSize, len - headerSize, senderEndpoint); break; case PAYLOAD_TYPE_RELAY_INTRO: - LogPrint (eLogDebug, "SSU: relay intro received"); + LogPrint (eLogDebug, "SSU: Relay intro received"); ProcessRelayIntro (buf + headerSize, len - headerSize); break; default: @@ -206,7 +205,7 @@ void SSUSession::ProcessSessionRequest (const uint8_t * buf, size_t len) { - LogPrint (eLogDebug, "SSU message: session request"); + LogPrint (eLogDebug, "SSU message: Session request"); bool sendRelayTag = true; auto headerSize = sizeof (SSUHeader); if (((SSUHeader *)buf)->IsExtendedOptions ()) @@ -222,7 +221,7 @@ } if (headerSize >= len) { - LogPrint (eLogError, "Session request header size ", headerSize, " exceeds packet length ", len); + LogPrint (eLogError, "SSU message: Session request header size ", headerSize, " exceeds packet length ", len); return; } if (!m_DHKeysPair) @@ -230,7 +229,7 @@ auto pair = std::make_shared (); pair->GenerateKeys (); m_DHKeysPair = pair; - } + } CreateAESandMacKey (buf + headerSize); SendSessionCreated (buf + headerSize, sendRelayTag); } @@ -249,7 +248,7 @@ auto headerSize = GetSSUHeaderSize (buf); if (headerSize >= len) { - LogPrint (eLogError, "Session created header size ", headerSize, " exceeds packet length ", len); + LogPrint (eLogError, "SSU message: Session created header size ", headerSize, " exceeds packet length ", len); return; } uint8_t * payload = buf + headerSize; @@ -259,7 +258,7 @@ s.Insert (y, 256); // y payload += 256; boost::asio::ip::address ourIP; - uint16_t ourPort = 0; + uint16_t ourPort = 0; auto addressAndPortLen = ExtractIPAddressAndPort (payload, len, ourIP, ourPort); if (!addressAndPortLen) return; uint8_t * ourAddressAndPort = payload + 1; @@ -274,16 +273,7 @@ s.Insert (payload, 8); // relayTag and signed on time m_RelayTag = bufbe32toh (payload); payload += 4; // relayTag - if (ourIP.is_v4 () && i2p::context.GetStatus () == eRouterStatusTesting) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - uint32_t signedOnTime = bufbe32toh(payload); - if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW) - { - LogPrint (eLogError, "SSU: clock skew detected ", (int)ts - signedOnTime, ". Check your clock"); - i2p::context.SetError (eRouterErrorClockSkew); - } - } + uint32_t signedOnTime = bufbe32toh(payload); payload += 4; // signed on time // decrypt signature size_t signatureLen = m_RemoteIdentity->GetSignatureLen (); @@ -295,21 +285,39 @@ // verify signature if (s.Verify (m_RemoteIdentity, payload)) { + if (ourIP.is_v4 () && i2p::context.GetStatus () == eRouterStatusTesting) + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + int offset = (int)ts - signedOnTime; + if (m_Server.IsSyncClockFromPeers ()) + { + if (std::abs (offset) > SSU_CLOCK_THRESHOLD) + { + LogPrint (eLogWarning, "SSU: Clock adjusted by ", -offset, " seconds"); + i2p::util::AdjustTimeOffset (-offset); + } + } + else if (std::abs (offset) > SSU_CLOCK_SKEW) + { + LogPrint (eLogError, "SSU: Clock skew detected ", offset, ". Check your clock"); + i2p::context.SetError (eRouterErrorClockSkew); + } + } LogPrint (eLogInfo, "SSU: Our external address is ", ourIP.to_string (), ":", ourPort); if (!i2p::util::net::IsInReservedRange (ourIP)) - { + { i2p::context.UpdateAddress (ourIP); - SendSessionConfirmed (y, ourAddressAndPort, addressAndPortLen); - } + SendSessionConfirmed (y, ourAddressAndPort, addressAndPortLen); + } else - { - LogPrint (eLogError, "SSU: Wrong external address ", ourIP.to_string ()); + { + LogPrint (eLogError, "SSU: External address ", ourIP.to_string (), " is in reserved range"); Failed (); - } + } } else { - LogPrint (eLogError, "SSU: message 'created' signature verification failed"); + LogPrint (eLogError, "SSU: Message 'created' signature verification failed"); Failed (); } } @@ -317,7 +325,7 @@ void SSUSession::ProcessSessionConfirmed (const uint8_t * buf, size_t len) { LogPrint (eLogDebug, "SSU: Session confirmed received"); - m_ConnectTimer.cancel (); + m_ConnectTimer.cancel (); auto headerSize = GetSSUHeaderSize (buf); if (headerSize >= len) { @@ -331,7 +339,7 @@ { LogPrint (eLogError, "SSU: Session confirmed identity size ", identitySize, " exceeds packet length ", len); return; - } + } payload += 2; // size of identity fragment auto identity = std::make_shared (payload, identitySize); auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already @@ -342,7 +350,7 @@ uint32_t signedOnTime = bufbe32toh(payload); if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW) { - LogPrint (eLogError, "SSU message 'confirmed' time difference ", (int)ts - signedOnTime, " exceeds clock skew"); + LogPrint (eLogError, "SSU: Message 'confirmed' time difference ", (int)ts - signedOnTime, " exceeds clock skew"); Failed (); return; } @@ -357,7 +365,7 @@ { LogPrint (eLogError, "SSU: Session confirmed message is too short ", len); return; - } + } // verify signature if (m_SignedData && m_SignedData->Verify (m_RemoteIdentity, payload)) { @@ -366,7 +374,7 @@ } else { - LogPrint (eLogError, "SSU message 'confirmed' signature verification failed"); + LogPrint (eLogError, "SSU: Message 'confirmed' signature verification failed"); Failed (); } } @@ -379,11 +387,11 @@ // fill extended options, 3 bytes extended options don't change message size bool isV4 = m_RemoteEndpoint.address ().is_v4 (); if ((isV4 && i2p::context.GetStatus () == eRouterStatusOK) || - (!isV4 && i2p::context.GetStatusV6 () == eRouterStatusOK)) // we don't need relays + (!isV4 && i2p::context.GetStatusV6 () == eRouterStatusOK)) // we don't need relays { // tell out peer to now assign relay tag flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; - *payload = 2; payload++; // 1 byte length + *payload = 2; payload++; // 1 byte length uint16_t flags = 0; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG htobe16buf (payload, flags); payload += 2; @@ -413,7 +421,7 @@ i2p::context.GetRouterInfo ().GetSSUAddress (true); if (!address) { - LogPrint (eLogInfo, "SSU is not supported"); + LogPrint (eLogInfo, "SSU: SSU is not supported"); return; } @@ -427,7 +435,7 @@ payload += 2; *payload = 0; // challenge payload++; - memcpy (payload, (const uint8_t *)address->ssu->key, 32); + memcpy (payload, (const uint8_t *)address->i, 32); payload += 32; htobe32buf (payload, nonce); // nonce @@ -438,7 +446,7 @@ else FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, introducer.iKey, iv, introducer.iKey); m_Server.Send (buf, 96, m_RemoteEndpoint); - LogPrint (eLogDebug, "SSU: relay request sent"); + LogPrint (eLogDebug, "SSU: Relay request sent"); } void SSUSession::SendSessionCreated (const uint8_t * x, bool sendRelayTag) @@ -447,7 +455,7 @@ i2p::context.GetRouterInfo ().GetSSUAddress (true); //v4 only if (!address) { - LogPrint (eLogInfo, "SSU is not supported"); + LogPrint (eLogInfo, "SSU: SSU is not supported"); return; } SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time @@ -599,19 +607,19 @@ uint8_t * payload = buf + sizeof (SSUHeader); // Charlie if (isV4) - { + { *payload = 4; payload++; // size memcpy (payload, to.address ().to_v4 ().to_bytes ().data (), 4); // Charlie's IP V4 payload += 4; // address - } + } else { *payload = 16; payload++; // size - memcpy (payload, to.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6 + memcpy (payload, to.address ().to_v6 ().to_bytes ().data (), 16); // Charlie's IP V6 payload += 16; // address - } + } htobe16buf (payload, to.port ()); // Charlie's port payload += 2; // port // Alice @@ -647,7 +655,7 @@ FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80, introKey, iv, introKey); m_Server.Send (buf, isV4 ? 64 : 80, from); } - LogPrint (eLogDebug, "SSU: relay response sent"); + LogPrint (eLogDebug, "SSU: Relay response sent"); } void SSUSession::SendRelayIntro (std::shared_ptr session, const boost::asio::ip::udp::endpoint& from) @@ -683,7 +691,7 @@ RAND_bytes (iv, 16); // random iv FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, isV4 ? 48 : 64, session->m_SessionKey, iv, session->m_MacKey); m_Server.Send (buf, isV4 ? 48 : 64, session->m_RemoteEndpoint); - LogPrint (eLogDebug, "SSU: relay intro sent"); + LogPrint (eLogDebug, "SSU: Relay intro sent"); } void SSUSession::ProcessRelayResponse (const uint8_t * buf, size_t len) @@ -703,17 +711,17 @@ if (!i2p::util::net::IsInReservedRange (ourIP)) i2p::context.UpdateAddress (ourIP); else - LogPrint (eLogWarning, "SSU: Wrong external address ", ourIP.to_string ()); + LogPrint (eLogError, "SSU: External address ", ourIP.to_string (), " is in reserved range"); if (ourIP.is_v4 ()) - { + { if (ourPort != m_Server.GetPort ()) - { + { if (i2p::context.GetStatus () == eRouterStatusTesting) i2p::context.SetError (eRouterErrorSymmetricNAT); } else if (i2p::context.GetStatus () == eRouterStatusError && i2p::context.GetError () == eRouterErrorSymmetricNAT) i2p::context.SetStatus (eRouterStatusTesting); - } + } uint32_t nonce = bufbe32toh (buf); buf += 4; // nonce auto it = m_RelayRequests.find (nonce); @@ -727,10 +735,10 @@ // now we do LogPrint (eLogInfo, "SSU: RelayReponse connecting to endpoint ", remoteEndpoint); if ((remoteIP.is_v4 () && i2p::context.GetStatus () == eRouterStatusFirewalled) || - (remoteIP.is_v6 () && i2p::context.GetStatusV6 () == eRouterStatusFirewalled)) + (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); @@ -803,7 +811,7 @@ htobe16buf (out + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac); } - + void SSUSession::Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey) { if (len < sizeof (SSUHeader)) @@ -872,7 +880,7 @@ if (!IsOutgoing ()) // incoming session ScheduleConnectTimer (); else - LogPrint (eLogError, "SSU: wait for connect for outgoing session"); + LogPrint (eLogError, "SSU: Wait for connect for outgoing session"); } void SSUSession::ScheduleConnectTimer () @@ -888,7 +896,7 @@ if (!ecode) { // timeout expired - LogPrint (eLogWarning, "SSU: session with ", m_RemoteEndpoint, " was not established after ", SSU_CONNECT_TIMEOUT, " seconds"); + LogPrint (eLogWarning, "SSU: Session with ", m_RemoteEndpoint, " was not established after ", SSU_CONNECT_TIMEOUT, " seconds"); Failed (); } } @@ -905,7 +913,8 @@ } 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,10 +1013,22 @@ } } + 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 - boost::asio::ip::address addr; // Alice's addresss + boost::asio::ip::address addr; // Alice's address uint16_t port = 0; // and port auto size = ExtractIPAddressAndPort (buf + 4, len - 4, addr, port); if (port && (size != 7) && (size != 19)) @@ -1023,29 +1044,29 @@ { if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob { - LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice"); + LogPrint (eLogDebug, "SSU: Peer test from Bob. We are Alice"); if (IsV6 ()) { - if (i2p::context.GetStatusV6 () == eRouterStatusTesting) - { + if (i2p::context.GetStatusV6 () == eRouterStatusTesting) + { i2p::context.SetStatusV6 (eRouterStatusFirewalled); m_Server.RescheduleIntroducersUpdateTimerV6 (); - } - } + } + } else if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK - { + { i2p::context.SetStatus (eRouterStatusFirewalled); m_Server.RescheduleIntroducersUpdateTimer (); - } + } } else { - LogPrint (eLogDebug, "SSU: first peer test from Charlie. We are Alice"); + LogPrint (eLogDebug, "SSU: First peer test from Charlie. We are Alice"); if (m_State == eSessionStateEstablished) - LogPrint (eLogWarning, "SSU: first peer test from Charlie through established session. We are Alice"); + LogPrint (eLogWarning, "SSU: First peer test from Charlie through established session. We are Alice"); if (IsV6 ()) i2p::context.SetStatusV6 (eRouterStatusOK); - else + else i2p::context.SetStatus (eRouterStatusOK); m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2); SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie @@ -1055,14 +1076,14 @@ case ePeerTestParticipantAlice2: { if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob - LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice"); + LogPrint (eLogDebug, "SSU: Peer test from Bob. We are Alice"); else { // peer test successive - LogPrint (eLogDebug, "SSU: second peer test from Charlie. We are Alice"); + LogPrint (eLogDebug, "SSU: Second peer test from Charlie. We are Alice"); if (IsV6 ()) - i2p::context.SetStatusV6 (eRouterStatusOK); - else + i2p::context.SetStatusV6 (eRouterStatusOK); + else i2p::context.SetStatus (eRouterStatusOK); m_Server.RemovePeerTest (nonce); } @@ -1070,19 +1091,19 @@ } case ePeerTestParticipantBob: { - LogPrint (eLogDebug, "SSU: peer test from Charlie. We are Bob"); + LogPrint (eLogDebug, "SSU: Peer test from Charlie. We are Bob"); auto session = m_Server.GetPeerTestSession (nonce); // session with Alice from PeerTest if (session && session->m_State == eSessionStateEstablished) { const auto& ep = session->GetRemoteEndpoint (); // Alice's endpoint as known to Bob session->SendPeerTest (nonce, ep.address (), ep.port (), introKey, false, true); // send back to Alice - } + } m_Server.RemovePeerTest (nonce); // nonce has been used break; } case ePeerTestParticipantCharlie: { - LogPrint (eLogDebug, "SSU: peer test from Alice. We are Charlie"); + LogPrint (eLogDebug, "SSU: Peer test from Alice. We are Charlie"); SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey); // to Alice with her actual address m_Server.RemovePeerTest (nonce); // nonce has been used break; @@ -1095,17 +1116,17 @@ // new test if (port) { - LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie"); + LogPrint (eLogDebug, "SSU: Peer test from Bob. We are Charlie"); Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob if (!addr.is_unspecified () && !i2p::util::net::IsInReservedRange(addr)) - { + { m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie); SendPeerTest (nonce, addr, port, introKey); // to Alice with her address received from Bob - } + } } else { - LogPrint (eLogDebug, "SSU: peer test from Alice. We are Bob"); + LogPrint (eLogDebug, "SSU: Peer test from Alice. We are Bob"); auto session = senderEndpoint.address ().is_v4 () ? m_Server.GetRandomEstablishedV4Session (shared_from_this ()) : m_Server.GetRandomEstablishedV6Session (shared_from_this ()); // Charlie if (session) { @@ -1115,7 +1136,7 @@ } } else - LogPrint (eLogError, "SSU: unexpected peer test"); + LogPrint (eLogError, "SSU: Unexpected peer test"); } } } @@ -1161,9 +1182,9 @@ auto addr = address.is_v4 () ? i2p::context.GetRouterInfo ().GetSSUAddress (true) : // ipv4 i2p::context.GetRouterInfo ().GetSSUV6Address (); if (addr) - memcpy (payload, addr->ssu->key, 32); // intro key + memcpy (payload, addr->i, 32); // intro key else - LogPrint (eLogInfo, "SSU is not supported. Can't send peer test"); + LogPrint (eLogInfo, "SSU: SSU is not supported. Can't send peer test"); } else memcpy (payload, introKey, 32); // intro key @@ -1188,11 +1209,11 @@ void SSUSession::SendPeerTest () { // we are Alice - LogPrint (eLogDebug, "SSU: sending peer test"); + LogPrint (eLogDebug, "SSU: Sending peer test"); auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : i2p::context.GetRouterInfo ().GetSSUAddress (true); if (!address) { - LogPrint (eLogInfo, "SSU is not supported. Can't send peer test"); + LogPrint (eLogInfo, "SSU: SSU is not supported. Can't send peer test"); return; } uint32_t nonce; @@ -1200,7 +1221,7 @@ if (!nonce) nonce = 1; m_IsPeerTest = false; m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1, shared_from_this ()); - SendPeerTest (nonce, boost::asio::ip::address(), 0, address->ssu->key, false, false); // address and port always zero for Alice + SendPeerTest (nonce, boost::asio::ip::address(), 0, address->i, false, false); // address and port always zero for Alice } void SSUSession::SendKeepAlive () @@ -1233,9 +1254,9 @@ } catch (std::exception& ex) { - LogPrint (eLogWarning, "SSU: exception while sending session destoroyed: ", ex.what ()); + LogPrint (eLogWarning, "SSU: Exception while sending session destoroyed: ", ex.what ()); } - LogPrint (eLogDebug, "SSU: session destroyed sent"); + LogPrint (eLogDebug, "SSU: Session destroyed sent"); } } @@ -1247,7 +1268,7 @@ if (paddingSize > 0) msgSize += (16 - paddingSize); if (msgSize > SSU_MTU_V4) { - LogPrint (eLogWarning, "SSU: payload size ", msgSize, " exceeds MTU"); + LogPrint (eLogWarning, "SSU: Payload size ", msgSize, " exceeds MTU"); return; } memcpy (buf + sizeof (SSUHeader), payload, len); @@ -1268,12 +1289,12 @@ if (!len) return 0; uint8_t size = *buf; size_t s = 1 + size + 2; // size + address + port - if (len < s) + if (len < s) { LogPrint (eLogWarning, "SSU: Address is too short ", len); port = 0; return len; - } + } buf++; // size if (size == 4) { @@ -1286,12 +1307,12 @@ boost::asio::ip::address_v6::bytes_type bytes; memcpy (bytes.data (), buf, 16); ip = boost::asio::ip::address_v6 (bytes); - } + } else - LogPrint (eLogWarning, "SSU: Address size ", size, " is not supported"); + LogPrint (eLogWarning, "SSU: Address size ", int(size), " is not supported"); buf += size; port = bufbe16toh (buf); return s; - } + } } } diff -Nru i2pd-2.39.0/libi2pd/SSUSession.h i2pd-2.43.0/libi2pd/SSUSession.h --- i2pd-2.39.0/libi2pd/SSUSession.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/SSUSession.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -36,6 +36,7 @@ const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes const int SSU_CLOCK_SKEW = 60; // in seconds + const int SSU_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust const size_t SSU_MAX_I2NP_MESSAGE_SIZE = 32768; // payload types (4 bits) @@ -89,6 +90,7 @@ 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); @@ -101,10 +103,9 @@ void SendKeepAlive (); uint32_t GetRelayTag () const { return m_RelayTag; }; const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; }; - uint32_t GetCreationTime () const { return m_CreationTime; }; - void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers - + void FlushData (); + void CleanUp (uint64_t ts); private: @@ -147,7 +148,7 @@ void Reset (); static size_t ExtractIPAddressAndPort (const uint8_t * buf, size_t len, boost::asio::ip::address& ip, uint16_t& port); // returns actual buf size - + private: friend class SSUData; // TODO: change in later @@ -164,11 +165,10 @@ i2p::crypto::AESKey m_SessionKey; i2p::crypto::MACKey m_MacKey; i2p::data::RouterInfo::IntroKey m_IntroKey; - uint32_t m_CreationTime; // seconds since epoch SSUData m_Data; bool m_IsDataReceived; std::unique_ptr 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 -Nru i2pd-2.39.0/libi2pd/Streaming.cpp i2pd-2.43.0/libi2pd/Streaming.cpp --- i2pd-2.39.0/libi2pd/Streaming.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Streaming.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -27,12 +27,12 @@ void SendBufferQueue::Add (std::shared_ptr buf) { if (buf) - { + { m_Buffers.push_back (buf); m_Size += buf->len; - } - } - + } + } + size_t SendBufferQueue::Get (uint8_t * buf, size_t len) { size_t offset = 0; @@ -102,7 +102,7 @@ LogPrint (eLogDebug, "Streaming: Stream deleted"); } - void Stream::Terminate (bool deleteFromDestination) // shoudl be called from StreamingDestination::Stop only + void Stream::Terminate (bool deleteFromDestination) // should be called from StreamingDestination::Stop only { m_Status = eStreamStatusTerminated; m_AckSendTimer.cancel (); @@ -182,6 +182,7 @@ m_IsAckSendScheduled = true; auto ackTimeout = m_RTT/10; if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; + else if (ackTimeout < MIN_SEND_ACK_TIMEOUT) ackTimeout = MIN_SEND_ACK_TIMEOUT; m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ackTimeout)); m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, shared_from_this (), std::placeholders::_1)); @@ -277,20 +278,20 @@ 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) { @@ -391,10 +392,10 @@ p.len = payloadLen + 22; SendPackets (std::vector { &p }); LogPrint (eLogDebug, "Streaming: Pong of ", p.len, " bytes sent"); - } + } m_LocalDestination.DeletePacket (packet); - } - + } + void Stream::ProcessAck (Packet * packet) { bool acknowledged = false; @@ -665,6 +666,42 @@ 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); @@ -792,13 +829,6 @@ m_RTO = m_RTT*1.5; // TODO: implement it better } } - if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ()) - m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); - if (!m_CurrentOutboundTunnel) - { - LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID); - return; - } auto ts = i2p::util::GetMillisecondsSinceEpoch (); if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate || // excluded from LeaseSet @@ -806,6 +836,21 @@ UpdateCurrentRemoteLease (true); if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) { + if (!m_CurrentOutboundTunnel) + { + auto leaseRouter = i2p::data::netdb.FindRouter (m_CurrentRemoteLease->tunnelGateway); + m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (nullptr, + leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports); + } + else if (!m_CurrentOutboundTunnel->IsEstablished ()) + m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); + if (!m_CurrentOutboundTunnel) + { + LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID); + m_CurrentRemoteLease = nullptr; + return; + } + std::vector msgs; for (const auto& it: packets) { @@ -859,14 +904,14 @@ void Stream::ScheduleResend () { 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) @@ -964,16 +1009,16 @@ { LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), m_RemoteLeaseSet ? " expired" : " not found"); if (m_RemoteLeaseSet && m_RemoteLeaseSet->IsPublishedEncrypted ()) - { + { m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet ( std::make_shared(m_RemoteIdentity)); return; // we keep m_RemoteLeaseSet for possible next request - } - else - { - m_RemoteLeaseSet = nullptr; + } + else + { + m_RemoteLeaseSet = nullptr; m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt - } + } } else { @@ -1040,6 +1085,8 @@ 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 () @@ -1067,7 +1114,7 @@ it.second->Terminate (false); // we delete here m_Streams.clear (); m_IncomingStreams.clear (); - m_LastStream = nullptr; + m_LastStream = nullptr; } } @@ -1077,7 +1124,7 @@ if (sendStreamID) { if (!m_LastStream || sendStreamID != m_LastStream->GetRecvStreamID ()) - { + { auto it = m_Streams.find (sendStreamID); if (it != m_Streams.end ()) m_LastStream = it->second; @@ -1092,7 +1139,7 @@ LogPrint (eLogInfo, "Streaming: Ping received sSID=", sendStreamID); auto s = std::make_shared (m_Owner->GetService (), *this); s->HandlePing (packet); - } + } else { LogPrint (eLogInfo, "Streaming: Unknown stream sSID=", sendStreamID); @@ -1101,6 +1148,13 @@ } 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 (); @@ -1195,6 +1249,12 @@ 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); @@ -1213,6 +1273,11 @@ m_IncomingStreams.erase (stream->GetSendStreamID ()); if (m_LastStream == stream) m_LastStream = nullptr; } + if (m_Streams.empty ()) + { + m_PacketsPool.CleanUp (); + m_I2NPMsgsPool.CleanUp (); + } } bool StreamingDestination::DeleteStream (uint32_t recvStreamID) @@ -1220,7 +1285,13 @@ auto it = m_Streams.find (recvStreamID); if (it == m_Streams.end ()) return false; - DeleteStream (it->second); + auto s = it->second; + m_Owner->GetService ().post ([this, s] () + { + s->Close (); // try to send FIN + s->Terminate (false); + DeleteStream (s); + }); return true; } @@ -1296,13 +1367,17 @@ 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 -Nru i2pd-2.39.0/libi2pd/Streaming.h i2pd-2.43.0/libi2pd/Streaming.h --- i2pd-2.39.0/libi2pd/Streaming.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Streaming.h 2022-08-21 19:40:41.000000000 +0000 @@ -58,6 +58,7 @@ const int MAX_WINDOW_SIZE = 128; const int INITIAL_RTT = 8000; // in milliseconds const int INITIAL_RTO = 9000; // in milliseconds + const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds const int SYN_TIMEOUT = 200; // how long we wait for SYN after follow-on, in milliseconds const size_t MAX_PENDING_INCOMING_BACKLOG = 128; const int PENDING_INCOMING_TIMEOUT = 10; // in seconds @@ -111,11 +112,11 @@ buf = new uint8_t[len]; memcpy (buf, b, len); } - SendBuffer (size_t l): // creat empty buffer - len(l), offset (0) + SendBuffer (size_t l): // create empty buffer + len(l), offset (0) { buf = new uint8_t[len]; - } + } ~SendBuffer () { delete[] buf; @@ -179,6 +180,7 @@ 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); @@ -261,13 +263,14 @@ 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); @@ -312,7 +315,7 @@ 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; }; @@ -364,7 +367,7 @@ handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received); else { - // itermediate iterrupt + // itermediate interrupt SendUpdatedLeaseSet (); // send our leaseset if applicable AsyncReceive (buffer, handler, remainingTimeout); } diff -Nru i2pd-2.39.0/libi2pd/Tag.h i2pd-2.43.0/libi2pd/Tag.h --- i2pd-2.39.0/libi2pd/Tag.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Tag.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -9,14 +9,6 @@ #ifndef TAG_H__ #define TAG_H__ -/* -* Copyright (c) 2013-2017, 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 @@ -64,17 +56,17 @@ RAND_bytes(m_Buf, sz); } - std::string ToBase64 () const + std::string ToBase64 (size_t len = sz) const { char str[sz*2]; - size_t l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2); + size_t l = i2p::data::ByteStreamToBase64 (m_Buf, len, str, sz*2); return std::string (str, str + l); } - std::string ToBase32 () const + std::string ToBase32 (size_t len = sz) const { char str[sz*2]; - size_t l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2); + size_t l = i2p::data::ByteStreamToBase32 (m_Buf, len, str, sz*2); return std::string (str, str + l); } diff -Nru i2pd-2.39.0/libi2pd/Timestamp.cpp i2pd-2.43.0/libi2pd/Timestamp.cpp --- i2pd-2.39.0/libi2pd/Timestamp.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Timestamp.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -48,7 +48,7 @@ return std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count (); } - + static uint32_t GetLocalHoursSinceEpoch () { return std::chrono::duration_cast( @@ -70,23 +70,23 @@ 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; + { + 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++; } @@ -94,8 +94,8 @@ { LogPrint (eLogError, "Timestamp: can't find compatible address for ", address); return; - } - + } + boost::asio::ip::udp::socket socket (service); socket.open (ep.protocol (), ec); if (!ec) @@ -220,13 +220,13 @@ uint64_t GetSecondsSinceEpoch () { return GetLocalSecondsSinceEpoch () + g_TimeOffset; - } - + } + uint32_t GetMinutesSinceEpoch () { return GetLocalMinutesSinceEpoch () + g_TimeOffset/60; } - + uint32_t GetHoursSinceEpoch () { return GetLocalHoursSinceEpoch () + g_TimeOffset/3600; @@ -250,5 +250,10 @@ sprintf(date, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); #endif } + + void AdjustTimeOffset (int64_t offset) + { + g_TimeOffset += offset; + } } } diff -Nru i2pd-2.39.0/libi2pd/Timestamp.h i2pd-2.43.0/libi2pd/Timestamp.h --- i2pd-2.39.0/libi2pd/Timestamp.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Timestamp.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -26,6 +26,7 @@ void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes void GetDateString (uint64_t timestamp, char * date); // timestap is seconds since epoch, returns date as YYYYMMDD string, 9 bytes + void AdjustTimeOffset (int64_t offset); // in seconds from current class NTPTimeSync { diff -Nru i2pd-2.39.0/libi2pd/TransitTunnel.cpp i2pd-2.43.0/libi2pd/TransitTunnel.cpp --- i2pd-2.39.0/libi2pd/TransitTunnel.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TransitTunnel.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -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 * @@ -37,15 +37,14 @@ { } - void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) + void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) { - auto newMsg = CreateEmptyTunnelDataMsg (false); - EncryptTunnelMsg (tunnelMsg, newMsg); + EncryptTunnelMsg (tunnelMsg, tunnelMsg); m_NumTransmittedBytes += tunnelMsg->GetLength (); - htobe32buf (newMsg->GetPayload (), GetNextTunnelID ()); - newMsg->FillI2NPMessageHeader (eI2NPTunnelData); - m_TunnelDataMsgs.push_back (newMsg); + htobe32buf (tunnelMsg->GetPayload (), GetNextTunnelID ()); + tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData); + m_TunnelDataMsgs.push_back (tunnelMsg); } void TransitTunnelParticipant::FlushTunnelDataMsgs () @@ -65,7 +64,7 @@ LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ()); } - void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) + void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) { LogPrint (eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID ()); } @@ -85,7 +84,7 @@ m_Gateway.SendBuffer (); } - void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) + void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) { auto newMsg = CreateEmptyTunnelDataMsg (true); EncryptTunnelMsg (tunnelMsg, newMsg); diff -Nru i2pd-2.39.0/libi2pd/TransitTunnel.h i2pd-2.43.0/libi2pd/TransitTunnel.h --- i2pd-2.39.0/libi2pd/TransitTunnel.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TransitTunnel.h 2022-08-21 19:40:41.000000000 +0000 @@ -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 * @@ -35,7 +35,7 @@ // implements TunnelBase void SendTunnelDataMsg (std::shared_ptr msg); - void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); + void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg); void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out); private: @@ -54,7 +54,7 @@ ~TransitTunnelParticipant (); size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; }; - void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); + void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg); void FlushTunnelDataMsgs (); private: @@ -95,7 +95,7 @@ void Cleanup () { m_Endpoint.Cleanup (); } - void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); + void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg); size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); } private: diff -Nru i2pd-2.39.0/libi2pd/Transports.cpp i2pd-2.43.0/libi2pd/Transports.cpp --- i2pd-2.39.0/libi2pd/Transports.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Transports.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -128,7 +128,7 @@ m_Queue.push (pair); } else - LogPrint(eLogError, "Transports: return null DHKeys"); + LogPrint(eLogError, "Transports: Return null DHKeys"); } Transports transports; @@ -136,7 +136,7 @@ Transports::Transports (): m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_CheckReserved(true), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr), - m_SSUServer (nullptr), m_NTCP2Server (nullptr), + m_SSUServer (nullptr), m_SSU2Server (nullptr), m_NTCP2Server (nullptr), m_X25519KeysPairSupplier (15), // 15 pre-generated keys m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0), m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0), @@ -157,7 +157,7 @@ } } - void Transports::Start (bool enableNTCP2, bool enableSSU) + void Transports::Start (bool enableNTCP2, bool enableSSU, bool enableSSU2) { if (!m_Service) { @@ -192,10 +192,10 @@ i2p::context.SetStatus (eRouterStatusProxy); } else - LogPrint(eLogError, "Transports: unsupported NTCP2 proxy URL ", ntcp2proxy); + LogPrint(eLogError, "Transports: Unsupported NTCP2 proxy URL ", ntcp2proxy); } else - LogPrint(eLogError, "Transports: invalid NTCP2 proxy url ", ntcp2proxy); + LogPrint(eLogError, "Transports: Invalid NTCP2 proxy URL ", ntcp2proxy); } else m_NTCP2Server = new NTCP2Server (); @@ -204,7 +204,7 @@ // create SSU server int ssuPort = 0; if (enableSSU) - { + { auto& addresses = context.GetRouterInfo ().GetAddresses (); for (const auto& address: addresses) { @@ -216,39 +216,43 @@ break; } } - } - + } + // create SSU2 server + if (enableSSU2) m_SSU2Server = new SSU2Server (); + // bind to interfaces bool ipv4; i2p::config::GetOption("ipv4", ipv4); if (ipv4) { std::string address; i2p::config::GetOption("address4", address); if (!address.empty ()) - { + { boost::system::error_code ec; auto addr = boost::asio::ip::address::from_string (address, ec); if (!ec) - { + { if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr); if (m_SSUServer) m_SSUServer->SetLocalAddress (addr); - } - } - } + if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr); + } + } + } bool ipv6; i2p::config::GetOption("ipv6", ipv6); if (ipv6) { std::string address; i2p::config::GetOption("address6", address); if (!address.empty ()) - { + { boost::system::error_code ec; auto addr = boost::asio::ip::address::from_string (address, ec); - if (!ec) - { + if (!ec) + { if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr); if (m_SSUServer) m_SSUServer->SetLocalAddress (addr); - } - } + if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr); + } + } } bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); @@ -256,33 +260,34 @@ { std::string address; i2p::config::GetOption("meshnets.yggaddress", address); if (!address.empty ()) - { + { boost::system::error_code ec; auto addr = boost::asio::ip::address::from_string (address, ec); if (!ec && m_NTCP2Server && i2p::util::net::IsYggdrasilAddress (addr)) m_NTCP2Server->SetLocalAddress (addr); - } + } } // start servers if (m_NTCP2Server) m_NTCP2Server->Start (); + if (m_SSU2Server) m_SSU2Server->Start (); if (m_SSUServer) { LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort); - try + try { m_SSUServer->Start (); - } - catch (std::exception& ex ) + } + catch (std::exception& ex ) { LogPrint(eLogError, "Transports: Failed to bind to UDP port", ssuPort); m_SSUServer->Stop (); delete m_SSUServer; m_SSUServer = nullptr; } - if (m_SSUServer) DetectExternalIP (); - } - + } + if (m_SSUServer || m_SSU2Server) DetectExternalIP (); + m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); @@ -305,6 +310,13 @@ m_SSUServer = nullptr; } + if (m_SSU2Server) + { + m_SSU2Server->Stop (); + delete m_SSU2Server; + m_SSU2Server = nullptr; + } + if (m_NTCP2Server) { m_NTCP2Server->Stop (); @@ -335,7 +347,7 @@ } catch (std::exception& ex) { - LogPrint (eLogError, "Transports: runtime exception: ", ex.what ()); + LogPrint (eLogError, "Transports: Runtime exception: ", ex.what ()); } } } @@ -389,7 +401,7 @@ { // we send it to ourself for (auto& it: msgs) - m_LoopbackHandler.PutNextMessage (it); + m_LoopbackHandler.PutNextMessage (std::move (it)); m_LoopbackHandler.Flush (); return; } @@ -403,9 +415,10 @@ auto r = netdb.FindRouter (ident); if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable { + auto ts = i2p::util::GetSecondsSinceEpoch (); std::unique_lock l(m_PeersMutex); it = m_Peers.insert (std::pair(ident, { 0, r, {}, - i2p::util::GetSecondsSinceEpoch (), {} })).first; + ts, ts + PEER_ROUTER_INFO_UPDATE_INTERVAL, {} })).first; } connected = ConnectToPeer (ident, it->second); } @@ -426,7 +439,7 @@ } else { - LogPrint (eLogWarning, "Transports: delayed messages queue size to ", + LogPrint (eLogWarning, "Transports: Delayed messages queue size to ", ident.ToBase64 (), " exceeds ", MAX_NUM_DELAYED_MESSAGES); std::unique_lock l(m_PeersMutex); m_Peers.erase (it); @@ -440,7 +453,7 @@ peer.router = netdb.FindRouter (ident); // try to get new one from netdb if (peer.router) // we have RI already { - if (peer.numAttempts < 2) // NTCP2, 0 - ipv6, 1- ipv4 + if (peer.numAttempts < 2) // NTCP2, 0 - ipv6, 1 - ipv4 { if (m_NTCP2Server) // we support NTCP2 { @@ -448,23 +461,23 @@ if (!peer.numAttempts) // NTCP2 ipv6 { if (context.GetRouterInfo ().IsNTCP2V6 () && peer.router->IsReachableBy (RouterInfo::eNTCP2V6)) - { + { address = peer.router->GetPublishedNTCP2V6Address (); if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) address = nullptr; - } + } peer.numAttempts++; } - if (!address && peer.numAttempts == 1) // NTCP2 ipv4 - { + if (!address && peer.numAttempts == 1) // NTCP2 ipv4 + { if (context.GetRouterInfo ().IsNTCP2 (true) && peer.router->IsReachableBy (RouterInfo::eNTCP2V4)) - { + { address = peer.router->GetPublishedNTCP2V4Address (); if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) address = nullptr; - } + } peer.numAttempts++; - } + } if (address) { auto s = std::make_shared (*m_NTCP2Server, peer.router, address); @@ -478,26 +491,26 @@ else peer.numAttempts = 2; // switch to SSU } - if (peer.numAttempts == 2 || peer.numAttempts == 3) // SSU + if (peer.numAttempts == 2 || peer.numAttempts == 3) // SSU2, 2 - ipv6, 3 - ipv4 { - if (m_SSUServer) - { + if (m_SSU2Server) + { std::shared_ptr address; - if (peer.numAttempts == 2) // SSU ipv6 + if (peer.numAttempts == 2) // SSU2 ipv6 { - if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsReachableBy (RouterInfo::eSSUV6)) + if (context.GetRouterInfo ().IsSSU2V6 () && peer.router->IsReachableBy (RouterInfo::eSSU2V6)) { - address = peer.router->GetSSUV6Address (); + address = peer.router->GetSSU2V6Address (); if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) address = nullptr; } peer.numAttempts++; } - if (!address && peer.numAttempts == 3) // SSU ipv4 + if (!address && peer.numAttempts == 3) // SSU2 ipv4 { - if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsReachableBy (RouterInfo::eSSUV4)) + if (context.GetRouterInfo ().IsSSU2V4 () && peer.router->IsReachableBy (RouterInfo::eSSU2V4)) { - address = peer.router->GetSSUAddress (true); + address = peer.router->GetSSU2V4Address (); if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) address = nullptr; } @@ -505,12 +518,12 @@ } if (address && address->IsReachableSSU ()) { - if (m_SSUServer->CreateSession (peer.router, address)) + if (m_SSU2Server->CreateSession (peer.router, address)) return true; - } + } } else - peer.numAttempts += 2; // switch to Mesh + peer.numAttempts += 2; // switch to mesh } if (peer.numAttempts == 4) // Mesh { @@ -523,8 +536,42 @@ auto s = std::make_shared (*m_NTCP2Server, peer.router, address); m_NTCP2Server->Connect (s); return true; - } - } + } + } + } + if (peer.numAttempts == 5 || peer.numAttempts == 6) // SSU, 5 - ipv6, 6 - ipv4 + { + if (m_SSUServer) + { + std::shared_ptr address; + if (peer.numAttempts == 5) // SSU ipv6 + { + if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsReachableBy (RouterInfo::eSSUV6)) + { + address = peer.router->GetSSUV6Address (); + if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + address = nullptr; + } + peer.numAttempts++; + } + if (!address && peer.numAttempts == 6) // SSU ipv4 + { + if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsReachableBy (RouterInfo::eSSUV4)) + { + address = peer.router->GetSSUAddress (true); + if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + address = nullptr; + } + peer.numAttempts++; + } + if (address && address->IsReachableSSU ()) + { + if (m_SSUServer->CreateSession (peer.router, address)) + return true; + } + } + else + peer.numAttempts += 2; } LogPrint (eLogInfo, "Transports: No compatble NTCP2 or SSU addresses available"); i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed @@ -554,13 +601,13 @@ { if (r) { - LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, Trying to connect"); + LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, trying to connect"); it->second.router = r; ConnectToPeer (ident, it->second); } else { - LogPrint (eLogWarning, "Transports: RouterInfo not found, Failed to send messages"); + LogPrint (eLogWarning, "Transports: RouterInfo not found, failed to send messages"); std::unique_lock l(m_PeersMutex); m_Peers.erase (it); } @@ -571,71 +618,114 @@ { if (RoutesRestricted()) { - LogPrint(eLogInfo, "Transports: restricted routes enabled, not detecting ip"); + LogPrint(eLogInfo, "Transports: Restricted routes enabled, not detecting IP"); i2p::context.SetStatus (eRouterStatusOK); return; } - if (m_SSUServer) + if (m_SSUServer || m_SSU2Server) PeerTest (); else - LogPrint (eLogError, "Transports: Can't detect external IP. SSU is not available"); + LogPrint (eLogWarning, "Transports: Can't detect external IP. SSU or SSU2 is not available"); } void Transports::PeerTest (bool ipv4, bool ipv6) { - if (RoutesRestricted() || !m_SSUServer) return; + if (RoutesRestricted() || (!m_SSUServer && !m_SSU2Server)) return; if (ipv4 && i2p::context.SupportsV4 ()) { - LogPrint (eLogInfo, "Transports: Started peer test ipv4"); + LogPrint (eLogInfo, "Transports: Started peer test IPv4"); std::set excluded; - bool statusChanged = false; - for (int i = 0; i < 5; i++) + excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router + if (m_SSUServer) { - auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4 - if (router) + bool statusChanged = false; + for (int i = 0; i < 5; i++) { - auto addr = router->GetSSUAddress (true); // ipv4 - if (addr && !i2p::util::net::IsInReservedRange(addr->host)) + auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4 + if (router) { - if (!statusChanged) + auto addr = router->GetSSUAddress (true); // ipv4 + if (addr && !i2p::util::net::IsInReservedRange(addr->host)) { - statusChanged = true; - i2p::context.SetStatus (eRouterStatusTesting); // first time only + if (!statusChanged) + { + statusChanged = true; + i2p::context.SetStatus (eRouterStatusTesting); // first time only + } + m_SSUServer->CreateSession (router, addr, true); // peer test v4 } - m_SSUServer->CreateSession (router, addr, true); // peer test v4 - } - excluded.insert (router->GetIdentHash ()); + excluded.insert (router->GetIdentHash ()); + } + } + if (!statusChanged) + LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4"); + } + // SSU2 + if (m_SSU2Server) + { + excluded.clear (); + excluded.insert (i2p::context.GetIdentHash ()); + for (int i = 0; i < 3; i++) + { + auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4 + if (router) + { + if (i2p::context.GetStatus () != eRouterStatusTesting) + i2p::context.SetStatusSSU2 (eRouterStatusTesting); + m_SSU2Server->StartPeerTest (router, true); + excluded.insert (router->GetIdentHash ()); + } } } - if (!statusChanged) - LogPrint (eLogWarning, "Transports: Can't find routers for peer test ipv4"); } if (ipv6 && i2p::context.SupportsV6 ()) { - LogPrint (eLogInfo, "Transports: Started peer test ipv6"); + LogPrint (eLogInfo, "Transports: Started peer test IPv6"); std::set excluded; - bool statusChanged = false; - for (int i = 0; i < 5; i++) + excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router + if (m_SSUServer) { - auto router = i2p::data::netdb.GetRandomPeerTestRouter (false, excluded); // v6 - if (router) + bool statusChanged = false; + for (int i = 0; i < 5; i++) { - auto addr = router->GetSSUV6Address (); - if (addr && !i2p::util::net::IsInReservedRange(addr->host)) - { - if (!statusChanged) + auto router = i2p::data::netdb.GetRandomPeerTestRouter (false, excluded); // v6 + if (router) + { + auto addr = router->GetSSUV6Address (); + if (addr && !i2p::util::net::IsInReservedRange(addr->host)) { - statusChanged = true; - i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only + if (!statusChanged) + { + statusChanged = true; + i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only + } + m_SSUServer->CreateSession (router, addr, true); // peer test v6 } - m_SSUServer->CreateSession (router, addr, true); // peer test v6 - } - excluded.insert (router->GetIdentHash ()); + excluded.insert (router->GetIdentHash ()); + } } + if (!statusChanged) + LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6"); } - if (!statusChanged) - LogPrint (eLogWarning, "Transports: Can't find routers for peer test ipv6"); - } + + // SSU2 + if (m_SSU2Server) + { + excluded.clear (); + excluded.insert (i2p::context.GetIdentHash ()); + for (int i = 0; i < 3; i++) + { + auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6 + if (router) + { + if (i2p::context.GetStatusV6 () != eRouterStatusTesting) + i2p::context.SetStatusV6SSU2 (eRouterStatusTesting); + m_SSU2Server->StartPeerTest (router, false); + excluded.insert (router->GetIdentHash ()); + } + } + } + } } std::shared_ptr Transports::GetNextX25519KeysPair () @@ -680,13 +770,15 @@ { if(RoutesRestricted() && ! IsRestrictedPeer(ident)) { // not trusted - LogPrint(eLogWarning, "Transports: closing untrusted inbound connection from ", ident.ToBase64()); + LogPrint(eLogWarning, "Transports: Closing untrusted inbound connection from ", ident.ToBase64()); session->Done(); return; } session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore - std::unique_lock l(m_PeersMutex); - m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch (), {} })); + auto ts = i2p::util::GetSecondsSinceEpoch (); + std::unique_lock l(m_PeersMutex); + m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, + ts, ts + PEER_ROUTER_INFO_UPDATE_INTERVAL, {} })); } }); } @@ -747,7 +839,17 @@ it = m_Peers.erase (it); } else + { + if (ts > it->second.nextRouterInfoUpdateTime) + { + auto session = it->second.sessions.front (); + if (session) + session->SendLocalRouterInfo (true); + it->second.nextRouterInfoUpdateTime = ts + PEER_ROUTER_INFO_UPDATE_INTERVAL + + rand () % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE; + } ++it; + } } UpdateBandwidth (); // TODO: use separate timer(s) for it bool ipv4Testing = i2p::context.GetStatus () == eRouterStatusTesting; @@ -780,15 +882,21 @@ std::advance (it, rand () % m_Peers.size ()); if (it == m_Peers.end () || it->second.router) return nullptr; // not connected ident = it->first; - } + } return i2p::data::netdb.FindRouter (ident); } - void Transports::RestrictRoutesToFamilies(std::set families) + + void Transports::RestrictRoutesToFamilies(const std::set& families) { std::lock_guard lock(m_FamilyMutex); m_TrustedFamilies.clear(); - for ( const auto& fam : families ) - m_TrustedFamilies.push_back(fam); + for (auto fam : families) + { + boost::to_lower (fam); + auto id = i2p::data::netdb.GetFamilies ().GetFamilyID (fam); + if (id) + m_TrustedFamilies.push_back (id); + } } void Transports::RestrictRoutesToRouters(std::set routers) @@ -810,20 +918,19 @@ { { std::lock_guard l(m_FamilyMutex); - std::string fam; + i2p::data::FamilyID fam = 0; auto sz = m_TrustedFamilies.size(); if(sz > 1) { auto it = m_TrustedFamilies.begin (); std::advance(it, rand() % sz); fam = *it; - boost::to_lower(fam); } else if (sz == 1) { fam = m_TrustedFamilies[0]; } - if (fam.size()) + if (fam) return i2p::data::netdb.GetRandomRouterInFamily(fam); } { diff -Nru i2pd-2.39.0/libi2pd/TransportSession.h i2pd-2.43.0/libi2pd/TransportSession.h --- i2pd-2.39.0/libi2pd/TransportSession.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TransportSession.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -24,6 +24,10 @@ { namespace transport { + const size_t IPV4_HEADER_SIZE = 20; + const size_t IPV6_HEADER_SIZE = 40; + const size_t UDP_HEADER_SIZE = 8; + class SignedData { public: @@ -33,6 +37,12 @@ { m_Stream << other.m_Stream.rdbuf (); } + + void Reset () + { + m_Stream.str(""); + } + void Insert (const uint8_t * buf, size_t len) { m_Stream.write ((char *)buf, len); @@ -58,7 +68,7 @@ std::stringstream m_Stream; }; - + class TransportSession { public: @@ -69,6 +79,7 @@ { if (router) m_RemoteIdentity = router->GetRouterIdentity (); + m_CreationTime = m_LastActivityTimestamp; } virtual ~TransportSession () {}; @@ -96,7 +107,11 @@ bool IsTerminationTimeoutExpired (uint64_t ts) const { return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); }; - virtual void SendLocalRouterInfo () { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); }; + uint32_t GetCreationTime () const { return m_CreationTime; }; + void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers + + virtual uint32_t GetRelayTag () const { return 0; }; + virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); }; virtual void SendI2NPMessages (const std::vector >& msgs) = 0; protected: @@ -107,6 +122,7 @@ bool m_IsOutgoing; int m_TerminationTimeout; uint64_t m_LastActivityTimestamp; + uint32_t m_CreationTime; // seconds since epoch }; } } diff -Nru i2pd-2.39.0/libi2pd/Transports.h i2pd-2.43.0/libi2pd/Transports.h --- i2pd-2.39.0/libi2pd/Transports.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Transports.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -22,6 +22,7 @@ #include #include "TransportSession.h" #include "SSU.h" +#include "SSU2.h" #include "NTCP2.h" #include "RouterInfo.h" #include "I2NPProtocol.h" @@ -60,13 +61,15 @@ std::mutex m_AcquiredMutex; }; typedef EphemeralKeysSupplier X25519KeysPairSupplier; - + + const int PEER_ROUTER_INFO_UPDATE_INTERVAL = 31*60; // in seconds + const int PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE = 7*60; // in seconds struct Peer { int numAttempts; std::shared_ptr router; std::list > sessions; - uint64_t creationTime; + uint64_t creationTime, nextRouterInfoUpdateTime; std::vector > delayedMessages; void Done () @@ -76,7 +79,7 @@ } }; - const size_t SESSION_CREATION_TIMEOUT = 15; // in seconds + const uint64_t SESSION_CREATION_TIMEOUT = 15; // in seconds const int PEER_TEST_INTERVAL = 71; // in minutes const int MAX_NUM_DELAYED_MESSAGES = 150; class Transports @@ -86,10 +89,11 @@ Transports (); ~Transports (); - void Start (bool enableNTCP2=true, bool enableSSU=true); + void Start (bool enableNTCP2=true, bool enableSSU=true, bool enableSSU2=false); void Stop (); bool IsBoundSSU() const { return m_SSUServer != nullptr; } + bool IsBoundSSU2() const { return m_SSU2Server != nullptr; } bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; } bool IsOnline() const { return m_IsOnline; }; @@ -125,7 +129,7 @@ /** do we want to use restricted routes? */ bool RoutesRestricted() const; /** restrict routes to use only these router families for first hops */ - void RestrictRoutesToFamilies(std::set families); + void RestrictRoutesToFamilies(const std::set& families); /** restrict routes to use only these routers for first hops */ void RestrictRoutesToRouters(std::set routers); @@ -159,6 +163,7 @@ boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer; SSUServer * m_SSUServer; + SSU2Server * m_SSU2Server; NTCP2Server * m_NTCP2Server; mutable std::mutex m_PeersMutex; std::unordered_map m_Peers; @@ -171,7 +176,7 @@ uint64_t m_LastBandwidthUpdateTime; /** which router families to trust for first hops */ - std::vector m_TrustedFamilies; + std::vector m_TrustedFamilies; mutable std::mutex m_FamilyMutex; /** which routers for first hop to trust */ @@ -185,6 +190,7 @@ // for HTTP only const SSUServer * GetSSUServer () const { return m_SSUServer; }; const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; }; + const SSU2Server * GetSSU2Server () const { return m_SSU2Server; }; const decltype(m_Peers)& GetPeers () const { return m_Peers; }; }; diff -Nru i2pd-2.39.0/libi2pd/TunnelBase.h i2pd-2.43.0/libi2pd/TunnelBase.h --- i2pd-2.39.0/libi2pd/TunnelBase.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TunnelBase.h 2022-08-21 19:40:41.000000000 +0000 @@ -47,7 +47,7 @@ virtual ~TunnelBase () {}; virtual void Cleanup () {}; - virtual void HandleTunnelDataMsg (std::shared_ptr tunnelMsg) = 0; + virtual void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) = 0; virtual void SendTunnelDataMsg (std::shared_ptr msg) = 0; virtual void FlushTunnelDataMsgs () {}; virtual void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) = 0; diff -Nru i2pd-2.39.0/libi2pd/TunnelConfig.cpp i2pd-2.43.0/libi2pd/TunnelConfig.cpp --- i2pd-2.39.0/libi2pd/TunnelConfig.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TunnelConfig.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -81,80 +81,34 @@ decryption.SetKey (replyKey); decryption.SetIV (replyIV); decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); - } - - void ElGamalTunnelHopConfig::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[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); - // encrypt - uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE; - auto encryptor = ident->CreateEncryptor (nullptr); - if (encryptor) - { - BN_CTX * ctx = BN_CTX_new (); - encryptor->Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx, false); - BN_CTX_free (ctx); - } - memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); - } - - bool ElGamalTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const - { - uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE; - i2p::crypto::CBCDecryption decryption; - decryption.SetKey (replyKey); - decryption.SetIV (replyIV); - decryption.Decrypt (record, TUNNEL_BUILD_RECORD_SIZE, record); - return true; - } + } 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); + memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32); MixHash (encrypted, 32); // h = SHA256(h || sepk) encrypted += 32; uint8_t sharedSecret[32]; ephemeralKeys->Agree (ident->GetEncryptionPublicKey (), sharedSecret); // x25519(sesk, hepk) - MixKey (sharedSecret); + MixKey (sharedSecret); uint8_t nonce[12]; memset (nonce, 0, 12); 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, 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 @@ -165,7 +119,7 @@ // fill clear text uint8_t flag = 0; if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; - if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_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); @@ -195,16 +149,16 @@ { 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; + 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); @@ -213,28 +167,28 @@ 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_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); + 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); + 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, "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; @@ -245,9 +199,9 @@ { LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); return false; - } + } return true; - } + } void ShortECIESTunnelHopConfig::DecryptRecord (uint8_t * records, int index) const { @@ -256,7 +210,7 @@ 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 { @@ -264,6 +218,33 @@ 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 -Nru i2pd-2.39.0/libi2pd/TunnelConfig.h i2pd-2.43.0/libi2pd/TunnelConfig.h --- i2pd-2.39.0/libi2pd/TunnelConfig.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TunnelConfig.h 2022-08-21 19:40:41.000000000 +0000 @@ -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,14 +31,14 @@ 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); virtual uint8_t GetRetCode (const uint8_t * records) const = 0; virtual void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) = 0; @@ -47,68 +47,59 @@ virtual uint64_t GetGarlicKey (uint8_t * key) const { return 0; }; // return tag }; - struct ElGamalTunnelHopConfig: public TunnelHopConfig - { - ElGamalTunnelHopConfig (std::shared_ptr r): - TunnelHopConfig (r) {}; - uint8_t GetRetCode (const uint8_t * records) const - { return (records + recordIndex*TUNNEL_BUILD_RECORD_SIZE)[BUILD_RESPONSE_RECORD_RET_OFFSET]; }; - void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID); - bool DecryptBuildResponseRecord (uint8_t * records) const; - }; - 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); + 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 - { return (records + recordIndex*TUNNEL_BUILD_RECORD_SIZE)[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET]; }; - void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID); - bool DecryptBuildResponseRecord (uint8_t * records) const; - }; + 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 - { return (records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE)[SHORT_RESPONSE_RECORD_RET_OFFSET]; }; - void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID); - bool DecryptBuildResponseRecord (uint8_t * records) const; + 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; - }; - + uint64_t GetGarlicKey (uint8_t * key) const override; + }; + class TunnelConfig { public: - TunnelConfig (const std::vector >& peers, - bool isShort = false): // inbound - m_IsShort (isShort) + TunnelConfig (const std::vector >& peers, + bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // inbound + m_IsShort (isShort), m_FarEndTransports (farEndTransports) { CreatePeers (peers); m_LastHop->SetNextIdent (i2p::context.GetIdentHash ()); } TunnelConfig (const std::vector >& peers, - uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort = false): // outbound - m_IsShort (isShort) + uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort, + i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // outbound + m_IsShort (isShort), m_FarEndTransports (farEndTransports) { CreatePeers (peers); m_FirstHop->isGateway = false; m_LastHop->SetReplyHop (replyTunnelID, replyIdent); } - ~TunnelConfig () + virtual ~TunnelConfig () { TunnelHopConfig * hop = m_FirstHop; @@ -121,7 +112,12 @@ } bool IsShort () const { return m_IsShort; } - + + i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const + { + return m_FarEndTransports; + } + TunnelHopConfig * GetFirstHop () const { return m_FirstHop; @@ -188,40 +184,20 @@ protected: // this constructor can't be called from outside - TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr), m_IsShort (false) + TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr), m_IsShort (false), + m_FarEndTransports (i2p::data::RouterInfo::eAllTransports) { } private: - void CreatePeers (const std::vector >& peers) - { - TunnelHopConfig * prev = nullptr; - for (const auto& it: peers) - { - TunnelHopConfig * hop; - if (m_IsShort) - hop = new ShortECIESTunnelHopConfig (it); - else - { - if (it->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - hop = new LongECIESTunnelHopConfig (it); - else - hop = new ElGamalTunnelHopConfig (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 -Nru i2pd-2.39.0/libi2pd/Tunnel.cpp i2pd-2.43.0/libi2pd/Tunnel.cpp --- i2pd-2.39.0/libi2pd/Tunnel.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Tunnel.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -31,8 +31,9 @@ { 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 (i2p::data::RouterInfo::eAllTransports), + m_IsRecreated (false), m_Latency (0) { } @@ -46,7 +47,7 @@ const int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS; auto msg = numRecords <= STANDARD_NUM_RECORDS ? NewI2NPShortMessage () : NewI2NPMessage (); *msg->GetPayload () = numRecords; - const size_t recordSize = m_Config->IsShort () ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; + 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; @@ -97,28 +98,28 @@ { 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) + 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 + m_Pool->GetLocalDestination ()->SubmitECIESx25519Key (key, tag); + else i2p::context.AddECIESx25519Key (key, tag); - } + } i2p::transport::transports.SendMessage (GetNextIdentHash (), msg); - } + } } bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) @@ -133,14 +134,14 @@ { if (!hop->DecryptBuildResponseRecord (msg + 1)) return false; - } + } else { - LogPrint (eLogWarning, "Tunnel: hop index ", hop->recordIndex, " is out of range"); + LogPrint (eLogWarning, "Tunnel: Hop index ", hop->recordIndex, " is out of range"); return false; - } - - // decrypt records before current hop + } + + // decrypt records before current hop TunnelHopConfig * hop1 = hop->prev; while (hop1) { @@ -148,13 +149,14 @@ if (idx >= 0 && idx < msg[0]) hop->DecryptRecord (msg + 1, idx); else - LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range"); + LogPrint (eLogWarning, "Tunnel: Hop index ", idx, " is out of range"); hop1 = hop1->prev; } hop = hop->prev; } bool established = true; + size_t numHops = 0; hop = m_Config->GetFirstHop (); while (hop) { @@ -167,19 +169,23 @@ // if any of participants declined the tunnel is not established established = false; hop = hop->next; + numHops++; } if (established) { // create tunnel decryptions from layer and iv keys in reverse order + m_Hops.resize (numHops); hop = m_Config->GetLastHop (); + int i = 0; while (hop) { - auto tunnelHop = new TunnelHop; - tunnelHop->ident = hop->ident; - tunnelHop->decryption.SetKeys (hop->layerKey, hop->ivKey); - m_Hops.push_back (std::unique_ptr(tunnelHop)); + m_Hops[i].ident = hop->ident; + m_Hops[i].decryption.SetKeys (hop->layerKey, hop->ivKey); hop = hop->prev; + i++; } + m_IsShortBuildMessage = m_Config->IsShort (); + m_FarEndTransports = m_Config->GetFarEndTransports (); m_Config = nullptr; } if (established) m_State = eTunnelStateEstablished; @@ -198,7 +204,7 @@ uint8_t * outPayload = out->GetPayload () + 4; for (auto& it: m_Hops) { - it->decryption.Decrypt (inPayload, outPayload); + it.decryption.Decrypt (inPayload, outPayload); inPayload = outPayload; } } @@ -219,8 +225,8 @@ { // hops are in inverted order std::vector > ret; - for (auto& it: m_Hops) - ret.push_back (it->ident); + for (const auto& it: m_Hops) + ret.push_back (it.ident); return ret; } @@ -229,30 +235,19 @@ m_State = state; } - - void Tunnel::PrintHops (std::stringstream& s) const + void Tunnel::VisitTunnelHops(TunnelHopVisitor v) { - // hops are in inverted order, we must print in direct order + // hops are in inverted order, we must return in direct order for (auto it = m_Hops.rbegin (); it != m_Hops.rend (); it++) - { - s << " ⇒ "; - s << i2p::data::GetIdentHashAbbreviation ((*it)->ident->GetIdentHash ()); - } + v((*it).ident); } - void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr msg) + void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr&& msg) { if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive - auto newMsg = CreateEmptyTunnelDataMsg (true); - EncryptTunnelMsg (msg, newMsg); - newMsg->from = shared_from_this (); - m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); - } - - void InboundTunnel::Print (std::stringstream& s) const - { - PrintHops (s); - s << " ⇒ " << GetTunnelID () << ":me"; + EncryptTunnelMsg (msg, msg); + msg->from = shared_from_this (); + m_Endpoint.HandleDecryptedTunnelDataMsg (msg); } ZeroHopsInboundTunnel::ZeroHopsInboundTunnel (): @@ -271,11 +266,6 @@ } } - void ZeroHopsInboundTunnel::Print (std::stringstream& s) const - { - s << " ⇒ " << GetTunnelID () << ":me"; - } - void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg) { TunnelMessageBlock block; @@ -305,16 +295,9 @@ m_Gateway.SendBuffer (); } - void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) + void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) { - LogPrint (eLogError, "Tunnel: incoming message for outbound tunnel ", GetTunnelID ()); - } - - void OutboundTunnel::Print (std::stringstream& s) const - { - s << GetTunnelID () << ":me"; - PrintHops (s); - s << " ⇒ "; + LogPrint (eLogError, "Tunnel: Incoming message for outbound tunnel ", GetTunnelID ()); } ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel (): @@ -346,11 +329,6 @@ } } - void ZeroHopsOutboundTunnel::Print (std::stringstream& s) const - { - s << GetTunnelID () << ":me ⇒ "; - } - Tunnels tunnels; Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), @@ -425,10 +403,10 @@ return tunnel; } - std::shared_ptr Tunnels::CreateTunnelPool (int numInboundHops, - int numOutboundHops, int numInboundTunnels, int numOutboundTunnels) + std::shared_ptr Tunnels::CreateTunnelPool (int numInboundHops, int numOutboundHops, + int numInboundTunnels, int numOutboundTunnels, int inboundVariance, int outboundVariance) { - auto pool = std::make_shared (numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels); + auto pool = std::make_shared (numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels, inboundVariance, outboundVariance); std::unique_lock l(m_PoolsMutex); m_Pools.push_back (pool); return pool; @@ -460,7 +438,7 @@ if (m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second) m_TransitTunnels.push_back (tunnel); else - LogPrint (eLogError, "Tunnel: tunnel with id ", tunnel->GetTunnelID (), " already exists"); + LogPrint (eLogError, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists"); } void Tunnels::Start () @@ -486,7 +464,7 @@ i2p::util::SetThreadName("Tunnels"); std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready - uint64_t lastTs = 0, lastPoolsTs = 0; + uint64_t lastTs = 0, lastPoolsTs = 0, lastMemoryPoolTs = 0; while (m_IsRunning) { try @@ -516,12 +494,12 @@ if (tunnel) { if (typeID == eI2NPTunnelData) - tunnel->HandleTunnelDataMsg (msg); + tunnel->HandleTunnelDataMsg (std::move (msg)); else // tunnel gateway assumed HandleTunnelGatewayMsg (tunnel, msg); } else - LogPrint (eLogWarning, "Tunnel: tunnel not found, tunnelID=", tunnelID, " previousTunnelID=", prevTunnelID, " type=", (int)typeID); + LogPrint (eLogWarning, "Tunnel: Tunnel not found, tunnelID=", tunnelID, " previousTunnelID=", prevTunnelID, " type=", (int)typeID); break; } @@ -530,11 +508,11 @@ case eI2NPShortTunnelBuild: case eI2NPShortTunnelBuildReply: case eI2NPTunnelBuild: - case eI2NPTunnelBuildReply: + case eI2NPTunnelBuildReply: HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); break; default: - LogPrint (eLogWarning, "Tunnel: unexpected message type ", (int) typeID); + LogPrint (eLogWarning, "Tunnel: Unexpected message type ", (int) typeID); } msg = m_Queue.Get (); @@ -561,12 +539,18 @@ { ManageTunnelPools (ts); lastPoolsTs = ts; - } - } + } + if (ts - lastMemoryPoolTs >= 120) // manage memory pool every 2 minutes + { + m_I2NPTunnelEndpointMessagesMemoryPool.CleanUpMt (); + m_I2NPTunnelMessagesMemoryPool.CleanUpMt (); + lastMemoryPoolTs = ts; + } + } } catch (std::exception& ex) { - LogPrint (eLogError, "Tunnel: runtime exception: ", ex.what ()); + LogPrint (eLogError, "Tunnel: Runtime exception: ", ex.what ()); } } } @@ -575,7 +559,7 @@ { if (!tunnel) { - LogPrint (eLogError, "Tunnel: missing tunnel for gateway"); + LogPrint (eLogError, "Tunnel: Missing tunnel for gateway"); return; } const uint8_t * payload = msg->GetPayload (); @@ -584,12 +568,12 @@ msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; if (msg->offset + len > msg->len) { - LogPrint (eLogError, "Tunnel: gateway payload ", (int)len, " exceeds message length ", (int)msg->len); + LogPrint (eLogError, "Tunnel: Gateway payload ", (int)len, " exceeds message length ", (int)msg->len); return; } msg->len = msg->offset + len; auto typeID = msg->GetTypeID (); - LogPrint (eLogDebug, "Tunnel: gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID (), ", msg type ", (int)typeID); + LogPrint (eLogDebug, "Tunnel: Gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID (), ", msg type ", (int)typeID); if (IsRouterInfoMsg (msg) || typeID == eI2NPDatabaseSearchReply) // transit DatabaseStore my contain new/updated RI @@ -625,7 +609,7 @@ case eTunnelStatePending: if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT) { - LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " timeout, deleted"); + LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " timeout, deleted"); // update stats auto config = tunnel->GetTunnelConfig (); if (config) @@ -650,7 +634,7 @@ ++it; break; case eTunnelStateBuildFailed: - LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " failed, deleted"); + LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " failed, deleted"); it = pendingTunnels.erase (it); m_NumFailedTunnelCreations++; break; @@ -675,7 +659,7 @@ auto tunnel = *it; if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) { - LogPrint (eLogDebug, "Tunnel: tunnel with id ", tunnel->GetTunnelID (), " expired"); + LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired"); auto pool = tunnel->GetTunnelPool (); if (pool) pool->TunnelExpired (tunnel); @@ -692,7 +676,7 @@ // 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); } } @@ -712,10 +696,10 @@ i2p::transport::transports.GetRestrictedPeer() : i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); // reachable by us if (!inboundTunnel || !router) return; - LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel"); + LogPrint (eLogDebug, "Tunnel: Creating one hop outbound tunnel"); CreateTunnel ( std::make_shared (std::vector > { router->GetRouterIdentity () }, - inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()), nullptr + inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash (), false), nullptr ); } } @@ -729,7 +713,7 @@ auto tunnel = *it; if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) { - LogPrint (eLogDebug, "Tunnel: tunnel with id ", tunnel->GetTunnelID (), " expired"); + LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired"); auto pool = tunnel->GetTunnelPool (); if (pool) pool->TunnelExpired (tunnel); @@ -746,7 +730,7 @@ // 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); } } @@ -772,7 +756,7 @@ int obLen; i2p::config::GetOption("exploratory.outbound.length", obLen); int ibNum; i2p::config::GetOption("exploratory.inbound.quantity", ibNum); int obNum; i2p::config::GetOption("exploratory.outbound.quantity", obNum); - m_ExploratoryPool = CreateTunnelPool (ibLen, obLen, ibNum, obNum); + m_ExploratoryPool = CreateTunnelPool (ibLen, obLen, ibNum, obNum, 0, 0); m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ()); } return; @@ -786,12 +770,12 @@ // should be reachable by us because we send build request directly i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); if (!router) { - LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel"); + LogPrint (eLogWarning, "Tunnel: Can't find any router, skip creating tunnel"); return; } - LogPrint (eLogDebug, "Tunnel: creating one hop inbound tunnel"); + LogPrint (eLogDebug, "Tunnel: Creating one hop inbound tunnel"); CreateTunnel ( - std::make_shared (std::vector > { router->GetRouterIdentity () }), nullptr + std::make_shared (std::vector > { router->GetRouterIdentity () }, false), nullptr ); } } @@ -837,8 +821,8 @@ } template - std::shared_ptr Tunnels::CreateTunnel (std::shared_ptr config, - std::shared_ptr pool, 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); @@ -849,7 +833,7 @@ return newTunnel; } - std::shared_ptr Tunnels::CreateInboundTunnel (std::shared_ptr config, + std::shared_ptr Tunnels::CreateInboundTunnel (std::shared_ptr config, std::shared_ptr pool, std::shared_ptr outboundTunnel) { if (config) @@ -897,7 +881,7 @@ { // build symmetric outbound tunnel CreateTunnel (std::make_shared(newTunnel->GetInvertedPeers (), - newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash ()), nullptr, + newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash (), false), nullptr, GetNextOutboundTunnel ()); } else @@ -909,11 +893,11 @@ } } else - LogPrint (eLogError, "Tunnel: tunnel with id ", newTunnel->GetTunnelID (), " already exists"); + LogPrint (eLogError, "Tunnel: Tunnel with id ", newTunnel->GetTunnelID (), " already exists"); } - std::shared_ptr Tunnels::CreateZeroHopsInboundTunnel (std::shared_ptr pool) + std::shared_ptr Tunnels::CreateZeroHopsInboundTunnel (std::shared_ptr pool) { auto inboundTunnel = std::make_shared (); inboundTunnel->SetTunnelPool (pool); @@ -933,6 +917,24 @@ return outboundTunnel; } + std::shared_ptr Tunnels::NewI2NPTunnelMessage (bool endpoint) + { + if (endpoint) + { + // should fit two tunnel message + tunnel gateway header, enough for one garlic encrypted streaming packet + auto msg = m_I2NPTunnelEndpointMessagesMemoryPool.AcquireSharedMt (); + msg->Align (6); + msg->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header + return msg; + } + else + { + auto msg = m_I2NPTunnelMessagesMemoryPool.AcquireSharedMt (); + msg->Align (12); + return msg; + } + } + int Tunnels::GetTransitTunnelsExpirationTimeout () { int timeout = 0; diff -Nru i2pd-2.39.0/libi2pd/TunnelEndpoint.cpp i2pd-2.43.0/libi2pd/TunnelEndpoint.cpp --- i2pd-2.39.0/libi2pd/TunnelEndpoint.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TunnelEndpoint.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -30,7 +30,7 @@ m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE; uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16 - uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // witout 4-byte checksum + uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // without 4-byte checksum if (zero) { uint8_t * fragment = zero + 1; @@ -40,7 +40,7 @@ SHA256(fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16, hash); // payload + iv if (memcmp (hash, decrypted, 4)) { - LogPrint (eLogError, "TunnelMessage: checksum verification failed"); + LogPrint (eLogError, "TunnelMessage: Checksum verification failed"); return; } // process fragments @@ -57,7 +57,7 @@ // first fragment 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) { @@ -108,8 +108,8 @@ { HandleFollowOnFragment (msgID, isLastFragment, fragmentNum, fragment, size); // another m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - } + } + } else { // new message @@ -118,7 +118,7 @@ // check message size if (msg->len > msg->maxLen) { - LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size); + LogPrint (eLogError, "TunnelMessage: Fragment is too long ", (int)size); m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; return; } @@ -131,35 +131,35 @@ } 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; } } else - LogPrint (eLogError, "TunnelMessage: zero not found"); + LogPrint (eLogError, "TunnelMessage: Zero not found"); } - void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, + void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, uint8_t fragmentNum, const uint8_t * fragment, size_t size) { auto it = m_IncompleteMessages.find (msgID); @@ -213,15 +213,15 @@ 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) { @@ -244,7 +244,7 @@ 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 () { @@ -255,16 +255,16 @@ 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)); + 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); + LogPrint (eLogInfo, "TunnelMessage: Duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID); } void TunnelEndpoint::HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg) @@ -276,10 +276,10 @@ HandleNextMessage (msg); if (&msg == &m_CurrentMessage) { - m_CurrentMsgID = 0; + m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - else + } + else m_IncompleteMessages.erase (msgID); LogPrint (eLogDebug, "TunnelMessage: All fragments of message ", msgID, " found"); break; @@ -318,11 +318,11 @@ { if (!m_IsInbound && msg.data->IsExpired ()) { - LogPrint (eLogInfo, "TunnelMessage: message expired"); + LogPrint (eLogInfo, "TunnelMessage: Message expired"); return; } uint8_t typeID = msg.data->GetTypeID (); - LogPrint (eLogDebug, "TunnelMessage: handle fragment of ", msg.data->GetLength (), " bytes, msg type ", (int)typeID); + LogPrint (eLogDebug, "TunnelMessage: Handle fragment of ", msg.data->GetLength (), " bytes, msg type ", (int)typeID); // catch RI or reply with new list of routers if ((IsRouterInfoMsg (msg.data) || typeID == eI2NPDatabaseSearchReply) && !m_IsInbound && msg.deliveryType != eDeliveryTypeLocal) diff -Nru i2pd-2.39.0/libi2pd/TunnelEndpoint.h i2pd-2.43.0/libi2pd/TunnelEndpoint.h --- i2pd-2.39.0/libi2pd/TunnelEndpoint.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TunnelEndpoint.h 2022-08-21 19:40:41.000000000 +0000 @@ -10,9 +10,9 @@ #define TUNNEL_ENDPOINT_H__ #include -#include #include #include +#include #include "I2NPProtocol.h" #include "TunnelBase.h" @@ -49,14 +49,14 @@ 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 HandleCurrenMessageFollowOnFragment (const uint8_t * fragment, size_t size, bool isLastFragment); void HandleNextMessage (const TunnelMessageBlock& msg); 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::unordered_map m_IncompleteMessages; diff -Nru i2pd-2.39.0/libi2pd/TunnelGateway.cpp i2pd-2.43.0/libi2pd/TunnelGateway.cpp --- i2pd-2.39.0/libi2pd/TunnelGateway.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TunnelGateway.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -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 * @@ -19,16 +19,14 @@ namespace tunnel { TunnelGatewayBuffer::TunnelGatewayBuffer (): - m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) + m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0), m_NonZeroRandomBuffer (nullptr) { - RAND_bytes (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE); - for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++) - if (!m_NonZeroRandomBuffer[i]) m_NonZeroRandomBuffer[i] = 1; } TunnelGatewayBuffer::~TunnelGatewayBuffer () { ClearTunnelDataMsgs (); + if (m_NonZeroRandomBuffer) delete[] m_NonZeroRandomBuffer; } void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block) @@ -158,8 +156,7 @@ void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage () { m_CurrentTunnelDataMsg = nullptr; - m_CurrentTunnelDataMsg = NewI2NPShortMessage (); - m_CurrentTunnelDataMsg->Align (12); + m_CurrentTunnelDataMsg = NewI2NPTunnelMessage (true); // tunnel endpoint is at least of two tunnel messages size // we reserve space for padding m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE; m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset; @@ -184,6 +181,13 @@ if (paddingSize > 0) { // non-zero padding + if (!m_NonZeroRandomBuffer) // first time? + { + m_NonZeroRandomBuffer = new uint8_t[TUNNEL_DATA_MAX_PAYLOAD_SIZE]; + RAND_bytes (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE); + for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++) + if (!m_NonZeroRandomBuffer[i]) m_NonZeroRandomBuffer[i] = 1; + } auto randomOffset = rand () % (TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize + 1); memcpy (buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize); } diff -Nru i2pd-2.39.0/libi2pd/TunnelGateway.h i2pd-2.43.0/libi2pd/TunnelGateway.h --- i2pd-2.39.0/libi2pd/TunnelGateway.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TunnelGateway.h 2022-08-21 19:40:41.000000000 +0000 @@ -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 * @@ -38,7 +38,7 @@ std::vector > m_TunnelDataMsgs; std::shared_ptr m_CurrentTunnelDataMsg; size_t m_RemainingSize; - uint8_t m_NonZeroRandomBuffer[TUNNEL_DATA_MAX_PAYLOAD_SIZE]; + uint8_t * m_NonZeroRandomBuffer; }; class TunnelGateway diff -Nru i2pd-2.39.0/libi2pd/Tunnel.h i2pd-2.43.0/libi2pd/Tunnel.h --- i2pd-2.39.0/libi2pd/Tunnel.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/Tunnel.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -18,6 +18,7 @@ #include #include #include +#include "util.h" #include "Queue.h" #include "Crypto.h" #include "TunnelConfig.h" @@ -39,7 +40,10 @@ const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message const int MAX_NUM_RECORDS = 8; const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds - + + const size_t I2NP_TUNNEL_MESSAGE_SIZE = TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34; // reserved for alignment and NTCP 16 + 6 + 12 + const size_t I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE = 2*TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28; // reserved for alignment and NTCP 16 + 6 + 6 + enum TunnelState { eTunnelStatePending, @@ -63,6 +67,9 @@ public: + /** function for visiting a hops stored in a tunnel */ + typedef std::function)> TunnelHopVisitor; + Tunnel (std::shared_ptr config); ~Tunnel (); @@ -71,12 +78,14 @@ std::shared_ptr GetTunnelConfig () const { return m_Config; } std::vector > GetPeers () const; std::vector > GetInvertedPeers () const; + bool IsShortBuildMessage () const { return m_IsShortBuildMessage; }; + i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const { return m_FarEndTransports; }; 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; @@ -85,8 +94,6 @@ bool HandleTunnelBuildResponse (uint8_t * msg, size_t len); - virtual void Print (std::stringstream&) const {}; - // implements TunnelBase void SendTunnelDataMsg (std::shared_ptr msg); void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out); @@ -100,18 +107,19 @@ bool LatencyIsKnown() const { return m_Latency > 0; } bool IsSlow () const { return LatencyIsKnown() && (int)m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); } - - protected: - void PrintHops (std::stringstream& s) const; + /** visit all hops we currently store */ + void VisitTunnelHops(TunnelHopVisitor v); private: std::shared_ptr m_Config; - std::vector > m_Hops; + 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 }; @@ -126,10 +134,9 @@ virtual void SendTunnelDataMsg (const std::vector& msgs); // multiple messages const i2p::data::IdentHash& GetEndpointIdentHash () const { return m_EndpointIdentHash; }; virtual size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); }; - void Print (std::stringstream& s) const; // implements TunnelBase - void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); + void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg); bool IsInbound() const { return false; } @@ -145,9 +152,8 @@ public: InboundTunnel (std::shared_ptr config): Tunnel (config), m_Endpoint (true) {}; - void HandleTunnelDataMsg (std::shared_ptr msg); + void HandleTunnelDataMsg (std::shared_ptr&& msg); virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; - void Print (std::stringstream& s) const; bool IsInbound() const { return true; } // override TunnelBase @@ -164,7 +170,6 @@ ZeroHopsInboundTunnel (); void SendTunnelDataMsg (std::shared_ptr msg); - void Print (std::stringstream& s) const; size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; private: @@ -178,7 +183,6 @@ ZeroHopsOutboundTunnel (); void SendTunnelDataMsg (const std::vector& msgs); - void Print (std::stringstream& s) const; size_t GetNumSentBytes () const { return m_NumSentBytes; }; private: @@ -211,16 +215,18 @@ void PostTunnelData (const std::vector >& msgs); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); - std::shared_ptr CreateTunnelPool (int numInboundHops, - int numOuboundHops, int numInboundTunnels, int numOutboundTunnels); + std::shared_ptr CreateTunnelPool (int numInboundHops, int numOuboundHops, + int numInboundTunnels, int numOutboundTunnels, int inboundVariance, int outboundVariance); void DeleteTunnelPool (std::shared_ptr pool); void StopTunnelPool (std::shared_ptr pool); + std::shared_ptr NewI2NPTunnelMessage (bool endpoint); + private: template - std::shared_ptr CreateTunnel (std::shared_ptr config, - std::shared_ptr pool, 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); @@ -254,7 +260,9 @@ std::list> m_Pools; std::shared_ptr m_ExploratoryPool; i2p::util::Queue > m_Queue; - + i2p::util::MemoryPoolMt > m_I2NPTunnelEndpointMessagesMemoryPool; + i2p::util::MemoryPoolMt > m_I2NPTunnelMessagesMemoryPool; + // some stats int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations; diff -Nru i2pd-2.39.0/libi2pd/TunnelPool.cpp i2pd-2.43.0/libi2pd/TunnelPool.cpp --- i2pd-2.39.0/libi2pd/TunnelPool.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TunnelPool.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -27,28 +27,38 @@ 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) + 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): + } + + TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, + int numOutboundTunnels, int inboundVariance, int outboundVariance): m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), + m_InboundVariance (inboundVariance), m_OutboundVariance (outboundVariance), m_IsActive (true), m_CustomPeerSelector(nullptr) { - if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY) + if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY) m_NumInboundTunnels = TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY; - if (m_NumOutboundTunnels > TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY) - m_NumOutboundTunnels = TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY; + if (m_NumOutboundTunnels > TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY) + m_NumOutboundTunnels = TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY; + if (m_InboundVariance < 0 && m_NumInboundHops + m_InboundVariance <= 0) + m_InboundVariance = m_NumInboundHops ? -m_NumInboundHops + 1 : 0; + if (m_OutboundVariance < 0 && m_NumOutboundHops + m_OutboundVariance <= 0) + m_OutboundVariance = m_NumOutboundHops ? -m_NumOutboundHops + 1 : 0; + if (m_InboundVariance > 0 && m_NumInboundHops + m_InboundVariance > STANDARD_NUM_RECORDS) + m_InboundVariance = (m_NumInboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumInboundHops : 0; + if (m_OutboundVariance > 0 && m_NumOutboundHops + m_OutboundVariance > STANDARD_NUM_RECORDS) + m_OutboundVariance = (m_NumOutboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumOutboundHops : 0; m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + rand () % TUNNEL_POOL_MANAGE_INTERVAL; } @@ -113,6 +123,17 @@ 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) @@ -168,10 +189,10 @@ if (it->IsSlow () && !slowTunnel) slowTunnel = it; else - { + { v.push_back (it); i++; - } + } } } if (slowTunnel && (int)v.size () < (num/2+1)) @@ -179,20 +200,23 @@ return v; } - std::shared_ptr TunnelPool::GetNextOutboundTunnel (std::shared_ptr excluded) const + std::shared_ptr TunnelPool::GetNextOutboundTunnel (std::shared_ptr excluded, + i2p::data::RouterInfo::CompatibleTransports compatible) const { std::unique_lock l(m_OutboundTunnelsMutex); - return GetNextTunnel (m_OutboundTunnels, excluded); + return GetNextTunnel (m_OutboundTunnels, excluded, compatible); } - std::shared_ptr TunnelPool::GetNextInboundTunnel (std::shared_ptr excluded) const + std::shared_ptr TunnelPool::GetNextInboundTunnel (std::shared_ptr excluded, + i2p::data::RouterInfo::CompatibleTransports compatible) const { std::unique_lock l(m_InboundTunnelsMutex); - return GetNextTunnel (m_InboundTunnels, excluded); + return GetNextTunnel (m_InboundTunnels, excluded, compatible); } template - typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const + typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, + typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible) const { if (tunnels.empty ()) return nullptr; uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0; @@ -200,10 +224,10 @@ typename TTunnels::value_type tunnel = nullptr; for (const auto& it: tunnels) { - if (it->IsEstablished () && it != excluded) + if (it->IsEstablished () && it != excluded && (compatible & it->GetFarEndTransports ())) { - if (it->IsSlow () || (HasLatencyRequirement() && it->LatencyIsKnown() && - !it->LatencyFitsRange(m_MinLatency, m_MaxLatency))) + if (it->IsSlow () || (HasLatencyRequirement() && it->LatencyIsKnown() && + !it->LatencyFitsRange(m_MinLatency, m_MaxLatency))) { i++; skipped = true; continue; @@ -213,7 +237,7 @@ } if (i > ind && tunnel) break; } - if (!tunnel && skipped) + if (!tunnel && skipped) { ind = rand () % (tunnels.size ()/2 + 1), i = 0; for (const auto& it: tunnels) @@ -270,12 +294,12 @@ if (!num && !m_OutboundTunnels.empty () && m_NumOutboundHops > 0) { for (auto it: m_OutboundTunnels) - { + { CreatePairedInboundTunnel (it); num++; if (num >= m_NumInboundTunnels) break; - } - } + } + } for (int i = num; i < m_NumInboundTunnels; i++) CreateInboundTunnel (); @@ -293,7 +317,7 @@ for (auto& it: tests) { - LogPrint (eLogWarning, "Tunnels: test of tunnel ", it.first, " failed"); + LogPrint (eLogWarning, "Tunnels: Test of tunnel ", it.first, " failed"); // if test failed again with another tunnel we consider it failed if (it.second.first) { @@ -324,7 +348,9 @@ } // new tests + std::unique_lock l1(m_OutboundTunnelsMutex); auto it1 = m_OutboundTunnels.begin (); + std::unique_lock l2(m_InboundTunnelsMutex); auto it2 = m_InboundTunnels.begin (); while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ()) { @@ -356,20 +382,20 @@ void TunnelPool::ManageTunnels (uint64_t ts) { - if (ts > m_NextManageTime) - { + if (ts > m_NextManageTime || ts + 2*TUNNEL_POOL_MANAGE_INTERVAL < m_NextManageTime) // in case if clock was adjusted + { CreateTunnels (); TestTunnels (); - m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (rand () % TUNNEL_POOL_MANAGE_INTERVAL)/2; - } - } - + m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (rand () % TUNNEL_POOL_MANAGE_INTERVAL)/2; + } + } + void TunnelPool::ProcessGarlicMessage (std::shared_ptr msg) { if (m_LocalDestination) m_LocalDestination->ProcessGarlicMessage (msg); else - LogPrint (eLogWarning, "Tunnels: local destination doesn't exist, dropped"); + LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); } void TunnelPool::ProcessDeliveryStatus (std::shared_ptr msg) @@ -394,23 +420,31 @@ if (found) { uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp; - LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", dlt, " milliseconds"); - uint64_t latency = dlt / 2; + LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " milliseconds"); + int numHops = 0; + if (test.first) numHops += test.first->GetNumHops (); + if (test.second) numHops += test.second->GetNumHops (); // restore from test failed state if any if (test.first) - { + { if (test.first->GetState () == eTunnelStateTestFailed) test.first->SetState (eTunnelStateEstablished); // update latency + uint64_t latency = 0; + if (numHops) latency = dlt*test.first->GetNumHops ()/numHops; + if (!latency) latency = dlt/2; test.first->AddLatencySample(latency); - } + } if (test.second) - { + { if (test.second->GetState () == eTunnelStateTestFailed) test.second->SetState (eTunnelStateEstablished); // update latency + uint64_t latency = 0; + if (numHops) latency = dlt*test.second->GetNumHops ()/numHops; + if (!latency) latency = dlt/2; test.second->AddLatencySample(latency); - } + } } else { @@ -424,8 +458,8 @@ bool TunnelPool::IsExploratory () const { return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this (); - } - + } + std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop, bool reverse) const { auto hop = IsExploratory () ? i2p::data::netdb.GetRandomRouter (prevHop, reverse): @@ -453,7 +487,7 @@ (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; @@ -466,30 +500,55 @@ { auto hop = nextHop (prevHop, inbound); if (!hop && !i) // if no suitable peer found for first hop, try already connected - { + { 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) { LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ()); return false; } if ((i == numHops - 1) && (!hop->IsV4 () || // doesn't support ipv4 - (inbound && !hop->IsReachable ()))) // IBGW is not reachable + (inbound && !hop->IsReachable ()))) // IBGW is not reachable { auto hop1 = nextHop (prevHop, true); if (hop1) hop = hop1; - } + } prevHop = hop; path.Add (hop); } + path.farEndTransports = prevHop->GetCompatibleTransports (inbound); // last hop return true; } bool TunnelPool::SelectPeers (Path& path, bool isInbound) { - int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; + // explicit peers in use + if (m_ExplicitPeers) return SelectExplicitPeers (path, isInbound); + // calculate num hops + int numHops; + if (isInbound) + { + numHops = m_NumInboundHops; + if (m_InboundVariance) + { + int offset = rand () % (std::abs (m_InboundVariance) + 1); + if (m_InboundVariance < 0) offset = -offset; + numHops += offset; + } + } + else + { + numHops = m_NumOutboundHops; + if (m_OutboundVariance) + { + int offset = rand () % (std::abs (m_OutboundVariance) + 1); + if (m_OutboundVariance < 0) offset = -offset; + numHops += offset; + } + } // peers is empty if (numHops <= 0) return true; // custom peer selector in use ? @@ -498,8 +557,6 @@ if (m_CustomPeerSelector) return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound); } - // explicit peers in use - if (m_ExplicitPeers) return SelectExplicitPeers (path, isInbound); return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1, std::placeholders::_2)); } @@ -507,13 +564,25 @@ { int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; if (numHops > (int)m_ExplicitPeers->size ()) numHops = m_ExplicitPeers->size (); - if (!numHops) return false; + if (!numHops) return false; for (int i = 0; i < numHops; i++) { auto& ident = (*m_ExplicitPeers)[i]; auto r = i2p::data::netdb.FindRouter (ident); if (r) - path.Add (r); + { + 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 ()); @@ -526,18 +595,18 @@ void TunnelPool::CreateInboundTunnel () { - auto outboundTunnel = GetNextOutboundTunnel (); - if (!outboundTunnel) - outboundTunnel = tunnels.GetNextOutboundTunnel (); LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel..."); Path path; if (SelectPeers (path, true)) { + auto outboundTunnel = GetNextOutboundTunnel (nullptr, path.farEndTransports); + if (!outboundTunnel) + outboundTunnel = tunnels.GetNextOutboundTunnel (); std::shared_ptr config; if (m_NumInboundHops > 0) { path.Reverse (); - config = std::make_shared (path.peers, path.isShort); + config = std::make_shared (path.peers, path.isShort, path.farEndTransports); } auto tunnel = tunnels.CreateInboundTunnel (config, shared_from_this (), outboundTunnel); if (tunnel->IsEstablished ()) // zero hops @@ -554,55 +623,60 @@ CreateInboundTunnel (); return; } - auto outboundTunnel = GetNextOutboundTunnel (); + auto outboundTunnel = GetNextOutboundTunnel (nullptr, tunnel->GetFarEndTransports ()); if (!outboundTunnel) outboundTunnel = tunnels.GetNextOutboundTunnel (); LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound 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 (), tunnel->GetFarEndTransports ()); if (!m_NumInboundHops || config) { auto newTunnel = tunnels.CreateInboundTunnel (config, shared_from_this(), outboundTunnel); if (newTunnel->IsEstablished ()) // zero hops TunnelCreated (newTunnel); + else + newTunnel->SetRecreated (true); } } void TunnelPool::CreateOutboundTunnel () { - auto inboundTunnel = GetNextInboundTunnel (); - if (!inboundTunnel) - inboundTunnel = tunnels.GetNextInboundTunnel (); - if (inboundTunnel) + LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel..."); + Path path; + if (SelectPeers (path, false)) { - LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel..."); - Path path; - if (SelectPeers (path, false)) - { - std::shared_ptr config; - if (m_NumOutboundHops > 0) - config = std::make_shared(path.peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash (), path.isShort); + auto inboundTunnel = GetNextInboundTunnel (nullptr, path.farEndTransports); + if (!inboundTunnel) + inboundTunnel = tunnels.GetNextInboundTunnel (); + if (!inboundTunnel) + { + LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no inbound tunnels found"); + return; + } - 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); + 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(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 - LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available"); + tunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ()); + if (tunnel && tunnel->IsEstablished ()) // zero hops + TunnelCreated (tunnel); } else - LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no inbound tunnels found"); + LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available"); } void TunnelPool::RecreateOutboundTunnel (std::shared_ptr tunnel) @@ -612,7 +686,7 @@ CreateOutboundTunnel (); return; } - auto inboundTunnel = GetNextInboundTunnel (); + auto inboundTunnel = GetNextInboundTunnel (nullptr, tunnel->GetFarEndTransports ()); if (!inboundTunnel) inboundTunnel = tunnels.GetNextInboundTunnel (); if (inboundTunnel) @@ -621,7 +695,8 @@ 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 (), tunnel->GetFarEndTransports ()); } if (!m_NumOutboundHops || config) { @@ -638,8 +713,9 @@ { LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel..."); auto tunnel = tunnels.CreateInboundTunnel ( - m_NumOutboundHops > 0 ? std::make_shared(outboundTunnel->GetInvertedPeers ()) : nullptr, - shared_from_this (), outboundTunnel); + m_NumOutboundHops > 0 ? std::make_shared(outboundTunnel->GetInvertedPeers (), + outboundTunnel->IsShortBuildMessage ()) : nullptr, + shared_from_this (), outboundTunnel); if (tunnel->IsEstablished ()) // zero hops TunnelCreated (tunnel); } diff -Nru i2pd-2.39.0/libi2pd/TunnelPool.h i2pd-2.43.0/libi2pd/TunnelPool.h --- i2pd-2.39.0/libi2pd/TunnelPool.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/TunnelPool.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -30,7 +30,7 @@ const int TUNNEL_POOL_MANAGE_INTERVAL = 10; // in seconds const int TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY = 16; const int TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY = 16; - + class Tunnel; class InboundTunnel; class OutboundTunnel; @@ -40,7 +40,8 @@ { std::vector peers; bool isShort = true; - + i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports; + void Add (std::shared_ptr r); void Reverse (); }; @@ -55,12 +56,13 @@ typedef std::function(std::shared_ptr, bool)> SelectHopFunc; bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop); - + class TunnelPool: public std::enable_shared_from_this // per local destination { public: - TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels); + TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, + int numOutboundTunnels, int inboundVariance, int outboundVariance); ~TunnelPool (); std::shared_ptr GetLocalDestination () const { return m_LocalDestination; }; @@ -75,10 +77,11 @@ void RecreateInboundTunnel (std::shared_ptr tunnel); void RecreateOutboundTunnel (std::shared_ptr tunnel); std::vector > GetInboundTunnels (int num) const; - std::shared_ptr GetNextOutboundTunnel (std::shared_ptr excluded = nullptr) const; - std::shared_ptr GetNextInboundTunnel (std::shared_ptr excluded = nullptr) const; + std::shared_ptr GetNextOutboundTunnel (std::shared_ptr excluded = nullptr, + i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports) const; + std::shared_ptr GetNextInboundTunnel (std::shared_ptr excluded = nullptr, + i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports) const; std::shared_ptr GetNewOutboundTunnel (std::shared_ptr old) const; - void TestTunnels (); void ManageTunnels (uint64_t ts); void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatus (std::shared_ptr msg); @@ -115,18 +118,21 @@ private: + void TestTunnels (); void CreateInboundTunnel (); void CreateOutboundTunnel (); void CreatePairedInboundTunnel (std::shared_ptr outboundTunnel); template - typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const; + typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, + typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible) const; bool SelectPeers (Path& path, bool isInbound); bool SelectExplicitPeers (Path& path, bool isInbound); private: std::shared_ptr m_LocalDestination; - int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels; + int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels, + m_InboundVariance, m_OutboundVariance; std::shared_ptr > m_ExplicitPeers; mutable std::mutex m_InboundTunnelsMutex; std::set, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first diff -Nru i2pd-2.39.0/libi2pd/util.cpp i2pd-2.43.0/libi2pd/util.cpp --- i2pd-2.39.0/libi2pd/util.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/util.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -12,6 +12,7 @@ #include "util.h" #include "Log.h" +#include "I2PEndian.h" #if not defined (__FreeBSD__) #include @@ -32,14 +33,10 @@ #include #include -#ifdef _MSC_VER -#pragma comment(lib, "IPHLPAPI.lib") -#endif // _MSC_VER - #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) #define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) -// inet_pton exists Windows since Vista, but XP doesn't have that function! +// inet_pton and inet_ntop have been in Windows since Vista, but XP doesn't have these functions! // This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found int inet_pton_xp (int af, const char *src, void *dst) { @@ -65,6 +62,29 @@ } return 0; } + +const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size) +{ + struct sockaddr_storage ss; + unsigned long s = size; + + ZeroMemory(&ss, sizeof(ss)); + ss.ss_family = af; + + switch(af) { + case AF_INET: + ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src; + break; + case AF_INET6: + ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src; + break; + default: + return NULL; + } + /* cannot direclty use &size because of strict aliasing rules */ + return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? dst : NULL; +} + #else /* !_WIN32 => UNIX */ #include #ifdef ANDROID @@ -117,19 +137,19 @@ } catch (std::exception& ex) { - LogPrint (eLogError, m_Name, ": runtime exception: ", ex.what ()); + LogPrint (eLogError, m_Name, ": Runtime exception: ", ex.what ()); } } } void SetThreadName (const char *name) { -#if defined(__APPLE__) - pthread_setname_np(name); +#if defined(__APPLE__) && !defined(__powerpc__) + pthread_setname_np((char*)name); #elif defined(__FreeBSD__) || defined(__OpenBSD__) 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 } @@ -137,27 +157,12 @@ namespace net { #ifdef _WIN32 - bool IsWindowsXPorLater () - { - static bool isRequested = false; - static bool isXP = false; - if (!isRequested) - { - // request - OSVERSIONINFO osvi; - - ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&osvi); - - isXP = osvi.dwMajorVersion <= 5; - isRequested = true; - } - return isXP; - } - int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback) { + typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size); + IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); + if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found + ULONG outBufLen = 0; PIP_ADAPTER_ADDRESSES pAddresses = nullptr; PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; @@ -176,7 +181,7 @@ if(dwRetVal != NO_ERROR) { - LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed"); + LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed"); FREE(pAddresses); return fallback; } @@ -188,7 +193,7 @@ pUnicast = pCurrAddresses->FirstUnicastAddress; if(pUnicast == nullptr) - LogPrint(eLogError, "NetIface: GetMTU(): not a unicast ipv4 address, this is not supported"); + LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv4 address, this is not supported"); for(int i = 0; pUnicast != nullptr; ++i) { @@ -196,8 +201,13 @@ sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr; if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr) { - auto result = pAddresses->Mtu; + char addr[INET_ADDRSTRLEN]; + inetntop(AF_INET, &(((struct sockaddr_in *)localInterfaceAddress)->sin_addr), addr, INET_ADDRSTRLEN); + + auto result = pCurrAddresses->Mtu; FREE(pAddresses); + pAddresses = nullptr; + LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv4 address ", addr); return result; } pUnicast = pUnicast->Next; @@ -205,19 +215,23 @@ pCurrAddresses = pCurrAddresses->Next; } - LogPrint(eLogError, "NetIface: GetMTU(): no usable unicast ipv4 addresses found"); + LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv4 addresses found"); FREE(pAddresses); return fallback; } int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback) { + typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size); + IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); + if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found + ULONG outBufLen = 0; PIP_ADAPTER_ADDRESSES pAddresses = nullptr; PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; - if(GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) { FREE(pAddresses); @@ -228,23 +242,23 @@ AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen ); - if(dwRetVal != NO_ERROR) + if (dwRetVal != NO_ERROR) { - LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed"); + LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed"); FREE(pAddresses); return fallback; } bool found_address = false; pCurrAddresses = pAddresses; - while(pCurrAddresses) + while (pCurrAddresses) { PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; pUnicast = pCurrAddresses->FirstUnicastAddress; - if(pUnicast == nullptr) - LogPrint(eLogError, "NetIface: GetMTU(): not a unicast ipv6 address, this is not supported"); + if (pUnicast == nullptr) + LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv6 address, this is not supported"); - for(int i = 0; pUnicast != nullptr; ++i) + for (int i = 0; pUnicast != nullptr; ++i) { LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr; @@ -259,9 +273,13 @@ if (found_address) { - auto result = pAddresses->Mtu; + char addr[INET6_ADDRSTRLEN]; + inetntop(AF_INET6, &(((struct sockaddr_in6 *)localInterfaceAddress)->sin6_addr), addr, INET6_ADDRSTRLEN); + + auto result = pCurrAddresses->Mtu; FREE(pAddresses); pAddresses = nullptr; + LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv6 address ", addr); return result; } pUnicast = pUnicast->Next; @@ -270,7 +288,7 @@ pCurrAddresses = pCurrAddresses->Next; } - LogPrint(eLogError, "NetIface: GetMTU(): no usable unicast ipv6 addresses found"); + LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv6 addresses found"); FREE(pAddresses); return fallback; } @@ -302,7 +320,7 @@ } else { - LogPrint(eLogError, "NetIface: GetMTU(): address family is not supported"); + LogPrint(eLogError, "NetIface: GetMTU: Address family is not supported"); return fallback; } } @@ -355,7 +373,7 @@ LogPrint(eLogError, "NetIface: Failed to create datagram socket"); } else - LogPrint(eLogWarning, "NetIface: interface for local address", localAddress.to_string(), " not found"); + LogPrint(eLogWarning, "NetIface: Interface for local address", localAddress.to_string(), " not found"); freeifaddrs(ifaddr); return mtu; @@ -377,62 +395,88 @@ const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6) { #ifdef _WIN32 - LogPrint(eLogError, "NetIface: cannot get address by interface name, not implemented on WIN32"); + LogPrint(eLogError, "NetIface: Cannot get address by interface name, not implemented on WIN32"); if(ipv6) return boost::asio::ip::address::from_string("::1"); else return boost::asio::ip::address::from_string("127.0.0.1"); #else int af = (ipv6 ? AF_INET6 : AF_INET); - ifaddrs *addrs, *cur = nullptr; - if(getifaddrs(&addrs) == 0) + ifaddrs *addrs; + try { - // got ifaddrs - cur = addrs; - while(cur) + if (!getifaddrs(&addrs)) { - std::string cur_ifname(cur->ifa_name); - if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af) + for (auto cur = addrs; cur; cur = cur->ifa_next) { - // match - char addr[INET6_ADDRSTRLEN]; - memset (addr, 0, INET6_ADDRSTRLEN); - if(af == AF_INET) - inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN); - else - inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN); - freeifaddrs(addrs); - std::string cur_ifaddr(addr); - return boost::asio::ip::address::from_string(cur_ifaddr); + std::string cur_ifname(cur->ifa_name); + if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af) + { + // match + char addr[INET6_ADDRSTRLEN]; + memset (addr, 0, INET6_ADDRSTRLEN); + if(af == AF_INET) + inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN); + else + inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN); + freeifaddrs(addrs); + std::string cur_ifaddr(addr); + return boost::asio::ip::address::from_string(cur_ifaddr); + } } - cur = cur->ifa_next; } } + catch (std::exception& ex) + { + LogPrint(eLogError, "NetIface: Exception while searching address using ifaddr: ", ex.what()); + } + if(addrs) freeifaddrs(addrs); std::string fallback; if(ipv6) { fallback = "::1"; - LogPrint(eLogWarning, "NetIface: cannot find ipv6 address for interface ", ifname); + LogPrint(eLogWarning, "NetIface: Cannot find IPv6 address for interface ", ifname); } else { fallback = "127.0.0.1"; - LogPrint(eLogWarning, "NetIface: cannot find ipv4 address for interface ", ifname); + LogPrint(eLogWarning, "NetIface: Cannot find IPv4 address for interface ", ifname); } return boost::asio::ip::address::from_string(fallback); #endif } + int GetMaxMTU (const boost::asio::ip::address_v6& localAddress) + { + uint32_t prefix = bufbe32toh (localAddress.to_bytes ().data ()); + switch (prefix) + { + case 0x20010470: + case 0x260070ff: + // Hurricane Electric + return 1480; + break; + case 0x2a06a003: + case 0x2a06a004: + case 0x2a06a005: + // route48 + return 1420; + break; + default: ; + } + return 1500; + } + static bool IsYggdrasilAddress (const uint8_t addr[16]) { return addr[0] == 0x02 || addr[0] == 0x03; - } + } bool IsYggdrasilAddress (const boost::asio::ip::address& addr) { if (!addr.is_v6 ()) return false; return IsYggdrasilAddress (addr.to_v6 ().to_bytes ().data ()); - } - + } + boost::asio::ip::address_v6 GetYggdrasilAddress () { #if defined(_WIN32) @@ -479,32 +523,36 @@ } pCurrAddresses = pCurrAddresses->Next; } - LogPrint(eLogWarning, "NetIface: interface with yggdrasil network address not found"); + LogPrint(eLogWarning, "NetIface: Interface with Yggdrasil network address not found"); FREE(pAddresses); return boost::asio::ip::address_v6 (); #else - ifaddrs *addrs, *cur = nullptr; - auto err = getifaddrs(&addrs); - if (!err) + ifaddrs *addrs; + try { - cur = addrs; - while(cur) + if (!getifaddrs(&addrs)) { - if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_INET6) + for (auto cur = addrs; cur; cur = cur->ifa_next) { - sockaddr_in6* sa = (sockaddr_in6*)cur->ifa_addr; - if (IsYggdrasilAddress(sa->sin6_addr.s6_addr)) + if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_INET6) { - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), &sa->sin6_addr, 16); - freeifaddrs(addrs); - return boost::asio::ip::address_v6 (bytes); + sockaddr_in6* sa = (sockaddr_in6*)cur->ifa_addr; + if (IsYggdrasilAddress(sa->sin6_addr.s6_addr)) + { + boost::asio::ip::address_v6::bytes_type bytes; + memcpy (bytes.data (), &sa->sin6_addr, 16); + freeifaddrs(addrs); + return boost::asio::ip::address_v6 (bytes); + } } } - cur = cur->ifa_next; } } - LogPrint(eLogWarning, "NetIface: interface with yggdrasil network address not found"); + catch (std::exception& ex) + { + LogPrint(eLogError, "NetIface: Exception while searching Yggdrasill address using ifaddr: ", ex.what()); + } + LogPrint(eLogWarning, "NetIface: Interface with Yggdrasil network address not found"); if(addrs) freeifaddrs(addrs); return boost::asio::ip::address_v6 (); #endif @@ -512,16 +560,16 @@ bool IsLocalAddress (const boost::asio::ip::address& addr) { - auto mtu = // TODO: implement better + auto mtu = // TODO: implement better #ifdef _WIN32 GetMTUWindows(addr, 0); #else GetMTUUnix(addr, 0); -#endif +#endif return mtu > 0; - } - - bool IsInReservedRange (const boost::asio::ip::address& host) + } + + bool IsInReservedRange (const boost::asio::ip::address& host) { // https://en.wikipedia.org/wiki/Reserved_IP_addresses if (host.is_unspecified ()) return false; diff -Nru i2pd-2.39.0/libi2pd/util.h i2pd-2.43.0/libi2pd/util.h --- i2pd-2.39.0/libi2pd/util.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/util.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -51,12 +51,13 @@ MemoryPool (): m_Head (nullptr) {} ~MemoryPool () { - while (m_Head) - { - auto tmp = m_Head; - m_Head = static_cast(*(void * *)m_Head); // next - ::operator delete ((void *)tmp); - } + CleanUp (); + } + + void CleanUp () + { + CleanUp (m_Head); + m_Head = nullptr; } template @@ -95,6 +96,18 @@ protected: + void CleanUp (T * head) + { + while (head) + { + auto tmp = head; + head = static_cast(*(void * *)head); // next + ::operator delete ((void *)tmp); + } + } + + protected: + T * m_Head; }; @@ -126,6 +139,24 @@ this->Release (it); } + template + std::shared_ptr AcquireSharedMt (TArgs&&... args) + { + return std::shared_ptr(AcquireMt (std::forward(args)...), + std::bind::*)(T *)> (&MemoryPoolMt::ReleaseMt, this, std::placeholders::_1)); + } + + void CleanUpMt () + { + T * head; + { + std::lock_guard l(m_Mutex); + head = this->m_Head; + this->m_Head = nullptr; + } + if (head) this->CleanUp (head); + } + private: std::mutex m_Mutex; @@ -187,6 +218,7 @@ namespace net { int GetMTU (const boost::asio::ip::address& localAddress); + int GetMaxMTU (const boost::asio::ip::address_v6& localAddress); // check tunnel broker for ipv6 address const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6=false); boost::asio::ip::address_v6 GetYggdrasilAddress (); bool IsLocalAddress (const boost::asio::ip::address& addr); diff -Nru i2pd-2.39.0/libi2pd/version.h i2pd-2.43.0/libi2pd/version.h --- i2pd-2.39.0/libi2pd/version.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd/version.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -16,21 +16,22 @@ #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 39 +#define I2PD_VERSION_MINOR 43 #define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 -#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) +#ifdef GITVER + #define I2PD_VERSION GITVER +#else + #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) +#endif + #define VERSION I2PD_VERSION -#ifdef MESHNET -#define I2PD_NET_ID 3 -#else #define I2PD_NET_ID 2 -#endif #define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MINOR 9 -#define I2P_VERSION_MICRO 51 +#define I2P_VERSION_MICRO 55 #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 -Nru i2pd-2.39.0/libi2pd_client/AddressBook.cpp i2pd-2.43.0/libi2pd_client/AddressBook.cpp --- i2pd-2.39.0/libi2pd_client/AddressBook.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/AddressBook.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -35,7 +35,7 @@ class AddressBookFilesystemStorage: public AddressBookStorage { public: - + AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") { i2p::config::GetOption("persist.addressbook", m_IsPersist); @@ -62,7 +62,7 @@ private: i2p::fs::HashedStorage storage; - std::string etagsPath, indexPath, localPath; + std::string etagsPath, indexPath, localPath; bool m_IsPersist; std::string m_HostsFile; // file to dump hosts.txt, empty if not used }; @@ -119,7 +119,7 @@ std::string path = storage.Path( address->GetIdentHash().ToBase32() ); std::ofstream f (path, std::ofstream::binary | std::ofstream::out); if (!f.is_open ()) { - LogPrint (eLogError, "Addressbook: can't open file ", path); + LogPrint (eLogError, "Addressbook: Can't open file ", path); return; } size_t len = address->GetFullLen (); @@ -169,7 +169,7 @@ LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath); return 0; } - LogPrint(eLogInfo, "Addressbook: using index file ", indexPath); + LogPrint(eLogInfo, "Addressbook: Using index file ", indexPath); LogPrint (eLogInfo, "Addressbook: ", num, " addresses loaded from storage"); return num; @@ -185,9 +185,9 @@ int AddressBookFilesystemStorage::Save (const std::map >& addresses) { - if (addresses.empty()) + if (addresses.empty()) { - LogPrint(eLogWarning, "Addressbook: not saving empty addressbook"); + LogPrint(eLogWarning, "Addressbook: Not saving empty addressbook"); return 0; } @@ -200,7 +200,7 @@ for (const auto& it: addresses) { if (it.second->IsValid ()) - { + { f << it.first << ","; if (it.second->IsIdentHash ()) f << it.second->identHash.ToBase32 (); @@ -208,15 +208,15 @@ f << it.second->blindedPublicKey->ToB33 (); f << std::endl; num++; - } + } else - LogPrint (eLogWarning, "Addressbook: invalid address ", it.first); + LogPrint (eLogWarning, "Addressbook: Invalid address ", it.first); } LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved"); - } + } else LogPrint (eLogWarning, "Addressbook: Can't open ", indexPath); - } + } if (!m_HostsFile.empty ()) { // dump full hosts.txt @@ -226,18 +226,18 @@ for (const auto& it: addresses) { std::shared_ptr addr; - if (it.second->IsIdentHash ()) - { + if (it.second->IsIdentHash ()) + { addr = GetAddress (it.second->identHash); if (addr) f << it.first << "=" << addr->ToBase64 () << std::endl; - } - } - } + } + } + } else LogPrint (eLogWarning, "Addressbook: Can't open ", m_HostsFile); - } - + } + return num; } @@ -265,7 +265,7 @@ void AddressBookFilesystemStorage::ResetEtags () { - LogPrint (eLogError, "Addressbook: resetting eTags"); + LogPrint (eLogError, "Addressbook: Resetting eTags"); for (boost::filesystem::directory_iterator it (etagsPath); it != boost::filesystem::directory_iterator (); ++it) { if (!boost::filesystem::is_regular_file (it->status ())) @@ -299,7 +299,8 @@ } AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false), - m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr) + m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr), + m_IsEnabled (true) { } @@ -310,12 +311,16 @@ void AddressBook::Start () { - if (!m_Storage) - m_Storage = new AddressBookFilesystemStorage; - m_Storage->Init(); - LoadHosts (); /* try storage, then hosts.txt, then download */ - StartSubscriptions (); - StartLookups (); + i2p::config::GetOption("addressbook.enabled", m_IsEnabled); + if (m_IsEnabled) + { + if (!m_Storage) + m_Storage = new AddressBookFilesystemStorage; + m_Storage->Init(); + LoadHosts (); /* try storage, then hosts.txt, then download */ + StartSubscriptions (); + StartLookups (); + } } void AddressBook::StartResolvers () @@ -334,17 +339,17 @@ } if (m_IsDownloading) { - LogPrint (eLogInfo, "Addressbook: subscriptions are downloading, abort"); + LogPrint (eLogInfo, "Addressbook: Subscriptions are downloading, abort"); for (int i = 0; i < 30; i++) { if (!m_IsDownloading) { - LogPrint (eLogInfo, "Addressbook: subscriptions download complete"); + LogPrint (eLogInfo, "Addressbook: Subscriptions download complete"); break; } std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds } - LogPrint (eLogError, "Addressbook: subscription download timeout"); + LogPrint (eLogError, "Addressbook: Subscription download timeout"); m_IsDownloading = false; } if (m_Storage) @@ -370,9 +375,10 @@ pos = address.find (".i2p"); if (pos != std::string::npos) { + if (!m_IsEnabled) return nullptr; auto addr = FindAddress (address); if (!addr) - LookupAddress (address); // TODO: + LookupAddress (address); // TODO: return addr; } } @@ -397,7 +403,7 @@ if (pos != std::string::npos) { m_Addresses[address] = std::make_shared
(jump.substr (0, pos)); - LogPrint (eLogInfo, "Addressbook: added ", address," -> ", jump); + LogPrint (eLogInfo, "Addressbook: Added ", address," -> ", jump); } else { @@ -407,10 +413,10 @@ { m_Storage->AddAddress (ident); m_Addresses[address] = std::make_shared
(ident->GetIdentHash ()); - LogPrint (eLogInfo, "Addressbook: added ", address," -> ", ToAddress(ident->GetIdentHash ())); + LogPrint (eLogInfo, "Addressbook: Added ", address," -> ", ToAddress(ident->GetIdentHash ())); } else - LogPrint (eLogError, "Addressbook: malformed address ", jump); + LogPrint (eLogError, "Addressbook: Malformed address ", jump); } } @@ -470,9 +476,23 @@ 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); + LogPrint (eLogError, "Addressbook: Malformed address ", addr, " for ", name); incomplete = f.eof (); continue; } @@ -480,13 +500,13 @@ auto it = m_Addresses.find (name); if (it != m_Addresses.end ()) // already exists ? { - if (it->second->IsIdentHash () && it->second->identHash != ident->GetIdentHash () && // address changed? - ident->GetSigningKeyType () != i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) // don't replace by DSA + if (it->second->IsIdentHash () && it->second->identHash != ident->GetIdentHash () && // address changed? + ident->GetSigningKeyType () != i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) // don't replace by DSA { it->second->identHash = ident->GetIdentHash (); m_Storage->AddAddress (ident); m_Storage->RemoveAddress (it->second->identHash); - LogPrint (eLogInfo, "Addressbook: updated host: ", name); + LogPrint (eLogInfo, "Addressbook: Updated host: ", name); } } else @@ -494,7 +514,7 @@ m_Addresses.emplace (name, std::make_shared
(ident->GetIdentHash ())); m_Storage->AddAddress (ident); if (is_update) - LogPrint (eLogInfo, "Addressbook: added new host: ", name); + LogPrint (eLogInfo, "Addressbook: Added new host: ", name); } } else @@ -526,8 +546,9 @@ LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded"); LogPrint (eLogWarning, "Addressbook: subscriptions.txt usage is deprecated, use config file instead"); } - else if (!i2p::config::IsDefault("addressbook.subscriptions")) + else { + LogPrint (eLogInfo, "Addressbook: Loading subscriptions from config file"); // using config file items std::string subscriptionURLs; i2p::config::GetOption("addressbook.subscriptions", subscriptionURLs); std::vector subsList; @@ -535,14 +556,13 @@ for (const auto& s: subsList) { - if (s.empty () || s[0] == '#') continue; // skip empty line or comment m_Subscriptions.push_back (std::make_shared (*this, s)); } LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded"); } } else - LogPrint (eLogError, "Addressbook: subscriptions already loaded"); + LogPrint (eLogError, "Addressbook: Subscriptions already loaded"); } void AddressBook::LoadLocal () @@ -627,7 +647,7 @@ this, std::placeholders::_1)); } else - LogPrint (eLogError, "Addressbook: can't start subscriptions: missing shared local destination"); + LogPrint (eLogError, "Addressbook: Can't start subscriptions: missing shared local destination"); } void AddressBook::StopSubscriptions () @@ -642,7 +662,7 @@ { auto dest = i2p::client::context.GetSharedLocalDestination (); if (!dest) { - LogPrint(eLogWarning, "Addressbook: missing local destination, skip subscription update"); + LogPrint(eLogWarning, "Addressbook: Missing local destination, skip subscription update"); return; } if (!m_IsDownloading && dest->IsReady ()) @@ -650,7 +670,7 @@ if (!m_IsLoaded) { // download it from default subscription - LogPrint (eLogInfo, "Addressbook: trying to download it from default subscription."); + LogPrint (eLogInfo, "Addressbook: Trying to download it from default subscription."); std::string defaultSubURL; i2p::config::GetOption("addressbook.defaulturl", defaultSubURL); if (!m_DefaultSubscription) m_DefaultSubscription = std::make_shared(*this, defaultSubURL); @@ -788,7 +808,7 @@ LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link); if (!url.parse(m_Link)) { - LogPrint(eLogError, "Addressbook: failed to parse url: ", m_Link); + LogPrint(eLogError, "Addressbook: Failed to parse url: ", m_Link); return false; } auto addr = m_Book.GetAddress (url.host); @@ -827,7 +847,7 @@ } if (m_Etag.empty() && m_LastModified.empty()) { m_Book.GetEtag (m_Ident, m_Etag, m_LastModified); - LogPrint (eLogDebug, "Addressbook: loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified); + LogPrint (eLogDebug, "Addressbook: Loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified); } /* save url parts for later use */ std::string dest_host = url.host; @@ -844,9 +864,9 @@ if (!m_LastModified.empty()) req.AddHeader("If-Modified-Since", m_LastModified); /* convert url to relative */ - url.schema = ""; - url.host = ""; - req.uri = url.to_string(); + url.schema = ""; + url.host = ""; + req.uri = url.to_string(); req.version = "HTTP/1.1"; auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port); std::string request = req.to_string(); @@ -872,7 +892,7 @@ // wait 1 more second if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT + 1)) == std::cv_status::timeout) { - LogPrint (eLogError, "Addressbook: subscriptions request timeout expired"); + LogPrint (eLogError, "Addressbook: Subscriptions request timeout expired"); numAttempts++; if (numAttempts > 5) end = true; } @@ -885,35 +905,35 @@ int res_head_len = res.parse(response); if (res_head_len < 0) { - LogPrint(eLogError, "Addressbook: can't parse http response from ", dest_host); + LogPrint(eLogError, "Addressbook: Can't parse http response from ", dest_host); return false; } if (res_head_len == 0) { - LogPrint(eLogError, "Addressbook: incomplete http response from ", dest_host, ", interrupted by timeout"); + LogPrint(eLogError, "Addressbook: Incomplete http response from ", dest_host, ", interrupted by timeout"); return false; } /* assert: res_head_len > 0 */ response.erase(0, res_head_len); if (res.code == 304) { - LogPrint (eLogInfo, "Addressbook: no updates from ", dest_host, ", code 304"); + LogPrint (eLogInfo, "Addressbook: No updates from ", dest_host, ", code 304"); return false; } if (res.code != 200) { - LogPrint (eLogWarning, "Adressbook: can't get updates from ", dest_host, ", response code ", res.code); + LogPrint (eLogWarning, "Adressbook: Can't get updates from ", dest_host, ", response code ", res.code); return false; } int len = res.content_length(); if (response.empty()) { - LogPrint(eLogError, "Addressbook: empty response from ", dest_host, ", expected ", len, " bytes"); + LogPrint(eLogError, "Addressbook: Empty response from ", dest_host, ", expected ", len, " bytes"); return false; } if (!res.is_gzipped () && len > 0 && len != (int) response.length()) { - LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", len, ", got: ", response.length(), "bytes"); + LogPrint(eLogError, "Addressbook: Response size mismatch, expected: ", len, ", got: ", response.length(), "bytes"); return false; } /* assert: res.code == 200 */ @@ -934,13 +954,13 @@ inflator.Inflate ((const uint8_t *) response.data(), response.length(), out); if (out.fail()) { - LogPrint(eLogError, "Addressbook: can't gunzip http response"); + LogPrint(eLogError, "Addressbook: Can't gunzip http response"); return false; } response = out.str(); } std::stringstream ss(response); - LogPrint (eLogInfo, "Addressbook: got update from ", dest_host); + LogPrint (eLogInfo, "Addressbook: Got update from ", dest_host); m_Book.LoadHostsFromStream (ss, true); return true; } diff -Nru i2pd-2.39.0/libi2pd_client/AddressBook.h i2pd-2.43.0/libi2pd_client/AddressBook.h --- i2pd-2.39.0/libi2pd_client/AddressBook.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/AddressBook.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -116,7 +116,7 @@ private: std::mutex m_AddressBookMutex; - std::map > m_Addresses; + std::map > m_Addresses; std::map > m_Resolvers; // local destination->resolver std::mutex m_LookupsMutex; std::map m_Lookups; // nonce -> address @@ -126,6 +126,7 @@ std::vector > m_Subscriptions; std::shared_ptr m_DefaultSubscription; // in case if we don't know any addresses yet boost::asio::deadline_timer * m_SubscriptionsUpdateTimer; + bool m_IsEnabled; }; class AddressBookSubscription @@ -162,7 +163,7 @@ private: std::shared_ptr m_LocalDestination; - std::map m_LocalAddresses; + std::map m_LocalAddresses; }; } } diff -Nru i2pd-2.39.0/libi2pd_client/BOB.cpp i2pd-2.43.0/libi2pd_client/BOB.cpp --- i2pd-2.39.0/libi2pd_client/BOB.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/BOB.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -68,7 +68,7 @@ std::shared_ptr receiver) { if (ecode) - LogPrint (eLogError, "BOB: inbound tunnel read error: ", ecode.message ()); + LogPrint (eLogError, "BOB: Inbound tunnel read error: ", ecode.message ()); else { receiver->bufferOffset += bytes_transferred; @@ -83,7 +83,7 @@ auto addr = context.GetAddressBook ().GetAddress (receiver->buffer); if (!addr) { - LogPrint (eLogError, "BOB: address ", receiver->buffer, " not found"); + LogPrint (eLogError, "BOB: Address ", receiver->buffer, " not found"); return; } if (addr->IsIdentHash ()) @@ -106,7 +106,7 @@ if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE) ReceiveAddress (receiver); else - LogPrint (eLogError, "BOB: missing inbound address"); + LogPrint (eLogError, "BOB: Missing inbound address"); } } } @@ -168,7 +168,7 @@ m_LocalDestination (localDestination), m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr), m_Nickname(nickname), m_InHost(inhost), m_OutHost(outhost), - m_InPort(inport), m_OutPort(outport), m_Quiet(quiet) + m_InPort(inport), m_OutPort(outport), m_Quiet(quiet), m_IsRunning(false) { } @@ -183,6 +183,7 @@ { if (m_OutboundTunnel) m_OutboundTunnel->Start (); if (m_InboundTunnel) m_InboundTunnel->Start (); + m_IsRunning = true; } void BOBDestination::Stop () @@ -193,6 +194,7 @@ void BOBDestination::StopTunnels () { + m_IsRunning = false; if (m_OutboundTunnel) { m_OutboundTunnel->Stop (); @@ -268,7 +270,7 @@ { if(ecode) { - LogPrint (eLogError, "BOB: command channel read error: ", ecode.message()); + LogPrint (eLogError, "BOB: Command channel read error: ", ecode.message()); if (ecode != boost::asio::error::operation_aborted) Terminate (); } @@ -292,7 +294,7 @@ } else { - LogPrint (eLogError, "BOB: unknown command ", command.c_str()); + LogPrint (eLogError, "BOB: Unknown command ", command.c_str()); SendReplyError ("unknown command"); } } @@ -310,7 +312,7 @@ { if (ecode) { - LogPrint (eLogError, "BOB: command channel send error: ", ecode.message ()); + LogPrint (eLogError, "BOB: Command channel send error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate (); } @@ -361,7 +363,7 @@ const auto issetStr = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost const auto issetNum = [&issetStr](const int p) { return issetStr(p == 0 ? "" : std::to_string(p)); }; // for inport, outport const auto destExists = [](const BOBDestination * const dest) { return dest != nullptr; }; - const auto destReady = [](const BOBDestination * const dest) { return dest->GetLocalDestination()->IsReady(); }; + const auto destReady = [](const BOBDestination * const dest) { return dest->IsRunning(); }; const auto bool_str = [](const bool v) { return v ? "true" : "false"; }; // bool -> str // tunnel info @@ -479,26 +481,43 @@ void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: setnick ", operand); - m_Nickname = operand; - std::string msg ("Nickname set to "); - msg += m_Nickname; - SendReplyOK (msg.c_str ()); + if(*operand) + { + auto dest = m_Owner.FindDestination (operand); + if (!dest) + { + m_Nickname = operand; + std::string msg ("Nickname set to "); + msg += m_Nickname; + SendReplyOK (msg.c_str ()); + } + else + SendReplyError ("tunnel is active"); + } + else + SendReplyError ("no nickname has been set"); } void BOBCommandSession::GetNickCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: getnick ", operand); - m_CurrentDestination = m_Owner.FindDestination (operand); - if (m_CurrentDestination) - { - m_Keys = m_CurrentDestination->GetKeys (); - m_Nickname = operand; - } - if (m_Nickname == operand) + if(*operand) { - std::string msg ("Nickname set to "); - msg += m_Nickname; - SendReplyOK (msg.c_str ()); + m_CurrentDestination = m_Owner.FindDestination (operand); + if (m_CurrentDestination) + { + m_Keys = m_CurrentDestination->GetKeys (); + m_IsActive = m_CurrentDestination->IsRunning (); + m_Nickname = operand; + } + if (m_Nickname == operand) + { + std::string msg ("Nickname set to "); + msg += m_Nickname; + SendReplyOK (msg.c_str ()); + } + else + SendReplyError ("no nickname has been set"); } else SendReplyError ("no nickname has been set"); @@ -523,7 +542,7 @@ } catch (std::invalid_argument& ex) { - LogPrint (eLogWarning, "BOB: newkeys ", ex.what ()); + LogPrint (eLogWarning, "BOB: Error on newkeys: ", ex.what ()); } } @@ -535,7 +554,7 @@ void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: setkeys ", operand); - if (m_Keys.FromBase64 (operand)) + if (*operand && m_Keys.FromBase64 (operand)) SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ()); else SendReplyError ("invalid keys"); @@ -562,35 +581,55 @@ void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: outhost ", operand); - m_OutHost = operand; - SendReplyOK ("outhost set"); + if (*operand) + { + m_OutHost = operand; + SendReplyOK ("outhost set"); + } + else + SendReplyError ("empty outhost"); } void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: outport ", operand); - m_OutPort = std::stoi(operand); - if (m_OutPort >= 0) - SendReplyOK ("outbound port set"); + if (*operand) + { + m_OutPort = std::stoi(operand); + if (m_OutPort >= 0) + SendReplyOK ("outbound port set"); + else + SendReplyError ("port out of range"); + } else - SendReplyError ("port out of range"); + SendReplyError ("empty outport"); } void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: inhost ", operand); - m_InHost = operand; - SendReplyOK ("inhost set"); + if (*operand) + { + m_InHost = operand; + SendReplyOK ("inhost set"); + } + else + SendReplyError ("empty inhost"); } void BOBCommandSession::InportCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: inport ", operand); - m_InPort = std::stoi(operand); - if (m_InPort >= 0) - SendReplyOK ("inbound port set"); + if (*operand) + { + m_InPort = std::stoi(operand); + if (m_InPort >= 0) + SendReplyOK ("inbound port set"); + else + SendReplyError ("port out of range"); + } else - SendReplyError ("port out of range"); + SendReplyError ("empty inport"); } void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len) @@ -613,54 +652,64 @@ void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: lookup ", operand); - auto addr = context.GetAddressBook ().GetAddress (operand); - if (!addr) - { - SendReplyError ("Address Not found"); - return; - } - auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination (); - if (addr->IsIdentHash ()) + if (*operand) { - // we might have leaseset already - auto leaseSet = localDestination->FindLeaseSet (addr->identHash); - if (leaseSet) + auto addr = context.GetAddressBook ().GetAddress (operand); + if (!addr) { - SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ()); + SendReplyError ("Address Not found"); return; } - } - // trying to request - auto s = shared_from_this (); - auto requstCallback = [s](std::shared_ptr ls) + auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination (); + if (addr->IsIdentHash ()) { - if (ls) - s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); - else - s->SendReplyError ("LeaseSet Not found"); - }; - if (addr->IsIdentHash ()) - localDestination->RequestDestination (addr->identHash, requstCallback); + // we might have leaseset already + auto leaseSet = localDestination->FindLeaseSet (addr->identHash); + if (leaseSet) + { + SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ()); + return; + } + } + // trying to request + auto s = shared_from_this (); + auto requstCallback = [s](std::shared_ptr ls) + { + if (ls) + s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); + else + s->SendReplyError ("LeaseSet Not found"); + }; + if (addr->IsIdentHash ()) + localDestination->RequestDestination (addr->identHash, requstCallback); + else + localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback); + } else - localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback); + SendReplyError ("empty lookup address"); } void BOBCommandSession::LookupLocalCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: lookup local ", operand); - auto addr = context.GetAddressBook ().GetAddress (operand); - if (!addr) + if (*operand) { - SendReplyError ("Address Not found"); - return; + auto addr = context.GetAddressBook ().GetAddress (operand); + if (!addr) + { + SendReplyError ("Address Not found"); + return; + } + auto ls = i2p::data::netdb.FindLeaseSet (addr->identHash); + if (ls) + SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); + else + SendReplyError ("Local LeaseSet Not found"); } - auto ls = i2p::data::netdb.FindLeaseSet (addr->identHash); - if (ls) - SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); else - SendReplyError ("Local LeaseSet Not found"); + SendReplyError ("empty lookup address"); } - + void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: clear"); @@ -704,7 +753,7 @@ msg += operand; *(const_cast(value)) = '='; msg += " set to "; - msg += value; + msg += value + 1; SendReplyOK (msg.c_str ()); } else @@ -718,11 +767,11 @@ std::string statusLine; // always prefer destination - auto ptr = m_Owner.FindDestination(name); - if(ptr != nullptr) + auto dest = m_Owner.FindDestination(name); + if(dest) { // tunnel destination exists - BuildStatusLine(false, ptr, statusLine); + BuildStatusLine(false, dest, statusLine); SendReplyOK(statusLine.c_str()); } else @@ -742,7 +791,7 @@ void BOBCommandSession::HelpCommandHandler (const char * operand, size_t len) { auto helpStrings = m_Owner.GetHelpStrings(); - if(len == 0) + if(!*operand) { std::stringstream ss; ss << "COMMANDS:"; @@ -786,7 +835,7 @@ m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler; m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler; m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler; - m_CommandHandlers[BOB_COMMAND_LOOKUP_LOCAL] = &BOBCommandSession::LookupLocalCommandHandler; + m_CommandHandlers[BOB_COMMAND_LOOKUP_LOCAL] = &BOBCommandSession::LookupLocalCommandHandler; m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler; m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler; m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler; @@ -880,7 +929,7 @@ session->SendVersion (); } else - LogPrint (eLogError, "BOB: accept error: ", ecode.message ()); + LogPrint (eLogError, "BOB: Accept error: ", ecode.message ()); } } } diff -Nru i2pd-2.39.0/libi2pd_client/BOB.h i2pd-2.43.0/libi2pd_client/BOB.h --- i2pd-2.39.0/libi2pd_client/BOB.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/BOB.h 2022-08-21 19:40:41.000000000 +0000 @@ -63,7 +63,7 @@ const char BOB_HELP_OUTPORT[] = "outport - Set the outbound port that nickname contacts."; const char BOB_HELP_INHOST[] = "inhost - Set the inbound hostname or IP."; const char BOB_HELP_INPORT[] = "inport - Set the inbound port number nickname listens on."; - const char BOB_HELP_QUIET[] = "quiet - Wether to send the incoming destination."; + const char BOB_HELP_QUIET[] = "quiet - Whether to send the incoming destination."; const char BOB_HELP_LOOKUP[] = "lookup - Look up an I2P hostname."; const char BOB_HELP_CLEAR[] = "clear - Clear the current nickname out of the list."; const char BOB_HELP_LIST[] = "list - List all tunnels."; @@ -163,6 +163,7 @@ int GetInPort() const { return m_InPort; } int GetOutPort() const { return m_OutPort; } bool GetQuiet() const { return m_Quiet; } + bool IsRunning() const { return m_IsRunning; } const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); }; std::shared_ptr GetLocalDestination () const { return m_LocalDestination; }; @@ -176,6 +177,7 @@ std::string m_InHost, m_OutHost; int m_InPort, m_OutPort; bool m_Quiet; + bool m_IsRunning; }; class BOBCommandChannel; diff -Nru i2pd-2.39.0/libi2pd_client/ClientContext.cpp i2pd-2.43.0/libi2pd_client/ClientContext.cpp --- i2pd-2.39.0/libi2pd_client/ClientContext.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/ClientContext.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -65,7 +65,7 @@ std::string samAddr; i2p::config::GetOption("sam.address", samAddr); uint16_t samPort; i2p::config::GetOption("sam.port", samPort); bool singleThread; i2p::config::GetOption("sam.singlethread", singleThread); - LogPrint(eLogInfo, "Clients: starting SAM bridge at ", samAddr, ":", samPort); + LogPrint(eLogInfo, "Clients: Starting SAM bridge at ", samAddr, ":", samPort); try { m_SamBridge = new SAMBridge (samAddr, samPort, singleThread); @@ -83,7 +83,7 @@ if (bob) { std::string bobAddr; i2p::config::GetOption("bob.address", bobAddr); uint16_t bobPort; i2p::config::GetOption("bob.port", bobPort); - LogPrint(eLogInfo, "Clients: starting BOB command channel at ", bobAddr, ":", bobPort); + LogPrint(eLogInfo, "Clients: Starting BOB command channel at ", bobAddr, ":", bobPort); try { m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort); @@ -103,7 +103,7 @@ std::string i2cpAddr; i2p::config::GetOption("i2cp.address", i2cpAddr); uint16_t i2cpPort; i2p::config::GetOption("i2cp.port", i2cpPort); bool singleThread; i2p::config::GetOption("i2cp.singlethread", singleThread); - LogPrint(eLogInfo, "Clients: starting I2CP at ", i2cpAddr, ":", i2cpPort); + LogPrint(eLogInfo, "Clients: Starting I2CP at ", i2cpAddr, ":", i2cpPort); try { m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort, singleThread); @@ -130,7 +130,7 @@ { if (m_HttpProxy) { - LogPrint(eLogInfo, "Clients: stopping HTTP Proxy"); + LogPrint(eLogInfo, "Clients: Stopping HTTP Proxy"); m_HttpProxy->Stop(); delete m_HttpProxy; m_HttpProxy = nullptr; @@ -138,7 +138,7 @@ if (m_SocksProxy) { - LogPrint(eLogInfo, "Clients: stopping SOCKS Proxy"); + LogPrint(eLogInfo, "Clients: Stopping SOCKS Proxy"); m_SocksProxy->Stop(); delete m_SocksProxy; m_SocksProxy = nullptr; @@ -146,21 +146,21 @@ for (auto& it: m_ClientTunnels) { - LogPrint(eLogInfo, "Clients: stopping I2P client tunnel on port ", it.first); + LogPrint(eLogInfo, "Clients: Stopping I2P client tunnel on port ", it.first); it.second->Stop (); } m_ClientTunnels.clear (); for (auto& it: m_ServerTunnels) { - LogPrint(eLogInfo, "Clients: stopping I2P server tunnel"); + LogPrint(eLogInfo, "Clients: Stopping I2P server tunnel"); it.second->Stop (); } m_ServerTunnels.clear (); if (m_SamBridge) { - LogPrint(eLogInfo, "Clients: stopping SAM bridge"); + LogPrint(eLogInfo, "Clients: Stopping SAM bridge"); m_SamBridge->Stop (); delete m_SamBridge; m_SamBridge = nullptr; @@ -168,7 +168,7 @@ if (m_BOBCommandChannel) { - LogPrint(eLogInfo, "Clients: stopping BOB command channel"); + LogPrint(eLogInfo, "Clients: Stopping BOB command channel"); m_BOBCommandChannel->Stop (); delete m_BOBCommandChannel; m_BOBCommandChannel = nullptr; @@ -176,16 +176,16 @@ if (m_I2CPServer) { - LogPrint(eLogInfo, "Clients: stopping I2CP"); + LogPrint(eLogInfo, "Clients: Stopping I2CP"); m_I2CPServer->Stop (); delete m_I2CPServer; m_I2CPServer = nullptr; } - LogPrint(eLogInfo, "Clients: stopping AddressBook"); + LogPrint(eLogInfo, "Clients: Stopping AddressBook"); m_AddressBook.Stop (); - { + { std::lock_guard lock(m_ForwardsMutex); m_ServerForwards.clear(); m_ClientForwards.clear(); @@ -200,6 +200,8 @@ for (auto& it: m_Destinations) it.second->Stop (); m_Destinations.clear (); + + m_SharedLocalDestination->Release (); m_SharedLocalDestination = nullptr; } @@ -209,14 +211,6 @@ /*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 @@ if (m_HttpProxy) { m_HttpProxy->Stop (); + delete m_HttpProxy; m_HttpProxy = nullptr; } ReadHttpProxy (); @@ -233,10 +228,19 @@ 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 ();) @@ -274,7 +278,7 @@ s.read ((char *)buf, len); if(!keys.FromBuffer (buf, len)) { - LogPrint (eLogError, "Clients: failed to load keyfile ", filename); + LogPrint (eLogError, "Clients: Failed to load keyfile ", filename); success = false; } else @@ -283,7 +287,7 @@ } else { - LogPrint (eLogError, "Clients: can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType); + LogPrint (eLogError, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType); keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); size_t len = keys.GetFullLen (); @@ -402,7 +406,7 @@ void ClientContext::CreateNewSharedLocalDestination () { - std::map params + std::map params { { I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, "3" }, { I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, "3" }, @@ -451,6 +455,8 @@ options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH); options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY); options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY); + options[I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE); + options[I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE); options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND); options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY); options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY); @@ -483,10 +489,14 @@ options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value; if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value)) options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, value)) + options[I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE] = value; if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value)) options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value; if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value)) options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, value)) + options[I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE] = value; if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value)) options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value; if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value)) @@ -504,20 +514,15 @@ 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"); - } - LogPrint(eLogDebug, "Clients: tunnels config file: ", tunConf); + 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; @@ -526,7 +531,7 @@ for (auto& it: files) { if (it.substr(it.size() - 5) != ".conf") continue; // skip files which not ends with ".conf" - LogPrint(eLogDebug, "Clients: tunnels extra config file: ", it); + LogPrint(eLogDebug, "Clients: Tunnels extra config file: ", it); ReadTunnels (it, numClientTunnels, numServerTunnels); } } @@ -582,7 +587,7 @@ if (it != destinations.end ()) localDestination = it->second; else - { + { i2p::data::PrivateKeys k; if(LoadPrivateKeys (k, keys, sigType, cryptoType)) { @@ -597,22 +602,38 @@ destinations[keys] = localDestination; } } - } + } } if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { // udp client // TODO: hostnames - boost::asio::ip::udp::endpoint end(boost::asio::ip::address::from_string(address), port); + boost::asio::ip::udp::endpoint end (boost::asio::ip::address::from_string(address), port); if (!localDestination) localDestination = m_SharedLocalDestination; 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) - clientTunnel->Start(); + auto clientTunnel = std::make_shared (name, dest, end, localDestination, destinationPort, gzip); + + auto ins = m_ClientForwards.insert (std::make_pair (end, clientTunnel)); + if (ins.second) + { + clientTunnel->Start (); + numClientTunnels++; + } else + { + // TODO: update + if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ()) + { + LogPrint (eLogInfo, "Clients: I2P UDP client tunnel destination updated"); + ins.first->second->Stop (); + ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ()); + ins.first->second->Start (); + } + ins.first->second->isUpdated = true; LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists"); + } } else { boost::asio::ip::tcp::endpoint clientEndpoint; @@ -645,6 +666,13 @@ 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 +694,16 @@ 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 +720,7 @@ 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,19 +734,24 @@ std::shared_ptr localDestination = nullptr; auto it = destinations.find (keys); if (it != destinations.end ()) + { 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 @@ -727,26 +763,28 @@ address = "::1"; else address = "127.0.0.1"; - } - auto localAddress = boost::asio::ip::address::from_string(address); + } + auto localAddress = boost::asio::ip::address::from_string(address); auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, port, gzip); if(!isUniqueLocal) { - LogPrint(eLogInfo, "Clients: disabling loopback address mapping"); + LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); 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; } @@ -763,7 +801,7 @@ serverTunnel->SetLocalAddress (address); if(!isUniqueLocal) { - LogPrint(eLogInfo, "Clients: disabling loopback address mapping"); + LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); serverTunnel->SetUniqueLocal(isUniqueLocal); } if (accessList.length () > 0) @@ -795,7 +833,9 @@ 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"); @@ -824,8 +864,10 @@ uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort); std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL); bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper); + if (httpAddresshelper) + i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType); - LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort); + LogPrint(eLogInfo, "Clients: Starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort); if (httpProxyKeys.length () > 0) { i2p::data::PrivateKeys keys; @@ -837,7 +879,7 @@ if (localDestination) localDestination->Acquire (); } else - LogPrint(eLogError, "Clients: failed to load HTTP Proxy key"); + LogPrint(eLogError, "Clients: Failed to load HTTP Proxy key"); } try { @@ -858,7 +900,7 @@ bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy); if (socksproxy) { - std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys); + std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys); // we still need httpProxyKeys to compare with sockProxyKeys std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys); std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr); @@ -867,12 +909,12 @@ std::string socksOutProxyAddr; i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr); uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort); i2p::data::SigningKeyType sigType; i2p::config::GetOption("socksproxy.signaturetype", sigType); - LogPrint(eLogInfo, "Clients: starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort); + LogPrint(eLogInfo, "Clients: Starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort); if (httpProxyKeys == socksProxyKeys && m_HttpProxy) { localDestination = m_HttpProxy->GetLocalDestination (); localDestination->Acquire (); - } + } else if (socksProxyKeys.length () > 0) { i2p::data::PrivateKeys keys; @@ -884,7 +926,7 @@ if (localDestination) localDestination->Acquire (); } else - LogPrint(eLogError, "Clients: failed to load SOCKS Proxy key"); + LogPrint(eLogError, "Clients: Failed to load SOCKS Proxy key"); } try { @@ -920,27 +962,52 @@ } } - 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); + it = m_ClientTunnels.erase(it); + } else { + it->second->isUpdated = false; + it++; } - else + } + + 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++; + } } - } - template - void ClientContext::VisitTunnels (Visitor v) - { - VisitTunnelsContainer (m_ClientTunnels, v); - VisitTunnelsContainer (m_ServerTunnels, v); - // TODO: implement UDP forwards + // TODO: Write correct UDP tunnels stop + for (auto it = m_ClientForwards.begin (); it != m_ClientForwards.end ();) + { + if(clean && !it->second->isUpdated) { + it->second->Stop (); + 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->Stop (); + it = m_ServerForwards.erase(it); + } else { + it->second->isUpdated = false; + it++; + } + } } } } diff -Nru i2pd-2.39.0/libi2pd_client/ClientContext.h i2pd-2.43.0/libi2pd_client/ClientContext.h --- i2pd-2.39.0/libi2pd_client/ClientContext.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/ClientContext.h 2022-08-21 19:40:41.000000000 +0000 @@ -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 @@ 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 @@ 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 @@ 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); @@ -137,8 +142,8 @@ i2p::proxy::HTTPProxy * m_HttpProxy; i2p::proxy::SOCKSProxy * m_SocksProxy; - std::map > m_ClientTunnels; // local endpoint->tunnel - std::map, std::shared_ptr > m_ServerTunnels; // ->tunnel + std::map > m_ClientTunnels; // local endpoint -> tunnel + std::map, std::shared_ptr > m_ServerTunnels; // -> tunnel std::mutex m_ForwardsMutex; std::map > m_ClientForwards; // local endpoint -> udp tunnel @@ -150,6 +155,9 @@ std::unique_ptr m_CleanupUDPTimer; + // i18n + std::shared_ptr m_Language; + public: // for HTTP diff -Nru i2pd-2.39.0/libi2pd_client/HTTPProxy.cpp i2pd-2.43.0/libi2pd_client/HTTPProxy.cpp --- i2pd-2.39.0/libi2pd_client/HTTPProxy.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/HTTPProxy.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -32,7 +32,13 @@ namespace i2p { namespace proxy { - std::map jumpservices = { + static const std::vector jumporder = { + "reg.i2p", + "stats.i2p", + "identiguy.i2p", + }; + + static const std::map jumpservices = { { "reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/" }, { "identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname=" }, { "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" }, @@ -124,9 +130,9 @@ void HTTPReqHandler::AsyncSockRead() { - LogPrint(eLogDebug, "HTTPProxy: async sock read"); + LogPrint(eLogDebug, "HTTPProxy: Async sock read"); if (!m_sock) { - LogPrint(eLogError, "HTTPProxy: no socket for read"); + LogPrint(eLogError, "HTTPProxy: No socket for read"); return; } m_sock->async_read_some(boost::asio::buffer(m_recv_chunk, sizeof(m_recv_chunk)), @@ -138,13 +144,13 @@ if (Kill()) return; if (m_sock) { - LogPrint(eLogDebug, "HTTPProxy: close sock"); + LogPrint(eLogDebug, "HTTPProxy: Close sock"); m_sock->close(); m_sock = nullptr; } if(m_proxysock) { - LogPrint(eLogDebug, "HTTPProxy: close proxysock"); + LogPrint(eLogDebug, "HTTPProxy: Close proxysock"); if(m_proxysock->is_open()) m_proxysock->close(); m_proxysock = nullptr; @@ -174,8 +180,11 @@ << "

" << tr("Remote host not found in router's addressbook") << "

\r\n" << "

" << tr("You may try to find this host on jump services below") << ":

\r\n" << "\r\n"; std::string content = ss.str(); @@ -237,14 +246,14 @@ /** * according to i2p ticket #1862: - * leave Referrer if requested URL with same schema, host and port, + * leave Referer if requested URL with same schema, host and port, * otherwise, drop it. */ - if(req.GetHeader("Referrer") != "") { + if(req.GetHeader("Referer") != "") { i2p::http::URL reqURL; reqURL.parse(req.uri); - i2p::http::URL refURL; refURL.parse(req.GetHeader("Referrer")); + i2p::http::URL refURL; refURL.parse(req.GetHeader("Referer")); if(!boost::iequals(reqURL.schema, refURL.schema) || !boost::iequals(reqURL.host, refURL.host) || reqURL.port != refURL.port) - req.RemoveHeader("Referrer"); + req.RemoveHeader("Referer"); } /* add headers */ @@ -269,13 +278,13 @@ return false; /* need more data */ if (m_req_len < 0) { - LogPrint(eLogError, "HTTPProxy: unable to parse request"); + LogPrint(eLogError, "HTTPProxy: Unable to parse request"); GenericProxyError(tr("Invalid request"), tr("Proxy unable to parse your request")); return true; /* parse error */ } /* parsing success, now let's look inside request */ - LogPrint(eLogDebug, "HTTPProxy: requested: ", m_ClientRequest.uri); + LogPrint(eLogDebug, "HTTPProxy: Requested: ", m_ClientRequest.uri); m_RequestURL.parse(m_ClientRequest.uri); bool m_Confirm; @@ -284,14 +293,14 @@ { if (!m_Addresshelper) { - LogPrint(eLogWarning, "HTTPProxy: addresshelper request rejected"); + LogPrint(eLogWarning, "HTTPProxy: Addresshelper request rejected"); GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported")); return true; } if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm) { i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump); - LogPrint (eLogInfo, "HTTPProxy: added address from addresshelper for ", m_RequestURL.host); + LogPrint (eLogInfo, "HTTPProxy: Added address from addresshelper for ", m_RequestURL.host); 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") << ". "; @@ -304,7 +313,7 @@ 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 here to update record:") << " " << tr("Continue") << "."; GenericProxyInfo(tr("Addresshelper found"), ss.str()); return true; /* request processed */ @@ -375,13 +384,13 @@ } } else { if(m_OutproxyUrl.size()) { - LogPrint (eLogDebug, "HTTPProxy: use outproxy ", m_OutproxyUrl); + LogPrint (eLogDebug, "HTTPProxy: Using outproxy ", m_OutproxyUrl); if(m_ProxyURL.parse(m_OutproxyUrl)) ForwardToUpstreamProxy(); else GenericProxyError(tr("Outproxy failure"), tr("bad outproxy settings")); } else { - LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outproxy enabled"); + LogPrint (eLogWarning, "HTTPProxy: Outproxy failure for ", dest_host, ": no outproxy enabled"); std::stringstream ss; ss << tr("Host") << " " << dest_host << " " << tr("not inside I2P network, but outproxy is not enabled"); GenericProxyError(tr("Outproxy failure"), ss.str()); } @@ -404,7 +413,7 @@ m_send_buf = m_ClientRequest.to_string(); m_send_buf.append(m_recv_buf); /* connect to destination */ - LogPrint(eLogDebug, "HTTPProxy: connecting to host ", dest_host, ":", dest_port); + LogPrint(eLogDebug, "HTTPProxy: Connecting to host ", dest_host, ":", dest_port); GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1), dest_host, dest_port); return true; @@ -412,9 +421,9 @@ void HTTPReqHandler::ForwardToUpstreamProxy() { - LogPrint(eLogDebug, "HTTPProxy: forward to upstream"); - // build http request + LogPrint(eLogDebug, "HTTPProxy: Forwarded to upstream"); + /* build http request */ m_ClientRequestURL = m_RequestURL; LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host); m_ClientRequestURL.schema = ""; @@ -422,17 +431,17 @@ std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for? m_ClientRequest.uri = m_ClientRequestURL.to_string(); - // update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections + /* update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections */ if(m_ClientRequest.method != "CONNECT") m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0"); m_ClientRequest.write(m_ClientRequestBuffer); m_ClientRequestBuffer << m_recv_buf.substr(m_req_len); - // assume http if empty schema + /* assume http if empty schema */ if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") { - // handle upstream http proxy + /* handle upstream http proxy */ if (!m_ProxyURL.port) m_ProxyURL.port = 80; if (m_ProxyURL.is_i2p()) { @@ -440,9 +449,9 @@ auto auth = i2p::http::CreateBasicAuthorizationString (m_ProxyURL.user, m_ProxyURL.pass); if (!auth.empty ()) { - // remove existing authorization if any + /* remove existing authorization if any */ m_ClientRequest.RemoveHeader("Proxy-"); - // add own http proxy authorization + /* add own http proxy authorization */ m_ClientRequest.AddHeader("Proxy-Authorization", auth); } m_send_buf = m_ClientRequest.to_string(); @@ -461,7 +470,7 @@ } else if (m_ProxyURL.schema == "socks") { - // handle upstream socks proxy + /* handle upstream socks proxy */ if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { @@ -470,7 +479,7 @@ } else { - // unknown type, complain + /* unknown type, complain */ GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string()); } } @@ -490,7 +499,7 @@ } uint16_t port = m_RequestURL.port; if(!port) port = 80; - LogPrint(eLogDebug, "HTTPProxy: connected to socks upstream"); + LogPrint(eLogDebug, "HTTPProxy: Connected to SOCKS upstream"); std::string host = m_RequestURL.host; std::size_t reqsize = 0; @@ -517,14 +526,14 @@ void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred) { - LogPrint(eLogDebug, "HTTPProxy: upstream socks handshake sent"); + LogPrint(eLogDebug, "HTTPProxy: Upstream SOCKS handshake sent"); if(ec) GenericProxyError(tr("Cannot negotiate with socks proxy"), ec.message()); else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2)); } void HTTPReqHandler::HandoverToUpstreamProxy() { - LogPrint(eLogDebug, "HTTPProxy: handover to socks proxy"); + LogPrint(eLogDebug, "HTTPProxy: Handover to SOCKS proxy"); auto connection = std::make_shared(GetOwner(), m_proxysock, m_sock); m_sock = nullptr; m_proxysock = nullptr; @@ -576,7 +585,7 @@ }); } else { m_send_buf = m_ClientRequestBuffer.str(); - LogPrint(eLogDebug, "HTTPProxy: send ", m_send_buf.size(), " bytes"); + LogPrint(eLogDebug, "HTTPProxy: Send ", m_send_buf.size(), " bytes"); boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred) { if(ec) GenericProxyError(tr("failed to send request to upstream"), ec.message()); @@ -606,7 +615,7 @@ void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec) { if(!ec) { - LogPrint(eLogDebug, "HTTPProxy: connected to http upstream"); + LogPrint(eLogDebug, "HTTPProxy: Connected to http upstream"); GenericProxyError(tr("cannot connect"), tr("http out proxy not implemented")); } else GenericProxyError(tr("cannot connect to upstream http proxy"), ec.message()); } @@ -614,10 +623,10 @@ /* will be called after some data received from client */ void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) { - LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes, recv buf: ", m_recv_buf.length(), ", send buf: ", m_send_buf.length()); + LogPrint(eLogDebug, "HTTPProxy: Sock recv: ", len, " bytes, recv buf: ", m_recv_buf.length(), ", send buf: ", m_send_buf.length()); if(ecode) { - LogPrint(eLogWarning, "HTTPProxy: sock recv got error: ", ecode); + LogPrint(eLogWarning, "HTTPProxy: Sock recv got error: ", ecode); Terminate(); return; } @@ -640,7 +649,7 @@ void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr stream) { if (!stream) { - LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info"); + LogPrint (eLogError, "HTTPProxy: Error when creating the stream, check the previous warnings for more info"); GenericProxyError(tr("Host is down"), tr("Can't create connection to requested host, it may be down. Please try again later.")); return; } diff -Nru i2pd-2.39.0/libi2pd_client/I2CP.cpp i2pd-2.43.0/libi2pd_client/I2CP.cpp --- i2pd-2.39.0/libi2pd_client/I2CP.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/I2CP.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -23,8 +23,8 @@ namespace client { - I2CPDestination::I2CPDestination (boost::asio::io_service& service, std::shared_ptr owner, - std::shared_ptr identity, bool isPublic, const std::map& params): + I2CPDestination::I2CPDestination (boost::asio::io_service& service, std::shared_ptr owner, + std::shared_ptr identity, bool isPublic, const std::map& params): LeaseSetDestination (service, isPublic, ¶ms), m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()), m_IsCreatingLeaseSet (false), m_LeaseSetCreationTimer (service) @@ -36,8 +36,8 @@ LeaseSetDestination::Stop (); m_Owner = nullptr; m_LeaseSetCreationTimer.cancel (); - } - + } + void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key) { m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_Identity->GetCryptoKeyType (), key); @@ -46,20 +46,20 @@ void I2CPDestination::SetECIESx25519EncryptionPrivateKey (const uint8_t * key) { if (!m_ECIESx25519Decryptor || memcmp (m_ECIESx25519PrivateKey, key, 32)) // new key? - { + { m_ECIESx25519Decryptor = std::make_shared(key, true); // calculate public memcpy (m_ECIESx25519PrivateKey, key, 32); - } - } - - 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"); + LogPrint (eLogError, "I2CP: Decryptor is not set"); return false; } @@ -68,14 +68,14 @@ if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor) return m_ECIESx25519Decryptor->GetPubicKey (); return nullptr; - } + } - bool I2CPDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const - { - return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519Decryptor : m_EncryptionKeyType == keyType; + bool I2CPDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const + { + return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519Decryptor : m_EncryptionKeyType == keyType; } - - + + void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len) { uint32_t length = bufbe32toh (buf); @@ -88,25 +88,25 @@ { GetService ().post (std::bind (&I2CPDestination::PostCreateNewLeaseSet, this, tunnels)); } - + void I2CPDestination::PostCreateNewLeaseSet (std::vector > tunnels) { if (m_IsCreatingLeaseSet) { LogPrint (eLogInfo, "I2CP: LeaseSet is being created"); return; - } + } uint8_t priv[256] = {0}; i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only m_LeaseSetExpirationTime = ls.GetExpirationTime (); uint8_t * leases = ls.GetLeases (); leases[-1] = tunnels.size (); if (m_Owner) - { + { uint16_t sessionID = m_Owner->GetSessionID (); if (sessionID != 0xFFFF) - { - m_IsCreatingLeaseSet = true; + { + m_IsCreatingLeaseSet = true; htobe16buf (leases - 3, sessionID); size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size (); m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l); @@ -120,8 +120,8 @@ if (s->m_Owner) s->m_Owner->Stop (); } }); - } - } + } + } } void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len) @@ -146,7 +146,7 @@ void I2CPDestination::SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce) { - auto msg = NewI2NPMessage (); + auto msg = m_I2NPMsgsPool.AcquireSharedMt (); uint8_t * buf = msg->GetPayload (); htobe32buf (buf, len); memcpy (buf + 4, payload, len); @@ -204,10 +204,16 @@ } else { - outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel (); - auto leases = remote->GetNonExpiredLeases (); + auto leases = remote->GetNonExpiredLeases (false); // without threshold + if (leases.empty ()) + leases = remote->GetNonExpiredLeases (true); // with threshold if (!leases.empty ()) + { remoteLease = leases[rand () % leases.size ()]; + auto leaseRouter = i2p::data::netdb.FindRouter (remoteLease->tunnelGateway); + outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel (nullptr, + leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports); + } if (remoteLease && outboundTunnel) remoteSession->SetSharedRoutingPath (std::make_shared ( i2p::garlic::GarlicRoutingPath{outboundTunnel, remoteLease, 10000, 0, 0})); // 10 secs RTT @@ -237,18 +243,18 @@ } } - RunnableI2CPDestination::RunnableI2CPDestination (std::shared_ptr owner, + RunnableI2CPDestination::RunnableI2CPDestination (std::shared_ptr owner, std::shared_ptr identity, bool isPublic, const std::map& params): RunnableService ("I2CP"), I2CPDestination (GetIOService (), owner, identity, isPublic, params) { - } + } RunnableI2CPDestination::~RunnableI2CPDestination () { if (IsRunning ()) Stop (); - } + } void RunnableI2CPDestination::Start () { @@ -267,9 +273,9 @@ StopIOService (); } } - - I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr socket): - m_Owner (owner), m_Socket (socket), m_SessionID (0xFFFF), + + I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr socket): + m_Owner (owner), m_Socket (socket), m_SessionID (0xFFFF), m_MessageID (0), m_IsSendAccepted (true), m_IsSending (false) { } @@ -307,11 +313,11 @@ void I2CPSession::ReceiveHeader () { - if (!m_Socket) + if (!m_Socket) { LogPrint (eLogError, "I2CP: Can't receive header"); return; - } + } boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Header, I2CP_HEADER_SIZE), boost::asio::transfer_all (), std::bind (&I2CPSession::HandleReceivedHeader, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -344,11 +350,11 @@ void I2CPSession::ReceivePayload () { - if (!m_Socket) - { + if (!m_Socket) + { LogPrint (eLogError, "I2CP: Can't receive payload"); return; - } + } boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Payload, m_PayloadLen), boost::asio::transfer_all (), std::bind (&I2CPSession::HandleReceivedPayload, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); @@ -390,11 +396,11 @@ if (!m_SendQueue.IsEmpty ()) m_SendQueue.CleanUp (); if (m_SessionID != 0xFFFF) - { + { m_Owner.RemoveSession (GetSessionID ()); - LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " terminated"); + LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " terminated"); m_SessionID = 0xFFFF; - } + } } void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len) @@ -404,39 +410,39 @@ { LogPrint (eLogError, "I2CP: Message to send is too long ", l); return; - } + } auto sendBuf = m_IsSending ? std::make_shared (l) : nullptr; uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer; htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len); buf[I2CP_HEADER_TYPE_OFFSET] = type; memcpy (buf + I2CP_HEADER_SIZE, payload, len); if (sendBuf) - { + { if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE) m_SendQueue.Add (sendBuf); - else - { - LogPrint (eLogWarning, "I2CP: send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); - return; - } - } + else + { + LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); + return; + } + } else { auto socket = m_Socket; if (socket) - { + { m_IsSending = true; - boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l), - boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent, + boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l), + boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - } + } + } } void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) - { + { if (ecode != boost::asio::error::operation_aborted) Terminate (); } @@ -444,19 +450,19 @@ { auto socket = m_Socket; if (socket) - { + { auto len = m_SendQueue.Get (m_SendBuffer, I2CP_MAX_MESSAGE_LENGTH); - boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, len), - boost::asio::transfer_all (),std::bind(&I2CPSession::HandleI2CPMessageSent, - shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, len), + boost::asio::transfer_all (),std::bind(&I2CPSession::HandleI2CPMessageSent, + shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } else m_IsSending = false; } else m_IsSending = false; } - + std::string I2CPSession::ExtractString (const uint8_t * buf, size_t len) { uint8_t l = buf[0]; @@ -519,21 +525,26 @@ void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len) { RAND_bytes ((uint8_t *)&m_SessionID, 2); - m_Owner.InsertSession (shared_from_this ()); auto identity = std::make_shared(); size_t offset = identity->FromBuffer (buf, len); if (!offset) { - LogPrint (eLogError, "I2CP: create session malformed identity"); - SendSessionStatusMessage (3); // invalid + LogPrint (eLogError, "I2CP: Create session malformed identity"); + SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid + return; + } + if (m_Owner.FindSessionByIdentHash (identity->GetIdentHash ())) + { + LogPrint (eLogError, "I2CP: Create session duplicate address ", identity->GetIdentHash ().ToBase32 ()); + SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid return; } uint16_t optionsSize = bufbe16toh (buf + offset); offset += 2; if (optionsSize > len - offset) { - LogPrint (eLogError, "I2CP: options size ", optionsSize, "exceeds message size"); - SendSessionStatusMessage (3); // invalid + LogPrint (eLogError, "I2CP: Options size ", optionsSize, "exceeds message size"); + SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid return; } std::map params; @@ -549,33 +560,41 @@ m_Destination = m_Owner.IsSingleThread () ? std::make_shared(m_Owner.GetService (), shared_from_this (), identity, true, params): std::make_shared(shared_from_this (), identity, true, params); - SendSessionStatusMessage (1); // created - LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created"); - m_Destination->Start (); + if (m_Owner.InsertSession (shared_from_this ())) + { + SendSessionStatusMessage (eI2CPSessionStatusCreated); // created + LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " created"); + m_Destination->Start (); + } + else + { + LogPrint (eLogError, "I2CP: Session already exists"); + SendSessionStatusMessage (eI2CPSessionStatusRefused); + } } else { - LogPrint (eLogError, "I2CP: session already exists"); - SendSessionStatusMessage (4); // refused + LogPrint (eLogError, "I2CP: Session already exists"); + SendSessionStatusMessage (eI2CPSessionStatusRefused); // refused } } else { - LogPrint (eLogError, "I2CP: create session signature verification failed"); - SendSessionStatusMessage (3); // invalid + LogPrint (eLogError, "I2CP: Create session signature verification failed"); + SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid } } void I2CPSession::DestroySessionMessageHandler (const uint8_t * buf, size_t len) { - SendSessionStatusMessage (0); // destroy - LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " destroyed"); + SendSessionStatusMessage (eI2CPSessionStatusDestroyed); // destroy + LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " destroyed"); Terminate (); } void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len) { - uint8_t status = 3; // rejected + I2CPSessionStatus status = eI2CPSessionStatusInvalid; // rejected if(len > sizeof(uint16_t)) { uint16_t sessionID = bufbe16toh(buf); @@ -604,37 +623,37 @@ { if(m_Destination->Reconfigure(opts)) { - LogPrint(eLogInfo, "I2CP: reconfigured destination"); - status = 2; // updated + LogPrint(eLogInfo, "I2CP: Reconfigured destination"); + status = eI2CPSessionStatusUpdated; // updated } else - LogPrint(eLogWarning, "I2CP: failed to reconfigure destination"); + LogPrint(eLogWarning, "I2CP: Failed to reconfigure destination"); } else - LogPrint(eLogError, "I2CP: invalid reconfigure message signature"); + LogPrint(eLogError, "I2CP: Invalid reconfigure message signature"); } else - LogPrint(eLogError, "I2CP: mapping size mismatch"); + LogPrint(eLogError, "I2CP: Mapping size mismatch"); } else - LogPrint(eLogError, "I2CP: destination mismatch"); + LogPrint(eLogError, "I2CP: Destination mismatch"); } else - LogPrint(eLogError, "I2CP: malfromed destination"); + LogPrint(eLogError, "I2CP: Malfromed destination"); } else - LogPrint(eLogError, "I2CP: session mismatch"); + LogPrint(eLogError, "I2CP: Session mismatch"); } else - LogPrint(eLogError, "I2CP: short message"); + LogPrint(eLogError, "I2CP: Short message"); SendSessionStatusMessage (status); } - void I2CPSession::SendSessionStatusMessage (uint8_t status) + void I2CPSession::SendSessionStatusMessage (I2CPSessionStatus status) { uint8_t buf[3]; htobe16buf (buf, m_SessionID); - buf[2] = status; + buf[2] = (uint8_t)status; SendI2CPMessage (I2CP_SESSION_STATUS_MESSAGE, buf, 3); } @@ -668,7 +687,7 @@ } } else - LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID); + LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); } void I2CPSession::CreateLeaseSet2MessageHandler (const uint8_t * buf, size_t len) @@ -683,7 +702,7 @@ i2p::data::LeaseSet2 ls (storeType, buf + offset, len - offset); // outer layer only for encrypted if (!ls.IsValid ()) { - LogPrint (eLogError, "I2CP: invalid LeaseSet2 of type ", storeType); + LogPrint (eLogError, "I2CP: Invalid LeaseSet2 of type ", storeType); return; } offset += ls.GetBufferLen (); @@ -693,23 +712,23 @@ { if (offset + 4 > len) return; uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type - uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length + uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length if (offset + keyLen > len) return; if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) m_Destination->SetECIESx25519EncryptionPrivateKey (buf + offset); else { - m_Destination->SetEncryptionType (keyType); + m_Destination->SetEncryptionType (keyType); m_Destination->SetEncryptionPrivateKey (buf + offset); } offset += keyLen; } - + m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ()); } } else - LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID); + LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); } void I2CPSession::SendMessageMessageHandler (const uint8_t * buf, size_t len) @@ -735,14 +754,14 @@ m_Destination->SendMsgTo (buf + offset, payloadLen, identity.GetIdentHash (), nonce); } else - LogPrint(eLogError, "I2CP: cannot send message, too big"); + LogPrint(eLogError, "I2CP: Cannot send message, too big"); } else - LogPrint(eLogError, "I2CP: invalid identity"); + LogPrint(eLogError, "I2CP: Invalid identity"); } } else - LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID); + LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); } void I2CPSession::SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len) @@ -770,7 +789,7 @@ if (!addr || !addr->IsIdentHash ()) { // TODO: handle blinded addresses - LogPrint (eLogError, "I2CP: address ", name, " not found"); + LogPrint (eLogError, "I2CP: Address ", name, " not found"); SendHostReplyMessage (requestID, nullptr); return; } @@ -779,7 +798,7 @@ break; } default: - LogPrint (eLogError, "I2CP: request type ", (int)buf[10], " is not supported"); + LogPrint (eLogError, "I2CP: Request type ", (int)buf[10], " is not supported"); SendHostReplyMessage (requestID, nullptr); return; } @@ -805,7 +824,7 @@ SendHostReplyMessage (requestID, nullptr); } else - LogPrint (eLogError, "I2CP: unexpected sessionID ", sessionID); + LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); } void I2CPSession::SendHostReplyMessage (uint32_t requestID, std::shared_ptr identity) @@ -885,7 +904,7 @@ { LogPrint (eLogError, "I2CP: Message to send is too long ", l); return; - } + } auto sendBuf = m_IsSending ? std::make_shared (l) : nullptr; uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer; htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len + 10); @@ -895,36 +914,32 @@ htobe32buf (buf + I2CP_HEADER_SIZE + 6, len); memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len); if (sendBuf) - { + { if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE) m_SendQueue.Add (sendBuf); - else - { - LogPrint (eLogWarning, "I2CP: send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); - return; - } - } + else + { + LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); + return; + } + } else { auto socket = m_Socket; if (socket) - { + { m_IsSending = true; - boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l), - boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent, + boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l), + boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - } + } + } } I2CPServer::I2CPServer (const std::string& interface, int port, bool isSingleThread): RunnableService ("I2CP"), m_IsSingleThread (isSingleThread), m_Acceptor (GetIOService (), -#ifdef ANDROID - I2CPSession::proto::endpoint(std::string (1, '\0') + interface)) // leading 0 for abstract address -#else - I2CPSession::proto::endpoint(boost::asio::ip::address::from_string(interface), port)) -#endif + boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(interface), port)) { memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers)); m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler; @@ -966,13 +981,13 @@ void I2CPServer::Accept () { - auto newSocket = std::make_shared (GetIOService ()); + auto newSocket = std::make_shared (GetIOService ()); m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this, std::placeholders::_1, newSocket)); } void I2CPServer::HandleAccept(const boost::system::error_code& ecode, - std::shared_ptr socket) + std::shared_ptr socket) { if (!ecode && socket) { @@ -980,15 +995,15 @@ auto ep = socket->remote_endpoint (ec); if (!ec) { - LogPrint (eLogDebug, "I2CP: new connection from ", ep); + LogPrint (eLogDebug, "I2CP: New connection from ", ep); auto session = std::make_shared(*this, socket); session->Start (); } else - LogPrint (eLogError, "I2CP: incoming connection error ", ec.message ()); + LogPrint (eLogError, "I2CP: Incoming connection error ", ec.message ()); } else - LogPrint (eLogError, "I2CP: accept error: ", ecode.message ()); + LogPrint (eLogError, "I2CP: Accept error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Accept (); @@ -999,7 +1014,7 @@ if (!session) return false; if (!m_Sessions.insert({session->GetSessionID (), session}).second) { - LogPrint (eLogError, "I2CP: duplicate session id ", session->GetSessionID ()); + LogPrint (eLogError, "I2CP: Duplicate session id ", session->GetSessionID ()); return false; } return true; @@ -1009,5 +1024,19 @@ { m_Sessions.erase (sessionID); } + + std::shared_ptr I2CPServer::FindSessionByIdentHash (const i2p::data::IdentHash& ident) const + { + for (const auto& it: m_Sessions) + { + if (it.second) + { + auto dest = it.second->GetDestination (); + if (dest && dest->GetIdentHash () == ident) + return it.second; + } + } + return nullptr; + } } } diff -Nru i2pd-2.39.0/libi2pd_client/I2CP.h i2pd-2.43.0/libi2pd_client/I2CP.h --- i2pd-2.39.0/libi2pd_client/I2CP.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/I2CP.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -27,8 +27,8 @@ const size_t I2CP_SESSION_BUFFER_SIZE = 4096; const size_t I2CP_MAX_MESSAGE_LENGTH = 65535; const size_t I2CP_MAX_SEND_QUEUE_SIZE = 1024*1024; // in bytes, 1M - const int I2CP_LEASESET_CREATION_TIMEOUT = 10; // in seconds - + const int I2CP_LEASESET_CREATION_TIMEOUT = 10; // in seconds + const size_t I2CP_HEADER_LENGTH_OFFSET = 0; const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4; const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1; @@ -61,6 +61,15 @@ eI2CPMessageStatusNoLeaseSet = 21 }; + enum I2CPSessionStatus + { + eI2CPSessionStatusDestroyed = 0, + eI2CPSessionStatusCreated = 1, + eI2CPSessionStatusUpdated = 2, + eI2CPSessionStatusInvalid = 3, + eI2CPSessionStatusRefused = 4 + }; + // params const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability"; @@ -69,12 +78,12 @@ { public: - I2CPDestination (boost::asio::io_service& service, std::shared_ptr owner, + I2CPDestination (boost::asio::io_service& service, std::shared_ptr owner, std::shared_ptr identity, bool isPublic, const std::map& params); ~I2CPDestination () {}; void Stop (); - + void SetEncryptionPrivateKey (const uint8_t * key); void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; }; void SetECIESx25519EncryptionPrivateKey (const uint8_t * key); @@ -83,8 +92,8 @@ 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 SupportsEncryptionType (i2p::data::CryptoKeyType keyType) 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; }; @@ -101,7 +110,7 @@ bool SendMsg (std::shared_ptr msg, std::shared_ptr remote); void PostCreateNewLeaseSet (std::vector > tunnels); - + private: std::shared_ptr m_Owner; @@ -113,32 +122,27 @@ uint64_t m_LeaseSetExpirationTime; bool m_IsCreatingLeaseSet; boost::asio::deadline_timer m_LeaseSetCreationTimer; + i2p::util::MemoryPoolMt > m_I2NPMsgsPool; }; - class RunnableI2CPDestination: private i2p::util::RunnableService, public I2CPDestination + class RunnableI2CPDestination: private i2p::util::RunnableService, public I2CPDestination { public: - RunnableI2CPDestination (std::shared_ptr owner, std::shared_ptr identity, - bool isPublic, const std::map& params); + RunnableI2CPDestination (std::shared_ptr owner, std::shared_ptr identity, + bool isPublic, const std::map& params); ~RunnableI2CPDestination (); void Start (); void Stop (); - }; - + }; + class I2CPServer; class I2CPSession: public std::enable_shared_from_this { public: -#ifdef ANDROID - typedef boost::asio::local::stream_protocol proto; -#else - typedef boost::asio::ip::tcp proto; -#endif - - I2CPSession (I2CPServer& owner, std::shared_ptr socket); + I2CPSession (I2CPServer& owner, std::shared_ptr socket); ~I2CPSession (); @@ -174,19 +178,19 @@ void HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleMessage (); void Terminate (); - + void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - + std::string ExtractString (const uint8_t * buf, size_t len); size_t PutString (uint8_t * buf, size_t len, const std::string& str); void ExtractMapping (const uint8_t * buf, size_t len, std::map& mapping); - void SendSessionStatusMessage (uint8_t status); + void SendSessionStatusMessage (I2CPSessionStatus status); void SendHostReplyMessage (uint32_t requestID, std::shared_ptr identity); private: I2CPServer& m_Owner; - std::shared_ptr m_Socket; + std::shared_ptr m_Socket; uint8_t m_Header[I2CP_HEADER_SIZE], m_Payload[I2CP_MAX_MESSAGE_LENGTH]; size_t m_PayloadLen; @@ -198,7 +202,7 @@ // to client bool m_IsSending; uint8_t m_SendBuffer[I2CP_MAX_MESSAGE_LENGTH]; - i2p::stream::SendBufferQueue m_SendQueue; + i2p::stream::SendBufferQueue m_SendQueue; }; typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len); @@ -213,9 +217,10 @@ void Stop (); boost::asio::io_service& GetService () { return GetIOService (); }; bool IsSingleThread () const { return m_IsSingleThread; }; - + bool InsertSession (std::shared_ptr session); void RemoveSession (uint16_t sessionID); + std::shared_ptr FindSessionByIdentHash (const i2p::data::IdentHash& ident) const; private: @@ -223,7 +228,7 @@ void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); private: @@ -231,7 +236,7 @@ I2CPMessageHandler m_MessagesHandlers[256]; std::map > m_Sessions; - I2CPSession::proto::acceptor m_Acceptor; + boost::asio::ip::tcp::acceptor m_Acceptor; public: diff -Nru i2pd-2.39.0/libi2pd_client/I2PService.cpp i2pd-2.43.0/libi2pd_client/I2PService.cpp --- i2pd-2.39.0/libi2pd_client/I2PService.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/I2PService.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -193,7 +193,7 @@ std::placeholders::_1, std::placeholders::_2)); } else - LogPrint(eLogError, "TCPIPPipe: upstream receive: no socket"); + LogPrint(eLogError, "TCPIPPipe: Upstream receive: No socket"); } void TCPIPPipe::AsyncReceiveDownstream() @@ -204,14 +204,14 @@ std::placeholders::_1, std::placeholders::_2)); } else - LogPrint(eLogError, "TCPIPPipe: downstream receive: no socket"); + LogPrint(eLogError, "TCPIPPipe: Downstream receive: No socket"); } void TCPIPPipe::UpstreamWrite(size_t len) { if (m_up) { - LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written"); + LogPrint(eLogDebug, "TCPIPPipe: Upstream: ", (int) len, " bytes written"); boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len), boost::asio::transfer_all(), std::bind(&TCPIPPipe::HandleUpstreamWrite, @@ -219,14 +219,14 @@ std::placeholders::_1)); } else - LogPrint(eLogError, "TCPIPPipe: upstream write: no socket"); + LogPrint(eLogError, "TCPIPPipe: Upstream write: no socket"); } void TCPIPPipe::DownstreamWrite(size_t len) { if (m_down) { - LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written"); + LogPrint(eLogDebug, "TCPIPPipe: Downstream: ", (int) len, " bytes written"); boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len), boost::asio::transfer_all(), std::bind(&TCPIPPipe::HandleDownstreamWrite, @@ -234,16 +234,16 @@ std::placeholders::_1)); } else - LogPrint(eLogError, "TCPIPPipe: downstream write: no socket"); + LogPrint(eLogError, "TCPIPPipe: Downstream write: No socket"); } void TCPIPPipe::HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered) { - LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) bytes_transfered, " bytes received"); + LogPrint(eLogDebug, "TCPIPPipe: Downstream: ", (int) bytes_transfered, " bytes received"); if (ecode) { - LogPrint(eLogError, "TCPIPPipe: downstream read error:" , ecode.message()); + LogPrint(eLogError, "TCPIPPipe: Downstream read error:" , ecode.message()); if (ecode != boost::asio::error::operation_aborted) Terminate(); } else { @@ -256,7 +256,7 @@ void TCPIPPipe::HandleDownstreamWrite(const boost::system::error_code & ecode) { if (ecode) { - LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message()); + LogPrint(eLogError, "TCPIPPipe: Downstream write error:" , ecode.message()); if (ecode != boost::asio::error::operation_aborted) Terminate(); } @@ -267,7 +267,7 @@ void TCPIPPipe::HandleUpstreamWrite(const boost::system::error_code & ecode) { if (ecode) { - LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message()); + LogPrint(eLogError, "TCPIPPipe: Upstream write error:" , ecode.message()); if (ecode != boost::asio::error::operation_aborted) Terminate(); } @@ -277,10 +277,10 @@ void TCPIPPipe::HandleUpstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transfered) { - LogPrint(eLogDebug, "TCPIPPipe: upstream ", (int)bytes_transfered, " bytes received"); + LogPrint(eLogDebug, "TCPIPPipe: Upstream ", (int)bytes_transfered, " bytes received"); if (ecode) { - LogPrint(eLogError, "TCPIPPipe: upstream read error:" , ecode.message()); + LogPrint(eLogError, "TCPIPPipe: Upstream read error:" , ecode.message()); if (ecode != boost::asio::error::operation_aborted) Terminate(); } else { diff -Nru i2pd-2.39.0/libi2pd_client/I2PTunnel.cpp i2pd-2.43.0/libi2pd_client/I2PTunnel.cpp --- i2pd-2.39.0/libi2pd_client/I2PTunnel.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/I2PTunnel.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -7,6 +7,7 @@ */ #include +#include #include "Base.h" #include "Log.h" #include "Destination.h" @@ -87,7 +88,7 @@ boost::system::error_code ec; sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec); if (ec) - LogPrint (eLogError, "I2PTunnel: can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ()); + LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ()); } #endif @@ -122,11 +123,11 @@ boost::system::error_code ec; m_Socket->bind (boost::asio::ip::tcp::endpoint (localAddress, 0), ec); if (ec) - LogPrint (eLogError, "I2PTunnel: can't bind to ", localAddress.to_string (), ": ", ec.message ()); - } + LogPrint (eLogError, "I2PTunnel: Can't bind to ", localAddress.to_string (), ": ", ec.message ()); + } Connect (false); - } - + } + void I2PTunnelConnection::Terminate () { if (Kill()) return; @@ -155,7 +156,7 @@ { if (ecode != boost::asio::error::operation_aborted) { - LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ()); + LogPrint (eLogError, "I2PTunnel: Read error: ", ecode.message ()); Terminate (); } } @@ -177,13 +178,13 @@ s->Terminate (); }); } - } - + } + void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode) { if (ecode) { - LogPrint (eLogError, "I2PTunnel: write error: ", ecode.message ()); + LogPrint (eLogError, "I2PTunnel: Write error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate (); } @@ -205,7 +206,7 @@ } else // closed by peer { - // get remaning data + // get remaining data auto len = m_Stream->ReadSome (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE); if (len > 0) // still some data Write (m_StreamBuffer, len); @@ -221,7 +222,7 @@ { if (ecode != boost::asio::error::operation_aborted) { - LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ()); + LogPrint (eLogError, "I2PTunnel: Stream read error: ", ecode.message ()); if (bytes_transferred > 0) Write (m_StreamBuffer, bytes_transferred); // postpone termination else if (ecode == boost::asio::error::timed_out && m_Stream && m_Stream->IsOpen ()) @@ -246,12 +247,12 @@ { if (ecode) { - LogPrint (eLogError, "I2PTunnel: connect error: ", ecode.message ()); + LogPrint (eLogError, "I2PTunnel: Connect error: ", ecode.message ()); Terminate (); } else { - LogPrint (eLogDebug, "I2PTunnel: connected"); + LogPrint (eLogDebug, "I2PTunnel: Connected"); if (m_IsQuiet) StreamReceive (); else @@ -303,7 +304,7 @@ m_ProxyConnectionSent = true; } else - m_OutHeader << line << "\n"; + m_OutHeader << line << "\n"; } } else @@ -326,7 +327,7 @@ I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, const std::string& host): - I2PTunnelConnection (owner, stream, socket, target), m_Host (host), + I2PTunnelConnection (owner, stream, socket, target), m_Host (host), m_HeaderSent (false), m_ResponseHeaderSent (false), m_From (stream->GetRemoteIdentity ()) { } @@ -340,7 +341,7 @@ m_InHeader.clear (); m_InHeader.write ((const char *)buf, len); std::string line; - bool endOfHeader = false; + bool endOfHeader = false, connection = false; while (!endOfHeader) { std::getline(m_InHeader, line); @@ -349,9 +350,33 @@ if (line == "\r") endOfHeader = true; else { - if (m_Host.length () > 0 && !line.compare(0, 5, "Host:")) + // strip up some headers + static const std::vector excluded // list of excluded headers + { + "Keep-Alive:", "X-I2P" + }; + bool matched = false; + for (const auto& it: excluded) + if (boost::iequals (line.substr (0, it.length ()), it)) + { + matched = true; + break; + } + if (matched) break; + + // replace some headers + if (!m_Host.empty () && boost::iequals (line.substr (0, 5), "Host:")) m_OutHeader << "Host: " << m_Host << "\r\n"; // override host - else + else if (boost::iequals (line.substr (0, 11), "Connection:")) + { + auto x = line.find("pgrade"); + if (x != std::string::npos && x && std::tolower(line[x - 1]) != 'u') // upgrade or Upgrade + m_OutHeader << line << "\n"; + else + m_OutHeader << "Connection: close\r\n"; + connection = true; + } + else // forward as is m_OutHeader << line << "\n"; } } @@ -361,6 +386,9 @@ if (endOfHeader) { + // add Connection if not presented + if (!connection) + m_OutHeader << "Connection: close\r\n"; // add X-I2P fields if (m_From) { @@ -368,7 +396,7 @@ m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash ().ToBase64 () << "\r\n"; m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64 () << "\r\n"; } - + m_OutHeader << "\r\n"; // end of header m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header m_InHeader.str (""); @@ -404,11 +432,11 @@ }; bool matched = false; for (const auto& it: excluded) - if (!line.compare(0, it.length (), it)) + if (!line.compare(0, it.length (), it)) { matched = true; - break; - } + break; + } if (!matched) m_OutHeader << line << "\n"; } @@ -425,12 +453,12 @@ m_ResponseHeaderSent = true; I2PTunnelConnection::WriteToStream ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); m_OutHeader.str (""); - } + } else Receive (); - } + } } - + I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass): @@ -505,7 +533,7 @@ if (stream) { if (Kill()) return; - LogPrint (eLogDebug, "I2PTunnel: new connection"); + LogPrint (eLogDebug, "I2PTunnel: New connection"); auto connection = std::make_shared(GetOwner(), m_Socket, stream); GetOwner()->AddHandler (connection); connection->I2PConnect (); @@ -532,7 +560,7 @@ 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,12 +568,22 @@ { 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 */ @@ -569,6 +607,31 @@ 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) @@ -597,6 +660,12 @@ void I2PServerTunnel::Stop () { + if (m_PortDestination) + m_PortDestination->ResetAcceptor (); + auto localDestination = GetLocalDestination (); + if (localDestination) + localDestination->StopAcceptingStreams (); + ClearHandlers (); } @@ -608,16 +677,16 @@ 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; + { + if (m_LocalAddress->is_v4 ()) found = true; } else if (ep.address ().is_v6 ()) { @@ -625,28 +694,28 @@ { if (i2p::util::net::IsYggdrasilAddress (*m_LocalAddress)) found = true; - } - else if (m_LocalAddress->is_v6 ()) + } + 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); + LogPrint (eLogInfo, "I2PTunnel: Server tunnel ", (*it).host_name (), " has been resolved to ", addr); m_Endpoint.address (addr); Accept (); } @@ -667,9 +736,9 @@ if (!ec) m_LocalAddress.reset (new boost::asio::ip::address (addr)); else - LogPrint (eLogError, "I2PTunnel: can't set local address ", localAddress); - } - + LogPrint (eLogError, "I2PTunnel: Can't set local address ", localAddress); + } + void I2PServerTunnel::Accept () { if (m_PortDestination) @@ -703,7 +772,7 @@ AddHandler (conn); if (m_LocalAddress) conn->Connect (*m_LocalAddress); - else + else conn->Connect (m_IsUniqueLocal); } } @@ -758,10 +827,11 @@ { m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); - } - } - - void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) { + } + } + + void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) + { std::lock_guard lock(m_SessionsMutex); uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); auto itr = m_Sessions.begin(); @@ -773,7 +843,8 @@ } } - void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) { + void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) + { std::lock_guard lock(m_SessionsMutex); uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); std::vector removePorts; @@ -794,7 +865,7 @@ if (s->Identity.GetLL()[0] == ih.GetLL()[0] && remotePort == s->RemotePort) { /** found existing session */ - LogPrint(eLogDebug, "UDPServer: found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32()); + LogPrint(eLogDebug, "UDPServer: Found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32()); return s; } } @@ -829,7 +900,8 @@ Receive(); } - void UDPSession::Receive() { + void UDPSession::Receive() + { LogPrint(eLogDebug, "UDPSession: Receive"); IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU), FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2)); @@ -839,7 +911,7 @@ { if(!ecode) { - LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint); + LogPrint(eLogDebug, "UDPSession: Forward ", len, "B from ", FromEndpoint); auto ts = i2p::util::GetMillisecondsSinceEpoch(); auto session = m_Destination->GetSession (Identity); if (ts > LastActivity + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL) @@ -848,241 +920,259 @@ m_Destination->SendRawDatagram(session, m_Buffer, len, LocalPort, RemotePort); size_t numPackets = 0; while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE) - { + { boost::system::error_code ec; size_t moreBytes = IPSocket.available(ec); if (ec || !moreBytes) break; len = IPSocket.receive_from (boost::asio::buffer (m_Buffer, I2P_UDP_MAX_MTU), FromEndpoint, 0, ec); - m_Destination->SendRawDatagram (session, m_Buffer, len, LocalPort, RemotePort); + m_Destination->SendRawDatagram (session, m_Buffer, len, LocalPort, RemotePort); numPackets++; - } + } if (numPackets > 0) - LogPrint(eLogDebug, "UDPSession: forward more ", numPackets, "packets B from ", FromEndpoint); + LogPrint(eLogDebug, "UDPSession: Forward more ", numPackets, "packets B from ", FromEndpoint); m_Destination->FlushSendQueue (session); LastActivity = ts; Receive(); - } + } else LogPrint(eLogError, "UDPSession: ", ecode.message()); } - I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr localDestination, + I2PUDPServerTunnel::I2PUDPServerTunnel (const std::string & name, std::shared_ptr localDestination, boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip) : - m_IsUniqueLocal(true), - m_Name(name), - m_LocalAddress(localAddress), - m_RemoteEndpoint(forwardTo) + m_IsUniqueLocal (true), m_Name (name), m_LocalAddress (localAddress), + m_RemoteEndpoint (forwardTo), m_LocalDest (localDestination), m_Gzip (gzip) + { + } + + I2PUDPServerTunnel::~I2PUDPServerTunnel () { - m_LocalDest = localDestination; - m_LocalDest->Start(); - auto dgram = m_LocalDest->CreateDatagramDestination(gzip); - dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - dgram->SetRawReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + Stop (); } - I2PUDPServerTunnel::~I2PUDPServerTunnel() + void I2PUDPServerTunnel::Start () { - auto dgram = m_LocalDest->GetDatagramDestination(); - if (dgram) dgram->ResetReceiver(); + m_LocalDest->Start (); - LogPrint(eLogInfo, "UDPServer: done"); + auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip); + dgram->SetReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); + dgram->SetRawReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); } - void I2PUDPServerTunnel::Start() { - m_LocalDest->Start(); + void I2PUDPServerTunnel::Stop () + { + auto dgram = m_LocalDest->GetDatagramDestination (); + if (dgram) dgram->ResetReceiver (); } - std::vector > I2PUDPServerTunnel::GetSessions() + std::vector > I2PUDPServerTunnel::GetSessions () { std::vector > sessions; - std::lock_guard lock(m_SessionsMutex); + std::lock_guard lock (m_SessionsMutex); - for ( UDPSessionPtr s : m_Sessions ) + for (UDPSessionPtr s: m_Sessions) { if (!s->m_Destination) continue; - auto info = s->m_Destination->GetInfoForRemote(s->Identity); - if(!info) continue; + auto info = s->m_Destination->GetInfoForRemote (s->Identity); + if (!info) continue; - auto sinfo = std::make_shared(); + auto sinfo = std::make_shared (); sinfo->Name = m_Name; - sinfo->LocalIdent = std::make_shared(m_LocalDest->GetIdentHash().data()); - sinfo->RemoteIdent = std::make_shared(s->Identity.data()); + sinfo->LocalIdent = std::make_shared (m_LocalDest->GetIdentHash ().data ()); + sinfo->RemoteIdent = std::make_shared (s->Identity.data ()); sinfo->CurrentIBGW = info->IBGW; sinfo->CurrentOBEP = info->OBEP; - sessions.push_back(sinfo); + sessions.push_back (sinfo); } return sessions; } - I2PUDPClientTunnel::I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest, + I2PUDPClientTunnel::I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest, boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr localDestination, uint16_t remotePort, bool gzip) : - m_Name(name), - m_RemoteDest(remoteDest), - m_LocalDest(localDestination), - m_LocalEndpoint(localEndpoint), - m_RemoteIdent(nullptr), - m_ResolveThread(nullptr), - m_LocalSocket(localDestination->GetService(), localEndpoint), - RemotePort(remotePort), m_LastPort (0), - m_cancel_resolve(false) + m_Name (name), m_RemoteDest (remoteDest), m_LocalDest (localDestination), m_LocalEndpoint (localEndpoint), + m_RemoteIdent (nullptr), m_ResolveThread (nullptr), m_LocalSocket (nullptr), RemotePort (remotePort), + m_LastPort (0), m_cancel_resolve (false), m_Gzip (gzip) + { + } + + I2PUDPClientTunnel::~I2PUDPClientTunnel () + { + Stop (); + } + + void I2PUDPClientTunnel::Start () { - m_LocalSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU )); + // Reset flag in case of tunnel reload + if (m_cancel_resolve) m_cancel_resolve = false; + + m_LocalSocket.reset (new boost::asio::ip::udp::socket (m_LocalDest->GetService (), m_LocalEndpoint)); + m_LocalSocket->set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU)); + m_LocalSocket->set_option (boost::asio::socket_base::reuse_address (true)); - auto dgram = m_LocalDest->CreateDatagramDestination(gzip); - dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this, + auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip); + dgram->SetReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - dgram->SetRawReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this, + dgram->SetRawReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); - } - void I2PUDPClientTunnel::Start() { - m_LocalDest->Start(); + m_LocalDest->Start (); if (m_ResolveThread == nullptr) - m_ResolveThread = new std::thread(std::bind(&I2PUDPClientTunnel::TryResolving, this)); - RecvFromLocal(); + m_ResolveThread = new std::thread (std::bind (&I2PUDPClientTunnel::TryResolving, this)); + RecvFromLocal (); + } + + void I2PUDPClientTunnel::Stop () + { + auto dgram = m_LocalDest->GetDatagramDestination (); + if (dgram) dgram->ResetReceiver (); + m_cancel_resolve = true; + + m_Sessions.clear(); + + if(m_LocalSocket && m_LocalSocket->is_open ()) + m_LocalSocket->close (); + + if(m_ResolveThread) + { + m_ResolveThread->join (); + delete m_ResolveThread; + m_ResolveThread = nullptr; + } + if (m_RemoteIdent) + { + delete m_RemoteIdent; + m_RemoteIdent = nullptr; + } } - void I2PUDPClientTunnel::RecvFromLocal() + void I2PUDPClientTunnel::RecvFromLocal () { - m_LocalSocket.async_receive_from(boost::asio::buffer(m_RecvBuff, I2P_UDP_MAX_MTU), - m_RecvEndpoint, std::bind(&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2)); + m_LocalSocket->async_receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), + m_RecvEndpoint, std::bind (&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2)); } - void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred) + void I2PUDPClientTunnel::HandleRecvFromLocal (const boost::system::error_code & ec, std::size_t transferred) { - if(ec) { - LogPrint(eLogError, "UDP Client: ", ec.message()); + if (m_cancel_resolve) { + LogPrint (eLogDebug, "UDP Client: Ignoring incomming data: stopping"); return; } - if(!m_RemoteIdent) { - LogPrint(eLogWarning, "UDP Client: remote endpoint not resolved yet"); - RecvFromLocal(); + if (ec) { + LogPrint (eLogError, "UDP Client: Reading from socket error: ", ec.message (), ". Restarting listener..."); + RecvFromLocal (); // Restart listener and continue work + return; + } + if (!m_RemoteIdent) { + LogPrint (eLogWarning, "UDP Client: Remote endpoint not resolved yet"); + RecvFromLocal (); return; // drop, remote not resolved } - auto remotePort = m_RecvEndpoint.port(); + auto remotePort = m_RecvEndpoint.port (); if (!m_LastPort || m_LastPort != remotePort) { - auto itr = m_Sessions.find(remotePort); - if (itr != m_Sessions.end()) + auto itr = m_Sessions.find (remotePort); + if (itr != m_Sessions.end ()) m_LastSession = itr->second; else { - m_LastSession = std::make_shared(boost::asio::ip::udp::endpoint(m_RecvEndpoint), 0); + m_LastSession = std::make_shared (boost::asio::ip::udp::endpoint (m_RecvEndpoint), 0); m_Sessions.emplace (remotePort, m_LastSession); - } + } m_LastPort = remotePort; - } + } // send off to remote i2p destination - auto ts = i2p::util::GetMillisecondsSinceEpoch(); - LogPrint(eLogDebug, "UDP Client: send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort); - auto session = m_LocalDest->GetDatagramDestination()->GetSession (*m_RemoteIdent); - if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL) - m_LocalDest->GetDatagramDestination()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + LogPrint (eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteIdent->ToBase32 (), ":", RemotePort); + auto session = m_LocalDest->GetDatagramDestination ()->GetSession (*m_RemoteIdent); + if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL) + m_LocalDest->GetDatagramDestination ()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); else - m_LocalDest->GetDatagramDestination()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); + m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); size_t numPackets = 0; while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE) - { + { boost::system::error_code ec; - size_t moreBytes = m_LocalSocket.available(ec); + size_t moreBytes = m_LocalSocket->available (ec); if (ec || !moreBytes) break; - transferred = m_LocalSocket.receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), m_RecvEndpoint, 0, ec); - remotePort = m_RecvEndpoint.port(); + transferred = m_LocalSocket->receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), m_RecvEndpoint, 0, ec); + remotePort = m_RecvEndpoint.port (); // TODO: check remotePort - m_LocalDest->GetDatagramDestination()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); + m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); numPackets++; - } + } if (numPackets) - LogPrint(eLogDebug, "UDP Client: sent ", numPackets, " more packets to ", m_RemoteIdent->ToBase32()); - m_LocalDest->GetDatagramDestination()->FlushSendQueue (session); - + LogPrint (eLogDebug, "UDP Client: Sent ", numPackets, " more packets to ", m_RemoteIdent->ToBase32 ()); + m_LocalDest->GetDatagramDestination ()->FlushSendQueue (session); + // mark convo as active if (m_LastSession) m_LastSession->second = ts; - RecvFromLocal(); + RecvFromLocal (); } - std::vector > I2PUDPClientTunnel::GetSessions() + std::vector > I2PUDPClientTunnel::GetSessions () { // TODO: implement std::vector > infos; return infos; } - void I2PUDPClientTunnel::TryResolving() { - i2p::util::SetThreadName("UDP Resolver"); - LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest); + void I2PUDPClientTunnel::TryResolving () + { + i2p::util::SetThreadName ("UDP Resolver"); + LogPrint (eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest); std::shared_ptr addr; - while(!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve) + while (!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve) { - LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest); - std::this_thread::sleep_for(std::chrono::seconds(1)); + LogPrint (eLogWarning, "UDP Tunnel: Failed to lookup ", m_RemoteDest); + std::this_thread::sleep_for (std::chrono::seconds (1)); } - if(m_cancel_resolve) + if (m_cancel_resolve) { - LogPrint(eLogError, "UDP Tunnel: lookup of ", m_RemoteDest, " was cancelled"); + LogPrint(eLogError, "UDP Tunnel: Lookup of ", m_RemoteDest, " was cancelled"); return; } if (!addr || !addr->IsIdentHash ()) { - LogPrint(eLogError, "UDP Tunnel: ", m_RemoteDest, " not found"); + LogPrint (eLogError, "UDP Tunnel: ", m_RemoteDest, " not found"); return; } m_RemoteIdent = new i2p::data::IdentHash; *m_RemoteIdent = addr->identHash; - LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32()); + LogPrint(eLogInfo, "UDP Tunnel: Resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32 ()); } - void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) + void I2PUDPClientTunnel::HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) { - if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent) + if (m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent) HandleRecvFromI2PRaw (fromPort, toPort, buf, len); else - LogPrint(eLogWarning, "UDP Client: unwarranted traffic from ", from.GetIdentHash().ToBase32()); + LogPrint(eLogWarning, "UDP Client: Unwarranted traffic from ", from.GetIdentHash().ToBase32 ()); } - void I2PUDPClientTunnel::HandleRecvFromI2PRaw(uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) + void I2PUDPClientTunnel::HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) { - auto itr = m_Sessions.find(toPort); + auto itr = m_Sessions.find (toPort); // found convo ? - if(itr != m_Sessions.end()) + if (itr != m_Sessions.end ()) { // found convo - if (len > 0) + if (len > 0) { - LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", m_RemoteIdent ? m_RemoteIdent->ToBase32() : ""); - m_LocalSocket.send_to(boost::asio::buffer(buf, len), itr->second->first); + LogPrint (eLogDebug, "UDP Client: Got ", len, "B from ", m_RemoteIdent ? m_RemoteIdent->ToBase32 () : ""); + m_LocalSocket->send_to (boost::asio::buffer (buf, len), itr->second->first); // mark convo as active - itr->second->second = i2p::util::GetMillisecondsSinceEpoch(); + itr->second->second = i2p::util::GetMillisecondsSinceEpoch (); } } else - LogPrint(eLogWarning, "UDP Client: not tracking udp session using port ", (int) toPort); - } - - I2PUDPClientTunnel::~I2PUDPClientTunnel() { - auto dgram = m_LocalDest->GetDatagramDestination(); - if (dgram) dgram->ResetReceiver(); - - m_Sessions.clear(); - - if(m_LocalSocket.is_open()) - m_LocalSocket.close(); - - m_cancel_resolve = true; - - if(m_ResolveThread) - { - m_ResolveThread->join(); - delete m_ResolveThread; - m_ResolveThread = nullptr; - } - if (m_RemoteIdent) delete m_RemoteIdent; + LogPrint (eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort); } } } + diff -Nru i2pd-2.39.0/libi2pd_client/I2PTunnel.h i2pd-2.43.0/libi2pd_client/I2PTunnel.h --- i2pd-2.39.0/libi2pd_client/I2PTunnel.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/I2PTunnel.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -31,7 +31,7 @@ const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds // for HTTP tunnels - const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64 + const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64 const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64 const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address @@ -43,13 +43,13 @@ std::shared_ptr leaseSet, int port = 0); // to I2P I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, std::shared_ptr stream); // to I2P using simplified API - I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, + I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P ~I2PTunnelConnection (); void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); void Connect (bool isUniqueLocal = true); void Connect (const boost::asio::ip::address& localAddress); - + protected: void Terminate (); @@ -59,7 +59,7 @@ virtual void Write (const uint8_t * buf, size_t len); // can be overloaded void HandleWrite (const boost::system::error_code& ecode); virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded - + void StreamReceive (); void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleConnect (const boost::system::error_code& ecode); @@ -105,7 +105,7 @@ protected: void Write (const uint8_t * buf, size_t len); - void WriteToStream (const uint8_t * buf, size_t len); + void WriteToStream (const uint8_t * buf, size_t len); private: @@ -153,23 +153,29 @@ 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; }; /** 2 minute timeout for udp sessions */ const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2; - const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds - + const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds + /** max size for i2p udp */ const size_t I2P_UDP_MAX_MTU = 64*1024; @@ -224,25 +230,27 @@ { public: - I2PUDPServerTunnel(const std::string & name, + I2PUDPServerTunnel (const std::string & name, std::shared_ptr localDestination, boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip); - ~I2PUDPServerTunnel(); + ~I2PUDPServerTunnel (); + /** expire stale udp conversations */ - void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); - void Start(); - const char * GetName() const { return m_Name.c_str(); } - std::vector > GetSessions(); + void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); + void Start (); + void Stop (); + const char * GetName () const { return m_Name.c_str(); } + std::vector > GetSessions (); std::shared_ptr GetLocalDestination () const { return m_LocalDest; } - void SetUniqueLocal(bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; } + void SetUniqueLocal (bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; } private: - void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); + void HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); + UDPSessionPtr ObtainUDPSession (const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); private: @@ -254,36 +262,50 @@ std::vector m_Sessions; std::shared_ptr m_LocalDest; UDPSessionPtr m_LastSession; + bool m_Gzip; + + public: + + bool isUpdated; // transient, used during reload only }; class I2PUDPClientTunnel { public: - I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest, + I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest, boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr localDestination, uint16_t remotePort, bool gzip); - ~I2PUDPClientTunnel(); - void Start(); - const char * GetName() const { return m_Name.c_str(); } - std::vector > GetSessions(); + ~I2PUDPClientTunnel (); - bool IsLocalDestination(const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); } + void Start (); + void Stop (); + const char * GetName () const { return m_Name.c_str(); } + std::vector > GetSessions (); + + bool IsLocalDestination (const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); } std::shared_ptr GetLocalDestination () const { return m_LocalDest; } - void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); + inline void SetLocalDestination (std::shared_ptr dest) + { + if (m_LocalDest) m_LocalDest->Release (); + if (dest) dest->Acquire (); + m_LocalDest = dest; + } + + void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); private: typedef std::pair UDPConvo; - void RecvFromLocal(); - void HandleRecvFromLocal(const boost::system::error_code & e, std::size_t transferred); - void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void HandleRecvFromI2PRaw(uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void TryResolving(); + void RecvFromLocal (); + void HandleRecvFromLocal (const boost::system::error_code & e, std::size_t transferred); + void HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); + void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); + void TryResolving (); private: - + const std::string m_Name; std::mutex m_SessionsMutex; std::unordered_map > m_Sessions; // maps i2p port -> local udp convo @@ -292,12 +314,17 @@ const boost::asio::ip::udp::endpoint m_LocalEndpoint; i2p::data::IdentHash * m_RemoteIdent; std::thread * m_ResolveThread; - boost::asio::ip::udp::socket m_LocalSocket; + std::unique_ptr m_LocalSocket; boost::asio::ip::udp::endpoint m_RecvEndpoint; uint8_t m_RecvBuff[I2P_UDP_MAX_MTU]; uint16_t RemotePort, m_LastPort; bool m_cancel_resolve; + bool m_Gzip; std::shared_ptr m_LastSession; + + public: + + bool isUpdated; // transient, used during reload only }; class I2PServerTunnel: public I2PService @@ -316,7 +343,7 @@ bool IsUniqueLocal () const { return m_IsUniqueLocal; } void SetLocalAddress (const std::string& localAddress); - + const std::string& GetAddress() const { return m_Address; } int GetPort () const { return m_Port; }; uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); }; diff -Nru i2pd-2.39.0/libi2pd_client/MatchedDestination.cpp i2pd-2.43.0/libi2pd_client/MatchedDestination.cpp --- i2pd-2.39.0/libi2pd_client/MatchedDestination.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/MatchedDestination.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -33,14 +33,14 @@ RequestDestination(m_RemoteIdent, std::bind(&MatchedTunnelDestination::HandleFoundCurrentLeaseSet, this, std::placeholders::_1)); } else - LogPrint(eLogWarning, "Destination: failed to resolve ", m_RemoteName); + LogPrint(eLogWarning, "Destination: Failed to resolve ", m_RemoteName); } void MatchedTunnelDestination::HandleFoundCurrentLeaseSet(std::shared_ptr ls) { if(ls) { - LogPrint(eLogDebug, "Destination: resolved remote lease set for ", m_RemoteName); + LogPrint(eLogDebug, "Destination: Resolved remote lease set for ", m_RemoteName); m_RemoteLeaseSet = ls; } else @@ -72,7 +72,7 @@ bool MatchedTunnelDestination::SelectPeers(i2p::tunnel::Path & path, int hops, bool inbound) { auto pool = GetTunnelPool(); - if(!i2p::tunnel::StandardSelectPeers(path, hops, inbound, + if(!i2p::tunnel::StandardSelectPeers(path, hops, inbound, std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1, std::placeholders::_2))) return false; // more here for outbound tunnels @@ -86,19 +86,19 @@ 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) + if(obep) { path.Add (obep); - LogPrint(eLogDebug, "Destination: found OBEP matching IBGW"); + LogPrint(eLogDebug, "Destination: Found OBEP matching IBGW"); } else - LogPrint(eLogWarning, "Destination: could not find proper IBGW for matched outbound tunnel"); + LogPrint(eLogWarning, "Destination: Could not find proper IBGW for matched outbound tunnel"); } } return true; diff -Nru i2pd-2.39.0/libi2pd_client/SAM.cpp i2pd-2.43.0/libi2pd_client/SAM.cpp --- i2pd-2.39.0/libi2pd_client/SAM.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/SAM.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -54,7 +54,7 @@ break; } case eSAMSocketTypeAcceptor: - case eSAMSocketTypeForward: + case eSAMSocketTypeForward: { if (Session) { @@ -101,7 +101,7 @@ { if (ecode) { - LogPrint (eLogError, "SAM: handshake read error: ", ecode.message ()); + LogPrint (eLogError, "SAM: Handshake read error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: handshake read error"); } @@ -111,7 +111,7 @@ char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred); if (eol) *eol = 0; - LogPrint (eLogDebug, "SAM: handshake ", m_Buffer); + LogPrint (eLogDebug, "SAM: Handshake ", m_Buffer); char * separator = strchr (m_Buffer, ' '); if (separator) { @@ -168,7 +168,7 @@ } else { - LogPrint (eLogError, "SAM: handshake mismatch"); + LogPrint (eLogError, "SAM: Handshake mismatch"); Terminate ("SAM: handshake mismatch"); } } @@ -183,7 +183,7 @@ { if (ecode) { - LogPrint (eLogError, "SAM: handshake reply send error: ", ecode.message ()); + LogPrint (eLogError, "SAM: Handshake reply send error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: handshake reply send error"); } @@ -216,7 +216,7 @@ { if (ecode) { - LogPrint (eLogError, "SAM: reply send error: ", ecode.message ()); + LogPrint (eLogError, "SAM: Reply send error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: reply send error"); } @@ -233,7 +233,7 @@ { if (ecode) { - LogPrint (eLogError, "SAM: read error: ", ecode.message ()); + LogPrint (eLogError, "SAM: Read error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: read error"); } @@ -295,20 +295,20 @@ } else { - LogPrint (eLogError, "SAM: unexpected message ", m_Buffer); + LogPrint (eLogError, "SAM: Unexpected message ", m_Buffer); Terminate ("SAM: unexpected message"); } } else { - LogPrint (eLogError, "SAM: malformed message ", m_Buffer); + LogPrint (eLogError, "SAM: Malformed message ", m_Buffer); Terminate ("malformed message"); } } else { - LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred); + LogPrint (eLogWarning, "SAM: Incomplete message ", bytes_transferred); m_BufferOffset = bytes_transferred; // try to receive remaining message Receive (); @@ -331,7 +331,7 @@ void SAMSocket::ProcessSessionCreate (char * buf, size_t len) { - LogPrint (eLogDebug, "SAM: session create: ", buf); + LogPrint (eLogDebug, "SAM: Session create: ", buf); std::map params; ExtractParams (buf, params); std::string& style = params[SAM_PARAM_STYLE]; @@ -476,12 +476,12 @@ void SAMSocket::ProcessStreamConnect (char * buf, size_t len, size_t rem) { - LogPrint (eLogDebug, "SAM: stream connect: ", buf); + LogPrint (eLogDebug, "SAM: Stream connect: ", buf); if ( m_SocketType != eSAMSocketTypeUnknown) { SendI2PError ("Socket already in use"); return; - } + } std::map params; ExtractParams (buf, params); std::string& id = params[SAM_PARAM_ID]; @@ -502,7 +502,7 @@ std::shared_ptr addr; if (destination.find(".i2p") != std::string::npos) - addr = context.GetAddressBook().GetAddress (destination); + addr = context.GetAddressBook().GetAddress (destination); else { auto dest = std::make_shared (); @@ -511,13 +511,13 @@ { context.GetAddressBook().InsertFullAddress(dest); addr = std::make_shared
(dest->GetIdentHash ()); - } - } - + } + } + if (addr && addr->IsValid ()) { if (addr->IsIdentHash ()) - { + { auto leaseSet = session->GetLocalDestination ()->FindLeaseSet(addr->identHash); if (leaseSet) Connect(leaseSet, session); @@ -527,7 +527,7 @@ std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete, shared_from_this(), std::placeholders::_1)); } - } + } else // B33 session->GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete, @@ -548,12 +548,12 @@ m_SocketType = eSAMSocketTypeStream; m_Stream = session->GetLocalDestination ()->CreateStream (remote); if (m_Stream) - { + { m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send m_BufferOffset = 0; I2PReceive (); SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } + } else SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); } @@ -567,19 +567,19 @@ Connect (leaseSet); else { - LogPrint (eLogError, "SAM: destination to connect not found"); + LogPrint (eLogError, "SAM: Destination to connect not found"); SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true); } } void SAMSocket::ProcessStreamAccept (char * buf, size_t len) { - LogPrint (eLogDebug, "SAM: stream accept: ", buf); + LogPrint (eLogDebug, "SAM: Stream accept: ", buf); if ( m_SocketType != eSAMSocketTypeUnknown) { SendI2PError ("Socket already in use"); return; - } + } std::map params; ExtractParams (buf, params); std::string& id = params[SAM_PARAM_ID]; @@ -603,7 +603,7 @@ void SAMSocket::ProcessStreamForward (char * buf, size_t len) { - LogPrint (eLogDebug, "SAM: stream forward: ", buf); + LogPrint (eLogDebug, "SAM: Stream forward: ", buf); std::map params; ExtractParams (buf, params); std::string& id = params[SAM_PARAM_ID]; @@ -612,45 +612,45 @@ { SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); return; - } + } if (session->GetLocalDestination ()->IsAcceptingStreams ()) - { + { SendI2PError ("Already accepting"); return; - } + } auto it = params.find (SAM_PARAM_PORT); if (it == params.end ()) { SendI2PError ("PORT is missing"); return; - } + } auto port = std::stoi (it->second); if (port <= 0 || port >= 0xFFFF) { SendI2PError ("Invalid PORT"); return; - } + } boost::system::error_code ec; auto ep = m_Socket.remote_endpoint (ec); if (ec) { SendI2PError ("Socket error"); return; - } + } ep.port (port); m_SocketType = eSAMSocketTypeForward; m_ID = id; m_IsAccepting = true; std::string& silent = params[SAM_PARAM_SILENT]; if (silent == SAM_VALUE_TRUE) m_IsSilent = true; - session->GetLocalDestination ()->AcceptStreams (std::bind (&SAMSocket::HandleI2PForward, + session->GetLocalDestination ()->AcceptStreams (std::bind (&SAMSocket::HandleI2PForward, shared_from_this (), std::placeholders::_1, ep)); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } - + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + } + size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data) { - LogPrint (eLogDebug, "SAM: datagram send: ", buf, " ", len); + LogPrint (eLogDebug, "SAM: Datagram send: ", buf, " ", len); std::map params; ExtractParams (buf, params); size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf; @@ -670,14 +670,14 @@ d->SendRawDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); } else - LogPrint (eLogError, "SAM: missing datagram destination"); + LogPrint (eLogError, "SAM: Missing datagram destination"); } else - LogPrint (eLogError, "SAM: session is not created from DATAGRAM SEND"); + LogPrint (eLogError, "SAM: Session is not created from DATAGRAM SEND"); } else { - LogPrint (eLogWarning, "SAM: sent datagram size ", size, " exceeds buffer ", len - offset); + LogPrint (eLogWarning, "SAM: Sent datagram size ", size, " exceeds buffer ", len - offset); return 0; // try to receive more } return offset + size; @@ -685,7 +685,7 @@ void SAMSocket::ProcessDestGenerate (char * buf, size_t len) { - LogPrint (eLogDebug, "SAM: dest generate"); + LogPrint (eLogDebug, "SAM: Dest generate"); std::map params; ExtractParams (buf, params); // extract signature type @@ -722,7 +722,7 @@ void SAMSocket::ProcessNamingLookup (char * buf, size_t len) { - LogPrint (eLogDebug, "SAM: naming lookup: ", buf); + LogPrint (eLogDebug, "SAM: Naming lookup: ", buf); std::map params; ExtractParams (buf, params); std::string& name = params[SAM_PARAM_NAME]; @@ -753,7 +753,7 @@ } else { - LogPrint (eLogError, "SAM: naming failed, unknown address ", name); + LogPrint (eLogError, "SAM: Naming failed, unknown address ", name); #ifdef _MSC_VER size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #else @@ -768,7 +768,7 @@ auto session = m_Owner.FindSession(m_ID); if (session && session->Type == eSAMSessionTypeMaster) { - LogPrint (eLogDebug, "SAM: subsession add: ", buf); + LogPrint (eLogDebug, "SAM: Subsession add: ", buf); auto masterSession = std::static_pointer_cast(session); std::map params; ExtractParams (buf, params); @@ -778,8 +778,8 @@ // session exists SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); return; - } - std::string& style = params[SAM_PARAM_STYLE]; + } + std::string& style = params[SAM_PARAM_STYLE]; SAMSessionType type = eSAMSessionTypeUnknown; if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream; // TODO: implement other styles @@ -800,39 +800,39 @@ { masterSession->subsessions.insert (id); SendSessionCreateReplyOk (); - } + } else SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); - } + } else SendI2PError ("Wrong session type"); } - + void SAMSocket::ProcessSessionRemove (char * buf, size_t len) { auto session = m_Owner.FindSession(m_ID); if (session && session->Type == eSAMSessionTypeMaster) { - LogPrint (eLogDebug, "SAM: subsession remove: ", buf); + LogPrint (eLogDebug, "SAM: Subsession remove: ", buf); auto masterSession = std::static_pointer_cast(session); std::map params; ExtractParams (buf, params); std::string& id = params[SAM_PARAM_ID]; if (!masterSession->subsessions.erase (id)) - { + { SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), false); return; } m_Owner.CloseSession (id); SendSessionCreateReplyOk (); - } + } else SendI2PError ("Wrong session type"); - } - + } + void SAMSocket::SendI2PError(const std::string & msg) { - LogPrint (eLogError, "SAM: i2p error ", msg); + LogPrint (eLogError, "SAM: I2P error: ", msg); #ifdef _MSC_VER size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); #else @@ -850,7 +850,7 @@ } else { - LogPrint (eLogError, "SAM: naming lookup failed. LeaseSet for ", name, " not found"); + LogPrint (eLogError, "SAM: Naming lookup failed. LeaseSet for ", name, " not found"); #ifdef _MSC_VER size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #else @@ -901,7 +901,7 @@ { if (ecode) { - LogPrint (eLogError, "SAM: read error: ", ecode.message ()); + LogPrint (eLogError, "SAM: Read error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("read error"); } @@ -936,7 +936,7 @@ else // closed by peer { uint8_t * buff = new uint8_t[SAM_SOCKET_BUFFER_SIZE]; - // get remaning data + // get remaining data auto len = m_Stream->ReadSome (buff, SAM_SOCKET_BUFFER_SIZE); if (len > 0) // still some data { @@ -978,7 +978,7 @@ { if (ecode) { - LogPrint (eLogError, "SAM: stream read error: ", ecode.message ()); + LogPrint (eLogError, "SAM: Stream read error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) { if (bytes_transferred > 0) @@ -1015,7 +1015,7 @@ { if (ecode) { - LogPrint (eLogError, "SAM: socket write error: ", ecode.message ()); + LogPrint (eLogError, "SAM: Socket write error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("socket write error at HandleWriteI2PData"); } @@ -1029,7 +1029,7 @@ { if (stream) { - LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID); + LogPrint (eLogDebug, "SAM: Incoming I2P connection for session ", m_ID); m_SocketType = eSAMSocketTypeStream; m_IsAccepting = false; m_Stream = stream; @@ -1067,18 +1067,18 @@ LogPrint (eLogWarning, "SAM: I2P acceptor has been reset"); } - void SAMSocket::HandleI2PForward (std::shared_ptr stream, + void SAMSocket::HandleI2PForward (std::shared_ptr stream, boost::asio::ip::tcp::endpoint ep) { if (stream) { - LogPrint (eLogDebug, "SAM: incoming forward I2P connection for session ", m_ID); + LogPrint (eLogDebug, "SAM: Incoming forward I2P connection for session ", m_ID); auto newSocket = std::make_shared(m_Owner); newSocket->SetSocketType (eSAMSocketTypeStream); auto s = shared_from_this (); - newSocket->GetSocket ().async_connect (ep, + newSocket->GetSocket ().async_connect (ep, [s, newSocket, stream](const boost::system::error_code& ecode) - { + { if (!ecode) { s->m_Owner.AddSocket (newSocket); @@ -1098,15 +1098,15 @@ } else stream->AsyncClose (); - }); + }); } else LogPrint (eLogWarning, "SAM: I2P forward acceptor has been reset"); - } - + } + void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) { - LogPrint (eLogDebug, "SAM: datagram received ", len); + LogPrint (eLogDebug, "SAM: Datagram received ", len); auto base64 = from.ToBase64 (); auto session = m_Owner.FindSession(m_ID); if(session) @@ -1115,19 +1115,9 @@ if (ep) { // udp forward enabled - size_t bsz = base64.size(); - size_t sz = bsz + 1 + len; - // build datagram body - uint8_t * data = new uint8_t[sz]; - // Destination - memcpy(data, base64.c_str(), bsz); - // linefeed - data[bsz] = '\n'; - // Payload - memcpy(data+bsz+1, buf, len); - // send to remote endpoint - m_Owner.SendTo(data, sz, ep); - delete [] data; + const char lf = '\n'; + // send to remote endpoint, { destination, linefeed, payload } + m_Owner.SendTo({ {(const uint8_t *)base64.c_str(), base64.size()}, {(const uint8_t *)&lf, 1}, {buf, len} }, *ep); } else { @@ -1142,21 +1132,21 @@ WriteI2PData(len + l); } else - LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer"); + LogPrint (eLogWarning, "SAM: Received datagram size ", len," exceeds buffer"); } } } void SAMSocket::HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) { - LogPrint (eLogDebug, "SAM: raw datagram received ", len); + LogPrint (eLogDebug, "SAM: Raw datagram received ", len); auto session = m_Owner.FindSession(m_ID); if(session) { auto ep = session->UDPEndpoint; if (ep) // udp forward enabled - m_Owner.SendTo(buf, len, ep); + m_Owner.SendTo({ {buf, len} }, *ep); else { #ifdef _MSC_VER @@ -1170,7 +1160,7 @@ WriteI2PData(len + l); } else - LogPrint (eLogWarning, "SAM: received raw datagram size ", len," exceeds buffer"); + LogPrint (eLogWarning, "SAM: Received raw datagram size ", len," exceeds buffer"); } } } @@ -1198,11 +1188,11 @@ localDestination (dest) { } - + SAMSingleSession::~SAMSingleSession () { i2p::client::context.DeleteLocalDestination (localDestination); - } + } void SAMSingleSession::StopLocalDestination () { @@ -1220,24 +1210,24 @@ for (const auto& it: subsessions) m_Bridge.CloseSession (it); subsessions.clear (); - } - + } + SAMSubSession::SAMSubSession (std::shared_ptr master, const std::string& name, SAMSessionType type, int port): SAMSession (master->m_Bridge, name, type), masterSession (master), inPort (port) { if (Type == eSAMSessionTypeStream) - { + { auto d = masterSession->GetLocalDestination ()->CreateStreamingDestination (inPort); if (d) d->Start (); - } - // TODO: implement datagrams - } - + } + // TODO: implement datagrams + } + std::shared_ptr SAMSubSession::GetLocalDestination () { return masterSession ? masterSession->GetLocalDestination () : nullptr; } - + void SAMSubSession::StopLocalDestination () { auto dest = GetLocalDestination (); @@ -1245,10 +1235,10 @@ { auto d = dest->RemoveStreamingDestination (inPort); if (d) d->Stop (); - } + } // TODO: implement datagrams - } - + } + SAMBridge::SAMBridge (const std::string& address, int port, bool singleThread): RunnableService ("SAM"), m_IsSingleThread (singleThread), m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port)), @@ -1288,7 +1278,7 @@ } catch (const std::exception& ex) { - LogPrint (eLogError, "SAM: runtime exception: ", ex.what ()); + LogPrint (eLogError, "SAM: Runtime exception: ", ex.what ()); } { @@ -1311,8 +1301,8 @@ { std::unique_lock lock(m_OpenSocketsMutex); m_OpenSockets.push_back(socket); - } - + } + void SAMBridge::RemoveSocket(const std::shared_ptr & socket) { std::unique_lock lock(m_OpenSocketsMutex); @@ -1327,15 +1317,15 @@ auto ep = socket->GetSocket ().remote_endpoint (ec); if (!ec) { - LogPrint (eLogDebug, "SAM: new connection from ", ep); + LogPrint (eLogDebug, "SAM: New connection from ", ep); AddSocket (socket); socket->ReceiveHandshake (); } else - LogPrint (eLogError, "SAM: incoming connection error ", ec.message ()); + LogPrint (eLogError, "SAM: Incoming connection error: ", ec.message ()); } else - LogPrint (eLogError, "SAM: accept error: ", ecode.message ()); + LogPrint (eLogError, "SAM: Accept error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Accept (); @@ -1403,7 +1393,7 @@ auto ret = m_Sessions.emplace (session->Name, session); return ret.second; } - + void SAMBridge::CloseSession (const std::string& id) { std::shared_ptr session; @@ -1453,12 +1443,9 @@ return list; } - void SAMBridge::SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote) + void SAMBridge::SendTo (const std::vector& bufs, const boost::asio::ip::udp::endpoint& ep) { - if(remote) - { - m_DatagramSocket.send_to(boost::asio::buffer(buf, len), *remote); - } + m_DatagramSocket.send_to (bufs, ep); } void SAMBridge::ReceiveDatagram () @@ -1479,7 +1466,7 @@ { *eol = 0; eol++; size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer); - LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen); + LogPrint (eLogDebug, "SAM: Datagram received ", m_DatagramReceiveBuffer," size=", payloadLen); char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' '); if (sessionID) { @@ -1491,14 +1478,21 @@ auto session = FindSession (sessionID); if (session) { - i2p::data::IdentityEx dest; - dest.FromBase64 (destination); - if (session->Type == eSAMSessionTypeDatagram) - session->GetLocalDestination ()->GetDatagramDestination ()-> - SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); - else // raw - session->GetLocalDestination ()->GetDatagramDestination ()-> - SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); + auto localDest = session->GetLocalDestination (); + auto datagramDest = localDest ? localDest->GetDatagramDestination () : nullptr; + if (datagramDest) + { + i2p::data::IdentityEx dest; + dest.FromBase64 (destination); + if (session->Type == eSAMSessionTypeDatagram) + datagramDest->SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); + else if (session->Type == eSAMSessionTypeRaw) + datagramDest->SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); + else + LogPrint (eLogError, "SAM: Unexpected session type ", (int)session->Type, "for session ", sessionID); + } + else + LogPrint (eLogError, "SAM: Datagram destination is not set for session ", sessionID); } else LogPrint (eLogError, "SAM: Session ", sessionID, " not found"); @@ -1510,11 +1504,11 @@ LogPrint (eLogError, "SAM: Missing sessionID"); } else - LogPrint(eLogError, "SAM: invalid datagram"); + LogPrint(eLogError, "SAM: Invalid datagram"); ReceiveDatagram (); } else - LogPrint (eLogError, "SAM: datagram receive error: ", ecode.message ()); + LogPrint (eLogError, "SAM: Datagram receive error: ", ecode.message ()); } bool SAMBridge::ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const diff -Nru i2pd-2.39.0/libi2pd_client/SAM.h i2pd-2.43.0/libi2pd_client/SAM.h --- i2pd-2.39.0/libi2pd_client/SAM.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/SAM.h 2022-08-21 19:40:41.000000000 +0000 @@ -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 * @@ -41,7 +41,7 @@ const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; const char SAM_SESSION_CREATE_INVALID_ID[] = "SESSION STATUS RESULT=INVALID_ID\n"; const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n"; - const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=%s\n"; + const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"%s\"\n"; const char SAM_SESSION_ADD[] = "SESSION ADD"; const char SAM_SESSION_REMOVE[] = "SESSION REMOVE"; const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; @@ -80,7 +80,7 @@ const char SAM_VALUE_STREAM[] = "STREAM"; const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; const char SAM_VALUE_RAW[] = "RAW"; - const char SAM_VALUE_MASTER[] = "MASTER"; + const char SAM_VALUE_MASTER[] = "MASTER"; const char SAM_VALUE_TRUE[] = "true"; const char SAM_VALUE_FALSE[] = "false"; @@ -188,14 +188,14 @@ std::string Name; SAMSessionType Type; std::shared_ptr UDPEndpoint; // TODO: move - + SAMSession (SAMBridge & parent, const std::string & name, SAMSessionType type); virtual ~SAMSession () {}; - + virtual std::shared_ptr GetLocalDestination () = 0; virtual void StopLocalDestination () = 0; virtual void Close () { CloseStreams (); }; - + void CloseStreams (); }; @@ -208,15 +208,15 @@ std::shared_ptr GetLocalDestination () { return localDestination; }; void StopLocalDestination (); - }; + }; struct SAMMasterSession: public SAMSingleSession { std::set subsessions; SAMMasterSession (SAMBridge & parent, const std::string & name, std::shared_ptr dest): SAMSingleSession (parent, name, eSAMSessionTypeMaster, dest) {}; - void Close (); - }; + void Close (); + }; struct SAMSubSession: public SAMSession { @@ -227,8 +227,8 @@ // implements SAMSession std::shared_ptr GetLocalDestination (); void StopLocalDestination (); - }; - + }; + class SAMBridge: private i2p::util::RunnableService { public: @@ -249,7 +249,7 @@ std::list > ListSockets(const std::string & id) const; /** send raw data to remote endpoint from our UDP Socket */ - void SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote); + void SendTo (const std::vector& bufs, const boost::asio::ip::udp::endpoint& ep); void AddSocket(std::shared_ptr socket); void RemoveSocket(const std::shared_ptr & socket); diff -Nru i2pd-2.39.0/libi2pd_client/SOCKS.cpp i2pd-2.43.0/libi2pd_client/SOCKS.cpp --- i2pd-2.39.0/libi2pd_client/SOCKS.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_client/SOCKS.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -191,13 +191,13 @@ void SOCKSHandler::AsyncSockRead() { - LogPrint(eLogDebug, "SOCKS: async sock read"); + LogPrint(eLogDebug, "SOCKS: Async sock read"); if (m_sock) { m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size), std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } else { - LogPrint(eLogError,"SOCKS: no socket for read"); + LogPrint(eLogError,"SOCKS: No socket for read"); } } @@ -206,19 +206,19 @@ if (Kill()) return; if (m_sock) { - LogPrint(eLogDebug, "SOCKS: closing socket"); + LogPrint(eLogDebug, "SOCKS: Closing socket"); m_sock->close(); m_sock = nullptr; } if (m_upstreamSock) { - LogPrint(eLogDebug, "SOCKS: closing upstream socket"); + LogPrint(eLogDebug, "SOCKS: Closing upstream socket"); m_upstreamSock->close(); m_upstreamSock = nullptr; } if (m_stream) { - LogPrint(eLogDebug, "SOCKS: closing stream"); + LogPrint(eLogDebug, "SOCKS: Closing stream"); m_stream.reset (); } Done(shared_from_this()); @@ -386,7 +386,7 @@ if ( m_cmd != CMD_CONNECT ) { // TODO: we need to support binds and other shit! - LogPrint(eLogError, "SOCKS: unsupported command: ", m_cmd); + LogPrint(eLogError, "SOCKS: Unsupported command: ", m_cmd); SocksRequestFailed(SOCKS5_CMD_UNSUP); return false; } @@ -399,7 +399,7 @@ LogPrint(eLogError, "SOCKS: v5 unsupported address type: ", m_addrtype); break; case SOCKS4: - LogPrint(eLogError, "SOCKS: request with v4a rejected because it's actually SOCKS4"); + LogPrint(eLogError, "SOCKS: Request with v4a rejected because it's actually SOCKS4"); break; } SocksRequestFailed(SOCKS5_ADDR_UNSUP); @@ -426,7 +426,7 @@ EnterState(GET5_AUTHNUM); //Initialize the parser at the right position break; default: - LogPrint(eLogError, "SOCKS: rejected invalid version: ", ((int)*sock_buff)); + LogPrint(eLogError, "SOCKS: Rejected invalid version: ", ((int)*sock_buff)); Terminate(); return false; } @@ -456,7 +456,7 @@ [[fallthrough]]; #endif default: - LogPrint(eLogError, "SOCKS: invalid command: ", ((int)*sock_buff)); + LogPrint(eLogError, "SOCKS: Invalid command: ", ((int)*sock_buff)); SocksRequestFailed(SOCKS5_GEN_FAIL); return false; } @@ -558,7 +558,7 @@ if (m_parseleft == 0) EnterState(GET_PORT); break; default: - LogPrint(eLogError, "SOCKS: parse state?? ", m_state); + LogPrint(eLogError, "SOCKS: Parse state?? ", m_state); Terminate(); return false; } @@ -576,10 +576,10 @@ void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) { - LogPrint(eLogDebug, "SOCKS: received ", len, " bytes"); + LogPrint(eLogDebug, "SOCKS: Received ", len, " bytes"); if(ecode) { - LogPrint(eLogWarning, "SOCKS: recv got error: ", ecode); + LogPrint(eLogWarning, "SOCKS: Recv got error: ", ecode); Terminate(); return; } @@ -589,7 +589,7 @@ if (m_state == READY) { const std::string addr = m_address.dns.ToString(); - LogPrint(eLogInfo, "SOCKS: requested ", addr, ":" , m_port); + LogPrint(eLogInfo, "SOCKS: Requested ", addr, ":" , m_port); const size_t addrlen = addr.size(); // does it end with .i2p? if ( addr.rfind(".i2p") == addrlen - 4) { @@ -612,7 +612,7 @@ void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode) { if (ecode) - LogPrint (eLogError, "SOCKS: closing socket after sending failure because: ", ecode.message ()); + LogPrint (eLogError, "SOCKS: Closing socket after sending failure because: ", ecode.message ()); Terminate(); } @@ -621,7 +621,7 @@ if (!ecode) { if (Kill()) return; - LogPrint (eLogInfo, "SOCKS: new I2PTunnel connection"); + LogPrint (eLogInfo, "SOCKS: New I2PTunnel connection"); auto connection = std::make_shared(GetOwner(), m_sock, m_stream); GetOwner()->AddHandler (connection); connection->I2PConnect (m_remaining_data,m_remaining_data_len); @@ -629,7 +629,7 @@ } else { - LogPrint (eLogError, "SOCKS: closing socket after completion reply because: ", ecode.message ()); + LogPrint (eLogError, "SOCKS: Closing socket after completion reply because: ", ecode.message ()); Terminate(); } } @@ -638,7 +638,7 @@ { if (ecode) { - LogPrint (eLogError, "SOCKS: closing socket after sending reply because: ", ecode.message ()); + LogPrint (eLogError, "SOCKS: Closing socket after sending reply because: ", ecode.message ()); Terminate(); } } @@ -652,14 +652,14 @@ } else { - LogPrint (eLogError, "SOCKS: error when creating the stream, check the previous warnings for more info"); + LogPrint (eLogError, "SOCKS: Error when creating the stream, check the previous warnings for more info"); SocksRequestFailed(SOCKS5_HOST_UNREACH); } } void SOCKSHandler::ForwardSOCKS() { - LogPrint(eLogInfo, "SOCKS: forwarding to upstream"); + LogPrint(eLogInfo, "SOCKS: Forwarding to upstream"); EnterState(UPSTREAM_RESOLVE); boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort)); m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(), @@ -668,12 +668,12 @@ void SOCKSHandler::AsyncUpstreamSockRead() { - LogPrint(eLogDebug, "SOCKS: async upstream sock read"); + LogPrint(eLogDebug, "SOCKS: Async upstream sock read"); if (m_upstreamSock) { m_upstreamSock->async_read_some(boost::asio::buffer(m_upstream_response, SOCKS_UPSTREAM_SOCKS4A_REPLY_SIZE), std::bind(&SOCKSHandler::HandleUpstreamSockRecv, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } else { - LogPrint(eLogError, "SOCKS: no upstream socket for read"); + LogPrint(eLogError, "SOCKS: No upstream socket for read"); SocksRequestFailed(SOCKS5_GEN_FAIL); } } @@ -685,7 +685,7 @@ // we are trying to handshake but it failed SocksRequestFailed(SOCKS5_NET_UNREACH); } else { - LogPrint(eLogError, "SOCKS: bad state when reading from upstream: ", (int) m_state); + LogPrint(eLogError, "SOCKS: Bad state when reading from upstream: ", (int) m_state); } return; } @@ -694,7 +694,7 @@ void SOCKSHandler::SocksUpstreamSuccess() { - LogPrint(eLogInfo, "SOCKS: upstream success"); + LogPrint(eLogInfo, "SOCKS: Upstream success"); boost::asio::const_buffers_1 response(nullptr, 0); switch (m_socksv) { @@ -734,7 +734,7 @@ SocksUpstreamSuccess(); } else { // upstream failure - LogPrint(eLogError, "SOCKS: upstream proxy failure: ", (int) resp); + LogPrint(eLogError, "SOCKS: Upstream proxy failure: ", (int) resp); // TODO: runtime error? SocksRequestFailed(SOCKS5_GEN_FAIL); } @@ -744,30 +744,30 @@ } } else { // invalid state - LogPrint(eLogError, "SOCKS: invalid state reading from upstream: ", (int) m_state); + LogPrint(eLogError, "SOCKS: Invalid state reading from upstream: ", (int) m_state); } } void SOCKSHandler::SendUpstreamRequest() { - LogPrint(eLogInfo, "SOCKS: negotiating with upstream proxy"); + LogPrint(eLogInfo, "SOCKS: Negotiating with upstream proxy"); EnterState(UPSTREAM_HANDSHAKE); if (m_upstreamSock) { boost::asio::write(*m_upstreamSock, GenerateUpstreamRequest()); AsyncUpstreamSockRead(); } else { - LogPrint(eLogError, "SOCKS: no upstream socket to send handshake to"); + LogPrint(eLogError, "SOCKS: No upstream socket to send handshake to"); } } void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr) { if (ecode) { - LogPrint(eLogWarning, "SOCKS: could not connect to upstream proxy: ", ecode.message()); + LogPrint(eLogWarning, "SOCKS: Could not connect to upstream proxy: ", ecode.message()); SocksRequestFailed(SOCKS5_NET_UNREACH); return; } - LogPrint(eLogInfo, "SOCKS: connected to upstream proxy"); + LogPrint(eLogInfo, "SOCKS: Connected to upstream proxy"); SendUpstreamRequest(); } @@ -775,11 +775,11 @@ { if (ecode) { // error resolving - LogPrint(eLogWarning, "SOCKS: upstream proxy", m_UpstreamProxyAddress, " not resolved: ", ecode.message()); + LogPrint(eLogWarning, "SOCKS: Upstream proxy", m_UpstreamProxyAddress, " not resolved: ", ecode.message()); SocksRequestFailed(SOCKS5_NET_UNREACH); return; } - LogPrint(eLogInfo, "SOCKS: upstream proxy resolved"); + LogPrint(eLogInfo, "SOCKS: Upstream proxy resolved"); EnterState(UPSTREAM_CONNECT); auto & service = GetOwner()->GetService(); m_upstreamSock = std::make_shared(service); diff -Nru i2pd-2.39.0/libi2pd_wrapper/api.go i2pd-2.43.0/libi2pd_wrapper/api.go --- i2pd-2.39.0/libi2pd_wrapper/api.go 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_wrapper/api.go 2022-08-21 19:40:41.000000000 +0000 @@ -1,15 +1,15 @@ package api /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2021-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree -*/ + */ /* #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++ +#cgo LDFLAGS: -L${SRCDIR}/ -l:../libi2pdwrapper.a -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 -Nru i2pd-2.39.0/libi2pd_wrapper/api.swigcxx i2pd-2.43.0/libi2pd_wrapper/api.swigcxx --- i2pd-2.39.0/libi2pd_wrapper/api.swigcxx 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_wrapper/api.swigcxx 2022-08-21 19:40:41.000000000 +0000 @@ -1,4 +1,4 @@ -// See swig.org for more inteface options, +// See swig.org for more interface options, // e.g. map std::string to Go string %{ diff -Nru i2pd-2.39.0/libi2pd_wrapper/capi.cpp i2pd-2.43.0/libi2pd_wrapper/capi.cpp --- i2pd-2.39.0/libi2pd_wrapper/capi.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_wrapper/capi.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,12 +1,12 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2021-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree */ -#include "api.h" +#include "../libi2pd/api.h" #include "capi.h" #include #include @@ -14,70 +14,14 @@ #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) +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); + return i2p::api::InitI2P(argc, argv, appName); } void C_TerminateI2P () diff -Nru i2pd-2.39.0/libi2pd_wrapper/capi.h i2pd-2.43.0/libi2pd_wrapper/capi.h --- i2pd-2.39.0/libi2pd_wrapper/capi.h 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/libi2pd_wrapper/capi.h 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2021-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -14,7 +14,7 @@ #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_InitI2P (int argc, char** argv, const char * appName); void C_TerminateI2P (); void C_StartI2P (); diff -Nru i2pd-2.39.0/Makefile i2pd-2.43.0/Makefile --- i2pd-2.39.0/Makefile 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/Makefile 2022-08-21 19:40:41.000000000 +0000 @@ -1,8 +1,10 @@ +.DEFAULT_GOAL := all + SYS := $(shell $(CXX) -dumpmachine) ifneq (, $(findstring darwin, $(SYS))) SHARED_SUFFIX = dylib -else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS))) +else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS))) SHARED_SUFFIX = dll else SHARED_SUFFIX = so @@ -27,11 +29,16 @@ # import source files lists 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) -DEBUG := $(or $(DEBUG),yes) +USE_AESNI := $(or $(USE_AESNI),yes) +USE_STATIC := $(or $(USE_STATIC),no) +USE_UPNP := $(or $(USE_UPNP),no) +DEBUG := $(or $(DEBUG),yes) + +# for debugging purposes only, when commit hash needed in trunk builds in i2pd version string +USE_GIT_VERSION := $(or $(USE_GIT_VERSION),no) + +# for MacOS only, waiting for "1", not "yes" +HOMEBREW := $(or $(HOMEBREW),0) ifeq ($(DEBUG),yes) CXX_DEBUG = -g @@ -53,18 +60,19 @@ else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS))) DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp include Makefile.bsd -else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS))) - DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32NetState.cpp +else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS))) + DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp include Makefile.mingw else # not supported $(error Not supported platform) endif -ifeq ($(USE_MESHNET),yes) - NEEDED_CXXFLAGS += -DMESHNET +ifeq ($(USE_GIT_VERSION),yes) + GIT_VERSION := $(shell git describe --tags) + NEEDED_CXXFLAGS += -DGITVER=\"$(GIT_VERSION)\" endif -NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR) +NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR) -DOPENSSL_SUPPRESS_DEPRECATED LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC)) LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC)) diff -Nru i2pd-2.39.0/Makefile.homebrew i2pd-2.43.0/Makefile.homebrew --- i2pd-2.39.0/Makefile.homebrew 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/Makefile.homebrew 2022-08-21 19:40:41.000000000 +0000 @@ -39,13 +39,19 @@ endif install: all - install -d ${PREFIX}/bin ${PREFIX}/etc/i2pd ${PREFIX}/share/doc/i2pd ${PREFIX}/share/i2pd ${PREFIX}/share/man/man1 ${PREFIX}/var/lib/i2pd - install -m 755 ${I2PD} ${PREFIX}/bin/ + install -d ${PREFIX}/bin + install -m 755 ${I2PD} ${PREFIX}/bin + install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd - @cp -R contrib/certificates ${PREFIX}/share/i2pd/ + install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd - @gzip debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1 - @ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/ + install -d ${PREFIX}/share/i2pd + @cp -R contrib/certificates ${PREFIX}/share/i2pd/ + install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1 + @gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1 + install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd + @ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates + @ln -sf ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d @ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf @ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt @ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf diff -Nru i2pd-2.39.0/Makefile.linux i2pd-2.43.0/Makefile.linux --- i2pd-2.39.0/Makefile.linux 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/Makefile.linux 2022-08-21 19:40:41.000000000 +0000 @@ -62,3 +62,21 @@ NEEDED_CXXFLAGS += -D__AES__ -maes endif endif + +install: all + install -d ${PREFIX}/bin + install -m 755 ${I2PD} ${PREFIX}/bin + install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d + install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd + install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd + install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd + install -d ${PREFIX}/share/i2pd + @cp -R contrib/certificates ${PREFIX}/share/i2pd/ + install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1 + @gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1 + install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd + @ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates + @ln -sf ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d + @ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf + @ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt + @ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf diff -Nru i2pd-2.39.0/Makefile.mingw i2pd-2.43.0/Makefile.mingw --- i2pd-2.39.0/Makefile.mingw 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/Makefile.mingw 2022-08-21 19:40:41.000000000 +0000 @@ -3,19 +3,11 @@ WINDRES = windres -CXXFLAGS := $(CXX_DEBUG) -DWIN32_LEAN_AND_MEAN -fPIC -msse +CXXFLAGS := $(CXX_DEBUG) -fPIC -msse INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32 LDFLAGS := ${LD_DEBUG} -static -# detect proper flag for c++11 support by compilers -CXXVER := $(shell $(CXX) -dumpversion) -ifeq ($(shell expr match ${CXXVER} "[4]\.[7-9]\|4\.1[0-9]\|[5-6]"),4) # gcc 4.7 - 6 - NEEDED_CXXFLAGS += -std=c++11 -else ifeq ($(shell expr match ${CXXVER} "[1,7-9]"),1) # gcc >= 7 - NEEDED_CXXFLAGS += -std=c++17 -else # not supported -$(error Compiler too old) -endif +NEEDED_CXXFLAGS += -std=c++17 -DWIN32_LEAN_AND_MEAN # Boost libraries suffix BOOST_SUFFIX = -mt diff -Nru i2pd-2.39.0/tests/Makefile i2pd-2.43.0/tests/Makefile --- i2pd-2.39.0/tests/Makefile 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/tests/Makefile 2022-08-21 19:40:41.000000000 +0000 @@ -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 diff -Nru i2pd-2.39.0/Win32/DaemonWin32.cpp i2pd-2.43.0/Win32/DaemonWin32.cpp --- i2pd-2.39.0/Win32/DaemonWin32.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/Win32/DaemonWin32.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -14,6 +14,7 @@ #include "Log.h" #ifdef _WIN32 +#include "Win32Service.h" #ifdef WIN32_APP #include #include "Win32App.h" @@ -39,6 +40,19 @@ if (!Daemon_Singleton::init(argc, argv)) return false; + + if (isDaemon) + { + LogPrint(eLogDebug, "Daemon: running as service"); + I2PService service((PSTR)SERVICE_NAME); + if (!I2PService::Run(service)) + { + LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError()); + return false; + } + return false; + } + return true; } diff -Nru i2pd-2.39.0/Win32/Resource.rc2 i2pd-2.43.0/Win32/Resource.rc2 --- i2pd-2.39.0/Win32/Resource.rc2 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/Win32/Resource.rc2 2022-08-21 19:40:41.000000000 +0000 @@ -25,7 +25,7 @@ VALUE "FileDescription", "C++ I2P daemon" VALUE "FileVersion", I2PD_VERSION VALUE "InternalName", CODENAME - VALUE "LegalCopyright", "Copyright (C) 2013-2020, The PurpleI2P Project" + VALUE "LegalCopyright", "Copyright (C) 2013-2022, The PurpleI2P Project" VALUE "OriginalFilename", "i2pd" VALUE "ProductName", "Purple I2P" VALUE "ProductVersion", I2P_VERSION diff -Nru i2pd-2.39.0/Win32/Win32App.cpp i2pd-2.43.0/Win32/Win32App.cpp --- i2pd-2.39.0/Win32/Win32App.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/Win32/Win32App.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -31,6 +31,7 @@ #define ID_RELOAD 2006 #define ID_ACCEPT_TRANSIT 2007 #define ID_DECLINE_TRANSIT 2008 +#define ID_DATADIR 2009 #define ID_TRAY_ICON 2050 #define WM_TRAYICON (WM_USER + 1) @@ -49,7 +50,8 @@ { HMENU hPopup = CreatePopupMenu(); InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DATADIR, "Open &datadir"); + InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "&Show app"); InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About..."); InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); if(!i2p::context.AcceptsTunnels()) @@ -303,6 +305,12 @@ SetTimer(hWnd, FRAME_UPDATE_TIMER, 3000, NULL); return 0; } + case ID_DATADIR: + { + std::string datadir(i2p::fs::GetUTF8DataDir()); + ShellExecute(NULL, "explore", datadir.c_str(), NULL, NULL, SW_SHOWNORMAL); + return 0; + } } break; } diff -Nru i2pd-2.39.0/Win32/Win32NetState.cpp i2pd-2.43.0/Win32/Win32NetState.cpp --- i2pd-2.39.0/Win32/Win32NetState.cpp 2021-08-23 14:03:26.000000000 +0000 +++ i2pd-2.43.0/Win32/Win32NetState.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -32,7 +32,7 @@ Result = pNetworkListManager->IsConnectedToInternet(&IsConnect); if (SUCCEEDED(Result)) { i2p::transport::transports.SetOnline (true); - LogPrint(eLogInfo, "NetState: current state: ", IsConnect == VARIANT_TRUE ? "connected" : "disconnected"); + LogPrint(eLogInfo, "NetState: Current state: ", IsConnect == VARIANT_TRUE ? "connected" : "disconnected"); } Result = pNetworkListManager->QueryInterface(IID_IConnectionPointContainer, (void **)&pCPContainer); @@ -79,7 +79,7 @@ } catch (std::exception& ex) { - LogPrint (eLogError, "NetState: received exception: ", ex.what ()); + LogPrint (eLogError, "NetState: Received exception: ", ex.what ()); } } diff -Nru i2pd-2.39.0/Win32/Win32Service.cpp i2pd-2.43.0/Win32/Win32Service.cpp --- i2pd-2.39.0/Win32/Win32Service.cpp 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/Win32/Win32Service.cpp 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,283 @@ +/* +* Copyright (c) 2013-2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include "Win32Service.h" +#include +#include + +#include "Daemon.h" +#include "Log.h" + +I2PService *I2PService::s_service = NULL; + +BOOL I2PService::isService() +{ + BOOL bIsService = FALSE; + HWINSTA hWinStation = GetProcessWindowStation(); + if (hWinStation != NULL) + { + USEROBJECTFLAGS uof = { 0 }; + if (GetUserObjectInformation(hWinStation, UOI_FLAGS, &uof, sizeof(USEROBJECTFLAGS), NULL) && ((uof.dwFlags & WSF_VISIBLE) == 0)) + { + bIsService = TRUE; + } + } + return bIsService; +} + +BOOL I2PService::Run(I2PService &service) +{ + s_service = &service; + SERVICE_TABLE_ENTRY serviceTable[] = + { + { service.m_name, ServiceMain }, + { NULL, NULL } + }; + return StartServiceCtrlDispatcher(serviceTable); +} + +void WINAPI I2PService::ServiceMain(DWORD dwArgc, PSTR *pszArgv) +{ + assert(s_service != NULL); + s_service->m_statusHandle = RegisterServiceCtrlHandler( + s_service->m_name, ServiceCtrlHandler); + if (s_service->m_statusHandle == NULL) + { + throw GetLastError(); + } + s_service->Start(dwArgc, pszArgv); +} + + +void WINAPI I2PService::ServiceCtrlHandler(DWORD dwCtrl) +{ + switch (dwCtrl) + { + case SERVICE_CONTROL_STOP: s_service->Stop(); break; + case SERVICE_CONTROL_PAUSE: s_service->Pause(); break; + case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break; + case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break; + case SERVICE_CONTROL_INTERROGATE: break; + default: break; + } +} + +I2PService::I2PService(PSTR pszServiceName, + BOOL fCanStop, + BOOL fCanShutdown, + BOOL fCanPauseContinue) +{ + m_name = (pszServiceName == NULL) ? (PSTR)"" : pszServiceName; + m_statusHandle = NULL; + m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + m_status.dwCurrentState = SERVICE_START_PENDING; + + DWORD dwControlsAccepted = 0; + if (fCanStop) + dwControlsAccepted |= SERVICE_ACCEPT_STOP; + if (fCanShutdown) + dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN; + if (fCanPauseContinue) + dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE; + + m_status.dwControlsAccepted = dwControlsAccepted; + m_status.dwWin32ExitCode = NO_ERROR; + m_status.dwServiceSpecificExitCode = 0; + m_status.dwCheckPoint = 0; + m_status.dwWaitHint = 0; + m_fStopping = FALSE; + // Create a manual-reset event that is not signaled at first to indicate + // the stopped signal of the service. + m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (m_hStoppedEvent == NULL) + { + throw GetLastError(); + } +} + +I2PService::~I2PService(void) +{ + if (m_hStoppedEvent) + { + CloseHandle(m_hStoppedEvent); + m_hStoppedEvent = NULL; + } +} + +void I2PService::Start(DWORD dwArgc, PSTR *pszArgv) +{ + try + { + SetServiceStatus(SERVICE_START_PENDING); + OnStart(dwArgc, pszArgv); + SetServiceStatus(SERVICE_RUNNING); + } + catch (DWORD dwError) + { + LogPrint(eLogError, "Win32Service: Start error: ", dwError); + SetServiceStatus(SERVICE_STOPPED, dwError); + } + catch (...) + { + LogPrint(eLogError, "Win32Service: failed to start: ", EVENTLOG_ERROR_TYPE); + SetServiceStatus(SERVICE_STOPPED); + } +} + +void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv) +{ + LogPrint(eLogInfo, "Win32Service: in OnStart (", EVENTLOG_INFORMATION_TYPE, ")"); + Daemon.start(); + _worker = new std::thread(std::bind(&I2PService::WorkerThread, this)); +} + +void I2PService::WorkerThread() +{ + while (!m_fStopping) + { + ::Sleep(1000); // Simulate some lengthy operations. + } + // Signal the stopped event. + SetEvent(m_hStoppedEvent); +} + +void I2PService::Stop() +{ + DWORD dwOriginalState = m_status.dwCurrentState; + try + { + SetServiceStatus(SERVICE_STOP_PENDING); + OnStop(); + SetServiceStatus(SERVICE_STOPPED); + } + catch (DWORD dwError) + { + LogPrint(eLogInfo, "Win32Service: Stop error: ", dwError); + SetServiceStatus(dwOriginalState); + } + catch (...) + { + LogPrint(eLogError, "Win32Service: Failed to stop: ", EVENTLOG_ERROR_TYPE); + SetServiceStatus(dwOriginalState); + } +} + +void I2PService::OnStop() +{ + // Log a service stop message to the Application log. + LogPrint(eLogInfo, "Win32Service: in OnStop (", EVENTLOG_INFORMATION_TYPE, ")"); + Daemon.stop(); + m_fStopping = TRUE; + if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0) + { + throw GetLastError(); + } + _worker->join(); + delete _worker; +} + +void I2PService::Pause() +{ + try + { + SetServiceStatus(SERVICE_PAUSE_PENDING); + OnPause(); + SetServiceStatus(SERVICE_PAUSED); + } + catch (DWORD dwError) + { + LogPrint(eLogError, "Win32Service: Pause error: ", dwError); + SetServiceStatus(SERVICE_RUNNING); + } + catch (...) + { + LogPrint(eLogError, "Win32Service: Failed to pause: ", EVENTLOG_ERROR_TYPE); + SetServiceStatus(SERVICE_RUNNING); + } +} + +void I2PService::OnPause() +{ +} + +void I2PService::Continue() +{ + try + { + SetServiceStatus(SERVICE_CONTINUE_PENDING); + OnContinue(); + SetServiceStatus(SERVICE_RUNNING); + } + catch (DWORD dwError) + { + LogPrint(eLogError, "Win32Service: Continue error: ", dwError); + SetServiceStatus(SERVICE_PAUSED); + } + catch (...) + { + LogPrint(eLogError, "Win32Service: Failed to resume: ", EVENTLOG_ERROR_TYPE); + SetServiceStatus(SERVICE_PAUSED); + } +} + +void I2PService::OnContinue() +{ +} + +void I2PService::Shutdown() +{ + try + { + OnShutdown(); + SetServiceStatus(SERVICE_STOPPED); + } + catch (DWORD dwError) + { + LogPrint(eLogError, "Win32Service: Shutdown error: ", dwError); + } + catch (...) + { + LogPrint(eLogError, "Win32Service: Failed to shut down: ", EVENTLOG_ERROR_TYPE); + } +} + +void I2PService::OnShutdown() +{ +} + +void I2PService::SetServiceStatus(DWORD dwCurrentState, + DWORD dwWin32ExitCode, + DWORD dwWaitHint) +{ + static DWORD dwCheckPoint = 1; + m_status.dwCurrentState = dwCurrentState; + m_status.dwWin32ExitCode = dwWin32ExitCode; + m_status.dwWaitHint = dwWaitHint; + m_status.dwCheckPoint = + ((dwCurrentState == SERVICE_RUNNING) || + (dwCurrentState == SERVICE_STOPPED)) ? + 0 : dwCheckPoint++; + + ::SetServiceStatus(m_statusHandle, &m_status); +} + +//***************************************************************************** + +void FreeHandles(SC_HANDLE schSCManager, SC_HANDLE schService) +{ + if (schSCManager) + { + CloseServiceHandle(schSCManager); + schSCManager = NULL; + } + if (schService) + { + CloseServiceHandle(schService); + schService = NULL; + } +} diff -Nru i2pd-2.39.0/Win32/Win32Service.h i2pd-2.43.0/Win32/Win32Service.h --- i2pd-2.39.0/Win32/Win32Service.h 1970-01-01 00:00:00.000000000 +0000 +++ i2pd-2.43.0/Win32/Win32Service.h 2022-08-21 19:40:41.000000000 +0000 @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2013-2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef WIN_32_SERVICE_H__ +#define WIN_32_SERVICE_H__ + +#include +#include + +#define SERVICE_NAME "i2pdService" + +class I2PService +{ + public: + + I2PService(PSTR pszServiceName, + BOOL fCanStop = TRUE, + BOOL fCanShutdown = TRUE, + BOOL fCanPauseContinue = FALSE); + + virtual ~I2PService(void); + + static BOOL isService(); + static BOOL Run(I2PService &service); + void Stop(); + + protected: + + virtual void OnStart(DWORD dwArgc, PSTR *pszArgv); + virtual void OnStop(); + virtual void OnPause(); + virtual void OnContinue(); + virtual void OnShutdown(); + void SetServiceStatus(DWORD dwCurrentState, + DWORD dwWin32ExitCode = NO_ERROR, + DWORD dwWaitHint = 0); + + private: + + static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv); + static void WINAPI ServiceCtrlHandler(DWORD dwCtrl); + void WorkerThread(); + void Start(DWORD dwArgc, PSTR *pszArgv); + void Pause(); + void Continue(); + void Shutdown(); + static I2PService* s_service; + PSTR m_name; + SERVICE_STATUS m_status; + SERVICE_STATUS_HANDLE m_statusHandle; + + BOOL m_fStopping; + HANDLE m_hStoppedEvent; + + std::thread* _worker; +}; + +#endif // WIN_32_SERVICE_H__
" + << tr("Streams") + << "
StreamID" // Stream closing button column + << "DestinationSentReceivedOutInBufRTTWindowStatus