diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..488400a3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +/build/build_mingw.cmd eol=crlf \ No newline at end of file diff --git a/.github/workflows/build-freebsd.yml b/.github/workflows/build-freebsd.yml index c6e0addf..b11569b7 100644 --- a/.github/workflows/build-freebsd.yml +++ b/.github/workflows/build-freebsd.yml @@ -4,15 +4,16 @@ on: [push, pull_request] jobs: build: - runs-on: macos-latest + runs-on: macos-10.15 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.1.5 with: usesh: true + mem: 2048 prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc run: | cd build diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 83b68d71..e7752d55 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -8,14 +8,15 @@ defaults: 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 @@ jobs: 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 --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 22ba60bf..affa0b8b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,7 +49,7 @@ jobs: 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 @@ jobs: 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 --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1c01d373..1ac5b552 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,6 +1,11 @@ name: Build containers -on: [push] +on: + push: + branches: + - openssl + tags: + - '*' jobs: docker: @@ -58,6 +63,8 @@ jobs: 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 --git a/.gitignore b/.gitignore index bafe2e18..75bd6abb 100644 --- a/.gitignore +++ b/.gitignore @@ -260,6 +260,7 @@ docs/generated build/Makefile # debian stuff +debian/i2pd.1.gz .pc/ # qt diff --git a/ChangeLog b/ChangeLog index 2128f5d2..b4e815e4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,95 @@ # for this file format description, # see https://github.com/olivierlacan/keep-a-changelog +## [2.42.1] - 2022-05-24 +### Fixed +- Incorrect jump link in HTTP Proxy + +## [2.42.0] - 2022-05-22 +### Added +- Preliminary SSU2 implementation +- Tunnel length variance +- Localization to French +- Daily cleanup of obsolete peer profiles +- Ordered jump services list in HTTP proxy +- Win32 service +- Show port for local non-published SSU addresses in web console +### Changed +- Maximum RouterInfo length increased to 3K +- Skip unknown addresses in RouterInfo +- Don't pick own router for peer test +- Reseeds list +- Internal numeric id for families +- Use ipv6 preference only when netinet headers not used +- Close stream if delete requested +- Remove version from title in web console +- Drop MESHNET build option +- Set data path before initialization +- Don't show registration block in web console if token is not provided +### Fixed +- Encrypted LeaseSet for EdDSA signature +- Clients tunnels are not built if clock is not synced on start +- Incorrect processing of i2cp.dontPublishLeaseSet param +- UDP tunnels reload +- Build for LibreSSL 3.5.2 +- Race condition in short tunnel build message +- Race condition in local RouterInfo buffer allocation + +## [2.41.0] - 2022-02-20 +### 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 +107,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 +135,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 +171,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 +198,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 +246,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 +257,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 +274,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 +285,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 +297,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 --git a/Makefile b/Makefile index d7765af7..1fca8ddc 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ DAEMON_SRC_DIR := daemon # 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 linux, $(SYS))$(findstring gnu, $(SYS))) 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 --git a/Makefile.homebrew b/Makefile.homebrew index c1992296..88b2a9e2 100644 --- a/Makefile.homebrew +++ b/Makefile.homebrew @@ -39,13 +39,19 @@ ifeq ($(USE_AESNI),yes) 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 --git a/Makefile.linux b/Makefile.linux index da5f68b4..28334082 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -62,3 +62,21 @@ ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64 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 --git a/Makefile.mingw b/Makefile.mingw index e31d895e..a1a861e6 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -3,19 +3,11 @@ USE_WIN32_APP := yes 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 --git a/Win32/DaemonWin32.cpp b/Win32/DaemonWin32.cpp index 6bb953e9..0badf802 100644 --- a/Win32/DaemonWin32.cpp +++ b/Win32/DaemonWin32.cpp @@ -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 @@ namespace util 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 --git a/Win32/Resource.rc2 b/Win32/Resource.rc2 index 9eecbc1f..873d6402 100644 --- a/Win32/Resource.rc2 +++ b/Win32/Resource.rc2 @@ -25,7 +25,7 @@ BEGIN 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 --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp index 7104ec7f..377e1076 100644 --- a/Win32/Win32App.cpp +++ b/Win32/Win32App.cpp @@ -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 @@ namespace win32 { 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 @@ namespace win32 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 --git a/Win32/Win32NetState.cpp b/Win32/Win32NetState.cpp index dd4dd08c..5c56711d 100644 --- a/Win32/Win32NetState.cpp +++ b/Win32/Win32NetState.cpp @@ -32,7 +32,7 @@ void SubscribeToEvents() 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 @@ void UnSubscribeFromEvents() } catch (std::exception& ex) { - LogPrint (eLogError, "NetState: received exception: ", ex.what ()); + LogPrint (eLogError, "NetState: Received exception: ", ex.what ()); } } diff --git a/Win32/Win32Service.cpp b/Win32/Win32Service.cpp new file mode 100644 index 00000000..d4ba0d76 --- /dev/null +++ b/Win32/Win32Service.cpp @@ -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 --git a/Win32/Win32Service.h b/Win32/Win32Service.h new file mode 100644 index 00000000..5cedbed6 --- /dev/null +++ b/Win32/Win32Service.h @@ -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__ diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 2cd598f4..d35bd526 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -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_LIBRARY "Build library" ON) option(WITH_BINARY "Build binary" ON) option(WITH_STATIC "Static build" OFF) option(WITH_UPNP "Include support for UPnP client" OFF) -option(WITH_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 @@ set(DAEMON_SRC "${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 @@ endif() # 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 @@ else() add_definitions(-DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_REGEX_DYN_LINK) endif() -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 @@ if(NOT DEFINED OPENSSL_INCLUDE_DIR) 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 @@ endif() # 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 " LIBRARY : ${WITH_LIBRARY}") 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 @@ if(WITH_BINARY) 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 --git a/build/build_mingw.cmd b/build/build_mingw.cmd index e8f1378b..e8bb2b84 100644 --- a/build/build_mingw.cmd +++ b/build/build_mingw.cmd @@ -2,26 +2,25 @@ setlocal enableextensions enabledelayedexpansion title Building i2pd -REM Copyright (c) 2013-2020, The PurpleI2P Project +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 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 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 "WD=C:\msys64\usr\bin\" +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 -REM set MSYSTEM=MSYS 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" +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% @@ -34,38 +33,73 @@ 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 ( +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 (usable in default notepad) -%xSH% "unix2dos contrib/i2pd.conf contrib/tunnels.conf contrib/tunnels.d/*" >> build\build.log 2>&1 +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=MINGW64 +set MSYSTEM=UCRT64 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 +REM build for Windows XP +if exist C:\msys64-xp\ ( 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 +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... @@ -74,13 +108,42 @@ 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 +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 -%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 +set MSYSTEM=MINGW32 +set bitness=32 +set "WD=C:\msys64-xp\usr\bin\" +set "xSH=%WD%bash -lc" -:EOF \ No newline at end of file +%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 --git a/build/cmake_modules/GetGitRevisionDescription.cmake b/build/cmake_modules/GetGitRevisionDescription.cmake new file mode 100644 index 00000000..4fbd90db --- /dev/null +++ b/build/cmake_modules/GetGitRevisionDescription.cmake @@ -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 --git a/build/cmake_modules/GetGitRevisionDescription.cmake.in b/build/cmake_modules/GetGitRevisionDescription.cmake.in new file mode 100644 index 00000000..116efc4e --- /dev/null +++ b/build/cmake_modules/GetGitRevisionDescription.cmake.in @@ -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 --git a/build/cmake_modules/TargetArch.cmake b/build/cmake_modules/TargetArch.cmake index 3761e4df..d9ebe7c9 100644 --- a/build/cmake_modules/TargetArch.cmake +++ b/build/cmake_modules/TargetArch.cmake @@ -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 @@ set(archdetect_c_code " || (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 @@ set(archdetect_c_code " #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 @@ set(archdetect_c_code " #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 @@ function(target_architecture output_var) 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 @@ function(target_architecture output_var) 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 @@ function(target_architecture output_var) 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 --git a/build/win_installer.iss b/build/win_installer.iss index 97cd4408..cfeff812 100644 --- a/build/win_installer.iss +++ b/build/win_installer.iss @@ -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 @@ ExtraDiskSpaceRequired=15 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\subscriptions.txt; DestDir: {userappdata}\i2pd; Flags: onlyif 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 --git a/contrib/android_binary_only/.gitignore b/contrib/android_binary_only/.gitignore deleted file mode 100644 index 6e42311a..00000000 --- a/contrib/android_binary_only/.gitignore +++ /dev/null @@ -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 --git a/contrib/android_binary_only/jni/Android.mk b/contrib/android_binary_only/jni/Android.mk deleted file mode 100644 index a59e32e5..00000000 --- a/contrib/android_binary_only/jni/Android.mk +++ /dev/null @@ -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 --git a/contrib/android_binary_only/jni/Application.mk b/contrib/android_binary_only/jni/Application.mk deleted file mode 100644 index 5d54645b..00000000 --- a/contrib/android_binary_only/jni/Application.mk +++ /dev/null @@ -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 --git a/contrib/android_binary_pack/.gitignore b/contrib/android_binary_pack/.gitignore deleted file mode 100644 index bad5f807..00000000 --- a/contrib/android_binary_pack/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -archive -i2pd_*_android_binary.zip diff --git a/contrib/android_binary_pack/build-archive b/contrib/android_binary_pack/build-archive deleted file mode 100755 index 30f5b48d..00000000 --- a/contrib/android_binary_pack/build-archive +++ /dev/null @@ -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 --git a/contrib/android_binary_pack/i2pd b/contrib/android_binary_pack/i2pd deleted file mode 100755 index c31cb4ad..00000000 --- a/contrib/android_binary_pack/i2pd +++ /dev/null @@ -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 --git a/contrib/certificates/family/stormycloud.crt b/contrib/certificates/family/stormycloud.crt new file mode 100644 index 00000000..4ec4765a --- /dev/null +++ b/contrib/certificates/family/stormycloud.crt @@ -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 --git a/contrib/certificates/reseed/echelon3_at_mail.i2p.crt b/contrib/certificates/reseed/echelon3_at_mail.i2p.crt new file mode 100644 index 00000000..7560a01e --- /dev/null +++ b/contrib/certificates/reseed/echelon3_at_mail.i2p.crt @@ -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 --git a/contrib/certificates/reseed/echelon_at_mail.i2p.crt b/contrib/certificates/reseed/echelon_at_mail.i2p.crt deleted file mode 100644 index ad10e3ac..00000000 --- a/contrib/certificates/reseed/echelon_at_mail.i2p.crt +++ /dev/null @@ -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 --git a/contrib/certificates/reseed/hiduser0_at_mail.i2p.crt b/contrib/certificates/reseed/hiduser0_at_mail.i2p.crt new file mode 100644 index 00000000..a332805a --- /dev/null +++ b/contrib/certificates/reseed/hiduser0_at_mail.i2p.crt @@ -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 --git a/contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt b/contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt new file mode 100644 index 00000000..3d1c4526 --- /dev/null +++ b/contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt @@ -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 --git a/contrib/certificates/reseed/rambler_at_mail.i2p.crt b/contrib/certificates/reseed/rambler_at_mail.i2p.crt new file mode 100644 index 00000000..40a5c9fe --- /dev/null +++ b/contrib/certificates/reseed/rambler_at_mail.i2p.crt @@ -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 --git a/contrib/i2pd.conf b/contrib/i2pd.conf index dbf74002..43cf7ddd 100644 --- a/contrib/i2pd.conf +++ b/contrib/i2pd.conf @@ -81,7 +81,9 @@ ipv6 = 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 @@ -108,7 +110,8 @@ port = 7070 # user = i2pd # pass = changeme ## Select webconsole language -## Currently supported english (default), afrikaans, russian, turkmen, ukrainian and uzbek languages +## Currently supported english (default), afrikaans, armenian, french, german, +## russian, turkmen, ukrainian and uzbek languages # lang = english [httpproxy] diff --git a/contrib/rpm/i2pd-git.spec b/contrib/rpm/i2pd-git.spec index b1354173..dc6aae70 100644 --- a/contrib/rpm/i2pd-git.spec +++ b/contrib/rpm/i2pd-git.spec @@ -1,7 +1,7 @@ %define git_hash %(git rev-parse HEAD | cut -c -7) Name: i2pd-git -Version: 2.39.0 +Version: 2.42.1 Release: git%{git_hash}%{?dist} Summary: I2P router written in C++ Conflicts: i2pd @@ -32,7 +32,7 @@ Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd C++ implementation of I2P. %prep -%setup -q +%setup -q -n i2pd-openssl %build @@ -57,8 +57,14 @@ cd build %endif -%if 0%{?fedora} >= 35 +%if 0%{?rhel} == 9 pushd redhat-linux-build +%endif + +%if 0%{?fedora} >= 35 +%if 0%{?fedora} < 37 +pushd redhat-linux-build +%endif %else %if 0%{?fedora} >= 33 pushd %{_target_platform} @@ -71,10 +77,16 @@ pushd build make %{?_smp_mflags} -%if 0%{?fedora} >= 33 +%if 0%{?rhel} == 9 popd %endif +%if 0%{?fedora} >= 33 +%if 0%{?fedora} < 37 +popd +%endif +%endif + %if 0%{?mageia} > 7 popd %endif @@ -82,8 +94,14 @@ popd %install pushd build -%if 0%{?fedora} >= 35 +%if 0%{?rhel} == 9 pushd redhat-linux-build +%endif + +%if 0%{?fedora} >= 35 +%if 0%{?fedora} < 37 +pushd redhat-linux-build +%endif %else %if 0%{?fedora} >= 33 pushd %{_target_platform} @@ -99,14 +117,14 @@ chrpath -d i2pd %{__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,7 +164,20 @@ getent passwd i2pd >/dev/null || \ %changelog -* Mon Aug 24 2021 r4sas - 2.39.0-2 +* 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 diff --git a/contrib/rpm/i2pd.spec b/contrib/rpm/i2pd.spec index ef8e32f2..98c804c6 100644 --- a/contrib/rpm/i2pd.spec +++ b/contrib/rpm/i2pd.spec @@ -1,6 +1,6 @@ Name: i2pd -Version: 2.39.0 -Release: 2%{?dist} +Version: 2.42.1 +Release: 1%{?dist} Summary: I2P router written in C++ Conflicts: i2pd-git @@ -54,8 +54,14 @@ cd build %endif %endif -%if 0%{?fedora} >= 35 +%if 0%{?rhel} == 9 pushd redhat-linux-build +%endif + +%if 0%{?fedora} >= 35 +%if 0%{?fedora} < 37 +pushd redhat-linux-build +%endif %else %if 0%{?fedora} >= 33 pushd %{_target_platform} @@ -68,10 +74,16 @@ pushd build make %{?_smp_mflags} -%if 0%{?fedora} >= 33 +%if 0%{?rhel} == 9 popd %endif +%if 0%{?fedora} >= 33 +%if 0%{?fedora} < 37 +popd +%endif +%endif + %if 0%{?mageia} > 7 popd %endif @@ -79,8 +91,14 @@ popd %install pushd build -%if 0%{?fedora} >= 35 +%if 0%{?rhel} == 9 pushd redhat-linux-build +%endif + +%if 0%{?fedora} >= 35 +%if 0%{?fedora} < 37 +pushd redhat-linux-build +%endif %else %if 0%{?fedora} >= 33 pushd %{_target_platform} @@ -143,7 +161,19 @@ getent passwd i2pd >/dev/null || \ %changelog -* Mon Aug 24 2021 r4sas - 2.39.0-2 +* 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 diff --git a/contrib/webconsole/style.css b/contrib/webconsole/style.css index 047839a6..5d63d1a7 100644 --- a/contrib/webconsole/style.css +++ b/contrib/webconsole/style.css @@ -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 @@ a.button { display: block; float: left; overflow: hidden; + padding: 4px; max-width: 12em; white-space: nowrap; text-overflow: ellipsis; @@ -63,8 +94,9 @@ a.button { .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 @@ a.button { caption { font-size: 1.5em; text-align: center; - color: #894C84; + color: var(--main-link-color); } table { @@ -105,6 +137,8 @@ table.services { } textarea { + background-color: var(--main-bg-color); + color: var(--main-text-color); word-break: break-all; } @@ -133,24 +167,45 @@ textarea { 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 @@ textarea { } a, .slide label { - /* margin-right: 10px; */ display: block; - /* font-size: 18px; */ } .header { @@ -193,13 +246,12 @@ textarea { } 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 @@ textarea { 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 { 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 --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index 445c4dfd..c6e6465a 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -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 @@ namespace util 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 @@ namespace util 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 @@ namespace util 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,26 +128,26 @@ namespace util 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); @@ -151,11 +161,7 @@ namespace util 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 +210,7 @@ namespace util 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 +250,18 @@ namespace util if (!ipv4 && !ipv6) i2p::context.SetStatus (eRouterStatusMesh); } + bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); + if (ssu2) + { + bool published; i2p::config::GetOption("ssu2.published", published); + if (published) + { + uint16_t ssu2port; i2p::config::GetOption("ssu2.port", ssu2port); + i2p::context.PublishSSU2Address (ssu2port, true, ipv4, ipv6); // publish + } + else + i2p::context.PublishSSU2Address (0, false, ipv4, ipv6); // unpublish + } bool transit; i2p::config::GetOption("notransit", transit); i2p::context.SetAcceptsTunnels (!transit); @@ -252,7 +270,7 @@ namespace util 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 +285,7 @@ namespace util 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 +293,18 @@ namespace util 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 +319,12 @@ namespace util 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 +354,18 @@ namespace util 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 +378,7 @@ namespace util 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 +395,20 @@ namespace util } 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); + i2p::transport::transports.Start(ntcp2, ssu, ssu2); if (i2p::transport::transports.IsBoundSSU() || 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 +419,7 @@ namespace util 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 +427,16 @@ namespace util } 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 +444,7 @@ namespace util 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 +452,7 @@ namespace util } 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 +461,10 @@ namespace util 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 +479,18 @@ namespace util 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 --git a/daemon/Daemon.h b/daemon/Daemon.h index 836c2a8e..26d4a047 100644 --- a/daemon/Daemon.h +++ b/daemon/Daemon.h @@ -20,27 +20,33 @@ namespace util 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 --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 1e002b1b..a325ad4b 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -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 * @@ -160,7 +160,7 @@ namespace http { 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 (); @@ -182,7 +182,7 @@ namespace http { " \r\n" " \r\n" " \r\n" - " Purple I2P " VERSION " Webconsole\r\n"; + " Purple I2P Webconsole\r\n"; GetStyles(s); s << "\r\n" @@ -196,8 +196,10 @@ namespace http { 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 ()) @@ -295,10 +297,10 @@ namespace http { 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"; @@ -307,41 +309,42 @@ namespace http { 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"); } - s << "\r\n\r\n"; + 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"; } 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 << "
" << address->host.to_string() << ":" << address->port << "" << tr("supported"); + if (address->port) + s << " :" << address->port; + s << "
\r\n"; } s << "
\r\n
\r\n"; - if(outputFormat == OutputFormatEnum::forQtUi) { + if (outputFormat == OutputFormatEnum::forQtUi) { s << "
"; } s << "" << tr("Routers") << ": " << i2p::data::netdb.GetNumRouters () << " "; @@ -355,7 +358,7 @@ namespace http { 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; @@ -416,7 +419,7 @@ namespace http { 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 (); @@ -430,7 +433,7 @@ namespace http { "\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"; @@ -446,8 +449,18 @@ namespace http { 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"; @@ -456,8 +469,18 @@ namespace http { 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"; @@ -630,8 +653,17 @@ namespace http { 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"; @@ -640,8 +672,18 @@ namespace http { 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"; @@ -713,7 +755,7 @@ namespace http { 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 ()) @@ -736,7 +778,7 @@ namespace http { } 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 ) @@ -749,6 +791,8 @@ namespace http { << it.second->GetRemoteEndpoint ().address ().to_string (); 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++; } @@ -760,6 +804,8 @@ namespace http { << "[" << it.second->GetRemoteEndpoint ().address ().to_string () << "]"; 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++; } @@ -786,7 +832,7 @@ namespace http { { auto sessions = ntcp2Server->GetNTCP2Sessions (); if (!sessions.empty ()) - ShowNTCPTransports (s, sessions, "NTCP2"); + ShowTransportSessions (s, sessions, "NTCP2"); } auto ssuServer = i2p::transport::transports.GetSSUServer (); if (ssuServer) @@ -828,6 +874,13 @@ namespace http { 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) @@ -840,7 +893,7 @@ namespace http { return; } - if(sam->GetSessions ().size ()) + if (sam->GetSessions ().size ()) { s << "" << tr("SAM sessions") << ":
\r\n
\r\n"; for (auto& it: sam->GetSessions ()) @@ -1039,7 +1092,7 @@ namespace http { 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; } @@ -1049,7 +1102,7 @@ namespace http { std::string content; HTTPRes res; - LogPrint(eLogDebug, "HTTPServer: request: ", req.uri); + LogPrint(eLogDebug, "HTTPServer: Request: ", req.uri); if (needAuth && !CheckAuth(req)) { res.code = 401; @@ -1057,6 +1110,7 @@ namespace http { SendReply(res, content); return; } + bool strictheaders; i2p::config::GetOption("http.strictheaders", strictheaders); if (strictheaders) @@ -1079,6 +1133,7 @@ namespace http { return; } } + // HTML head start ShowPageHead (s); if (req.uri.find("page=") != std::string::npos) { @@ -1199,7 +1254,7 @@ namespace http { 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 (); @@ -1231,7 +1286,7 @@ namespace http { { 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"; @@ -1377,7 +1432,7 @@ namespace http { 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; @@ -1391,7 +1446,13 @@ namespace http { 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) { @@ -1412,7 +1473,7 @@ namespace http { } catch (std::exception& ex) { - LogPrint (eLogError, "HTTPServer: runtime exception: ", ex.what ()); + LogPrint (eLogError, "HTTPServer: Runtime exception: ", ex.what ()); } } } @@ -1427,15 +1488,13 @@ namespace http { 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 --git a/daemon/HTTPServer.h b/daemon/HTTPServer.h index 8e1520b8..8646253f 100644 --- a/daemon/HTTPServer.h +++ b/daemon/HTTPServer.h @@ -98,8 +98,8 @@ namespace http 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 --git a/daemon/HTTPServerResources.h b/daemon/HTTPServerResources.h index 876948e8..0acbe8d1 100644 --- a/daemon/HTTPServerResources.h +++ b/daemon/HTTPServerResources.h @@ -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 * @@ -34,50 +34,58 @@ namespace http // bundled style sheet const std::string internalCSS = "\r\n"; // for external style sheet diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index 73dde618..46a219bf 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -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 @@ namespace client 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 @@ namespace client // 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client { if (ecode) { - LogPrint (eLogError, "I2PControl: read error: ", ecode.message ()); + LogPrint (eLogError, "I2PControl: Read error: ", ecode.message ()); return; } else @@ -225,7 +232,7 @@ namespace client } 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 @@ namespace client } 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 @@ namespace client } 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 @@ namespace client } catch (...) { - LogPrint (eLogError, "I2PControl: handle request unknown exception"); + LogPrint (eLogError, "I2PControl: Handle request unknown exception"); } } } @@ -278,11 +285,16 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client // 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 @@ namespace client 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 --git a/daemon/I2PControl.h b/daemon/I2PControl.h index d731c24e..32b8933c 100644 --- a/daemon/I2PControl.h +++ b/daemon/I2PControl.h @@ -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 @@ namespace client 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 --git a/daemon/UPnP.cpp b/daemon/UPnP.cpp index 25e24bb5..6ea4dc24 100644 --- a/daemon/UPnP.cpp +++ b/daemon/UPnP.cpp @@ -29,7 +29,7 @@ namespace transport { if (m_IsRunning) { - LogPrint(eLogInfo, "UPnP: stopping"); + LogPrint(eLogInfo, "UPnP: Stopping"); m_IsRunning = false; m_Timer.cancel (); m_Service.stop (); @@ -46,7 +46,7 @@ namespace transport 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 @@ namespace transport } catch (std::exception& ex) { - LogPrint (eLogError, "UPnP: runtime exception: ", ex.what ()); + LogPrint (eLogError, "UPnP: Runtime exception: ", ex.what ()); PortMapping (); } } @@ -93,7 +93,7 @@ namespace transport #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 @@ namespace transport 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 @@ namespace transport 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 @@ namespace transport 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 @@ namespace transport #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 @@ namespace transport } 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 --git a/daemon/UPnP.h b/daemon/UPnP.h index e8220e24..59f3b785 100644 --- a/daemon/UPnP.h +++ b/daemon/UPnP.h @@ -51,7 +51,7 @@ namespace transport 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 @@ namespace transport } } -#else // USE_UPNP +#else // USE_UPNP namespace i2p { namespace transport { /* class stub */ diff --git a/daemon/UnixDaemon.cpp b/daemon/UnixDaemon.cpp index ffc5f1c0..e6bad5a0 100644 --- a/daemon/UnixDaemon.cpp +++ b/daemon/UnixDaemon.cpp @@ -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 @@ void handle_signal(int sig) case SIGPIPE: LogPrint(eLogInfo, "SIGPIPE received"); break; + case SIGTSTP: + LogPrint(eLogInfo, "Daemon: Got SIGTSTP, disconnecting from network..."); + i2p::transport::transports.SetOnline(false); + break; + case SIGCONT: + LogPrint(eLogInfo, "Daemon: Got SIGCONT, restoring connection to network..."); + i2p::transport::transports.SetOnline(true); + break; } } @@ -72,7 +81,8 @@ namespace i2p 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 @@ namespace i2p 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 @@ namespace i2p 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 @@ namespace i2p 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 @@ namespace i2p 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 @@ namespace i2p 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 @@ namespace i2p sigaction(SIGTERM, &sa, 0); sigaction(SIGINT, &sa, 0); sigaction(SIGPIPE, &sa, 0); + if (handleTSTP) + { + sigaction(SIGTSTP, &sa, 0); + sigaction(SIGCONT, &sa, 0); + } return Daemon_Singleton::start(); } diff --git a/debian/changelog b/debian/changelog index c675f263..3b4d88f6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,28 @@ +i2pd (2.42.1-1) unstable; urgency=medium + + * updated to version 2.42.1/0.9.54 + * remove -O3 optimization flag + + -- r4sas Tue, 24 May 2022 12:00:00 +0000 + +i2pd (2.42.0-1) unstable; urgency=medium + + * updated to version 2.42.0/0.9.54 + + -- orignal Sun, 22 May 2022 16:00:00 +0000 + +i2pd (2.41.0-1) unstable; urgency=medium + + * updated to version 2.41.0/0.9.53 + + -- r4sas Sun, 20 Feb 2022 13:00:00 +0000 + +i2pd (2.40.0-1) unstable; urgency=medium + + * updated to version 2.40.0/0.9.52 + + -- orignal Mon, 29 Nov 2021 16:00:00 +0000 + i2pd (2.39.0-1) unstable; urgency=medium * updated to version 2.39.0/0.9.51 diff --git a/debian/patches/02-upnp.patch b/debian/patches/02-upnp.patch index 75ea2bfa..bec8f2b0 100644 --- a/debian/patches/02-upnp.patch +++ b/debian/patches/02-upnp.patch @@ -2,16 +2,16 @@ Description: Enable UPnP usage in package Author: r4sas Reviewed-By: r4sas -Last-Update: 2021-01-16 +Last-Update: 2022-03-23 --- i2pd.orig/Makefile +++ i2pd/Makefile -@@ -21,7 +21,7 @@ include filelist.mk - USE_AESNI := $(or $(USE_AESNI),yes) - USE_STATIC := $(or $(USE_STATIC),no) - USE_MESHNET := $(or $(USE_MESHNET),no) --USE_UPNP := $(or $(USE_UPNP),no) -+USE_UPNP := $(or $(USE_UPNP),yes) - DEBUG := $(or $(DEBUG),yes) +@@ -31,7 +31,7 @@ include filelist.mk - ifeq ($(DEBUG),yes) + USE_AESNI := $(or $(USE_AESNI),yes) + USE_STATIC := $(or $(USE_STATIC),no) +-USE_UPNP := $(or $(USE_UPNP),no) ++USE_UPNP := $(or $(USE_UPNP),yes) + DEBUG := $(or $(DEBUG),yes) + + # for debugging purposes only, when commit hash needed in trunk builds in i2pd version string diff --git a/debian/rules b/debian/rules index 24a44f55..11791d9b 100755 --- a/debian/rules +++ b/debian/rules @@ -1,16 +1,13 @@ #!/usr/bin/make -f #export DH_VERBOSE=1 - - export DEB_BUILD_MAINT_OPTIONS = hardening=+all - include /usr/share/dpkg/architecture.mk -export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic -O3 - +export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic export DEB_LDFLAGS_MAINT_APPEND = - %: dh $@ --parallel + +override_dh_auto_install: diff --git a/i18n/Armenian.cpp b/i18n/Armenian.cpp new file mode 100644 index 00000000..586e7579 --- /dev/null +++ b/i18n/Armenian.cpp @@ -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 --git a/i18n/French.cpp b/i18n/French.cpp new file mode 100644 index 00000000..d41d215b --- /dev/null +++ b/i18n/French.cpp @@ -0,0 +1,102 @@ +/* +* Copyright (c) 2022, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#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", "echoué"}, + {"expiring", "expiré"}, + {"established", "établi"}, + {"unknown", "inconnu"}, + {"exploratory", "exploratoire"}, + {"i2pd webconsole", "Console web i2pd"}, + {"Main page", "Page principale"}, + {"Router commands", "Commandes du routeur"}, + {"Local Destinations", "Destinations locales"}, + {"Tunnels", "Tunnels"}, + {"Transit Tunnels", "Tunnels transitoires"}, + {"I2P tunnels", "Tunnels I2P"}, + {"SAM sessions", "Sessions SAM"}, + {"ERROR", "ERREUR"}, + {"OK", "OK"}, + {"Firewalled", "Derrière un pare-feu"}, + {"Error", "Erreur"}, + {"Offline", "Hors ligne"}, + {"Uptime", "Temps de fonctionnement"}, + {"Network status", "État du réseau"}, + {"Network status v6", "État du réseau v6"}, + {"Stopping in", "Arrêt dans"}, + {"Family", "Famille"}, + {"Tunnel creation success rate", "Taux de succès de création de tunnels"}, + {"Received", "Reçu"}, + {"KiB/s", "kio/s"}, + {"Sent", "Envoyé"}, + {"Transit", "Transit"}, + {"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour regarder."}, + {"Router Ident", "Identifiant du routeur"}, + {"Router Family", "Famille du routeur"}, + {"Version", "Version"}, + {"Our external address", "Notre adresse externe"}, + {"Client Tunnels", "Tunnels clients"}, + {"Services", "Services"}, + {"Enabled", "Activé"}, + {"Disabled", "Désactivé"}, + {"Encrypted B33 address", "Adresse B33 chiffrée"}, + {"Domain", "Domaine"}, + {"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"}, + {"ms", "ms"}, + {"Outbound tunnels", "Tunnels sortants"}, + {"Destination", "Destination"}, + {"Local Destination", "Destination locale"}, + {"", ""}, + }; + + 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 --git a/i18n/German.cpp b/i18n/German.cpp new file mode 100644 index 00000000..fcef4cff --- /dev/null +++ b/i18n/German.cpp @@ -0,0 +1,215 @@ +/* +* 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 + { + {"KiB", "KiB"}, + {"MiB", "MiB"}, + {"GiB", "GiB"}, + {"building", "In Bau"}, + {"failed", "fehlgeschlagen"}, + {"expiring", "läuft ab in"}, + {"established", "hergestellt"}, + {"unknown", "Unbekannt"}, + {"exploratory", "erforschende"}, + {"i2pd webconsole", "i2pd Webkonsole"}, + {"Main page", "Startseite"}, + {"Router commands", "Router Befehle"}, + {"Local Destinations", "Lokale Destination"}, + {"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 eine 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", "Klienttunnel"}, + {"Services", "Services"}, + {"Enabled", "Aktiviert"}, + {"Disabled", "Deaktiviert"}, + {"Encrypted B33 address", "Verschlüsselte B33 Adresse"}, + {"Address registration line", "Adresseregistrierungszeile"}, + {"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", "Destination"}, + {"Amount", "Anzahl"}, + {"Incoming Tags", "Eingehende Tags"}, + {"Tags sessions", "Tags Sitzungen"}, + {"Status", "Status"}, + {"Local Destination", "Lokale Destination"}, + {"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", "Warteschlangengröße"}, + {"Run peer test", "Peer-Test ausführen"}, + {"Decline transit tunnels", "Transittunnel ablehnen"}, + {"Accept transit tunnels", "Transittunnel akzeptieren"}, + {"Cancel graceful shutdown", "Beende das kontrollierte herunterfahren"}, + {"Start graceful shutdown", "Starte das kontrollierte Herunterfahren"}, + {"Force shutdown", "Herunterfahren erzwingen"}, + {"Reload external CSS styles", "Lade externe CSS-Styles 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", "Verändern"}, + {"Change language", "Sprache ändern"}, + {"no transit tunnels currently built", "derzeit keine Transittunnel aufgebaut"}, + {"SAM disabled", "SAM deaktiviert"}, + {"no sessions currently running", "Derzeit keine laufenden Sitzungen"}, + {"SAM session not found", "SAM Sitzung nicht gefunden"}, + {"SAM Session", "SAM Sitzung"}, + {"Server Tunnels", "Servertunnel"}, + {"Client Forwards", "Klient-Weiterleitungen"}, + {"Server Forwards", "Server-Weiterleitungen"}, + {"Unknown page", "Unbekannte Seite"}, + {"Invalid token", "Ungültiger Token"}, + {"SUCCESS", "ERFOLGREICH"}, + {"Stream closed", "Stream geschlossen"}, + {"Stream not found or already was closed", "Stream nicht gefunden oder bereits geschlossen"}, + {"Destination not found", "Destination nicht gefunden"}, + {"StreamID can't be null", "StreamID kann nicht null sein"}, + {"Return to destination page", "Zurück zur Destination-Seite"}, + {"You will be redirected in 5 seconds", "Du wirst in 5 Sekunden weitergeleitet"}, + {"Transit tunnels count must not exceed 65535", "Es darf maximal 65535 Transittunnel geben"}, + {"Back to commands list", "Zurück zur Kommandoliste"}, + {"Register at reg.i2p", "Auf reg.i2p registrieren"}, + {"Description", "Beschreibung"}, + {"A bit information about service on domain", "Ein bisschen Informationen über den Service auf der Domain"}, + {"Submit", "Einreichen"}, + {"Domain can't end with .b32.i2p", "Domain kann nicht mit .b32.i2p enden"}, + {"Domain must end with .i2p", "Domain muss mit .i2p enden"}, + {"Such destination is not found", "Eine solche Destination konnte nicht gefunden werden"}, + {"Unknown command", "Unbekannter Befehl"}, + {"Command accepted", "Befehl akzeptiert"}, + {"Proxy error", "Proxy-Fehler"}, + {"Proxy info", "Proxy-Info"}, + {"Proxy error: Host not found", "Proxy-Fehler: Host nicht gefunden"}, + {"Remote host not found in router's addressbook", "Remote-Host nicht im Router Adressbuch gefunden"}, + {"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einen der Jump-Services unten finden"}, + {"Invalid request", "Ungültige Anfrage"}, + {"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht interpretieren"}, + {"addresshelper is not supported", "addresshelper wird nicht unterstützt"}, + {"Host", "Host"}, + {"added to router's addressbook from helper", "vom Helfer zum Router Adressbuch hinzugefügt"}, + {"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 Anhand der Anfrage den Destination-Host nicht erkennen"}, + {"Outproxy failure", "Outproxy-Fehler"}, + {"bad outproxy settings", "ungültige Outproxy-Einstellungen"}, + {"not inside I2P network, but outproxy is not enabled", "nicht innerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"}, + {"unknown outproxy url", "unbekannte Outproxy-URL"}, + {"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"}, + {"hostname too long", "Hostname zu lang"}, + {"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 aufbaunen, vielleicht ist es offline. Versuche es später noch einmal."}, + {"", ""}, + }; + + 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 --git a/i18n/I18N.h b/i18n/I18N.h index 5024fb56..dd804926 100644 --- a/i18n/I18N.h +++ b/i18n/I18N.h @@ -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 * diff --git a/i18n/I18N_langs.h b/i18n/I18N_langs.h index 559b17be..f3fc6691 100644 --- a/i18n/I18N_langs.h +++ b/i18n/I18N_langs.h @@ -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,25 @@ namespace i18n // Add localization here with language name as namespace namespace afrikaans { std::shared_ptr GetLocale (); } + namespace armenian { 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} }, { "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 --git a/i18n/Russian.cpp b/i18n/Russian.cpp index f6a19c7c..d7616e9e 100644 --- a/i18n/Russian.cpp +++ b/i18n/Russian.cpp @@ -26,7 +26,7 @@ namespace russian // language namespace // 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 --git a/i18n/Turkmen.cpp b/i18n/Turkmen.cpp index 1d4f5ee1..356ada85 100644 --- a/i18n/Turkmen.cpp +++ b/i18n/Turkmen.cpp @@ -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 @@ namespace turkmen // language namespace static std::map> plurals { - // ShowUptime {"days", {"gün", "gün"}}, {"hours", {"sagat", "sagat"}}, {"minutes", {"minut", "minut"}}, diff --git a/i18n/Uzbek.cpp b/i18n/Uzbek.cpp index 72734b58..e750918f 100644 --- a/i18n/Uzbek.cpp +++ b/i18n/Uzbek.cpp @@ -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 @@ namespace uzbek // language namespace {"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 @@ namespace uzbek // language namespace {"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 @@ namespace uzbek // language namespace {"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 @@ namespace uzbek // language namespace {"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 @@ namespace uzbek // language namespace {"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 @@ namespace uzbek // language namespace {"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 @@ namespace uzbek // language namespace 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 --git a/libi2pd/Base.cpp b/libi2pd/Base.cpp index 921c20af..94446e86 100644 --- a/libi2pd/Base.cpp +++ b/libi2pd/Base.cpp @@ -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 @@ namespace data if (InCount && !m) outCount = 3 * n; else - { - outCount = 0; return 0; - } ps = (unsigned char *)(InBuffer + InCount - 1); while ( *ps-- == P64 ) @@ -196,7 +193,7 @@ namespace data ps = (unsigned char *)InBuffer; if (outCount > len) - return -1; + return 0; pd = OutBuffer; auto endOfOutBuffer = OutBuffer + outCount; diff --git a/libi2pd/Base.h b/libi2pd/Base.h index 073d9b40..79152e02 100644 --- a/libi2pd/Base.h +++ b/libi2pd/Base.h @@ -24,8 +24,8 @@ namespace data { 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 --git a/libi2pd/Blinding.cpp b/libi2pd/Blinding.cpp index dbab9b94..65e5f78c 100644 --- a/libi2pd/Blinding.cpp +++ b/libi2pd/Blinding.cpp @@ -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 @@ namespace data 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 @@ namespace data 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 @@ namespace data 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 @@ namespace data 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 @@ namespace data 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 @@ namespace data 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 @@ namespace data 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 @@ namespace data 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 --git a/libi2pd/Blinding.h b/libi2pd/Blinding.h index 2f670882..c78db003 100644 --- a/libi2pd/Blinding.h +++ b/libi2pd/Blinding.h @@ -28,8 +28,8 @@ namespace data 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 --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index a8516e00..56da9b7c 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -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 @@ namespace config { ("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 @@ namespace config { ("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 @@ namespace config { ("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 @@ namespace config { ("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 @@ namespace config { ("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 @@ namespace config { ("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 @@ namespace config { "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 @@ namespace config { ("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 @@ namespace config { .add(websocket) // deprecated .add(exploratory) .add(ntcp2) + .add(ssu2) .add(nettime) .add(persist) .add(cpuext) .add(meshnets) +#ifdef __linux__ + .add(unix_specific) +#endif ; } diff --git a/libi2pd/Config.h b/libi2pd/Config.h index dac5fc80..79463e65 100644 --- a/libi2pd/Config.h +++ b/libi2pd/Config.h @@ -29,16 +29,16 @@ namespace config { 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 @@ namespace config { 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 @@ namespace config { 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 @@ namespace config { 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 @@ namespace config { } /** - * @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 --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 427bbbd6..d7fb965e 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -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 * @@ -389,7 +389,7 @@ namespace crypto { size_t len = 32; EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len); - } + } #else memcpy (m_PrivateKey, priv, 32); if (calculatePublic) @@ -440,7 +440,7 @@ namespace crypto bn2buf (a, encrypted + 1, 256); encrypted[257] = 0; bn2buf (b, encrypted + 258, 256); - + BN_free (a); BN_CTX_end (ctx); BN_CTX_free (ctx); @@ -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 @@ -1295,7 +1295,7 @@ namespace crypto } // Noise - + void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len) { SHA256_CTX ctx; @@ -1305,13 +1305,23 @@ namespace crypto 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) @@ -1320,23 +1330,23 @@ namespace crypto 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 @@ -1346,8 +1356,23 @@ namespace crypto 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) { @@ -1361,9 +1386,9 @@ namespace crypto 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 --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index 5f42b527..c6dcd2cc 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -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,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 @@ namespace crypto bool IsElligatorIneligible () const { return m_IsElligatorIneligible; } void SetElligatorIneligible () { m_IsElligatorIneligible = true; } - + private: uint8_t m_PublicKey[32]; @@ -104,11 +108,11 @@ namespace crypto 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); // 222 bytes data, 514 bytes encrypted + 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); @@ -315,13 +319,15 @@ namespace crypto 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 @@ inline int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) 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 --git a/libi2pd/CryptoKey.h b/libi2pd/CryptoKey.h index 0ac0bdd5..a7d86d09 100644 --- a/libi2pd/CryptoKey.h +++ b/libi2pd/CryptoKey.h @@ -29,7 +29,7 @@ namespace crypto public: virtual ~CryptoKeyDecryptor () {}; - virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data) = 0; + 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 }; diff --git a/libi2pd/Datagram.cpp b/libi2pd/Datagram.cpp index 627c0481..30635b09 100644 --- a/libi2pd/Datagram.cpp +++ b/libi2pd/Datagram.cpp @@ -303,7 +303,7 @@ namespace datagram } } - 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) @@ -324,7 +324,7 @@ namespace datagram 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; @@ -371,8 +371,6 @@ namespace datagram { // no current path, make one path = std::make_shared(); - path->outboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(); - if (!path->outboundTunnel) return nullptr; if (m_RemoteLeaseSet) { @@ -386,6 +384,11 @@ namespace datagram } 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 --git a/libi2pd/Datagram.h b/libi2pd/Datagram.h index 564eb10b..a55c8edf 100644 --- a/libi2pd/Datagram.h +++ b/libi2pd/Datagram.h @@ -117,12 +117,12 @@ namespace datagram 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; }; diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 54137664..719f5830 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -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,7 +13,6 @@ #include #include #include "Crypto.h" -#include "Config.h" #include "Log.h" #include "FS.h" #include "Timestamp.h" @@ -35,6 +34,8 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client } 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 @@ namespace client 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 @@ namespace client } 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client } 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 @@ namespace client 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 @@ namespace client { 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 @@ namespace client { 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 (); @@ -1099,21 +1140,21 @@ namespace client 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) { @@ -1122,9 +1163,9 @@ namespace client [s](std::shared_ptr ls) { if (ls) s->SendPing (ls); - }); - } - + }); + } + std::shared_ptr ClientDestination::GetStreamingDestination (int port) const { if (port) @@ -1182,18 +1223,18 @@ namespace client 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; @@ -1282,7 +1323,7 @@ namespace client if (m_StandardEncryptionKey && m_StandardEncryptionKey->decryptor) 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 --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 54df8706..4b08ec51 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -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 * @@ -53,6 +53,10 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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,12 +138,13 @@ namespace client // 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; }; + void SetPublic (bool pub) { m_IsPublic = pub; }; protected: @@ -251,7 +256,7 @@ namespace client 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); diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp index c9671a7e..e240e925 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.cpp +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -90,24 +90,24 @@ namespace garlic } 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 @@ namespace garlic 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 @@ namespace garlic 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 @@ namespace garlic { 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 @@ namespace garlic } 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 @@ namespace garlic // 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 @@ namespace garlic // 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"); @@ -243,7 +243,7 @@ namespace garlic { LogPrint (eLogWarning, "Garlic: Incorrect Alice ephemeral key"); return false; - } + } MixKey (sharedSecret); // decrypt flags/static @@ -267,7 +267,7 @@ namespace garlic { LogPrint (eLogWarning, "Garlic: Incorrect Alice static key"); return false; - } + } MixKey (sharedSecret); } else // all zeros flags @@ -280,13 +280,13 @@ namespace garlic 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 @@ namespace garlic 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 @@ namespace garlic 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 @@ namespace garlic } 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 @@ namespace garlic 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 @@ namespace garlic 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 @@ namespace garlic 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 @@ namespace garlic { LogPrint (eLogWarning, "Garlic: Incorrect Bob static key"); return false; - } + } MixKey (sharedSecret); // encrypt flags/static key section uint8_t nonce[12]; @@ -478,7 +478,7 @@ namespace garlic 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 @@ namespace garlic 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, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bpk) - MixKey (sharedSecret); + MixKey (sharedSecret); } else CreateNonce (1, nonce); @@ -503,10 +503,10 @@ namespace garlic 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 @@ namespace garlic 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 @@ namespace garlic 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 @@ namespace garlic { 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 @@ namespace garlic } 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 @@ namespace garlic 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,7 +637,7 @@ namespace garlic { LogPrint (eLogWarning, "Garlic: Incorrect Bob ephemeral key"); return false; - } + } MixKey (sharedSecret); GetOwner ()->Decrypt (bepk, sharedSecret, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk) MixKey (sharedSecret); @@ -700,11 +700,11 @@ namespace garlic 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 @@ namespace garlic } HandlePayload (payload, len - 16, receiveTagset, index); if (GetOwner ()) - { + { int moreTags = 0; if (GetOwner ()->GetNumRatchetInboundTags () > 0) // override in settings? { @@ -745,17 +745,17 @@ namespace garlic 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 @@ namespace garlic #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 @@ namespace garlic } return true; } - + std::shared_ptr ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr msg) { uint8_t * payload = GetOwner ()->GetPayloadBuffer (); @@ -829,7 +829,7 @@ namespace garlic if (!NewOutgoingSessionMessage (payload, len, buf, m->maxLen, false)) return nullptr; len += 96; - break; + break; default: return nullptr; } @@ -844,18 +844,18 @@ namespace garlic { 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 @@ namespace garlic 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 @@ namespace garlic { 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 @@ namespace garlic htobe16buf (payload + offset, paddingSize); offset += 2; memset (payload + offset, 0, paddingSize); offset += paddingSize; } - } + } return payloadLen; } @@ -1050,17 +1050,17 @@ namespace garlic 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 @@ namespace garlic 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) { @@ -1088,12 +1088,12 @@ namespace garlic { 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 @@ namespace garlic } 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 @@ namespace garlic 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 @@ namespace garlic { 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 @@ namespace garlic m->len += offset + 4; m->FillI2NPMessageHeader (eI2NPGarlic); return m; - } + } } } diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h index 5fd2c929..301f597a 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.h +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -1,4 +1,3 @@ - /* * Copyright (c) 2013-2021, The PurpleI2P Project * @@ -28,7 +27,7 @@ namespace garlic { 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 @@ namespace garlic RatchetTagSet () {}; virtual ~RatchetTagSet () {}; - + void DHInitialize (const uint8_t * rootKey, const uint8_t * k); void NextSessionTagRatchet (); uint64_t GetNextSessionTag (); @@ -57,14 +56,14 @@ namespace garlic 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 @@ namespace garlic { 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 @@ namespace garlic 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 @@ namespace garlic 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 @@ namespace garlic bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr receiveTagset, int index = 0); std::shared_ptr WrapSingleMessage (std::shared_ptr msg); std::shared_ptr WrapOneTimeMessage (std::shared_ptr msg); - + 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 @@ namespace garlic 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 @@ namespace garlic bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); - + 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 @@ namespace garlic 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 @@ namespace garlic 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 @@ namespace garlic { return m_Destination ? *m_Destination : i2p::data::IdentHash (); } - }; + }; // single session for all incoming messages class RouterIncomingRatchetSession: public ECIESX25519AEADRatchetSession @@ -240,12 +239,12 @@ namespace garlic 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 --git a/libi2pd/Ed25519.cpp b/libi2pd/Ed25519.cpp index 791bd685..0c6eb4f7 100644 --- a/libi2pd/Ed25519.cpp +++ b/libi2pd/Ed25519.cpp @@ -33,7 +33,7 @@ namespace crypto 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 @@ namespace crypto 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 @@ namespace crypto 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 @@ namespace crypto 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 @@ namespace crypto 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 @@ namespace crypto 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 @@ namespace crypto 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 --git a/libi2pd/Ed25519.h b/libi2pd/Ed25519.h index 28d4e930..470d802f 100644 --- a/libi2pd/Ed25519.h +++ b/libi2pd/Ed25519.h @@ -85,8 +85,8 @@ namespace crypto 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 --git a/libi2pd/Elligator.cpp b/libi2pd/Elligator.cpp index 712e514a..25e09893 100644 --- a/libi2pd/Elligator.cpp +++ b/libi2pd/Elligator.cpp @@ -189,7 +189,7 @@ namespace crypto // 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 --git a/libi2pd/FS.cpp b/libi2pd/FS.cpp index f6653e55..7334550f 100644 --- a/libi2pd/FS.cpp +++ b/libi2pd/FS.cpp @@ -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 @@ namespace fs { } 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 @@ namespace fs { 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 --git a/libi2pd/FS.h b/libi2pd/FS.h index d51aa955..7911c6a0 100644 --- a/libi2pd/FS.h +++ b/libi2pd/FS.h @@ -83,8 +83,8 @@ namespace fs { /** * @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 @@ namespace fs { * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/ * Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/ */ - void DetectDataDir(const std::string & cmdline_datadir, bool isService = false); + void DetectDataDir(const std::string & cmdline_datadir, bool isService = false); /** * @brief Set certsdir either from cmdline option or using autodetection - * @param cmdline_param Value of cmdline parameter --certsdir= + * @param cmdline_param Value of cmdline parameter --certsdir= * * Examples of autodetected paths: * @@ -106,7 +106,7 @@ namespace fs { * 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 @@ namespace fs { /** * @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 --git a/libi2pd/Family.cpp b/libi2pd/Family.cpp index a6f0e2ee..9a0700d0 100644 --- a/libi2pd/Family.cpp +++ b/libi2pd/Family.cpp @@ -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 @@ namespace data } 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 @@ namespace data } 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 @@ namespace data 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 --git a/libi2pd/Family.h b/libi2pd/Family.h index 2a9149ba..b19ea142 100644 --- a/libi2pd/Family.h +++ b/libi2pd/Family.h @@ -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 i2p { namespace data { + typedef int FamilyID; class Families { public: @@ -27,7 +28,8 @@ namespace data ~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 @@ namespace data private: - std::map > m_SigningKeys; + std::map, FamilyID> > m_SigningKeys; // family -> (verifier, id) }; std::string CreateFamilySignature (const std::string& family, const IdentHash& ident); diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index ea30a249..9daea1f0 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -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 * @@ -271,7 +271,7 @@ namespace garlic (*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)++; @@ -293,14 +293,14 @@ namespace garlic 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++; } @@ -452,7 +452,7 @@ namespace garlic { it.second->Terminate (); it.second->SetOwner (nullptr); - } + } m_ECIESx25519Sessions.clear (); m_ECIESx25519Tags.clear (); } @@ -470,27 +470,32 @@ namespace garlic uint64_t t; memcpy (&t, tag, 8); AddECIESx25519Key (key, t); - } + } void GarlicDestination::AddECIESx25519Key (const uint8_t * key, uint64_t tag) { auto tagset = std::make_shared(this, key); m_ECIESx25519Tags.emplace (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 @@ -498,10 +503,10 @@ namespace garlic 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 @@ -519,7 +524,7 @@ namespace garlic 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 { @@ -542,37 +547,37 @@ namespace garlic 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"); - } + } } } @@ -585,14 +590,14 @@ namespace garlic { 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) { @@ -629,7 +634,7 @@ namespace garlic 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); @@ -639,7 +644,7 @@ namespace garlic { if (len < 1) { - LogPrint (eLogError, "Garlic: payload is too short"); + LogPrint (eLogError, "Garlic: Payload is too short"); return; } int numCloves = buf[0]; @@ -654,7 +659,7 @@ namespace garlic if (flag & 0x80) // encrypted? { // TODO: implement - LogPrint (eLogWarning, "Garlic: clove encrypted"); + LogPrint (eLogWarning, "Garlic: Clove encrypted"); buf += 32; } ptrdiff_t offset = buf - buf1; @@ -662,35 +667,35 @@ namespace garlic 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); @@ -721,32 +726,32 @@ namespace garlic { 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; @@ -759,10 +764,10 @@ namespace garlic 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 ( @@ -776,14 +781,14 @@ namespace garlic 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); @@ -840,7 +845,7 @@ namespace garlic 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); } @@ -879,18 +884,18 @@ namespace garlic 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 ()); @@ -925,7 +930,7 @@ namespace garlic if (session) { session->MessageConfirmed (msgID); - LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged"); + LogPrint (eLogDebug, "Garlic: Message ", msgID, " acknowledged"); } } @@ -1019,7 +1024,7 @@ namespace garlic 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) @@ -1030,7 +1035,7 @@ namespace garlic 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]]; @@ -1038,7 +1043,7 @@ namespace garlic // 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 @@ -1046,19 +1051,19 @@ namespace garlic 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; @@ -1079,7 +1084,7 @@ namespace garlic break; } default: - LogPrint (eLogWarning, "Garlic: unexpected delivery type ", (int)deliveryType); + LogPrint (eLogWarning, "Garlic: Unexpected delivery type ", (int)deliveryType); } } @@ -1099,13 +1104,13 @@ namespace garlic 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; } } @@ -1127,6 +1132,6 @@ namespace garlic if (!m_PayloadBuffer) m_PayloadBuffer = new uint8_t[I2NP_MAX_MESSAGE_SIZE]; return m_PayloadBuffer; - } + } } } diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index 0d7b8461..b926abda 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -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 * @@ -115,7 +115,7 @@ namespace garlic 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 @@ namespace garlic 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 (); diff --git a/libi2pd/Gost.cpp b/libi2pd/Gost.cpp index 5e84a95d..2dafc9ae 100644 --- a/libi2pd/Gost.cpp +++ b/libi2pd/Gost.cpp @@ -96,7 +96,7 @@ namespace crypto 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 @@ namespace crypto 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 --git a/libi2pd/HTTP.cpp b/libi2pd/HTTP.cpp index 6ad245f0..e994b9b3 100644 --- a/libi2pd/HTTP.cpp +++ b/libi2pd/HTTP.cpp @@ -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 @@ namespace http method = tokens[0]; uri = tokens[1]; version = tokens[2]; - expect = HEADER_LINE; + expect = HEADER_LINE; } else { @@ -363,7 +363,7 @@ namespace http 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 @@ namespace http /* 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 @@ namespace http 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 @@ namespace http 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 @@ namespace http 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 @@ namespace http if (user.empty () && pass.empty ()) return ""; return "Basic " + i2p::data::ToBase64Standard (user + ":" + pass); } - + } // http } // i2p diff --git a/libi2pd/HTTP.h b/libi2pd/HTTP.h index 1d73461e..9445a01a 100644 --- a/libi2pd/HTTP.h +++ b/libi2pd/HTTP.h @@ -161,14 +161,14 @@ namespace http /** * @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 --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index 29d68b3c..e19e782d 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -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 @@ namespace i2p 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 @@ namespace i2p { 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 @@ namespace i2p 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 @@ namespace i2p 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 (); @@ -256,7 +243,7 @@ namespace i2p 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 @@ -411,7 +398,7 @@ namespace i2p retCode = 30; // always reject with bandwidth reason (30) memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options - record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; + record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; // encrypt reply i2p::crypto::CBCEncryption encryption; for (int j = 0; j < num; j++) @@ -422,7 +409,7 @@ namespace i2p uint8_t nonce[12]; memset (nonce, 0, 12); auto& noiseState = i2p::context.GetCurrentNoiseState (); - if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, + 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"); @@ -560,7 +547,7 @@ namespace i2p { LogPrint (eLogDebug, "I2NP: Short request record ", i, " is ours"); uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) + if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) { LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i); return; @@ -571,10 +558,10 @@ namespace i2p 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) @@ -615,8 +602,8 @@ namespace i2p 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"); @@ -624,7 +611,7 @@ namespace i2p } } else - i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); + i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; } // send reply @@ -633,16 +620,16 @@ namespace i2p 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 { @@ -698,7 +685,7 @@ namespace i2p 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; } @@ -729,7 +716,7 @@ namespace i2p 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; @@ -744,13 +731,13 @@ namespace i2p { 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; @@ -760,18 +747,18 @@ namespace i2p { 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) @@ -815,13 +802,8 @@ namespace i2p 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; @@ -860,7 +842,7 @@ namespace i2p Flush (); } - void I2NPMessagesHandler::PutNextMessage (std::shared_ptr msg) + void I2NPMessagesHandler::PutNextMessage (std::shared_ptr&& msg) { if (msg) { diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index f0777ac2..e60f6a9c 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -56,7 +56,7 @@ namespace i2p // TunnelBuild const size_t TUNNEL_BUILD_RECORD_SIZE = 528; const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 218; - + // 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; @@ -98,7 +98,7 @@ namespace i2p // ShortResponseRecord const size_t SHORT_RESPONSE_RECORD_OPTIONS_OFFSET = 0; const size_t SHORT_RESPONSE_RECORD_RET_OFFSET = 201; - + enum I2NPMessageType { eI2NPDummyMsg = 0, @@ -150,7 +150,7 @@ namespace tunnel 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 (); }; @@ -273,9 +273,9 @@ namespace tunnel 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); @@ -301,7 +301,7 @@ namespace tunnel public: ~I2NPMessagesHandler (); - void PutNextMessage (std::shared_ptr msg); + void PutNextMessage (std::shared_ptr&& msg); void Flush (); private: diff --git a/libi2pd/I2PEndian.cpp b/libi2pd/I2PEndian.cpp index 32ca4e26..5496f144 100644 --- a/libi2pd/I2PEndian.cpp +++ b/libi2pd/I2PEndian.cpp @@ -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 @@ uint64_t be64toh(uint64_t big64) 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 --git a/libi2pd/I2PEndian.h b/libi2pd/I2PEndian.h index 9ffc28d0..d97bd055 100644 --- a/libi2pd/I2PEndian.h +++ b/libi2pd/I2PEndian.h @@ -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 --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index aa03bfb7..cff0c37d 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -19,7 +19,8 @@ namespace data 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; } @@ -52,9 +53,9 @@ namespace data { 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 @@ namespace data { 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; @@ -127,7 +128,7 @@ namespace data { LogPrint (eLogError, "Identity: Unexpected excessive signing key len ", excessLen); excessLen = MAX_EXTENDED_BUFFER_SIZE - 4; - } + } memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen); delete[] excessBuf; } @@ -214,7 +215,7 @@ namespace data { 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); @@ -479,7 +480,7 @@ namespace data 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 @@ -508,7 +509,7 @@ namespace data 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 (); @@ -653,9 +654,9 @@ namespace data 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) @@ -680,7 +681,7 @@ namespace data 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); @@ -787,7 +788,7 @@ namespace data 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 --git a/libi2pd/Identity.h b/libi2pd/Identity.h index f96d5f41..10f1d5ed 100644 --- a/libi2pd/Identity.h +++ b/libi2pd/Identity.h @@ -84,9 +84,9 @@ namespace data typedef uint16_t SigningKeyType; typedef uint16_t CryptoKeyType; - const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521 + const size_t MAX_EXTENDED_BUFFER_SIZE = 8; // cryptoKeyType + signingKeyType + 4 extra bytes of P521 class IdentityEx - { + { public: IdentityEx (); @@ -120,7 +120,7 @@ namespace data 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); @@ -138,7 +138,7 @@ namespace data mutable i2p::crypto::Verifier * m_Verifier = nullptr; mutable std::mutex m_VerifierMutex; size_t m_ExtendedLen; - uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE]; + uint8_t m_ExtendedBuffer[MAX_EXTENDED_BUFFER_SIZE]; }; class PrivateKeys // for eepsites @@ -222,7 +222,7 @@ namespace data RoutingDestination () {}; virtual ~RoutingDestination () {}; - virtual std::shared_ptr GetIdentity () const = 0; + 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 diff --git a/libi2pd/LeaseSet.cpp b/libi2pd/LeaseSet.cpp index f6734b10..c844ab60 100644 --- a/libi2pd/LeaseSet.cpp +++ b/libi2pd/LeaseSet.cpp @@ -61,7 +61,7 @@ namespace data size_t size = m_Identity->GetFullLen (); if (size > m_BufferLen) { - LogPrint (eLogError, "LeaseSet: identity length ", size, " exceeds buffer size ", m_BufferLen); + LogPrint (eLogError, "LeaseSet: Identity length ", size, " exceeds buffer size ", m_BufferLen); m_IsValid = false; return; } @@ -77,23 +77,23 @@ namespace data LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", 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) - { + if (size + num*LEASE_SIZE > m_BufferLen) + { LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen); m_IsValid = false; return; - } - + } + UpdateLeasesBegin (); // process leases m_ExpirationTime = 0; @@ -112,7 +112,7 @@ namespace data } 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 @@ namespace data // verify if (verifySignature) - { + { auto signedSize = leases - m_Buffer; if (signedSize + m_Identity->GetSignatureLen () > m_BufferLen) { LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", 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; } } @@ -274,7 +274,7 @@ namespace data { 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 ", len , " exceeds full buffer size ", m_BufferLen); } LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto): @@ -331,7 +331,7 @@ namespace data m_TransientVerifier = ProcessOfflineSignature (identity, buf, len, offset); if (!m_TransientVerifier) { - LogPrint (eLogError, "LeaseSet2: offline signature failed"); + LogPrint (eLogError, "LeaseSet2: Offline signature failed"); return; } } @@ -378,7 +378,7 @@ namespace data 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; } @@ -489,7 +489,7 @@ namespace data 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 +515,7 @@ namespace data 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 +569,7 @@ namespace data 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 +582,7 @@ namespace data // 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 +606,7 @@ namespace data { 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 +632,7 @@ namespace data { 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,7 +653,7 @@ namespace data 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; } @@ -737,7 +737,7 @@ namespace data 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 +768,7 @@ namespace data 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 +779,7 @@ namespace data ++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 +863,17 @@ namespace data } // 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 } @@ -916,7 +916,7 @@ namespace data { 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 @@ -984,7 +984,7 @@ namespace data 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 @@ -995,7 +995,7 @@ namespace data 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 --git a/libi2pd/LeaseSet.h b/libi2pd/LeaseSet.h index 8d501cb1..a79a5870 100644 --- a/libi2pd/LeaseSet.h +++ b/libi2pd/LeaseSet.h @@ -128,8 +128,8 @@ namespace data }; /** - 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); diff --git a/libi2pd/Log.cpp b/libi2pd/Log.cpp index 2b555663..e90b5e2b 100644 --- a/libi2pd/Log.cpp +++ b/libi2pd/Log.cpp @@ -46,7 +46,7 @@ namespace log { #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 @@ namespace log { 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 @@ namespace log { 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 @@ namespace log { 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 @@ namespace log { 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 --git a/libi2pd/Log.h b/libi2pd/Log.h index 08c401a9..465e10bc 100644 --- a/libi2pd/Log.h +++ b/libi2pd/Log.h @@ -52,7 +52,7 @@ namespace log { { 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 @@ namespace log { /** * @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 @@ namespace log { 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 @@ namespace log { */ 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 @@ namespace log { Log & Logger(); - typedef std::function ThrowFunction; + typedef std::function ThrowFunction; ThrowFunction GetThrowFunction (); void SetThrowFunction (ThrowFunction f); } // log diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 05d68203..8a2d6c7c 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -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 * @@ -23,7 +23,7 @@ #include "HTTP.h" #include "util.h" -#ifdef __linux__ +#if defined(__linux__) && !defined(_NETINET_IN_H) #include #endif @@ -59,14 +59,14 @@ namespace transport 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); @@ -91,7 +91,7 @@ namespace transport void NTCP2Establisher::KDF3Alice () { uint8_t inputKeyMaterial[32]; - i2p::context.GetStaticKeys ().Agree (GetRemotePub (), inputKeyMaterial); + i2p::context.GetNTCP2StaticKeys ().Agree (GetRemotePub (), inputKeyMaterial); MixKey (inputKeyMaterial); } @@ -130,7 +130,7 @@ namespace transport // 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; @@ -195,8 +195,9 @@ namespace transport 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 ()); @@ -232,7 +233,8 @@ namespace transport 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 @@ -318,17 +320,18 @@ namespace transport } 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_NextReceivedBufferSize (0), m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), + m_NextReceivedBufferSize (0), m_ReceiveSequenceNumber (0), m_SendSequenceNumber (0), m_IsSending (false), m_IsReceiving (false), m_NextPaddingSize (16) { if (in_RemoteRouter) // Alice @@ -336,8 +339,8 @@ namespace transport 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 @@ -352,8 +355,6 @@ namespace transport 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 @@ -373,7 +374,7 @@ namespace transport transports.PeerDisconnected (shared_from_this ()); m_Server.RemoveNTCP2Session (shared_from_this ()); m_SendQueue.clear (); - LogPrint (eLogDebug, "NTCP2: session terminated"); + LogPrint (eLogDebug, "NTCP2: Session terminated"); } } @@ -402,7 +403,7 @@ namespace transport } void NTCP2Session::CreateNextReceivedBuffer (size_t size) - { + { if (m_NextReceivedBuffer) { if (size <= m_NextReceivedBufferSize) @@ -412,19 +413,19 @@ namespace transport } 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) + if (m_NextReceivedBuffer && !m_IsReceiving && + ts > m_LastActivityTimestamp + NTCP2_RECEIVE_BUFFER_DELETION_TIMEOUT) { delete[] m_NextReceivedBuffer; m_NextReceivedBuffer = nullptr; m_NextReceivedBufferSize = 0; - } - } - + } + } + void NTCP2Session::KeyDerivationFunctionDataPhase () { uint8_t k[64]; @@ -454,7 +455,7 @@ namespace transport (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 @@ -477,9 +478,16 @@ namespace transport { 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) + { + // 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 { @@ -488,7 +496,7 @@ namespace transport } else { - LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long"); + LogPrint (eLogWarning, "NTCP2: SessionRequest padding length ", (int)paddingLen, " is too long"); Terminate (); } } @@ -541,7 +549,7 @@ namespace transport } else { - LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long"); + LogPrint (eLogWarning, "NTCP2: SessionCreated padding length ", (int)paddingLen, " is too long"); Terminate (); } } @@ -584,7 +592,7 @@ namespace transport (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 @@ -611,7 +619,7 @@ namespace transport (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 @@ -654,7 +662,7 @@ namespace transport // 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; } @@ -682,7 +690,7 @@ namespace transport 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; } @@ -711,17 +719,19 @@ namespace transport 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; @@ -747,7 +757,7 @@ namespace transport 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)); @@ -758,7 +768,7 @@ namespace transport 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 @@ -773,7 +783,7 @@ namespace transport #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) { CreateNextReceivedBuffer (m_NextReceivedLen); @@ -790,7 +800,7 @@ namespace transport } else { - LogPrint (eLogError, "NTCP2: received length ", m_NextReceivedLen, " is too short"); + LogPrint (eLogError, "NTCP2: Received length ", m_NextReceivedLen, " is too short"); Terminate (); } } @@ -813,7 +823,7 @@ namespace transport 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 @@ -825,7 +835,7 @@ namespace transport 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); m_IsReceiving = false; ReceiveLength (); @@ -856,10 +866,10 @@ namespace transport 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: { @@ -875,25 +885,29 @@ namespace transport 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); @@ -905,7 +919,7 @@ namespace transport 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; @@ -915,7 +929,7 @@ namespace transport #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) @@ -968,7 +982,7 @@ namespace transport { // 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) @@ -1076,15 +1090,15 @@ namespace transport size_t paddingSize = (msgLen*NTCP2_MAX_PADDING_RATIO)/100; if (msgLen + paddingSize + 3 > NTCP2_UNENCRYPTED_FRAME_MAX_SIZE) paddingSize = NTCP2_UNENCRYPTED_FRAME_MAX_SIZE - msgLen -3; if (paddingSize > len) paddingSize = len; - if (paddingSize) + 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); @@ -1110,7 +1124,13 @@ namespace transport 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; @@ -1143,21 +1163,21 @@ namespace transport 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 ()) { } @@ -1223,7 +1243,7 @@ namespace transport 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 @@ -1249,7 +1269,7 @@ namespace transport } 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; } @@ -1290,7 +1310,7 @@ namespace transport 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 (); @@ -1359,7 +1379,7 @@ namespace transport 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)); } @@ -1528,7 +1548,7 @@ namespace transport { 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; @@ -1545,7 +1565,7 @@ namespace transport (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); @@ -1554,7 +1574,7 @@ namespace transport { if(ec) { - LogPrint(eLogError, "NTCP2: socks5 read error ", ec.message()); + LogPrint(eLogError, "NTCP2: SOCKS5 read error ", ec.message()); timer->cancel(); conn->Terminate(); return; @@ -1568,14 +1588,14 @@ namespace transport } 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(); }); @@ -1603,7 +1623,7 @@ namespace transport { (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; @@ -1612,7 +1632,7 @@ namespace transport { if(ec) { - LogPrint(eLogError, "NTCP2: http proxy read error ", ec.message()); + LogPrint(eLogError, "NTCP2: HTTP proxy read error ", ec.message()); timer->cancel(); conn->Terminate(); } @@ -1630,10 +1650,10 @@ namespace transport 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; @@ -1642,7 +1662,7 @@ namespace transport break; } default: - LogPrint(eLogError, "NTCP2: unknown proxy type, invalid state"); + LogPrint(eLogError, "NTCP2: Unknown proxy type, invalid state"); } } @@ -1683,7 +1703,7 @@ namespace transport { 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; } }); @@ -1693,7 +1713,7 @@ namespace transport { 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 --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h index f9f3e751..754f5a6d 100644 --- a/libi2pd/NTCP2.h +++ b/libi2pd/NTCP2.h @@ -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 * @@ -42,7 +42,7 @@ namespace transport const int NTCP2_CLOCK_SKEW = 60; // in seconds const int NTCP2_MAX_OUTGOING_QUEUE_SIZE = 500; // how many messages we can queue up - + enum NTCP2BlockType { eNTCP2BlkDateTime = 0, @@ -107,7 +107,7 @@ namespace transport 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); @@ -118,7 +118,7 @@ namespace transport i2p::data::IdentHash m_RemoteIdentHash; uint16_t m3p2Len; - uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE], + uint8_t m_SessionRequestBuffer[NTCP2_SESSION_REQUEST_MAX_SIZE], m_SessionCreatedBuffer[NTCP2_SESSION_CREATED_MAX_SIZE], * m_SessionConfirmedBuffer; size_t m_SessionRequestBufferLen, m_SessionCreatedBufferLen; @@ -137,7 +137,7 @@ namespace transport void Done (); void Close () { m_Socket.close (); }; // for accept void DeleteNextReceiveBuffer (uint64_t ts); - + boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; const boost::asio::ip::tcp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; void SetRemoteEndpoint (const boost::asio::ip::tcp::endpoint& ep) { m_RemoteEndpoint = ep; }; @@ -148,7 +148,7 @@ namespace transport 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: @@ -205,7 +205,6 @@ namespace transport 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; @@ -227,7 +226,7 @@ namespace transport uint64_t m_NextRouterInfoResendTime; // seconds since epoch uint16_t m_PaddingSizes[16]; - int m_NextPaddingSize; + int m_NextPaddingSize; }; class NTCP2Server: private i2p::util::RunnableServiceWithWork @@ -259,7 +258,7 @@ namespace transport 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); @@ -268,7 +267,7 @@ namespace transport 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); @@ -286,7 +285,7 @@ namespace transport 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 --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 8d0025ec..8566380a 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -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 * @@ -56,9 +56,9 @@ namespace data uint16_t threshold; i2p::config::GetOption("reseed.threshold", 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 @@ -66,14 +66,14 @@ namespace data 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; @@ -107,7 +107,10 @@ namespace data { 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 @@ -118,7 +121,7 @@ namespace data 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: @@ -138,7 +141,7 @@ namespace data 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; @@ -148,13 +151,14 @@ namespace data } 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) @@ -164,14 +168,22 @@ namespace data } 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) @@ -179,15 +191,15 @@ namespace data // 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); @@ -195,6 +207,7 @@ namespace data lastPublish = ts; } } + if (ts - lastExploratory >= 30) // exploratory every 30 seconds { auto numRouters = m_RouterInfos.size (); @@ -216,7 +229,7 @@ namespace data } catch (std::exception& ex) { - LogPrint (eLogError, "NetDb: runtime exception: ", ex.what ()); + LogPrint (eLogError, "NetDb: Runtime exception: ", ex.what ()); } } } @@ -290,7 +303,7 @@ namespace data 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); @@ -343,7 +356,7 @@ namespace data updated = true; } else - LogPrint (eLogError, "NetDb: new LeaseSet validation failed: ", ident.ToBase32()); + LogPrint (eLogError, "NetDb: New LeaseSet validation failed: ", ident.ToBase32()); } return updated; } @@ -373,7 +386,7 @@ namespace data } } else - LogPrint (eLogError, "NetDb: new LeaseSet2 validation failed: ", ident.ToBase32()); + LogPrint (eLogError, "NetDb: New LeaseSet2 validation failed: ", ident.ToBase32()); return false; } @@ -430,7 +443,7 @@ namespace data int riLen = ri->GetBufferLen(); 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; @@ -445,7 +458,7 @@ namespace data 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(); @@ -472,22 +485,22 @@ namespace data 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; @@ -568,11 +581,11 @@ namespace data 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)"); } @@ -591,15 +604,14 @@ namespace data expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL : NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total; - auto own = i2p::context.GetSharedRouterInfo (); + 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 (); @@ -608,8 +620,8 @@ namespace data } // 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); + (it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) + it.second->SetUnreachable (false); // find & mark expired routers if (!it.second->IsReachable () && it.second->IsSSU (false)) { @@ -626,15 +638,17 @@ namespace data // 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); @@ -666,7 +680,7 @@ namespace data 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; } @@ -674,24 +688,24 @@ namespace data 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"); @@ -705,10 +719,10 @@ namespace data 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)); } @@ -732,7 +746,7 @@ namespace data 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); @@ -751,14 +765,14 @@ namespace data 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; @@ -771,24 +785,24 @@ namespace data { 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]; @@ -797,7 +811,7 @@ namespace data updated = AddRouterInfo (ident, uncompressed, uncompressedSize); else { - LogPrint (eLogInfo, "NetDb: decompression failed ", uncompressedSize); + LogPrint (eLogInfo, "NetDb: Decompression failed ", uncompressedSize); return; } } @@ -872,7 +886,7 @@ namespace data 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++) @@ -887,7 +901,7 @@ namespace data 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 @@ -928,14 +942,14 @@ namespace data excluded += 2; if (numExcluded > 512) { - LogPrint (eLogWarning, "NetDb: number of excluded peers", numExcluded, " exceeds 512"); + LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " exceeds 512"); 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; for (int i = 0; i < numExcluded; i++) { @@ -957,14 +971,13 @@ namespace data 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"); - if (!router->GetBuffer ()) - router->LoadBuffer (m_Storage.Path (router->GetIdentHashBase64 ())); + LogPrint (eLogDebug, "NetDb: Requested RouterInfo ", key, " found"); + PopulateRouterInfoBuffer (router); if (router->GetBuffer ()) replyMsg = CreateDatabaseStoreMsg (router); } @@ -977,11 +990,11 @@ namespace data 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); } } @@ -1026,10 +1039,10 @@ namespace data 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; @@ -1051,8 +1064,8 @@ namespace data m_PublishExcluded.clear (); m_PublishReplyToken = 0; } - } - + } + void NetDb::Explore (int numDestinations) { // new requests @@ -1063,14 +1076,14 @@ namespace data 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 ()); @@ -1112,7 +1125,7 @@ namespace data 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) { @@ -1122,19 +1135,19 @@ namespace data 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)); + } } } @@ -1174,7 +1187,7 @@ namespace data { return !router->IsHidden () && router != compatibleWith && (reverse ? compatibleWith->IsReachableFrom (*router) : - router->IsReachableFrom (*compatibleWith)) && + router->IsReachableFrom (*compatibleWith)) && router->IsECIES (); }); } @@ -1184,11 +1197,21 @@ namespace data return GetRandomRouter ( [v4, &excluded](std::shared_ptr router)->bool { - return !router->IsHidden () && router->IsECIES () && + 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 ( @@ -1216,7 +1239,7 @@ namespace data return !router->IsHidden () && router != compatibleWith && (reverse ? compatibleWith->IsReachableFrom (*router) : router->IsReachableFrom (*compatibleWith)) && - (router->GetCaps () & RouterInfo::eHighBandwidth) && + (router->GetCaps () & RouterInfo::eHighBandwidth) && router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION && router->IsECIES (); }); @@ -1238,12 +1261,12 @@ namespace data 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; @@ -1254,14 +1277,14 @@ namespace data 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 ()) { @@ -1269,7 +1292,7 @@ namespace data 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 ()) { @@ -1363,7 +1386,8 @@ namespace data 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 { @@ -1408,5 +1432,11 @@ namespace data ++it; } } + + void NetDb::PopulateRouterInfoBuffer (std::shared_ptr r) + { + if (!r || r->GetBuffer ()) return; + r->LoadBuffer (m_Storage.Path (r->GetIdentHashBase64 ())); + } } } diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index 097f92e7..1a07903c 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -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,6 +30,7 @@ #include "NetDbRequests.h" #include "Family.h" #include "version.h" +#include "util.h" namespace i2p { @@ -41,9 +42,10 @@ namespace data 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 @@ -88,13 +90,14 @@ namespace data 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 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); @@ -120,13 +123,15 @@ namespace data 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); 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); @@ -153,7 +158,6 @@ namespace data std::list > m_Floodfills; bool m_IsRunning; - uint64_t m_LastLoad; std::thread * m_Thread; i2p::util::Queue > m_Queue; // of I2NPDatabaseStoreMsg @@ -175,6 +179,8 @@ namespace data std::set m_PublishExcluded; uint32_t m_PublishReplyToken = 0; + + i2p::util::MemoryPoolMt m_RouterInfoBuffersPool; }; extern NetDb netdb; diff --git a/libi2pd/NetDbRequests.h b/libi2pd/NetDbRequests.h index 16ea430d..cf2f0915 100644 --- a/libi2pd/NetDbRequests.h +++ b/libi2pd/NetDbRequests.h @@ -60,7 +60,7 @@ namespace data 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 --git a/libi2pd/Poly1305.cpp b/libi2pd/Poly1305.cpp index 23098d74..20b3ab2a 100644 --- a/libi2pd/Poly1305.cpp +++ b/libi2pd/Poly1305.cpp @@ -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 --git a/libi2pd/Poly1305.h b/libi2pd/Poly1305.h index f91a037e..db659b84 100644 --- a/libi2pd/Poly1305.h +++ b/libi2pd/Poly1305.h @@ -5,6 +5,7 @@ * Kovri go write your own code * */ + #ifndef LIBI2PD_POLY1305_H #define LIBI2PD_POLY1305_H #include diff --git a/libi2pd/Profiling.cpp b/libi2pd/Profiling.cpp index 850774d9..55b95831 100644 --- a/libi2pd/Profiling.cpp +++ b/libi2pd/Profiling.cpp @@ -73,7 +73,7 @@ namespace data 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 @@ namespace data } 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 @@ namespace data 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 --git a/libi2pd/Profiling.h b/libi2pd/Profiling.h index dab50e6b..49e362ca 100644 --- a/libi2pd/Profiling.h +++ b/libi2pd/Profiling.h @@ -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 @@ namespace data 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 --git a/libi2pd/Queue.h b/libi2pd/Queue.h index d43567a5..441f8c3a 100644 --- a/libi2pd/Queue.h +++ b/libi2pd/Queue.h @@ -28,7 +28,7 @@ namespace util 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 @@ namespace util { 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 --git a/libi2pd/Reseed.cpp b/libi2pd/Reseed.cpp index aec683d4..4c23b4cc 100644 --- a/libi2pd/Reseed.cpp +++ b/libi2pd/Reseed.cpp @@ -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 @@ namespace data 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 @@ namespace data */ 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 @@ namespace data { 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 @@ namespace data } 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 @@ namespace data { 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 @@ 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; @@ -509,7 +510,7 @@ namespace data 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 @@ namespace data } // 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 @@ namespace data 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 @@ namespace data 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 --git a/libi2pd/Reseed.h b/libi2pd/Reseed.h index 8039c07e..a6de6fa4 100644 --- a/libi2pd/Reseed.h +++ b/libi2pd/Reseed.h @@ -49,8 +49,8 @@ namespace data 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 --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 3710092b..1c8914e5 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -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 * @@ -57,7 +57,7 @@ namespace i2p void RouterContext::NewRouterInfo () { - i2p::data::RouterInfo routerInfo; + i2p::data::LocalRouterInfo routerInfo; routerInfo.SetRouterIdentity (GetIdentity ()); uint16_t port; i2p::config::GetOption("port", port); if (!port) @@ -65,15 +65,18 @@ namespace i2p 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); + 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) { @@ -84,6 +87,9 @@ namespace i2p if (!ntcp2proxy.empty ()) ntcp2Published = false; } } + bool ssu2Published = false; + if (ssu2) + i2p::config::GetOption("ssu2.published", ssu2Published); uint8_t caps = 0, addressCaps = 0; if (ipv4) { @@ -112,6 +118,16 @@ namespace i2p routerInfo.AddSSUAddress (host.c_str(), port, nullptr); caps |= i2p::data::RouterInfo::eReachable; // R } + if (ssu2) + { + if (ssu2Published) + routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), port); + else + { + addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4; + routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro); + } + } } if (ipv6) { @@ -147,6 +163,17 @@ namespace i2p routerInfo.AddSSUAddress (host.c_str(), port, nullptr); caps |= i2p::data::RouterInfo::eReachable; // R } + if (ssu2) + { + if (ssu2Published) + routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), port); + else + { + if (!ipv4) // no other ssu2 addresses yet + routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro); + addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; + } + } } if (ygg) { @@ -157,7 +184,7 @@ namespace i2p 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); @@ -174,17 +201,30 @@ namespace i2p 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)); + } + void RouterContext::SetStatus (RouterStatus status) { if (status != m_Status) @@ -229,7 +269,7 @@ namespace i2p bool updated = false; for (auto& address : m_RouterInfo.GetAddresses ()) { - if (!address->IsNTCP2 () && address->port != port) + if (!address->IsNTCP2 () && !address->IsSSU2 () && address->port != port) { address->port = port; updated = true; @@ -265,7 +305,7 @@ namespace i2p } if (port) address->port = port; address->published = publish; - address->ntcp2->iv = m_NTCP2Keys->iv; + memcpy (address->i, m_NTCP2Keys->iv, 16); updated = true; } } @@ -300,13 +340,66 @@ namespace i2p UpdateRouterInfo (); } + void RouterContext::PublishSSU2Address (int port, bool publish, bool v4, bool v6) + { + if (!m_SSU2Keys || (publish && !port)) return; + bool updated = false; + for (auto& address : m_RouterInfo.GetAddresses ()) + { + if (address->IsSSU2 () && (address->port != port || address->published != publish) && + ((v4 && address->IsV4 ()) || (v6 && address->IsV6 ()))) + { + address->port = port; + address->published = publish; + if (publish) + address->caps |= (i2p::data::RouterInfo::eSSUIntroducer | 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 (); ++it) + { + if ((*it)->IsSSU2 ()) + { + found = true; + if (!enable) + { + addresses.erase (it); + updated= true; + } + break; + } + } + if (enable && !found) + { + uint8_t addressCaps = 0; + bool ipv4; i2p::config::GetOption("ipv4", ipv4); + bool ipv6; i2p::config::GetOption("ipv6", ipv6); + if (ipv4) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4; + if (ipv6) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; + m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addressCaps); + updated = true; + } + if (updated) + UpdateRouterInfo (); + } + void RouterContext::UpdateAddress (const boost::asio::ip::address& host) { 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)) { address->host = host; if (host.is_v6 () && address->transportStyle == i2p::data::RouterInfo::eTransportSSU) @@ -349,10 +442,10 @@ namespace i2p { 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); @@ -414,7 +507,7 @@ namespace i2p // 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; } @@ -469,13 +562,13 @@ namespace i2p 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 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) { addr->published = false; addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer @@ -501,13 +594,13 @@ namespace i2p 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 auto& addresses = m_RouterInfo.GetAddresses (); for (auto& addr : addresses) - if (addr->ssu && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) + if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) { addr->published = true; addr->caps |= i2p::data::RouterInfo::eSSUIntroducer; @@ -536,17 +629,26 @@ namespace i2p 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; } @@ -566,10 +668,10 @@ namespace i2p { 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); @@ -578,11 +680,27 @@ namespace i2p 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); + 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 @@ -598,7 +716,7 @@ namespace i2p // 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 (); @@ -606,10 +724,19 @@ namespace i2p { 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; } @@ -638,6 +765,22 @@ namespace i2p m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV4); } } + // SSU2 + if (!foundSSU2) + { + bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); + if (ssu2) + { + bool ssu2Published; i2p::config::GetOption("ssu2.published", ssu2Published); + if (ssu2Published) + { + uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); + m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("127.0.0.1"), ssu2Port); + } + else + m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV6); + } + } m_RouterInfo.EnableV4 (); } else @@ -740,7 +883,7 @@ namespace i2p } 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"); @@ -792,7 +935,31 @@ namespace i2p 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; } @@ -839,13 +1006,13 @@ namespace i2p } 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"); - } + } } void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr msg) @@ -881,7 +1048,7 @@ namespace i2p } 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) @@ -895,7 +1062,7 @@ namespace i2p 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"); @@ -908,19 +1075,33 @@ namespace i2p bool RouterContext::DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data) { return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE); - } - - i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () + } + + i2p::crypto::X25519Keys& RouterContext::GetNTCP2StaticKeys () { - if (!m_StaticKeys) + if (!m_NTCP2StaticKeys) { if (!m_NTCP2Keys) NewNTCP2Keys (); 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_StaticKeys; + return *m_NTCP2StaticKeys; + } + + i2p::crypto::X25519Keys& RouterContext::GetSSU2StaticKeys () + { + if (!m_SSU2StaticKeys) + { + if (!m_SSU2Keys) NewSSU2Keys (); + auto x = new i2p::crypto::X25519Keys (m_SSU2Keys->staticPrivateKey, m_SSU2Keys->staticPublicKey); + if (!m_SSU2StaticKeys) + m_SSU2StaticKeys.reset (x); + else + delete x; + } + return *m_SSU2StaticKeys; } } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index 647c1a7f..eb5db38f 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -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,6 +29,7 @@ namespace garlic 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 @@ -61,13 +62,20 @@ namespace garlic 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, @@ -78,10 +86,16 @@ namespace garlic 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; }; @@ -97,11 +111,13 @@ namespace garlic 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 bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer); void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); @@ -156,14 +172,15 @@ namespace garlic void NewRouterInfo (); void UpdateRouterInfo (); void NewNTCP2Keys (); + void NewSSU2Keys (); bool Load (); void SaveKeys (); 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; @@ -177,7 +194,8 @@ namespace garlic 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; }; diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index cb5a3db7..1575cbbc 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -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 i2p { 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_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]; + 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 @@ namespace data } } + 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 @@ namespace data 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; } } @@ -126,8 +134,9 @@ namespace data 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 { @@ -147,11 +156,16 @@ namespace data 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 @@ namespace data } // 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,11 +190,11 @@ namespace data } // 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; } } @@ -194,26 +208,24 @@ namespace data // read addresses auto addresses = boost::make_shared(); uint8_t numAddresses; - s.read ((char *)&numAddresses, sizeof (numAddresses)); + 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; } @@ -224,6 +236,12 @@ namespace data 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]; @@ -250,21 +268,35 @@ namespace data 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 { - Base64ToByteStream (value, strlen (value), address->ntcp2->iv, 16); - address->published = true; // presence if "i" means "published" + 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")) + { + if (!strcmp (value, "2")) + isV2 = true; + else + LogPrint (eLogWarning, "RouterInfo: Unexpected value ", value, " for v"); } else if (key[0] == 'i') { @@ -283,11 +315,11 @@ namespace data if (s) continue; else return; } if (index >= address->ssu->introducers.size ()) - { + { if (address->ssu->introducers.empty ()) // first time address->ssu->introducers.reserve (3); address->ssu->introducers.resize (index + 1); - } + } Introducer& introducer = address->ssu->introducers.at (index); if (!strcmp (key, "ihost")) { @@ -298,7 +330,7 @@ namespace data 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); @@ -308,39 +340,39 @@ namespace data 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 ()) { @@ -350,27 +382,83 @@ namespace data 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; } } @@ -384,6 +472,9 @@ namespace data 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); @@ -395,7 +486,7 @@ namespace data 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")) @@ -416,36 +507,39 @@ namespace data } } // 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) @@ -492,10 +586,10 @@ namespace data { 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; @@ -508,13 +602,569 @@ namespace data } return caps; } + + bool RouterInfo::IsNewer (const uint8_t * buf, size_t len) const + { + 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 (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 + { + LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len); + s.seekg (l, std::ios::cur); // skip + str[0] = 0; + } + return l+1; + } + + + 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::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, + const boost::asio::ip::address& host, int port, uint8_t caps) + { + auto addr = std::make_shared
(); + addr->host = host; + addr->port = port; + addr->transportStyle = eTransportNTCP; + addr->caps = 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)); + } + + void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, uint8_t caps) + { + auto addr = std::make_shared
(); + addr->transportStyle = eTransportSSU2; + addr->caps = caps; + addr->date = 0; + addr->ssu.reset (new SSUExt ()); + addr->ssu->mtu = 0; + memcpy (addr->s, staticKey, 32); + memcpy (addr->i, introKey, 32); + if (addr->IsV4 ()) m_SupportedTransports |= eSSU2V4; + if (addr->IsV6 ()) m_SupportedTransports |= eSSU2V6; + m_Addresses->push_back(std::move(addr)); + } + + void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, + const boost::asio::ip::address& host, int port) + { + auto addr = std::make_shared
(); + addr->transportStyle = eTransportSSU2; + addr->host = host; + addr->port = port; + addr->published = true; + addr->caps = 0; + addr->date = 0; + addr->ssu.reset (new SSUExt ()); + addr->ssu->mtu = 0; + memcpy (addr->s, staticKey, 32); + memcpy (addr->i, introKey, 32); + if (addr->IsV4 ()) + { + m_SupportedTransports |= eSSU2V4; + m_ReachableTransports |= eSSU2V4; + } + if (addr->IsV6 ()) + { + m_SupportedTransports |= eSSU2V6; + m_ReachableTransports |= eSSU2V6; + } + m_Addresses->push_back(std::move(addr)); + } + + bool RouterInfo::AddIntroducer (const Introducer& introducer) + { + for (auto& addr : *m_Addresses) + { + if (addr->transportStyle == eTransportSSU && + ((addr->IsV4 () && introducer.iHost.is_v4 ()) || (addr->IsV6 () && introducer.iHost.is_v6 ()))) + { + 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 ()))) + { + 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; + } + + bool RouterInfo::IsSSU (bool v4only) const + { + if (v4only) + return m_SupportedTransports & eSSUV4; + else + return m_SupportedTransports & (eSSUV4 | eSSUV6); + } + + bool RouterInfo::IsNTCP2 (bool v4only) const + { + if (v4only) + return m_SupportedTransports & eNTCP2V4; + else + return m_SupportedTransports & (eNTCP2V4 | eNTCP2V6); + } + + + void RouterInfo::EnableV6 () + { + if (!IsV6 ()) + { + uint8_t addressCaps = AddressCaps::eV6; + if (IsV4 ()) addressCaps |= AddressCaps::eV4; + SetUnreachableAddressesTransportCaps (addressCaps); + UpdateSupportedTransports (); + } + } + + void RouterInfo::EnableV4 () + { + if (!IsV4 ()) + { + uint8_t addressCaps = AddressCaps::eV4; + if (IsV6 ()) addressCaps |= AddressCaps::eV6; + SetUnreachableAddressesTransportCaps (addressCaps); + UpdateSupportedTransports (); + } + } + + + void RouterInfo::DisableV6 () + { + if (IsV6 ()) + { + 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 (); + } + } + + void RouterInfo::DisableV4 () + { + if (IsV4 ()) + { + 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 (); + } + } + + 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 ();) + { + auto addr = *it; + if (i2p::util::net::IsYggdrasilAddress (addr->host)) + it = m_Addresses->erase (it); + else + ++it; + } + } + } + + std::shared_ptr RouterInfo::GetSSUAddress (bool v4only) const + { + return GetAddress ( + [v4only](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU) && (!v4only || address->IsV4 ()); + }); + } + + std::shared_ptr RouterInfo::GetSSUV6Address () const + { + return GetAddress ( + [](std::shared_ptr address)->bool + { + return (address->transportStyle == eTransportSSU) && address->IsV6(); + }); + } + + std::shared_ptr RouterInfo::GetSSU2V4Address () const + { + 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 + { + if (m_SupportedTransports & eSSU2V6) + return GetSSU2V6Address (); + } + return nullptr; + } - void RouterInfo::UpdateCapsProperty () + 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; + } + + std::shared_ptr RouterInfo::GetNTCP2AddressWithStaticKey (const uint8_t * key) const + { + if (!key) return nullptr; + return GetAddress ( + [key](std::shared_ptr address)->bool + { + return address->IsNTCP2 () && !memcmp (address->s, key, 32); + }); + } + + std::shared_ptr RouterInfo::GetSSU2AddressWithStaticKey (const uint8_t * key, bool isV6) const + { + if (!key) return nullptr; + return GetAddress ( + [key, isV6](std::shared_ptr address)->bool + { + return address->IsSSU2 () && !memcmp (address->s, key, 32) && address->IsV6 () == isV6; + }); + } + + 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); + }); + } + + 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 + { + if (!m_Profile) + m_Profile = GetRouterProfile (GetIdentHash ()); + return m_Profile; + } + + void RouterInfo::Encrypt (const uint8_t * data, uint8_t * encrypted) const + { + auto encryptor = m_RouterIdentity->CreateEncryptor (nullptr); + if (encryptor) + encryptor->Encrypt (data, encrypted); + } + + 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 + { + 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::IsSSU2PeerTesting (bool v4) const + { + 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 (); + }); + } + + void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) + { + for (auto& addr: *m_Addresses) + { + // TODO: implement SSU + if (!addr->published && (addr->transportStyle == eTransportNTCP || addr->transportStyle == eTransportSSU2)) + { + addr->caps &= ~(eV4 | eV6); + addr->caps |= transports; + } + } + } + + void RouterInfo::UpdateSupportedTransports () + { + 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) + { + if (!m_Buffer) + m_Buffer = NewBuffer (); + if (len > m_Buffer->size ()) len = m_Buffer->size (); + memcpy (m_Buffer->data (), buf, len); + m_BufferLen = len; + } + + std::shared_ptr RouterInfo::NewBuffer () const + { + return netdb.NewRouterInfoBuffer (); + } + + void RouterInfo::RefreshTimestamp () + { + m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); + } + + void LocalRouterInfo::CreateBuffer (const PrivateKeys& privateKeys) + { + 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 LocalRouterInfo::UpdateCaps (uint8_t caps) + { + SetCaps (caps); + UpdateCapsProperty (); + } + + void LocalRouterInfo::UpdateCapsProperty () { std::string caps; - if (m_Caps & eFloodfill) + uint8_t c = GetCaps (); + if (c & eFloodfill) { - if (m_Caps & eExtraBandwidth) caps += (m_Caps & eHighBandwidth) ? + if (c & eExtraBandwidth) caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X' CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' else @@ -523,27 +1173,28 @@ namespace data } else { - if (m_Caps & eExtraBandwidth) - caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */ + if (c & eExtraBandwidth) + caps += (c & 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 + caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth } - 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 + 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::WriteToStream (std::ostream& s) const + void LocalRouterInfo::WriteToStream (std::ostream& s) const { - uint64_t ts = htobe64 (m_Timestamp); + uint64_t ts = htobe64 (GetTimestamp ()); s.write ((const char *)&ts, sizeof (ts)); // addresses - uint8_t numAddresses = m_Addresses->size (); + const Addresses& addresses = GetAddresses (); + uint8_t numAddresses = addresses.size (); s.write ((char *)&numAddresses, sizeof (numAddresses)); - for (const auto& addr_ptr : *m_Addresses) + for (const auto& addr_ptr : addresses) { const Address& address = *addr_ptr; // calculate cost @@ -552,6 +1203,8 @@ namespace data 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; @@ -559,10 +1212,10 @@ namespace data if (address.transportStyle == eTransportNTCP) { if (address.IsNTCP2 ()) - { + { WriteString ("NTCP2", s); if (address.IsPublishedNTCP2 () && !address.host.is_unspecified () && address.port) - isPublished = true; + isPublished = true; else { WriteString ("caps", properties); @@ -573,48 +1226,73 @@ namespace data 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; + 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); @@ -625,10 +1303,17 @@ namespace data WriteString (address.host.to_string (), properties); properties << ';'; } - if (address.transportStyle == eTransportSSU) + if ((address.IsNTCP2 () && isPublished) || address.IsSSU2 ()) + { + // publish i for NTCP2 or SSU2 + WriteString ("i", properties); properties << '='; + size_t len = address.IsSSU2 () ? 32 : 16; + WriteString (address.i.ToBase64 (len), properties); properties << ';'; + } + if (address.transportStyle == eTransportSSU || address.IsSSU2 ()) { // write introducers if any - if (!address.ssu->introducers.empty()) + if (address.ssu && !address.ssu->introducers.empty()) { int i = 0; for (const auto& introducer: address.ssu->introducers) @@ -642,19 +1327,25 @@ namespace data } i++; } - i = 0; - for (const auto& introducer: address.ssu->introducers) + if (address.transportStyle == eTransportSSU) { - 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 ("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); + 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); @@ -663,14 +1354,17 @@ namespace data properties << ';'; i++; } - i = 0; - for (const auto& introducer: address.ssu->introducers) + if (address.transportStyle == eTransportSSU) { - 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 ("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) @@ -682,16 +1376,22 @@ namespace data i++; } } + } + if (address.transportStyle == eTransportSSU) + { // write intro key WriteString ("key", properties); properties << '='; char value[64]; - size_t l = ByteStreamToBase64 (address.ssu->key, 32, value, 64); + size_t l = ByteStreamToBase64 (address.i, 32, value, 64); value[l] = 0; WriteString (value, properties); properties << ';'; + } + if (address.transportStyle == eTransportSSU || address.IsSSU2 ()) + { // write mtu - if (address.ssu->mtu) + if (address.ssu && address.ssu->mtu) { WriteString ("mtu", properties); properties << '='; @@ -699,26 +1399,18 @@ namespace data properties << ';'; } } - - if (address.IsNTCP2 () && isPublished) - { - // publish i for NTCP2 - WriteString ("i", properties); properties << '='; - WriteString (address.ntcp2->iv.ToBase64 (), properties); properties << ';'; - } - - if (isPublished || address.ssu) + if (isPublished || (address.ssu && !address.IsSSU2 ())) { WriteString ("port", properties); properties << '='; WriteString (boost::lexical_cast(address.port), properties); properties << ';'; } - if (address.IsNTCP2 ()) + if (address.IsNTCP2 () || address.IsSSU2 ()) { - // publish s and v for NTCP2 + // publish s and v for NTCP2 or SSU2 WriteString ("s", properties); properties << '='; - WriteString (address.ntcp2->staticKey.ToBase64 (), properties); properties << ';'; + WriteString (address.s.ToBase64 (), properties); properties << ';'; WriteString ("v", properties); properties << '='; WriteString ("2", properties); properties << ';'; } @@ -746,198 +1438,17 @@ namespace data s.write (properties.str ().c_str (), properties.str ().size ()); } - bool RouterInfo::IsNewer (const uint8_t * buf, size_t len) const - { - 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 (LoadFile (fullPath)) - LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file"); - } - return m_Buffer; - } - - void RouterInfo::CreateBuffer (const PrivateKeys& privateKeys) - { - 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) - { - 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; - } - else - LogPrint (eLogError, "RouterInfo: Our RouterInfo is too long ", m_BufferLen + signatureLen); - } - - 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, 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 - { - LogPrint (eLogWarning, "RouterInfo: string length ", (int)l, " exceeds buffer size ", len); - s.seekg (l, std::ios::cur); // skip - str[0] = 0; - } - return l+1; - } - - void RouterInfo::WriteString (const std::string& str, std::ostream& s) const - { - uint8_t len = str.size (); - s.write ((char *)&len, 1); - s.write (str.c_str (), len); - } - - 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->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)); - } - - void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, - const boost::asio::ip::address& host, int port, uint8_t caps) - { - auto addr = std::make_shared
(); - addr->host = host; - addr->port = port; - addr->transportStyle = eTransportNTCP; - addr->caps = caps; - addr->date = 0; - addr->ntcp2.reset (new NTCP2Ext ()); - if (port) addr->published = true; - memcpy (addr->ntcp2->staticKey, staticKey, 32); - memcpy (addr->ntcp2->iv, iv, 16); - if (addr->IsV4 ()) - { - m_SupportedTransports |= eNTCP2V4; - if (addr->published) m_ReachableTransports |= eNTCP2V4; - } - if (addr->IsV6 ()) - { - m_SupportedTransports |= eNTCP2V6; - if (addr->published) m_ReachableTransports |= eNTCP2V6; - } - m_Addresses->push_back(std::move(addr)); - } - - 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 ()))) - { - 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 ()))) - { - 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; - } - - void RouterInfo::SetCaps (uint8_t caps) - { - m_Caps = caps; - UpdateCapsProperty (); - } - - void RouterInfo::SetCaps (const char * caps) - { - SetProperty ("caps", caps); - m_Caps = 0; - ExtractCaps (caps); - } - - void RouterInfo::SetProperty (const std::string& key, const std::string& value) + void LocalRouterInfo::SetProperty (const std::string& key, const std::string& value) { m_Properties[key] = value; } - void RouterInfo::DeleteProperty (const std::string& key) + void LocalRouterInfo::DeleteProperty (const std::string& key) { m_Properties.erase (key); } - std::string RouterInfo::GetProperty (const std::string& key) const + std::string LocalRouterInfo::GetProperty (const std::string& key) const { auto it = m_Properties.find (key); if (it != m_Properties.end ()) @@ -945,295 +1456,16 @@ namespace data return ""; } - bool RouterInfo::IsSSU (bool v4only) const + void LocalRouterInfo::WriteString (const std::string& str, std::ostream& s) const { - if (v4only) - return m_SupportedTransports & eSSUV4; - else - return m_SupportedTransports & (eSSUV4 | eSSUV6); + uint8_t len = str.size (); + s.write ((char *)&len, 1); + s.write (str.c_str (), len); } - bool RouterInfo::IsSSUV6 () const + std::shared_ptr LocalRouterInfo::NewBuffer () const { - return m_SupportedTransports & eSSUV6; + return std::make_shared (); } - - bool RouterInfo::IsNTCP2 (bool v4only) const - { - if (v4only) - return m_SupportedTransports & eNTCP2V4; - else - return m_SupportedTransports & (eNTCP2V4 | eNTCP2V6); - } - - bool RouterInfo::IsNTCP2V6 () const - { - return m_SupportedTransports & eNTCP2V6; - } - - bool RouterInfo::IsV6 () const - { - return m_SupportedTransports & (eSSUV6 | eNTCP2V6); - } - - bool RouterInfo::IsV4 () const - { - return m_SupportedTransports & (eSSUV4 | eNTCP2V4); - } - - bool RouterInfo::IsMesh () const - { - return m_SupportedTransports & eNTCP2V6Mesh; - } - - void RouterInfo::EnableV6 () - { - if (!IsV6 ()) - { - uint8_t addressCaps = AddressCaps::eV6; - if (IsV4 ()) addressCaps |= AddressCaps::eV4; - SetUnreachableAddressesTransportCaps (addressCaps); - UpdateSupportedTransports (); - } - } - - void RouterInfo::EnableV4 () - { - if (!IsV4 ()) - { - uint8_t addressCaps = AddressCaps::eV4; - if (IsV6 ()) addressCaps |= AddressCaps::eV6; - SetUnreachableAddressesTransportCaps (addressCaps); - UpdateSupportedTransports (); - } - } - - - void RouterInfo::DisableV6 () - { - if (IsV6 ()) - { - 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 (); - } - } - - void RouterInfo::DisableV4 () - { - if (IsV4 ()) - { - 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 (); - } - } - - 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 ();) - { - auto addr = *it; - if (i2p::util::net::IsYggdrasilAddress (addr->host)) - it = m_Addresses->erase (it); - else - ++it; - } - } - } - - std::shared_ptr RouterInfo::GetSSUAddress (bool v4only) const - { - return GetAddress ( - [v4only](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && (!v4only || address->IsV4 ()); - }); - } - - std::shared_ptr RouterInfo::GetSSUV6Address () const - { - return GetAddress ( - [](std::shared_ptr address)->bool - { - return (address->transportStyle == eTransportSSU) && address->IsV6(); - }); - } - - 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; - } - - std::shared_ptr RouterInfo::GetNTCP2AddressWithStaticKey (const uint8_t * key) const - { - if (!key) return nullptr; - return GetAddress ( - [key](std::shared_ptr address)->bool - { - return address->IsNTCP2 () && !memcmp (address->ntcp2->staticKey, key, 32); - }); - } - - 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); - }); - } - - 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 - { - if (!m_Profile) - m_Profile = GetRouterProfile (GetIdentHash ()); - return m_Profile; - } - - void RouterInfo::Encrypt (const uint8_t * data, uint8_t * encrypted) const - { - auto encryptor = m_RouterIdentity->CreateEncryptor (nullptr); - if (encryptor) - encryptor->Encrypt (data, encrypted); - } - - 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 - { - 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::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) - { - for (auto& addr: *m_Addresses) - { - // TODO: implement SSU - if (addr->transportStyle == eTransportNTCP && !addr->IsPublishedNTCP2 ()) - { - addr->caps &= ~(eV4 | eV6); - addr->caps |= transports; - } - } - } - - void RouterInfo::UpdateSupportedTransports () - { - m_SupportedTransports = 0; - m_ReachableTransports = 0; - for (const auto& addr: *m_Addresses) - { - uint8_t transports = 0; - if (addr->transportStyle == eTransportNTCP) - { - 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; - } - } } } diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index b2b883dc..1aa90ce3 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -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,11 +13,13 @@ #include #include #include +#include #include #include #include #include "Identity.h" #include "Profiling.h" +#include "Family.h" namespace i2p { @@ -50,10 +52,12 @@ namespace data 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: @@ -64,10 +68,13 @@ namespace data eNTCP2V6 = 0x02, eSSUV4 = 0x04, eSSUV6 = 0x08, - eNTCP2V6Mesh = 0x10 + eNTCP2V6Mesh = 0x10, + eSSU2V4 = 0x20, + eSSU2V6 = 0x40, + eAllTransports = 0xFF }; typedef uint8_t CompatibleTransports; - + enum Caps { eFloodfill = 0x01, @@ -85,12 +92,13 @@ namespace data 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 @@ namespace data 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 @@ namespace data 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 @@ namespace data bool operator==(const Address& other) const { - return transportStyle == other.transportStyle && IsNTCP2 () == other.IsNTCP2 () && + return transportStyle == other.transportStyle && host == other.host && port == other.port; } @@ -145,89 +146,105 @@ namespace data 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 ()); }; }; + + class Buffer: public std::array + { + public: + + Buffer () = default; + Buffer (const uint8_t * buf, size_t len); + }; + typedef std::vector > Addresses; - RouterInfo (); 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; }; - bool IsReachableFrom (const RouterInfo& other) const { return m_ReachableTransports & 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 (CompatibleTransports transports) const { return m_ReachableTransports & transports; }; - CompatibleTransports GetCompatibleTransports (bool incoming) const { return incoming ? m_ReachableTransports : m_SupportedTransports; }; - bool HasValidAddresses () const { return m_SupportedTransports; }; + 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; + 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 * GetBuffer () const { return m_Buffer->data (); }; const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary - int GetBufferLen () const { return m_BufferLen; }; - void CreateBuffer (const PrivateKeys& privateKeys); + size_t GetBufferLen () const { return m_BufferLen; }; bool IsUpdated () const { return m_IsUpdated; }; void SetUpdated (bool updated) { m_IsUpdated = updated; }; @@ -237,11 +254,11 @@ namespace data 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; }; @@ -249,36 +266,67 @@ namespace data 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; }; + private: 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_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; 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 (); }; + + 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 --git a/libi2pd/SSU.cpp b/libi2pd/SSU.cpp index be263370..eec55857 100644 --- a/libi2pd/SSU.cpp +++ b/libi2pd/SSU.cpp @@ -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 @@ namespace transport 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 @@ namespace transport } 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 @@ namespace transport 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 @@ namespace transport } 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 @@ namespace transport } catch (std::exception& ex) { - LogPrint (eLogError, "SSU: server runtime exception: ", ex.what ()); + LogPrint (eLogError, "SSU: Server runtime exception: ", ex.what ()); } } } @@ -173,7 +176,7 @@ namespace transport } 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 @@ -249,7 +252,7 @@ namespace transport 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, ")"); } } @@ -270,14 +273,14 @@ namespace transport 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, @@ -318,7 +321,7 @@ namespace transport 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 @@ namespace transport 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, @@ -409,7 +412,7 @@ namespace transport 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) @@ -579,7 +582,7 @@ namespace transport "] 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 @@ namespace transport 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 @@ namespace transport 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 @@ namespace transport } else { - LogPrint (eLogDebug, "SSU: can't find more introducers"); + LogPrint (eLogDebug, "SSU: Can't find more introducers"); break; } } @@ -923,7 +926,7 @@ namespace transport m_FragmentsPool.CleanUp (); m_IncompleteMessagesPool.CleanUp (); m_SentMessagesPool.CleanUp (); - + SchedulePeerTestsCleanupTimer (); } } @@ -946,10 +949,10 @@ namespace transport { 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 (); }); } @@ -977,10 +980,10 @@ namespace transport { 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 (); }); } diff --git a/libi2pd/SSU.h b/libi2pd/SSU.h index b97cacb4..25ce4d40 100644 --- a/libi2pd/SSU.h +++ b/libi2pd/SSU.h @@ -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 * @@ -67,17 +67,18 @@ namespace transport 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); @@ -98,7 +99,7 @@ namespace transport 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); @@ -134,8 +135,9 @@ namespace transport 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 @@ -145,7 +147,7 @@ namespace transport i2p::util::MemoryPool m_IncompleteMessagesPool; i2p::util::MemoryPool m_SentMessagesPool; i2p::util::MemoryPoolMt m_PacketsPool; - + public: // for HTTP only const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp new file mode 100644 index 00000000..beca55cd --- /dev/null +++ b/libi2pd/SSU2.cpp @@ -0,0 +1,715 @@ +/* +* 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 "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 ()) + { + } + + void SSU2Server::Start () + { + if (!IsRunning ()) + { + StartIOService (); + 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 + { + uint16_t p; i2p::config::GetOption ("port", p); + if (p) port = 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); + }); + } + if (address->IsV6 ()) + { + found = true; + OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV6, port)); + m_ReceiveService.GetService ().post( + [this]() + { + Receive (m_SocketV6); + }); + } + } + else + LogPrint (eLogError, "SSU2: Can't start server because port not specified"); + } + } + if (found) + m_ReceiveService.Start (); + ScheduleTermination (); + } + } + + void SSU2Server::Stop () + { + for (auto& it: m_Sessions) + it.second->Done (); + m_Sessions.clear (); + m_SessionsByRouterHash.clear (); + m_PendingOutgoingSessions.clear (); + + if (context.SupportsV4 () || context.SupportsV6 ()) + m_ReceiveService.Stop (); + + m_SocketV4.close (); + m_SocketV6.close (); + if (IsRunning ()) + m_TerminationTimer.cancel (); + + StopIOService (); + } + + void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) + { + if (localAddress.is_unspecified ()) return; + if (localAddress.is_v4 ()) + m_AddressV4 = localAddress; + else if (localAddress.is_v6 ()) + m_AddressV6 = localAddress; + } + + 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; + } + + 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_MTU), packet->from, + std::bind (&SSU2Server::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet, std::ref (socket))); + } + + void SSU2Server::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, + Packet * packet, boost::asio::ip::udp::socket& socket) + { + if (!ecode) + { + i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); + packet->len = bytes_transferred; + + boost::system::error_code ec; + size_t moreBytes = socket.available (ec); + if (!ec && moreBytes) + { + std::vector packets; + packets.push_back (packet); + while (moreBytes && packets.size () < 32) + { + packet = m_PacketsPool.AcquireMt (); + packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MTU), packet->from, 0, ec); + if (!ec) + { + i2p::transport::transports.UpdateReceivedBytes (packet->len); + packets.push_back (packet); + moreBytes = socket.available(ec); + if (ec) break; + } + else + { + LogPrint (eLogError, "SSU2: receive_from error: code ", ec.value(), ": ", ec.message ()); + m_PacketsPool.ReleaseMt (packet); + break; + } + } + GetService ().post (std::bind (&SSU2Server::HandleReceivedPackets, this, packets)); + } + else + GetService ().post (std::bind (&SSU2Server::HandleReceivedPacket, this, packet)); + Receive (socket); + } + else + { + m_PacketsPool.ReleaseMt (packet); + if (ecode != boost::asio::error::operation_aborted) + { + LogPrint (eLogError, "SSU2: Receive error: code ", ecode.value(), ": ", ecode.message ()); + auto ep = socket.local_endpoint (); + socket.close (); + OpenSocket (ep); + Receive (socket); + } + } + } + + void SSU2Server::HandleReceivedPacket (Packet * packet) + { + if (packet) + { + ProcessNextPacket (packet->buf, packet->len, packet->from); + m_PacketsPool.ReleaseMt (packet); + if (m_LastSession) m_LastSession->FlushData (); + } + } + + void SSU2Server::HandleReceivedPackets (std::vector packets) + { + for (auto& packet: packets) + ProcessNextPacket (packet->buf, packet->len, packet->from); + m_PacketsPool.ReleaseMt (packets); + if (m_LastSession) m_LastSession->FlushData (); + } + + void SSU2Server::AddSession (std::shared_ptr session) + { + if (session) + { + m_Sessions.emplace (session->GetConnID (), session); + AddSessionByRouterHash (session); + } + } + + void SSU2Server::RemoveSession (uint64_t connID) + { + auto it = m_Sessions.find (connID); + if (it != m_Sessions.end ()) + { + auto ident = it->second->GetRemoteIdentity (); + if (ident) + m_SessionsByRouterHash.erase (ident->GetIdentHash ()); + m_Sessions.erase (it); + } + } + + void SSU2Server::AddSessionByRouterHash (std::shared_ptr session) + { + if (session) + { + auto ident = session->GetRemoteIdentity (); + if (ident) + { + auto ret = m_SessionsByRouterHash.emplace (ident->GetIdentHash (), session); + if (!ret.second) + { + // session already exists + LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " aready exists"); + // terminate existing + GetService ().post (std::bind (&SSU2Session::Terminate, ret.first->second)); + // update session + ret.first->second = session; + } + } + } + } + + 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: + m_LastSession->ProcessSessionConfirmed (buf, len); + 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->Terminate (); + m_LastSession = nullptr; + } + break; + case eSSU2SessionStatePeerTest: + m_LastSession->SetRemoteEndpoint (senderEndpoint); + m_LastSession->ProcessPeerTest (buf, len); + 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 is no peding session + bool isValidEndpoint = !address->host.is_unspecified () && address->port; + if (isValidEndpoint) + { + 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; + for (auto& it: address->ssu->introducers) + { + if (it.iTag && ts < it.iExp) + { + r = i2p::data::netdb.FindRouter (it.iKey); + if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ())) + { + relayTag = it.iTag; + if (relayTag) break; + } + } + } + if (r) + { + if (relayTag) + { + // introducer and tag found connect to it through SSU2 + auto addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); + if (addr) + { + bool isValidEndpoint = !addr->host.is_unspecified () && addr->port; + 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; + } + 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.begin (); it != m_Sessions.end ();) + { + if (it->second->GetState () == eSSU2SessionStateTerminated || + it->second->IsTerminationTimeoutExpired (ts)) + { + if (it->second->IsEstablished ()) + it->second->TerminateByTimeout (); + if (it->second == m_LastSession) + m_LastSession = nullptr; + it = m_Sessions.erase (it); + } + else + { + it->second->CleanUp (ts); + it++; + } + } + + for (auto it = m_IncomingTokens.begin (); it != m_IncomingTokens.end (); ) + { + if (ts > it->second.second) + it = m_IncomingTokens.erase (it); + else + it++; + } + + for (auto it = m_OutgoingTokens.begin (); it != m_OutgoingTokens.end (); ) + { + if (ts > it->second.second) + it = m_OutgoingTokens.erase (it); + else + it++; + } + + ScheduleTermination (); + } + } + + void SSU2Server::ScheduleResend () + { + m_ResendTimer.expires_from_now (boost::posix_time::seconds(SSU2_RESEND_INTERVAL)); + m_ResendTimer.async_wait (std::bind (&SSU2Server::HandleResendTimer, + this, std::placeholders::_1)); + } + + void SSU2Server::HandleResendTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it: m_Sessions) + it.second->Resend (ts); + 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; + } +} +} diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h new file mode 100644 index 00000000..429cfdb7 --- /dev/null +++ b/libi2pd/SSU2.h @@ -0,0 +1,123 @@ +/* +* 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; // 30 seconds + const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K + const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K + + class SSU2Server: private i2p::util::RunnableServiceWithWork + { + struct Packet + { + uint8_t buf[SSU2_MTU]; + 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; + + 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); + + + 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); + + 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::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 + i2p::util::MemoryPoolMt m_PacketsPool; + boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer; + std::shared_ptr m_LastSession; + + public: + + // for HTTP/I2PControl + const decltype(m_Sessions)& GetSSU2Sessions () const { return m_Sessions; }; + }; +} +} + +#endif diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp new file mode 100644 index 00000000..55c07b90 --- /dev/null +++ b/libi2pd/SSU2Session.cpp @@ -0,0 +1,2153 @@ +/* +* 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 "RouterContext.h" +#include "Transports.h" +#include "Gzip.h" +#include "NetDb.hpp" +#include "SSU2.h" + +namespace i2p +{ +namespace transport +{ + 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_MAX_WINDOW_SIZE), m_RelayTag (0), + m_ConnectTimer (server.GetService ()) + { + 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_PAYLOAD_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, SSU2_MAX_PAYLOAD_SIZE - 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, SSU2_MAX_PAYLOAD_SIZE - 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::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_PAYLOAD_SIZE]; + size_t payloadSize = CreatePeerTestBlock (payload, SSU2_MAX_PAYLOAD_SIZE, nonce); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize); + SendData (payload, payloadSize); + } + + void SSU2Session::Terminate () + { + if (m_State != eSSU2SessionStateTerminated) + { + m_State = eSSU2SessionStateTerminated; + m_ConnectTimer.cancel (); + transports.PeerDisconnected (shared_from_this ()); + m_OnEstablished = nullptr; + m_Server.RemoveSession (m_SourceConnID); + if (m_RelayTag) + m_Server.RemoveRelay (m_RelayTag); + m_SentHandshakePacket.reset (nullptr); + m_SendQueue.clear (); + LogPrint (eLogDebug, "SSU2: Session terminated"); + } + } + + void SSU2Session::TerminateByTimeout () + { + SendTermination (); + m_Server.GetService ().post (std::bind (&SSU2Session::Terminate, shared_from_this ())); + } + + void SSU2Session::Established () + { + m_State = eSSU2SessionStateEstablished; + m_EphemeralKeys = nullptr; + m_NoiseState.reset (nullptr); + m_SessionConfirmedFragment1.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_PAYLOAD_SIZE]; + size_t payloadSize = s->CreateRouterInfoBlock (payload, SSU2_MAX_PAYLOAD_SIZE - 32, i2p::context.GetSharedRouterInfo ()); + if (payloadSize) + { + if (payloadSize < SSU2_MAX_PAYLOAD_SIZE) + payloadSize += s->CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - 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) + { + for (auto it: msgs) + m_SendQueue.push_back (it); + SendQueue (); + } + + bool SSU2Session::SendQueue () + { + if (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize) + { + auto nextResend = i2p::util::GetSecondsSinceEpoch () + SSU2_RESEND_INTERVAL; + auto packet = std::make_shared(); + packet->payloadSize += CreateAckBlock (packet->payload + packet->payloadSize, SSU2_MAX_PAYLOAD_SIZE - packet->payloadSize); + while (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize) + { + auto msg = m_SendQueue.front (); + size_t len = msg->GetNTCP2Length (); + if (len + 3 < SSU2_MAX_PAYLOAD_SIZE - packet->payloadSize) + { + m_SendQueue.pop_front (); + packet->payloadSize += CreateI2NPBlock (packet->payload + packet->payloadSize, SSU2_MAX_PAYLOAD_SIZE - packet->payloadSize, std::move (msg)); + } + else if (len > SSU2_MAX_PAYLOAD_SIZE) // message too long + { + m_SendQueue.pop_front (); + SendFragmentedMessage (msg); + } + else + { + // send right a way + if (packet->payloadSize + 16 < SSU2_MAX_PAYLOAD_SIZE) + packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, SSU2_MAX_PAYLOAD_SIZE - packet->payloadSize); + uint32_t packetNum = SendData (packet->payload, packet->payloadSize); + packet->nextResendTime = nextResend; + m_SentPackets.emplace (packetNum, packet); + packet = std::make_shared(); + packet->payloadSize += CreateAckBlock (packet->payload + packet->payloadSize, SSU2_MAX_PAYLOAD_SIZE - packet->payloadSize); + } + }; + if (packet->payloadSize) + { + if (packet->payloadSize + 16 < SSU2_MAX_PAYLOAD_SIZE) + packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, SSU2_MAX_PAYLOAD_SIZE - packet->payloadSize); + uint32_t packetNum = SendData (packet->payload, packet->payloadSize); + packet->nextResendTime = nextResend; + m_SentPackets.emplace (packetNum, packet); + } + return true; + } + return false; + } + + void SSU2Session::SendFragmentedMessage (std::shared_ptr msg) + { + uint32_t msgID; + memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); + auto nextResend = i2p::util::GetSecondsSinceEpoch () + SSU2_RESEND_INTERVAL; + auto packet = std::make_shared(); + packet->payloadSize = CreateAckBlock (packet->payload, SSU2_MAX_PAYLOAD_SIZE); + auto size = CreateFirstFragmentBlock (packet->payload + packet->payloadSize, SSU2_MAX_PAYLOAD_SIZE - 16 - packet->payloadSize, msg); + if (!size) return; + packet->payloadSize += size; + packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, SSU2_MAX_PAYLOAD_SIZE - 16 - packet->payloadSize); + uint32_t firstPacketNum = SendData (packet->payload, packet->payloadSize); + packet->nextResendTime = nextResend; + m_SentPackets.emplace (firstPacketNum, packet); + uint8_t fragmentNum = 0; + while (msg->offset < msg->len) + { + packet = std::make_shared(); + packet->payloadSize = CreateFollowOnFragmentBlock (packet->payload, SSU2_MAX_PAYLOAD_SIZE - 16, msg, fragmentNum, msgID); + packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, SSU2_MAX_PAYLOAD_SIZE - 16 - packet->payloadSize); + uint32_t followonPacketNum = SendData (packet->payload, packet->payloadSize); + packet->nextResendTime = nextResend; + m_SentPackets.emplace (followonPacketNum, packet); + } + } + + void SSU2Session::Resend (uint64_t ts) + { + // resend handshake packet + if (m_SentHandshakePacket && ts >= m_SentHandshakePacket->nextResendTime) + { + LogPrint (eLogDebug, "SSU2: Resending ", (int)m_State); + m_Server.Send (m_SentHandshakePacket->header.buf, 16, m_SentHandshakePacket->headerX, 48, + m_SentHandshakePacket->payload, m_SentHandshakePacket->payloadSize, m_RemoteEndpoint); + m_SentHandshakePacket->numResends++; + m_SentHandshakePacket->nextResendTime = ts + SSU2_HANDSHAKE_RESEND_INTERVAL; + 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->nextResendTime) + { + if (it->second->numResends > SSU2_MAX_NUM_RESENDS) + it = m_SentPackets.erase (it); + else + { + uint32_t packetNum = SendData (it->second->payload, it->second->payloadSize); + it->second->numResends++; + it->second->nextResendTime = ts + it->second->numResends*SSU2_RESEND_INTERVAL; + m_LastActivityTimestamp = 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 + } + SendQueue (); + } + + 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; + } + default: + { + LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " from ", m_RemoteEndpoint); + 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::GetSecondsSinceEpoch (); + m_SentHandshakePacket->nextResendTime = ts + SSU2_HANDSHAKE_RESEND_INTERVAL; + + 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); + size_t payloadSize = 7; + 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 + 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::GetSecondsSinceEpoch (); + m_SentHandshakePacket->nextResendTime = ts + SSU2_HANDSHAKE_RESEND_INTERVAL; + + 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 + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, ts); + size_t payloadSize = 7; + payloadSize += CreateAddressBlock (payload + payloadSize, 80 - 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; + } + payloadSize += CreatePaddingBlock (payload + payloadSize, 80 - 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); + } + + 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 + HandlePayload (decryptedPayload.data (), decryptedPayload.size ()); + + m_Server.AddSession (shared_from_this ()); + 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); + auto ts = i2p::util::GetSecondsSinceEpoch (); + m_SentHandshakePacket->nextResendTime = ts + SSU2_HANDSHAKE_RESEND_INTERVAL; + + 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 + const size_t maxPayloadSize = SSU2_MAX_PAYLOAD_SIZE - 48; // part 2 + uint8_t * payload = m_SentHandshakePacket->payload; + size_t payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.GetSharedRouterInfo ()); + // TODO: check is RouterInfo doesn't fit and split by two 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); + // Encrypt header + header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (kh2, payload + (payloadSize - 12)); + m_State = eSSU2SessionStateSessionConfirmedSent; + m_SentHandshakePacket->payloadSize = payloadSize; + // send + m_Server.Send (header.buf, 16, part1, 48, payload, payloadSize, m_RemoteEndpoint); + m_SendPacketNum++; + } + + 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 (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type); + return false; + } + // check if fragmented + if ((header.h.flags[0] & 0x0F) > 1) + { + // fragmented + if (!(header.h.flags[0] & 0xF0)) + { + // first fragment + m_SessionConfirmedFragment1.reset (new HandshakePacket); + m_SessionConfirmedFragment1->header = header; + memcpy (m_SessionConfirmedFragment1->payload, buf + 16, len - 16); + m_SessionConfirmedFragment1->payloadSize = len - 16; + return true; // wait for second fragment + } + else + { + // second fragment + if (!m_SessionConfirmedFragment1) return false; // out of sequence + uint8_t fullMsg[2*SSU2_MTU]; + header = m_SessionConfirmedFragment1->header; + memcpy (fullMsg + 16, m_SessionConfirmedFragment1->payload, m_SessionConfirmedFragment1->payloadSize); + memcpy (fullMsg + 16 + m_SessionConfirmedFragment1->payloadSize, buf + 16, len - 16); + buf = fullMsg; + len += m_SessionConfirmedFragment1->payloadSize; + } + } + // 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 + uint8_t sharedSecret[32]; + m_EphemeralKeys->Agree (S, sharedSecret); + m_NoiseState->MixKey (sharedSecret); + // 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; + } + SetRemoteIdentity (ri->GetRouterIdentity ()); + m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now + 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"); + return false; + } + m_RemoteTransports = ri->GetCompatibleTransports (false); + i2p::data::netdb.PostI2NPMsg (CreateDatabaseStoreMsg (ri)); // TODO: should insert ri + // handle other blocks + HandlePayload (decryptedPayload.data () + riSize + 3, decryptedPayload.size () - riSize - 3); + KDFDataPhase (m_KeyDataReceive, m_KeyDataSend); + 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); + 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) + { + // we are Charlie + Header header; + uint8_t h[32], payload[SSU2_MAX_PAYLOAD_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, SSU2_MAX_PAYLOAD_SIZE - payloadSize, ep); + payloadSize += CreateRelayResponseBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize, + eSSU2RelayResponseCodeAccept ,nonce, true); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - 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); + 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 + 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 (); + } + + 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_PAYLOAD_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, SSU2_MAX_PAYLOAD_SIZE - payloadSize, m_RemoteEndpoint); + payloadSize += CreatePeerTestBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize, + msg, eSSU2PeerTestCodeAccept, nullptr, signedData, signedDataLen); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - 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); + 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_MTU]; + i2p::crypto::AEADChaCha20Poly1305 (buf, len, header.buf, 16, m_KeyDataSend, nonce, payload, SSU2_MTU, 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); + return; + } + uint8_t payload[SSU2_MTU]; + 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 (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"); + break; + case eSSU2BlkOptions: + LogPrint (eLogDebug, "SSU2: Options"); + break; + case eSSU2BlkRouterInfo: + { + // not from SessionConfirmed + LogPrint (eLogDebug, "SSU2: RouterInfo"); + auto ri = ExtractRouterInfo (buf + offset, size); + if (ri) + i2p::data::netdb.PostI2NPMsg (CreateI2NPMessage (eI2NPDummyMsg, ri->GetBuffer (), ri->GetBufferLen ())); // TODO: should insert ri + break; + } + case eSSU2BlkI2NPMessage: + { + LogPrint (eLogDebug, "SSU2: I2NP message"); + auto nextMsg = 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"); + Terminate (); + 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); + break; + case eSSU2BlkNextNonce: + break; + case eSSU2BlkAck: + LogPrint (eLogDebug, "SSU2: Ack"); + HandleAck (buf + offset, size); + break; + case eSSU2BlkAddress: + { + boost::asio::ip::udp::endpoint ep; + if (ExtractEndpoint (buf + offset, size, ep)) + LogPrint (eLogInfo, "SSU2: Our external address is ", ep); + 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: + break; + case eSSU2BlkPathResponse: + break; + case eSSU2BlkFirstPacketNumber: + break; + case eSSU2BlkPadding: + LogPrint (eLogDebug, "SSU2: Padding"); + break; + default: + LogPrint (eLogWarning, "SSU2: Unknown block type ", (int)blk); + } + offset += size; + } + } + + 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); // 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); + } + } + + void SSU2Session::HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum) + { + 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; + while (it1 != m_SentPackets.end () && it1->first <= lastPacketNum) it1++; + m_SentPackets.erase (it, it1); + } + + void SSU2Session::HandleFirstFragment (const uint8_t * buf, size_t len) + { + uint32_t msgID; memcpy (&msgID, buf + 1, 4); + auto msg = NewI2NPMessage (); + // 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 && it->second->msg) + { + // in sequence + it->second->msg->Concat (buf + 5, len - 5); + if (isLast) + { + it->second->msg->FromNTCP2 (); + m_Handler.PutNextMessage (std::move (it->second->msg)); + m_IncompleteMessages.erase (it); + } + else + { + it->second->nextFragmentNum++; + 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 + 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->msg->Concat (it->second->buf, it->second->len); + isLast = it->second->isLast; + it = m->outOfSequenceFragments.erase (it); + m->nextFragmentNum++; + } + 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_PAYLOAD_SIZE]; + size_t payloadSize = CreateRelayResponseBlock (payload, SSU2_MAX_PAYLOAD_SIZE, + eSSU2RelayResponseCodeBobRelayTagNotFound, bufbe32toh (buf + 1), false); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - 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_PAYLOAD_SIZE]; + size_t payloadSize = r ? CreateRouterInfoBlock (payload, SSU2_MAX_PAYLOAD_SIZE - len - 32, r) : 0; + if (!payloadSize && r) + session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); + payloadSize += CreateRelayIntroBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize, buf + 1, len -1); + if (payloadSize < SSU2_MAX_PAYLOAD_SIZE) + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize); + session->SendData (payload, payloadSize); + } + + void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len) + { + // we are Charlie + SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept; + 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 ())) + SendHolePunch (bufbe32toh (buf + 33), ep, addr->i); + else + code = eSSU2RelayResponseCodeCharlieUnsupportedAddress; + } + else + { + LogPrint (eLogWarning, "SSU2: RelayInfo unknown address"); + 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_PAYLOAD_SIZE]; + size_t payloadSize = CreateRelayResponseBlock (payload, SSU2_MAX_PAYLOAD_SIZE, + code, bufbe32toh (buf + 33), true); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - 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_PAYLOAD_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, SSU2_MAX_PAYLOAD_SIZE - 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 + ExtractEndpoint (buf + 12, csz, it->second.first->m_RemoteEndpoint); + } + else + { + LogPrint (eLogWarning, "SSU2: RelayResponse signature verification failed"); + m_Server.GetService ().post (std::bind (&SSU2Session::Terminate, it->second.first)); + } + } + else + { + LogPrint (eLogInfo, "SSU2: RelayResponse status code=", (int)buf[1]); + m_Server.GetService ().post (std::bind (&SSU2Session::Terminate, it->second.first)); + } + } + 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_PAYLOAD_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, SSU2_MAX_PAYLOAD_SIZE - len - 32, r) : 0; + if (!payloadSize && r) + session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); + if (payloadSize + len + 48 > SSU2_MAX_PAYLOAD_SIZE) + { + // doesn't fit one message, send RouterInfo in separate message + session->SendData (payload, payloadSize); + payloadSize = 0; + } + // PeerTest to Charlie + payloadSize += CreatePeerTestBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize, 2, + eSSU2PeerTestCodeAccept, GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - payloadSize); + session->SendData (payload, payloadSize); + } + else + { + // Charlie not found, send error back to Alice + uint8_t payload[SSU2_MAX_PAYLOAD_SIZE], zeroHash[32] = {0}; + size_t payloadSize = CreatePeerTestBlock (payload, SSU2_MAX_PAYLOAD_SIZE, 4, + eSSU2PeerTestCodeBobNoCharlieAvailable, zeroHash, buf + offset, len - offset); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - 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 + 9, len - offset - 9, 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_PAYLOAD_SIZE]; + size_t payloadSize = CreatePeerTestBlock (payload, SSU2_MAX_PAYLOAD_SIZE, 3, + code, nullptr, newSignedData.data (), newSignedData.size ()); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - 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_PAYLOAD_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, SSU2_MAX_PAYLOAD_SIZE - len - 32, r) : 0; + if (!payloadSize && r) + it->second.first->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); + if (payloadSize + len + 16 > SSU2_MAX_PAYLOAD_SIZE) + { + // 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, SSU2_MAX_PAYLOAD_SIZE, 4, + (SSU2PeerTestCode)buf[1], GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset); + if (payloadSize < SSU2_MAX_PAYLOAD_SIZE) + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MAX_PAYLOAD_SIZE - 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) + { + 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 + it->second.first->m_State = eSSU2SessionStatePeerTest; + it->second.first->SendPeerTest (6, buf + offset, len - offset, addr->i); + } + } + else + { + LogPrint (eLogWarning, "SSU2: Peer test 4 address not found"); + it->second.first->Terminate (); + } + } + else + { + LogPrint (eLogWarning, "SSU2: Peer test 4 signature verification failed"); + it->second.first->Terminate (); + } + } + } + else + { + LogPrint (eLogInfo, "SSU2: Peer test 4 error code ", (int)buf[1]); + it->second.first->Terminate (); + } + 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) + 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)); + 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; + } + + 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; + 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; + buf[0] = eSSU2BlkAck; + uint32_t ackThrough = m_OutOfSequencePackets.empty () ? m_ReceivePacketNum : *m_OutOfSequencePackets.rbegin (); + htobe32buf (buf + 3, ackThrough); // Ack Through + uint8_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; + while (it != m_OutOfSequencePackets.rend () && numRanges < SSU2_MAX_NUM_ACK_RANGES) + { + if (lastNum - (*it) < 255) + { + buf[8 + numRanges*2] = lastNum - (*it) - 1; // NACKs + lastNum = *it; it++; + uint8_t numAcks = 1; + while (it != m_OutOfSequencePackets.rend () && numAcks < 255 && lastNum > 0 && *it == lastNum - 1) + { + numAcks++; lastNum--; + it++; + } + buf[8 + numRanges*2 + 1] = numAcks; // Acks + numRanges++; + if (numAcks == 255) break; + } + else + break; + } + if (numRanges < SSU2_MAX_NUM_ACK_RANGES && 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] = 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, bool endpoint) + { + 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 (endpoint) + { + csz = CreateEndpoint (buf + 15, len - 15, boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port)); + if (!csz) return 0; + } + buf[14] = csz; // csz + // signature + SignedData s; + s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue + s.Insert (endpoint ? GetRemoteIdentity ()->GetIdentHash () : 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 + i2p::context.GetIdentity ()->GetSignatureLen (); + 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 ()) + { + 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 ()); + } + + 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_MTU]; + size_t payloadSize = CreateAckBlock (payload, SSU2_MTU); + payloadSize += CreatePaddingBlock (payload + payloadSize, SSU2_MTU - payloadSize); + SendData (payload, payloadSize); + } + + void SSU2Session::SendTermination () + { + uint8_t payload[32]; + size_t payloadSize = 12; + payload[0] = eSSU2BlkTermination; + htobe16buf (payload + 1, 9); + memset (payload + 3, 0, 9); + payloadSize += CreatePaddingBlock (payload + payloadSize, 32 - payloadSize); + SendData (payload, payloadSize); + } + + 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.size () > 255) + { + 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 --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h new file mode 100644 index 00000000..f1dba8a7 --- /dev/null +++ b/libi2pd/SSU2Session.h @@ -0,0 +1,301 @@ +/* +* 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 "TransportSession.h" + +namespace i2p +{ +namespace transport +{ + const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds + const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes + const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // 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_MTU = 1440; // TODO: should be 1456 for ipv4 + const size_t SSU2_MAX_PAYLOAD_SIZE = SSU2_MTU - 32; + const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1; // in seconds + const int SSU2_RESEND_INTERVAL = 3; // in seconds + const int SSU2_MAX_NUM_RESENDS = 5; + const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds + const size_t SSU2_MAX_WINDOW_SIZE = 128; // in packets + const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send + + 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, + eSSU2SessionStateSessionCreatedSent, + eSSU2SessionStateSessionConfirmedSent, + eSSU2SessionStateEstablished, + 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 + }; + + struct SSU2IncompleteMessage + { + struct Fragment + { + uint8_t buf[SSU2_MTU]; + size_t len; + bool isLast; + }; + + std::shared_ptr msg; + int nextFragmentNum; + uint32_t lastFragmentInsertTime; // in seconds + std::map > outOfSequenceFragments; + }; + + // RouterInfo flags + const uint8_t SSU2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; + const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02; + + class SSU2Server; + class SSU2Session: public TransportSession, public std::enable_shared_from_this + { + union Header + { + uint64_t ll[2]; + uint8_t buf[16]; + struct + { + uint64_t connID; + uint32_t packetNum; + uint8_t type; + uint8_t flags[3]; + } h; + }; + + struct SentPacket + { + uint8_t payload[SSU2_MAX_PAYLOAD_SIZE]; + size_t payloadSize = 0; + uint32_t nextResendTime; // in seconds + int numResends = 0; + }; + + struct HandshakePacket: public SentPacket + { + Header header; + uint8_t headerX[48]; // part1 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 Terminate (); + void TerminateByTimeout (); + 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 Established (); + void ScheduleConnectTimer (); + void HandleConnectTimer (const boost::system::error_code& ecode); + void PostI2NPMessages (std::vector > msgs); + bool SendQueue (); + void SendFragmentedMessage (std::shared_ptr msg); + + void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len); + void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len); + + void SendSessionRequest (uint64_t token = 0); + void SendSessionCreated (const uint8_t * X); + void SendSessionConfirmed (const uint8_t * Y); + void KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba); + void SendTokenRequest (); + void SendRetry (); + uint32_t SendData (const uint8_t * buf, size_t len); // returns packet num + void SendQuickAck (); + void SendTermination (); + void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey); + void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message + + void HandlePayload (const uint8_t * buf, size_t len); + void HandleAck (const uint8_t * buf, size_t len); + void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum); + bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep); + size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); + std::shared_ptr FindLocalAddress () 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, bool endpoint); // add endpoint for Chralie and no endpoint for Bob + size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen); + size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice + + private: + + SSU2Server& m_Server; + std::shared_ptr m_EphemeralKeys; + std::unique_ptr m_NoiseState; + std::unique_ptr m_SessionConfirmedFragment1; // for Bob if applicable + std::unique_ptr m_SentHandshakePacket; // SessionRequest or SessionCreated + 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; + uint32_t m_RelayTag; // between Bob and Charlie + OnEstablished m_OnEstablished; // callback from Established + boost::asio::deadline_timer m_ConnectTimer; + }; + + 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 --git a/libi2pd/SSUData.cpp b/libi2pd/SSUData.cpp index ccc89b41..6365381e 100644 --- a/libi2pd/SSUData.cpp +++ b/libi2pd/SSUData.cpp @@ -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 * @@ -171,7 +171,7 @@ namespace transport return; } - // find message with msgID + // find message with msgID auto it = m_IncompleteMessages.find (msgID); if (it == m_IncompleteMessages.end ()) { @@ -179,15 +179,15 @@ namespace transport auto msg = NewI2NPShortMessage (); msg->len -= I2NP_SHORT_HEADER_SIZE; it = m_IncompleteMessages.insert (std::make_pair (msgID, - m_Session.GetServer ().GetIncompleteMessagesPool ().AcquireShared (msg))).first; + m_Session.GetServer ().GetIncompleteMessagesPool ().AcquireShared (std::move (msg)))).first; } 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) { @@ -222,7 +222,7 @@ namespace transport // missing fragment LogPrint (eLogWarning, "SSU: Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID); auto savedFragment = m_Session.GetServer ().GetFragmentsPool ().AcquireShared (fragmentNum, buf, fragmentSize, isLast); - if (incompleteMessage->savedFragments.insert (savedFragment).second) + if (incompleteMessage->savedFragments.insert (savedFragment).second) incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); else LogPrint (eLogWarning, "SSU: Fragment ", (int)fragmentNum, " of message ", msgID, " already saved"); @@ -247,7 +247,7 @@ namespace transport 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"); @@ -318,7 +318,7 @@ namespace transport 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 (); @@ -349,11 +349,11 @@ namespace transport 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 (fragment); @@ -408,14 +408,14 @@ namespace transport // 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 @@ -449,7 +449,7 @@ namespace transport if (f) { try - { + { m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, f->buf, f->len, buf); m_Session.Send (buf, f->len); // resend numResent++; @@ -496,21 +496,21 @@ namespace transport else ++it; } - + if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES || ts > m_LastMessageReceivedTime + DECAY_INTERVAL) // decay m_ReceivedMessages.clear (); else { - // delete old received messages + // delete old received messages for (auto it = m_ReceivedMessages.begin (); it != m_ReceivedMessages.end ();) { if (ts > it->second + RECEIVED_MESSAGES_CLEANUP_TIMEOUT) it = m_ReceivedMessages.erase (it); else ++it; - } - } - } + } + } + } } } diff --git a/libi2pd/SSUData.h b/libi2pd/SSUData.h index 8fe33a33..1746dcd8 100644 --- a/libi2pd/SSUData.h +++ b/libi2pd/SSUData.h @@ -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 * @@ -26,11 +26,7 @@ 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; @@ -79,7 +75,7 @@ namespace transport uint64_t receivedFragmentsBits; 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); }; @@ -102,7 +98,7 @@ namespace transport void Start (); void Stop (); void CleanUp (uint64_t ts); - + void ProcessMessage (uint8_t * buf, size_t len); void FlushReceivedMessage (); void Send (std::shared_ptr msg); diff --git a/libi2pd/SSUSession.cpp b/libi2pd/SSUSession.cpp index 3e4eecb8..817133e8 100644 --- a/libi2pd/SSUSession.cpp +++ b/libi2pd/SSUSession.cpp @@ -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 @@ namespace transport { // 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,7 +39,7 @@ namespace transport // 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 (); } @@ -79,7 +79,7 @@ namespace transport 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 +124,11 @@ namespace transport 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 +158,7 @@ namespace transport 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 +177,12 @@ namespace transport 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 +192,11 @@ namespace transport 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 +206,7 @@ namespace transport 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 +222,7 @@ namespace transport } 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 +230,7 @@ namespace transport auto pair = std::make_shared (); pair->GenerateKeys (); m_DHKeysPair = pair; - } + } CreateAESandMacKey (buf + headerSize); SendSessionCreated (buf + headerSize, sendRelayTag); } @@ -249,7 +249,7 @@ namespace transport 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 +259,7 @@ namespace transport 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 +274,7 @@ namespace transport 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 +286,39 @@ namespace transport // 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 +326,7 @@ namespace transport 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 +340,7 @@ namespace transport { 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 +351,7 @@ namespace transport 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 +366,7 @@ namespace transport { LogPrint (eLogError, "SSU: Session confirmed message is too short ", len); return; - } + } // verify signature if (m_SignedData && m_SignedData->Verify (m_RemoteIdentity, payload)) { @@ -366,7 +375,7 @@ namespace transport } else { - LogPrint (eLogError, "SSU message 'confirmed' signature verification failed"); + LogPrint (eLogError, "SSU: Message 'confirmed' signature verification failed"); Failed (); } } @@ -379,11 +388,11 @@ namespace transport // 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 +422,7 @@ namespace transport i2p::context.GetRouterInfo ().GetSSUAddress (true); if (!address) { - LogPrint (eLogInfo, "SSU is not supported"); + LogPrint (eLogInfo, "SSU: SSU is not supported"); return; } @@ -427,7 +436,7 @@ namespace transport 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 +447,7 @@ namespace transport 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 +456,7 @@ namespace transport 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 +608,19 @@ namespace transport 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 +656,7 @@ namespace transport 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 +692,7 @@ namespace transport 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 +712,17 @@ namespace transport 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,7 +736,7 @@ namespace transport // 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.first, remoteEndpoint, false); @@ -803,7 +812,7 @@ namespace transport 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 +881,7 @@ namespace transport 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 +897,7 @@ namespace transport 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 (); } } @@ -1011,16 +1020,16 @@ namespace transport for (auto it = m_RelayRequests.begin (); it != m_RelayRequests.end ();) { if (ts > it->second.second + SSU_CONNECT_TIMEOUT) - it = m_RelayRequests.erase (it); + 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)) @@ -1036,29 +1045,29 @@ namespace transport { 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 @@ -1068,14 +1077,14 @@ namespace transport 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); } @@ -1083,19 +1092,19 @@ namespace transport } 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; @@ -1108,17 +1117,17 @@ namespace transport // 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) { @@ -1128,7 +1137,7 @@ namespace transport } } else - LogPrint (eLogError, "SSU: unexpected peer test"); + LogPrint (eLogError, "SSU: Unexpected peer test"); } } } @@ -1174,9 +1183,9 @@ namespace transport 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 @@ -1201,11 +1210,11 @@ namespace transport 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; @@ -1213,7 +1222,7 @@ namespace transport 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 () @@ -1246,9 +1255,9 @@ namespace transport } 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"); } } @@ -1260,7 +1269,7 @@ namespace transport 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); @@ -1281,12 +1290,12 @@ namespace transport 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) { @@ -1299,12 +1308,12 @@ namespace transport 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 --git a/libi2pd/SSUSession.h b/libi2pd/SSUSession.h index bd4b4b93..535de328 100644 --- a/libi2pd/SSUSession.h +++ b/libi2pd/SSUSession.h @@ -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 * @@ -36,6 +36,7 @@ namespace transport 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) @@ -90,7 +91,7 @@ namespace transport void Failed (); const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; SSUServer& GetServer () { return m_Server; }; - + bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; void SendI2NPMessages (const std::vector >& msgs); void SendPeerTest (); // Alice @@ -104,7 +105,7 @@ namespace transport 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); @@ -149,7 +150,7 @@ namespace transport 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 diff --git a/libi2pd/Signature.cpp b/libi2pd/Signature.cpp index 88ee4060..ebc188a9 100644 --- a/libi2pd/Signature.cpp +++ b/libi2pd/Signature.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -15,8 +15,7 @@ namespace i2p namespace crypto { #if OPENSSL_EDDSA - EDDSA25519Verifier::EDDSA25519Verifier (): - m_Pkey (nullptr) + EDDSA25519Verifier::EDDSA25519Verifier () { m_MDCtx = EVP_MD_CTX_create (); } @@ -24,13 +23,13 @@ namespace crypto 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 @@ namespace crypto #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 @@ namespace crypto 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 --git a/libi2pd/Signature.h b/libi2pd/Signature.h index 18084603..e153e66d 100644 --- a/libi2pd/Signature.h +++ b/libi2pd/Signature.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -304,7 +304,6 @@ namespace crypto private: #if OPENSSL_EDDSA - EVP_PKEY * m_Pkey; EVP_MD_CTX * m_MDCtx; #else EDDSAPoint m_PublicKey; @@ -341,7 +340,7 @@ namespace crypto 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 --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index be3d09d8..af0a359f 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -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,12 +27,12 @@ namespace stream 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 @@ namespace stream 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 @@ namespace stream 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 @@ namespace stream const uint8_t * optionData = packet->GetOptionData (); size_t optionSize = packet->GetOptionSize (); if (flags & PACKET_FLAG_DELAY_REQUESTED) - { + { if (!m_IsAckSendScheduled) { uint16_t delayRequested = bufbe16toh (optionData); if (delayRequested > 0 && delayRequested < m_RTT) - { + { m_IsAckSendScheduled = true; m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(delayRequested)); m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, shared_from_this (), std::placeholders::_1)); - } + } } optionData += 2; - } + } if (flags & PACKET_FLAG_FROM_INCLUDED) { @@ -391,10 +392,10 @@ namespace stream 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; @@ -490,7 +491,7 @@ namespace stream handler(boost::system::error_code ()); m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ())); } - + void Stream::SendBuffer () { int numMsgs = m_WindowSize - m_SentPackets.size (); @@ -672,8 +673,8 @@ namespace stream size_t size = 0; htobe32buf (packet, m_RecvStreamID); size += 4; // sendStreamID - memset (packet + size, 0, 14); - size += 14; // all zeroes + 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; @@ -695,12 +696,12 @@ namespace stream 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); + 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); @@ -828,13 +829,6 @@ namespace stream 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 @@ -842,6 +836,21 @@ namespace stream 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) { @@ -895,14 +904,14 @@ namespace stream 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) @@ -1000,16 +1009,16 @@ namespace stream { 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 { @@ -1105,7 +1114,7 @@ namespace stream it.second->Terminate (false); // we delete here m_Streams.clear (); m_IncomingStreams.clear (); - m_LastStream = nullptr; + m_LastStream = nullptr; } } @@ -1115,7 +1124,7 @@ namespace stream if (sendStreamID) { if (!m_LastStream || sendStreamID != m_LastStream->GetRecvStreamID ()) - { + { auto it = m_Streams.find (sendStreamID); if (it != m_Streams.end ()) m_LastStream = it->second; @@ -1130,7 +1139,7 @@ namespace stream 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); @@ -1145,7 +1154,7 @@ namespace stream LogPrint (eLogInfo, "Streaming: Pong received rSID=", packet->GetReceiveStreamID ()); DeletePacket (packet); return; - } + } if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream { uint32_t receiveStreamID = packet->GetReceiveStreamID (); @@ -1240,12 +1249,12 @@ namespace stream return s; } - void StreamingDestination::SendPing (std::shared_ptr remote) + 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); @@ -1264,6 +1273,11 @@ namespace stream 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) @@ -1271,7 +1285,13 @@ namespace stream 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; } diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index c13dcab1..088c2e11 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -58,6 +58,7 @@ namespace stream 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 @@ namespace stream 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; @@ -180,7 +181,7 @@ namespace stream size_t Send (const uint8_t * buf, size_t len); void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler); void SendPing (); - + template void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; @@ -366,7 +367,7 @@ namespace stream 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 --git a/libi2pd/Tag.h b/libi2pd/Tag.h index 3856abd9..d898395f 100644 --- a/libi2pd/Tag.h +++ b/libi2pd/Tag.h @@ -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 @@ namespace data { 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 --git a/libi2pd/Timestamp.cpp b/libi2pd/Timestamp.cpp index ec0c25f2..99507398 100644 --- a/libi2pd/Timestamp.cpp +++ b/libi2pd/Timestamp.cpp @@ -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 @@ namespace util 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 @@ namespace util 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 @@ namespace util { 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 @@ namespace util 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 @@ namespace util 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 --git a/libi2pd/Timestamp.h b/libi2pd/Timestamp.h index b46f423d..995ea36f 100644 --- a/libi2pd/Timestamp.h +++ b/libi2pd/Timestamp.h @@ -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 @@ namespace util 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 --git a/libi2pd/TransitTunnel.cpp b/libi2pd/TransitTunnel.cpp index dc7655d1..c4f3fa19 100644 --- a/libi2pd/TransitTunnel.cpp +++ b/libi2pd/TransitTunnel.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -37,15 +37,14 @@ namespace tunnel { } - 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 @@ namespace tunnel 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 @@ namespace tunnel 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 --git a/libi2pd/TransitTunnel.h b/libi2pd/TransitTunnel.h index e71ec750..bce90958 100644 --- a/libi2pd/TransitTunnel.h +++ b/libi2pd/TransitTunnel.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -35,7 +35,7 @@ namespace tunnel // 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 @@ namespace tunnel ~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 @@ namespace tunnel 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 --git a/libi2pd/TransportSession.h b/libi2pd/TransportSession.h index 12d4894b..a51207af 100644 --- a/libi2pd/TransportSession.h +++ b/libi2pd/TransportSession.h @@ -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,6 +33,12 @@ namespace transport { m_Stream << other.m_Stream.rdbuf (); } + + void Reset () + { + m_Stream.str(""); + } + void Insert (const uint8_t * buf, size_t len) { m_Stream.write ((char *)buf, len); @@ -96,7 +102,8 @@ namespace transport bool IsTerminationTimeoutExpired (uint64_t ts) const { return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); }; - virtual void SendLocalRouterInfo () { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); }; + virtual uint32_t GetRelayTag () const { return 0; }; + virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); }; virtual void SendI2NPMessages (const std::vector >& msgs) = 0; protected: diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 181c3663..31abad6e 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -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 @@ namespace transport m_Queue.push (pair); } else - LogPrint(eLogError, "Transports: return null DHKeys"); + LogPrint(eLogError, "Transports: Return null DHKeys"); } Transports transports; @@ -136,7 +136,7 @@ namespace transport 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 @@ namespace transport } } - void Transports::Start (bool enableNTCP2, bool enableSSU) + void Transports::Start (bool enableNTCP2, bool enableSSU, bool enableSSU2) { if (!m_Service) { @@ -192,10 +192,10 @@ namespace transport 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 @@ namespace transport // create SSU server int ssuPort = 0; if (enableSSU) - { + { auto& addresses = context.GetRouterInfo ().GetAddresses (); for (const auto& address: addresses) { @@ -216,39 +216,43 @@ namespace transport 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,24 +260,25 @@ namespace transport { 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 (); @@ -281,8 +286,8 @@ namespace transport m_SSUServer = nullptr; } if (m_SSUServer) 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 @@ namespace transport 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 @@ namespace transport } catch (std::exception& ex) { - LogPrint (eLogError, "Transports: runtime exception: ", ex.what ()); + LogPrint (eLogError, "Transports: Runtime exception: ", ex.what ()); } } } @@ -389,7 +401,7 @@ namespace transport { // 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 @@ namespace transport auto r = netdb.FindRouter (ident); if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable { + auto ts = i2p::util::GetSecondsSinceEpoch (); std::unique_lock 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 @@ namespace transport } 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); @@ -448,23 +461,23 @@ namespace transport 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,10 +491,10 @@ namespace transport else peer.numAttempts = 2; // switch to SSU } - if (peer.numAttempts == 2 || peer.numAttempts == 3) // SSU + if (peer.numAttempts == 2 || peer.numAttempts == 3) // SSU { if (m_SSUServer) - { + { std::shared_ptr address; if (peer.numAttempts == 2) // SSU ipv6 { @@ -507,7 +520,7 @@ namespace transport { if (m_SSUServer->CreateSession (peer.router, address)) return true; - } + } } else peer.numAttempts += 2; // switch to Mesh @@ -523,8 +536,42 @@ namespace transport auto s = std::make_shared (*m_NTCP2Server, peer.router, address); m_NTCP2Server->Connect (s); return true; - } - } + } + } + } + if (peer.numAttempts == 5 || peer.numAttempts == 6) // SSU2 + { + if (m_SSU2Server) + { + std::shared_ptr address; + if (peer.numAttempts == 5) // SSU2 ipv6 + { + if (context.GetRouterInfo ().IsSSU2V6 () && peer.router->IsReachableBy (RouterInfo::eSSU2V6)) + { + address = peer.router->GetSSU2V6Address (); + if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + address = nullptr; + } + peer.numAttempts++; + } + if (!address && peer.numAttempts == 6) // SSU2 ipv4 + { + if (context.GetRouterInfo ().IsSSU2V4 () && peer.router->IsReachableBy (RouterInfo::eSSU2V4)) + { + address = peer.router->GetSSU2V4Address (); + if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) + address = nullptr; + } + peer.numAttempts++; + } + if (address && address->IsReachableSSU ()) + { + if (m_SSU2Server->CreateSession (peer.router, address)) + return true; + } + } + else + peer.numAttempts += 2; } LogPrint (eLogInfo, "Transports: No compatble NTCP2 or SSU addresses available"); i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed @@ -554,13 +601,13 @@ namespace transport { 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,7 +618,7 @@ namespace transport { 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; } @@ -586,12 +633,13 @@ namespace transport if (RoutesRestricted() || !m_SSUServer) return; if (ipv4 && i2p::context.SupportsV4 ()) { - LogPrint (eLogInfo, "Transports: Started peer test ipv4"); + LogPrint (eLogInfo, "Transports: Started peer test IPv4"); std::set excluded; + excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router bool statusChanged = false; for (int i = 0; i < 5; i++) { - auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4 + auto router = i2p::data::netdb.GetRandomPeerTestRouter (true, excluded); // v4 if (router) { auto addr = router->GetSSUAddress (true); // ipv4 @@ -602,18 +650,29 @@ namespace transport 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 ()); } } if (!statusChanged) - LogPrint (eLogWarning, "Transports: Can't find routers for peer test ipv4"); + LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4"); + + // SSU2 + if (m_SSU2Server) + { + excluded.clear (); + excluded.insert (i2p::context.GetIdentHash ()); + auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4 + if (router) + m_SSU2Server->StartPeerTest (router, true); + } } if (ipv6 && i2p::context.SupportsV6 ()) { - LogPrint (eLogInfo, "Transports: Started peer test ipv6"); + LogPrint (eLogInfo, "Transports: Started peer test IPv6"); std::set excluded; + excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router bool statusChanged = false; for (int i = 0; i < 5; i++) { @@ -622,20 +681,30 @@ namespace transport { auto addr = router->GetSSUV6Address (); if (addr && !i2p::util::net::IsInReservedRange(addr->host)) - { + { if (!statusChanged) { statusChanged = true; i2p::context.SetStatusV6 (eRouterStatusTesting); // first time only } m_SSUServer->CreateSession (router, addr, true); // peer test v6 - } + } excluded.insert (router->GetIdentHash ()); } } if (!statusChanged) - LogPrint (eLogWarning, "Transports: Can't find routers for peer test ipv6"); - } + LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6"); + + // SSU2 + if (m_SSU2Server) + { + excluded.clear (); + excluded.insert (i2p::context.GetIdentHash ()); + auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6 + if (router) + m_SSU2Server->StartPeerTest (router, false); + } + } } std::shared_ptr Transports::GetNextX25519KeysPair () @@ -680,13 +749,15 @@ namespace transport { 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 +818,17 @@ namespace transport it = m_Peers.erase (it); } else + { + if (ts > it->second.nextRouterInfoUpdateTime) + { + auto session = it->second.sessions.front (); + if (session) + session->SendLocalRouterInfo (true); + it->second.nextRouterInfoUpdateTime = ts + PEER_ROUTER_INFO_UPDATE_INTERVAL + + rand () % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE; + } ++it; + } } UpdateBandwidth (); // TODO: use separate timer(s) for it bool ipv4Testing = i2p::context.GetStatus () == eRouterStatusTesting; @@ -780,15 +861,21 @@ namespace transport 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 +897,19 @@ namespace transport { { 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 --git a/libi2pd/Transports.h b/libi2pd/Transports.h index 9377286c..8fdf6055 100644 --- a/libi2pd/Transports.h +++ b/libi2pd/Transports.h @@ -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 @@ namespace transport 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 @@ namespace transport } }; - 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,7 +89,7 @@ namespace transport 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; } @@ -125,7 +128,7 @@ namespace transport /** 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 +162,7 @@ namespace transport 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 +175,7 @@ namespace transport 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 +189,7 @@ namespace transport // 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 --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 1f69470c..b578f6c1 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -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,8 +31,8 @@ namespace tunnel { Tunnel::Tunnel (std::shared_ptr config): TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()), - m_Config (config), m_IsShortBuildMessage (false), m_Pool (nullptr), - m_State (eTunnelStatePending), m_FarEndTransports (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) { } @@ -47,7 +47,7 @@ namespace tunnel 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; @@ -98,28 +98,28 @@ namespace tunnel { 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) @@ -134,14 +134,14 @@ namespace tunnel { 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) { @@ -149,13 +149,14 @@ namespace tunnel 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) { @@ -168,18 +169,20 @@ namespace tunnel // 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 (); @@ -201,7 +204,7 @@ namespace tunnel uint8_t * outPayload = out->GetPayload () + 4; for (auto& it: m_Hops) { - it->decryption.Decrypt (inPayload, outPayload); + it.decryption.Decrypt (inPayload, outPayload); inPayload = outPayload; } } @@ -222,8 +225,8 @@ namespace tunnel { // 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; } @@ -232,30 +235,19 @@ namespace tunnel 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 (): @@ -274,11 +266,6 @@ namespace tunnel } } - void ZeroHopsInboundTunnel::Print (std::stringstream& s) const - { - s << " ⇒ " << GetTunnelID () << ":me"; - } - void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg) { TunnelMessageBlock block; @@ -308,16 +295,9 @@ namespace tunnel 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 (): @@ -349,11 +329,6 @@ namespace tunnel } } - void ZeroHopsOutboundTunnel::Print (std::stringstream& s) const - { - s << GetTunnelID () << ":me ⇒ "; - } - Tunnels tunnels; Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), @@ -428,10 +403,10 @@ namespace tunnel 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; @@ -463,7 +438,7 @@ namespace tunnel 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 () @@ -489,7 +464,7 @@ namespace tunnel 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 @@ -519,12 +494,12 @@ namespace tunnel 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; } @@ -533,11 +508,11 @@ namespace tunnel 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 (); @@ -564,12 +539,18 @@ namespace tunnel { 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 ()); } } } @@ -578,7 +559,7 @@ namespace tunnel { if (!tunnel) { - LogPrint (eLogError, "Tunnel: missing tunnel for gateway"); + LogPrint (eLogError, "Tunnel: Missing tunnel for gateway"); return; } const uint8_t * payload = msg->GetPayload (); @@ -587,12 +568,12 @@ namespace tunnel 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 @@ -628,7 +609,7 @@ namespace tunnel 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) @@ -653,7 +634,7 @@ namespace tunnel ++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; @@ -678,7 +659,7 @@ namespace tunnel 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); @@ -715,7 +696,7 @@ namespace tunnel 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 (), false), nullptr @@ -732,7 +713,7 @@ namespace tunnel 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); @@ -775,7 +756,7 @@ namespace tunnel 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; @@ -789,10 +770,10 @@ namespace tunnel // 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 () }, false), nullptr ); @@ -840,8 +821,8 @@ namespace tunnel } 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); @@ -852,7 +833,7 @@ namespace tunnel 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) @@ -912,11 +893,11 @@ namespace tunnel } } 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); @@ -936,6 +917,24 @@ namespace tunnel 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 --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index 2e1c9534..503b7f9c 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -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 * @@ -18,6 +18,7 @@ #include #include #include +#include "util.h" #include "Queue.h" #include "Crypto.h" #include "TunnelConfig.h" @@ -39,7 +40,10 @@ namespace tunnel 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 @@ namespace tunnel public: + /** function for visiting a hops stored in a tunnel */ + typedef std::function)> TunnelHopVisitor; + Tunnel (std::shared_ptr config); ~Tunnel (); @@ -72,6 +79,7 @@ namespace tunnel 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; }; @@ -86,8 +94,6 @@ namespace tunnel 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); @@ -101,20 +107,19 @@ namespace tunnel 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; i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; - bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace + bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace uint64_t m_Latency; // in milliseconds }; @@ -129,10 +134,9 @@ namespace tunnel 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; } @@ -148,9 +152,8 @@ namespace tunnel 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 @@ -167,7 +170,6 @@ namespace tunnel ZeroHopsInboundTunnel (); void SendTunnelDataMsg (std::shared_ptr msg); - void Print (std::stringstream& s) const; size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; private: @@ -181,7 +183,6 @@ namespace tunnel ZeroHopsOutboundTunnel (); void SendTunnelDataMsg (const std::vector& msgs); - void Print (std::stringstream& s) const; size_t GetNumSentBytes () const { return m_NumSentBytes; }; private: @@ -214,16 +215,18 @@ namespace tunnel 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); @@ -257,7 +260,9 @@ namespace tunnel 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 --git a/libi2pd/TunnelBase.h b/libi2pd/TunnelBase.h index f98066d3..8d0edff1 100644 --- a/libi2pd/TunnelBase.h +++ b/libi2pd/TunnelBase.h @@ -47,7 +47,7 @@ namespace tunnel 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 --git a/libi2pd/TunnelConfig.cpp b/libi2pd/TunnelConfig.cpp index 4592c663..e19b515d 100644 --- a/libi2pd/TunnelConfig.cpp +++ b/libi2pd/TunnelConfig.cpp @@ -81,34 +81,34 @@ namespace tunnel decryption.SetKey (replyKey); decryption.SetIV (replyIV); decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); - } + } void ECIESTunnelHopConfig::EncryptECIES (const uint8_t * plainText, size_t len, uint8_t * encrypted) { if (!ident) return; i2p::crypto::InitNoiseNState (*this, ident->GetEncryptionPublicKey ()); auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); - memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32); + 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 @@ -119,7 +119,7 @@ namespace tunnel // 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); @@ -149,16 +149,16 @@ namespace tunnel { 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); @@ -167,28 +167,28 @@ namespace tunnel 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; @@ -199,9 +199,9 @@ namespace tunnel { LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); return false; - } + } return true; - } + } void ShortECIESTunnelHopConfig::DecryptRecord (uint8_t * records, int index) const { @@ -210,7 +210,7 @@ namespace tunnel 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 { @@ -218,7 +218,7 @@ namespace tunnel memcpy (&tag, m_CK, 8); memcpy (key, m_CK + 32, 32); return tag; - } + } void TunnelConfig::CreatePeers (const std::vector >& peers) { @@ -236,13 +236,13 @@ namespace tunnel LogPrint (eLogError, "Tunnel: ElGamal router is not supported"); } if (hop) - { + { if (prev) prev->SetNext (hop); else m_FirstHop = hop; prev = hop; - } + } } m_LastHop = prev; } diff --git a/libi2pd/TunnelConfig.h b/libi2pd/TunnelConfig.h index f198dffd..9dcf2c02 100644 --- a/libi2pd/TunnelConfig.h +++ b/libi2pd/TunnelConfig.h @@ -82,7 +82,7 @@ namespace tunnel public: TunnelConfig (const std::vector >& peers, - bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports = 0): // inbound + bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // inbound m_IsShort (isShort), m_FarEndTransports (farEndTransports) { CreatePeers (peers); @@ -91,7 +91,7 @@ namespace tunnel TunnelConfig (const std::vector >& peers, uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort, - i2p::data::RouterInfo::CompatibleTransports farEndTransports = 0): // outbound + i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // outbound m_IsShort (isShort), m_FarEndTransports (farEndTransports) { CreatePeers (peers); @@ -99,7 +99,7 @@ namespace tunnel m_LastHop->SetReplyHop (replyTunnelID, replyIdent); } - ~TunnelConfig () + virtual ~TunnelConfig () { TunnelHopConfig * hop = m_FirstHop; @@ -116,8 +116,8 @@ namespace tunnel i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const { return m_FarEndTransports; - } - + } + TunnelHopConfig * GetFirstHop () const { return m_FirstHop; @@ -185,7 +185,7 @@ namespace tunnel // this constructor can't be called from outside TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr), m_IsShort (false), - m_FarEndTransports (0) + m_FarEndTransports (i2p::data::RouterInfo::eAllTransports) { } diff --git a/libi2pd/TunnelEndpoint.cpp b/libi2pd/TunnelEndpoint.cpp index 4885c090..9c294374 100644 --- a/libi2pd/TunnelEndpoint.cpp +++ b/libi2pd/TunnelEndpoint.cpp @@ -30,7 +30,7 @@ namespace tunnel 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 @@ namespace tunnel 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 @@ namespace tunnel // 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 @@ namespace tunnel { HandleFollowOnFragment (msgID, isLastFragment, fragmentNum, fragment, size); // another m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - } + } + } else { // new message @@ -118,7 +118,7 @@ namespace tunnel // 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 @@ namespace tunnel } 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 @@ namespace tunnel 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 @@ namespace tunnel 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 @@ namespace tunnel 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 @@ namespace tunnel 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 @@ namespace tunnel { 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 --git a/libi2pd/TunnelEndpoint.h b/libi2pd/TunnelEndpoint.h index e5a6bbc8..17590a5f 100644 --- a/libi2pd/TunnelEndpoint.h +++ b/libi2pd/TunnelEndpoint.h @@ -49,14 +49,14 @@ namespace tunnel 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 --git a/libi2pd/TunnelGateway.cpp b/libi2pd/TunnelGateway.cpp index 08df569c..12e7652f 100644 --- a/libi2pd/TunnelGateway.cpp +++ b/libi2pd/TunnelGateway.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -19,16 +19,14 @@ namespace i2p 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 @@ namespace tunnel 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 @@ namespace tunnel 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 --git a/libi2pd/TunnelGateway.h b/libi2pd/TunnelGateway.h index 01101a36..741bbe84 100644 --- a/libi2pd/TunnelGateway.h +++ b/libi2pd/TunnelGateway.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -38,7 +38,7 @@ namespace tunnel 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 --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 5ed330c8..204ac294 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -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 @@ namespace tunnel void Path::Add (std::shared_ptr r) { if (r) - { + { peers.push_back (r->GetRouterIdentity ()); - if (r->GetVersion () < i2p::data::NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION || - r->GetRouterIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) + 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; } @@ -114,7 +124,7 @@ namespace tunnel { std::unique_lock l(m_InboundTunnelsMutex); if (createdTunnel->IsRecreated ()) - { + { // find and mark old tunnel as expired createdTunnel->SetRecreated (false); for (auto& it: m_InboundTunnels) @@ -122,8 +132,8 @@ namespace tunnel { it->SetState (eTunnelStateExpiring); break; - } - } + } + } m_InboundTunnels.insert (createdTunnel); } if (m_LocalDestination) @@ -179,10 +189,10 @@ namespace tunnel if (it->IsSlow () && !slowTunnel) slowTunnel = it; else - { + { v.push_back (it); i++; - } + } } } if (slowTunnel && (int)v.size () < (num/2+1)) @@ -190,20 +200,23 @@ namespace tunnel 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; @@ -211,10 +224,10 @@ namespace tunnel 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; @@ -224,7 +237,7 @@ namespace tunnel } if (i > ind && tunnel) break; } - if (!tunnel && skipped) + if (!tunnel && skipped) { ind = rand () % (tunnels.size ()/2 + 1), i = 0; for (const auto& it: tunnels) @@ -281,12 +294,12 @@ namespace tunnel 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 (); @@ -304,7 +317,7 @@ namespace tunnel 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) { @@ -335,7 +348,9 @@ namespace tunnel } // 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 ()) { @@ -367,20 +382,20 @@ namespace tunnel 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) @@ -405,23 +420,31 @@ namespace tunnel 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 { @@ -435,8 +458,8 @@ namespace tunnel 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): @@ -464,7 +487,7 @@ namespace tunnel (inbound && i2p::transport::transports.GetNumPeers () > 25)) { auto r = i2p::transport::transports.GetRandomPeer (); - if (r && r->IsECIES () && !r->GetProfile ()->IsBad () && + if (r && r->IsECIES () && !r->GetProfile ()->IsBad () && (numHops > 1 || (r->IsV4 () && (!inbound || r->IsReachable ())))) // first inbound must be reachable { prevHop = r; @@ -477,33 +500,55 @@ namespace tunnel { 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); - if (i == numHops - 1) - path.farEndTransports = hop->GetCompatibleTransports (inbound); } + 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 ? @@ -512,8 +557,6 @@ namespace tunnel 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)); } @@ -521,25 +564,25 @@ namespace tunnel { 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) - { - if (r->IsECIES ()) - { + { + 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; - } - } + return false; + } + } else { LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ()); @@ -552,13 +595,13 @@ namespace tunnel 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) { @@ -580,15 +623,13 @@ namespace tunnel 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 (), tunnel->IsShortBuildMessage ()); - } + config = std::make_shared(tunnel->GetPeers (), tunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ()); if (!m_NumInboundHops || config) { auto newTunnel = tunnels.CreateInboundTunnel (config, shared_from_this(), outboundTunnel); @@ -601,40 +642,41 @@ namespace tunnel 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)) + auto inboundTunnel = GetNextInboundTunnel (nullptr, path.farEndTransports); + if (!inboundTunnel) + inboundTunnel = tunnels.GetNextInboundTunnel (); + if (!inboundTunnel) { - 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); + 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) @@ -644,7 +686,7 @@ namespace tunnel CreateOutboundTunnel (); return; } - auto inboundTunnel = GetNextInboundTunnel (); + auto inboundTunnel = GetNextInboundTunnel (nullptr, tunnel->GetFarEndTransports ()); if (!inboundTunnel) inboundTunnel = tunnels.GetNextInboundTunnel (); if (inboundTunnel) @@ -653,8 +695,8 @@ namespace tunnel std::shared_ptr config; if (m_NumOutboundHops > 0 && tunnel->GetPeers().size()) { - config = std::make_shared(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), - inboundTunnel->GetNextIdentHash (), inboundTunnel->IsShortBuildMessage ()); + config = std::make_shared(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), + inboundTunnel->GetNextIdentHash (), inboundTunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ()); } if (!m_NumOutboundHops || config) { @@ -672,8 +714,8 @@ namespace tunnel LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel..."); auto tunnel = tunnels.CreateInboundTunnel ( m_NumOutboundHops > 0 ? std::make_shared(outboundTunnel->GetInvertedPeers (), - outboundTunnel->IsShortBuildMessage ()) : nullptr, - shared_from_this (), outboundTunnel); + outboundTunnel->IsShortBuildMessage ()) : nullptr, + shared_from_this (), outboundTunnel); if (tunnel->IsEstablished ()) // zero hops TunnelCreated (tunnel); } diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index 875a9955..d8c60d69 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -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 @@ namespace tunnel 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,8 +40,8 @@ namespace tunnel { std::vector peers; bool isShort = true; - i2p::data::RouterInfo::CompatibleTransports farEndTransports = 0; - + i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports; + void Add (std::shared_ptr r); void Reverse (); }; @@ -56,12 +56,13 @@ namespace tunnel 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; }; @@ -76,10 +77,11 @@ namespace tunnel 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); @@ -116,18 +118,21 @@ namespace tunnel 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 --git a/libi2pd/api.cpp b/libi2pd/api.cpp index faeee84d..905fab75 100644 --- a/libi2pd/api.cpp +++ b/libi2pd/api.cpp @@ -60,22 +60,22 @@ namespace api 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 --git a/libi2pd/util.cpp b/libi2pd/util.cpp index 2b5c79f2..07681dbd 100644 --- a/libi2pd/util.cpp +++ b/libi2pd/util.cpp @@ -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 * @@ -32,10 +32,6 @@ #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)) @@ -117,14 +113,14 @@ namespace util } 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__) @@ -176,7 +172,7 @@ namespace net if(dwRetVal != NO_ERROR) { - LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed"); + LogPrint(eLogError, "NetIface: GetMTU(): Enclosed GetAdaptersAddresses() call has failed"); FREE(pAddresses); return fallback; } @@ -188,7 +184,7 @@ namespace net pUnicast = pCurrAddresses->FirstUnicastAddress; if(pUnicast == nullptr) - LogPrint(eLogError, "NetIface: GetMTU(): not a unicast ipv4 address, this is not supported"); + LogPrint(eLogError, "NetIface: GetMTU(): Not a unicast IPv4 address, this is not supported"); for(int i = 0; pUnicast != nullptr; ++i) { @@ -205,7 +201,7 @@ namespace net pCurrAddresses = pCurrAddresses->Next; } - LogPrint(eLogError, "NetIface: GetMTU(): no usable unicast ipv4 addresses found"); + LogPrint(eLogError, "NetIface: GetMTU(): No usable unicast IPv4 addresses found"); FREE(pAddresses); return fallback; } @@ -230,7 +226,7 @@ namespace net if(dwRetVal != NO_ERROR) { - LogPrint(eLogError, "NetIface: GetMTU(): enclosed GetAdaptersAddresses() call has failed"); + LogPrint(eLogError, "NetIface: GetMTU(): Enclosed GetAdaptersAddresses() call has failed"); FREE(pAddresses); return fallback; } @@ -242,7 +238,7 @@ namespace net 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"); + LogPrint(eLogError, "NetIface: GetMTU(): Not a unicast IPv6 address, this is not supported"); for(int i = 0; pUnicast != nullptr; ++i) { @@ -270,7 +266,7 @@ namespace net pCurrAddresses = pCurrAddresses->Next; } - LogPrint(eLogError, "NetIface: GetMTU(): no usable unicast ipv6 addresses found"); + LogPrint(eLogError, "NetIface: GetMTU(): No usable unicast IPv6 addresses found"); FREE(pAddresses); return fallback; } @@ -302,7 +298,7 @@ namespace net } else { - LogPrint(eLogError, "NetIface: GetMTU(): address family is not supported"); + LogPrint(eLogError, "NetIface: GetMTU(): Address family is not supported"); return fallback; } } @@ -355,7 +351,7 @@ namespace net 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,46 +373,51 @@ namespace net 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 @@ -425,14 +426,14 @@ namespace net 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 +480,36 @@ namespace net } 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 +517,16 @@ namespace net 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 --git a/libi2pd/util.h b/libi2pd/util.h index 282ce7aa..2c74d4dd 100644 --- a/libi2pd/util.h +++ b/libi2pd/util.h @@ -56,14 +56,10 @@ namespace util void CleanUp () { - while (m_Head) - { - auto tmp = m_Head; - m_Head = static_cast(*(void * *)m_Head); // next - ::operator delete ((void *)tmp); - } - } - + CleanUp (m_Head); + m_Head = nullptr; + } + template T * Acquire (TArgs&&... args) { @@ -98,6 +94,18 @@ namespace util std::bind (&MemoryPool::Release, this, std::placeholders::_1)); } + protected: + + void CleanUp (T * head) + { + while (head) + { + auto tmp = head; + head = static_cast(*(void * *)head); // next + ::operator delete ((void *)tmp); + } + } + protected: T * m_Head; @@ -131,6 +139,24 @@ namespace util 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; diff --git a/libi2pd/version.h b/libi2pd/version.h index 97351c40..be27358f 100644 --- a/libi2pd/version.h +++ b/libi2pd/version.h @@ -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_MICRO 0 +#define I2PD_VERSION_MINOR 42 +#define I2PD_VERSION_MICRO 1 #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 54 #define I2P_VERSION_PATCH 0 #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) diff --git a/libi2pd_client/AddressBook.cpp b/libi2pd_client/AddressBook.cpp index 3d5c83c0..e67e3f06 100644 --- a/libi2pd_client/AddressBook.cpp +++ b/libi2pd_client/AddressBook.cpp @@ -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 @@ namespace client class AddressBookFilesystemStorage: public AddressBookStorage { public: - + AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") { i2p::config::GetOption("persist.addressbook", m_IsPersist); @@ -62,7 +62,7 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client } 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 @@ namespace client 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 @@ namespace client } 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client { 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); } } @@ -473,20 +479,20 @@ namespace client pos = name.find(".b32.i2p"); if (pos != std::string::npos) { - LogPrint (eLogError, "Addressbook: skipped adding of b32 address: ", name); + 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); + 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; } @@ -494,13 +500,13 @@ namespace client 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 @@ -508,7 +514,7 @@ namespace client 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 @@ -540,8 +546,9 @@ namespace client 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; @@ -549,14 +556,13 @@ namespace client 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 () @@ -641,7 +647,7 @@ namespace client 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 () @@ -656,7 +662,7 @@ namespace client { 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 ()) @@ -664,7 +670,7 @@ namespace client 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); @@ -802,7 +808,7 @@ namespace client 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); @@ -841,7 +847,7 @@ namespace client } 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; @@ -858,9 +864,9 @@ namespace client 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(); @@ -886,7 +892,7 @@ namespace client // 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; } @@ -899,35 +905,35 @@ namespace client 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 */ @@ -948,13 +954,13 @@ namespace client 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 --git a/libi2pd_client/AddressBook.h b/libi2pd_client/AddressBook.h index 04600792..192c4ebb 100644 --- a/libi2pd_client/AddressBook.h +++ b/libi2pd_client/AddressBook.h @@ -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 @@ namespace client 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 @@ namespace client 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 @@ namespace client private: std::shared_ptr m_LocalDestination; - std::map m_LocalAddresses; + std::map m_LocalAddresses; }; } } diff --git a/libi2pd_client/BOB.cpp b/libi2pd_client/BOB.cpp index 69c516d2..a8520e9b 100644 --- a/libi2pd_client/BOB.cpp +++ b/libi2pd_client/BOB.cpp @@ -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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client m_LocalDestination (localDestination), m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr), m_Nickname(nickname), m_InHost(inhost), m_OutHost(outhost), - m_InPort(inport), m_OutPort(outport), m_Quiet(quiet) + m_InPort(inport), m_OutPort(outport), m_Quiet(quiet), m_IsRunning(false) { } @@ -183,6 +183,7 @@ namespace client { if (m_OutboundTunnel) m_OutboundTunnel->Start (); if (m_InboundTunnel) m_InboundTunnel->Start (); + m_IsRunning = true; } void BOBDestination::Stop () @@ -193,6 +194,7 @@ namespace client void BOBDestination::StopTunnels () { + m_IsRunning = false; if (m_OutboundTunnel) { m_OutboundTunnel->Stop (); @@ -268,7 +270,7 @@ namespace client { 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 @@ namespace client } else { - LogPrint (eLogError, "BOB: unknown command ", command.c_str()); + LogPrint (eLogError, "BOB: Unknown command ", command.c_str()); SendReplyError ("unknown command"); } } @@ -310,7 +312,7 @@ namespace client { 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 @@ namespace client const auto issetStr = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost const auto issetNum = [&issetStr](const int p) { return issetStr(p == 0 ? "" : std::to_string(p)); }; // for inport, outport const auto destExists = [](const BOBDestination * const dest) { return dest != nullptr; }; - const auto destReady = [](const BOBDestination * const dest) { return dest->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 @@ namespace client 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) + if(*operand) { - m_Keys = m_CurrentDestination->GetKeys (); - m_Nickname = operand; - } - if (m_Nickname == operand) - { - std::string msg ("Nickname set to "); - msg += m_Nickname; - SendReplyOK (msg.c_str ()); + 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 @@ namespace client } catch (std::invalid_argument& ex) { - LogPrint (eLogWarning, "BOB: newkeys ", ex.what ()); + LogPrint (eLogWarning, "BOB: Error on newkeys: ", ex.what ()); } } @@ -535,7 +554,7 @@ namespace client 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 @@ namespace client 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 @@ namespace client void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: lookup ", operand); - auto addr = context.GetAddressBook ().GetAddress (operand); - if (!addr) + if (*operand) { - SendReplyError ("Address Not found"); - return; - } - auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination (); - if (addr->IsIdentHash ()) - { - // we might have leaseset already - auto leaseSet = localDestination->FindLeaseSet (addr->identHash); - if (leaseSet) + 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 @@ namespace client msg += operand; *(const_cast(value)) = '='; msg += " set to "; - msg += value; + msg += value + 1; SendReplyOK (msg.c_str ()); } else @@ -718,11 +767,11 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client session->SendVersion (); } else - LogPrint (eLogError, "BOB: accept error: ", ecode.message ()); + LogPrint (eLogError, "BOB: Accept error: ", ecode.message ()); } } } diff --git a/libi2pd_client/BOB.h b/libi2pd_client/BOB.h index f5f0c8ee..2e6314e2 100644 --- a/libi2pd_client/BOB.h +++ b/libi2pd_client/BOB.h @@ -63,7 +63,7 @@ namespace client 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 @@ namespace client int GetInPort() const { return m_InPort; } int GetOutPort() const { return m_OutPort; } bool GetQuiet() const { return m_Quiet; } + bool IsRunning() const { return m_IsRunning; } const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); }; std::shared_ptr GetLocalDestination () const { return m_LocalDestination; }; @@ -176,6 +177,7 @@ namespace client std::string m_InHost, m_OutHost; int m_InPort, m_OutPort; bool m_Quiet; + bool m_IsRunning; }; class BOBCommandChannel; diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index d6871fbe..3f670f06 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -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 * @@ -65,7 +65,7 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client { 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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,13 +176,13 @@ namespace client 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 (); { @@ -278,7 +278,7 @@ namespace client 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 @@ -287,7 +287,7 @@ namespace client } 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 (); @@ -406,7 +406,7 @@ namespace client void ClientContext::CreateNewSharedLocalDestination () { - std::map params + std::map params { { I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, "3" }, { I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, "3" }, @@ -455,6 +455,8 @@ namespace client options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH); options[I2CP_PARAM_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); @@ -487,10 +489,14 @@ namespace client 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)) @@ -510,7 +516,7 @@ namespace client if (tunConf.empty ()) tunConf = i2p::fs::DataDirPath ("tunnels.conf"); - LogPrint(eLogDebug, "Clients: tunnels config file: ", tunConf); + LogPrint(eLogDebug, "Clients: Tunnels config file: ", tunConf); ReadTunnels (tunConf, numClientTunnels, numServerTunnels); std::string tunDir; i2p::config::GetOption("tunnelsdir", tunDir); @@ -525,7 +531,7 @@ namespace client 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); } } @@ -602,21 +608,29 @@ namespace client 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); + auto clientTunnel = std::make_shared (name, dest, end, localDestination, destinationPort, gzip); - auto ins = m_ClientForwards.insert(std::make_pair(end, clientTunnel)); + auto ins = m_ClientForwards.insert (std::make_pair (end, clientTunnel)); if (ins.second) { - clientTunnel->Start(); + 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"); } @@ -720,10 +734,10 @@ namespace client 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; @@ -754,7 +768,7 @@ namespace client 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); @@ -767,10 +781,10 @@ namespace client LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32()); } else - { + { ins.first->second->isUpdated = true; LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, " already exists"); - } + } continue; } @@ -787,7 +801,7 @@ namespace client 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) @@ -850,8 +864,10 @@ namespace client 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; @@ -863,7 +879,7 @@ namespace client if (localDestination) localDestination->Acquire (); } else - LogPrint(eLogError, "Clients: failed to load HTTP Proxy key"); + LogPrint(eLogError, "Clients: Failed to load HTTP Proxy key"); } try { @@ -884,7 +900,7 @@ namespace client 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); @@ -893,7 +909,7 @@ namespace client 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 (); @@ -910,7 +926,7 @@ namespace client if (localDestination) localDestination->Acquire (); } else - LogPrint(eLogError, "Clients: failed to load SOCKS Proxy key"); + LogPrint(eLogError, "Clients: Failed to load SOCKS Proxy key"); } try { @@ -970,11 +986,11 @@ namespace client } } - /* // TODO: Write correct UDP tunnels stop + // TODO: Write correct UDP tunnels stop for (auto it = m_ClientForwards.begin (); it != m_ClientForwards.end ();) { if(clean && !it->second->isUpdated) { - it->second = nullptr; + it->second->Stop (); it = m_ClientForwards.erase(it); } else { it->second->isUpdated = false; @@ -985,13 +1001,13 @@ namespace client for (auto it = m_ServerForwards.begin (); it != m_ServerForwards.end ();) { if(clean && !it->second->isUpdated) { - it->second = nullptr; + it->second->Stop (); it = m_ServerForwards.erase(it); } else { it->second->isUpdated = false; it++; } - } */ + } } } } diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index cb28e40e..d512a55b 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -107,7 +107,7 @@ namespace client // i18n std::shared_ptr GetLanguage () { return m_Language; }; void SetLanguage (const std::shared_ptr language) { m_Language = language; }; - + private: void ReadTunnels (); @@ -142,8 +142,8 @@ namespace client 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 @@ -157,7 +157,7 @@ namespace client // i18n std::shared_ptr m_Language; - + public: // for HTTP diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index 7acdc333..5cb08bfb 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -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 @@ namespace proxy { 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 @@ namespace proxy { 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 @@ namespace proxy { << "

" << 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 @@ namespace proxy { /** * 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 @@ namespace proxy { 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 @@ namespace proxy { { 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") << ". "; @@ -375,13 +384,13 @@ namespace proxy { } } 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 @@ namespace proxy { 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,7 +421,7 @@ namespace proxy { void HTTPReqHandler::ForwardToUpstreamProxy() { - LogPrint(eLogDebug, "HTTPProxy: forward to upstream"); + LogPrint(eLogDebug, "HTTPProxy: Forwarded to upstream"); // build http request m_ClientRequestURL = m_RequestURL; @@ -490,7 +499,7 @@ namespace proxy { } 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 @@ namespace proxy { 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 @@ namespace proxy { }); } 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 @@ namespace proxy { 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 @@ namespace proxy { /* 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 @@ namespace proxy { 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 --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index 7c76d359..cc0837b7 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -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 * @@ -23,8 +23,8 @@ namespace i2p 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 @@ namespace client 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,12 +46,12 @@ namespace client 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, i2p::data::CryptoKeyType preferredCrypto) const { if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor) @@ -59,7 +59,7 @@ namespace client if (m_Decryptor) 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 @@ namespace client 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 @@ namespace client { 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 @@ namespace client if (s->m_Owner) s->m_Owner->Stop (); } }); - } - } + } + } } void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len) @@ -146,7 +146,7 @@ namespace client 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 @@ namespace client } 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 @@ namespace client } } - 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client { 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 @@ namespace client { 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client { 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 @@ namespace client } } 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 @@ namespace client 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 @@ namespace client { 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client { 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client { 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 --git a/libi2pd_client/I2CP.h b/libi2pd_client/I2CP.h index 08a96af6..e38da0ac 100644 --- a/libi2pd_client/I2CP.h +++ b/libi2pd_client/I2CP.h @@ -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 @@ namespace client 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 @@ namespace client 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 @@ namespace client { 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); @@ -84,7 +93,7 @@ namespace client // implements LocalDestination bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const; - bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client // 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client I2CPMessageHandler m_MessagesHandlers[256]; std::map > m_Sessions; - I2CPSession::proto::acceptor m_Acceptor; + boost::asio::ip::tcp::acceptor m_Acceptor; public: diff --git a/libi2pd_client/I2PService.cpp b/libi2pd_client/I2PService.cpp index 83838106..2a6fe44e 100644 --- a/libi2pd_client/I2PService.cpp +++ b/libi2pd_client/I2PService.cpp @@ -193,7 +193,7 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 --git a/libi2pd_client/I2PTunnel.cpp b/libi2pd_client/I2PTunnel.cpp index b97270e4..4d749927 100644 --- a/libi2pd_client/I2PTunnel.cpp +++ b/libi2pd_client/I2PTunnel.cpp @@ -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 * @@ -7,6 +7,7 @@ */ #include +#include #include "Base.h" #include "Log.h" #include "Destination.h" @@ -87,7 +88,7 @@ namespace client 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 @@ namespace client 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 @@ namespace client { 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 @@ namespace client 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 @@ namespace client } 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 @@ namespace client { 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 @@ namespace client { 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 @@ -326,7 +327,7 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client }; 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 @@ namespace client 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 @@ namespace client 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 (); @@ -556,8 +584,8 @@ namespace client m_KeepAliveInterval = keepAliveInterval; if (m_KeepAliveInterval) m_KeepAliveTimer.reset (new boost::asio::deadline_timer (GetLocalDestination ()->GetService ())); - } - + } + /* HACK: maybe we should create a caching IdentHash provider in AddressBook */ std::shared_ptr I2PClientTunnel::GetAddress () { @@ -583,27 +611,27 @@ namespace client { if (m_KeepAliveTimer) { - m_KeepAliveTimer->expires_from_now (boost::posix_time::seconds(m_KeepAliveInterval)); + m_KeepAliveTimer->expires_from_now (boost::posix_time::seconds (m_KeepAliveInterval)); m_KeepAliveTimer->async_wait (std::bind (&I2PClientTunnel::HandleKeepAliveTimer, 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) @@ -643,16 +671,16 @@ namespace client 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 ()) { @@ -660,28 +688,28 @@ namespace client { 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 (); } @@ -702,9 +730,9 @@ namespace client 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) @@ -738,7 +766,7 @@ namespace client AddHandler (conn); if (m_LocalAddress) conn->Connect (*m_LocalAddress); - else + else conn->Connect (m_IsUniqueLocal); } } @@ -793,10 +821,11 @@ namespace client { 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(); @@ -808,7 +837,8 @@ namespace client } } - 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; @@ -829,7 +859,7 @@ namespace client 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; } } @@ -864,7 +894,8 @@ namespace client 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)); @@ -874,7 +905,7 @@ namespace client { 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) @@ -883,245 +914,259 @@ namespace client 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) { - m_LocalDest = localDestination; - m_LocalDest->Start(); - auto dgram = m_LocalDest->CreateDatagramDestination(gzip); - dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - dgram->SetRawReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); } - I2PUDPServerTunnel::~I2PUDPServerTunnel() + I2PUDPServerTunnel::~I2PUDPServerTunnel () { - auto dgram = m_LocalDest->GetDatagramDestination(); - if (dgram) dgram->ResetReceiver(); - - LogPrint(eLogInfo, "UDPServer: done"); + Stop (); } - void I2PUDPServerTunnel::Start() + void I2PUDPServerTunnel::Start () { - m_LocalDest->Start(); + m_LocalDest->Start (); + + auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip); + dgram->SetReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); + dgram->SetRawReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); } - std::vector > I2PUDPServerTunnel::GetSessions() + void I2PUDPServerTunnel::Stop () + { + auto dgram = m_LocalDest->GetDatagramDestination (); + if (dgram) dgram->ResetReceiver (); + } + + 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) { - 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, + I2PUDPClientTunnel::~I2PUDPClientTunnel () + { + Stop (); + } + + void I2PUDPClientTunnel::Start () + { + // Reset flag in case of tunnel reload + if (m_cancel_resolve) m_cancel_resolve = false; + + m_LocalSocket.reset (new boost::asio::ip::udp::socket (m_LocalDest->GetService (), m_LocalEndpoint)); + m_LocalSocket->set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU)); + m_LocalSocket->set_option (boost::asio::socket_base::reuse_address (true)); + + auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip); + dgram->SetReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - dgram->SetRawReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this, + 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::RecvFromLocal() + void I2PUDPClientTunnel::Stop () { - 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)); + 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::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred) + void I2PUDPClientTunnel::RecvFromLocal () { - if(ec) { - LogPrint(eLogError, "UDP Client: Reading from socket error: ", ec.message(), ". Restarting listener..."); - RecvFromLocal(); // Restart listener and continue work + m_LocalSocket->async_receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), + m_RecvEndpoint, std::bind (&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2)); + } + + void I2PUDPClientTunnel::HandleRecvFromLocal (const boost::system::error_code & ec, std::size_t transferred) + { + if (m_cancel_resolve) { + LogPrint (eLogDebug, "UDP Client: Ignoring incomming data: stopping"); return; } - if(!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); + 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); + 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 --git a/libi2pd_client/I2PTunnel.h b/libi2pd_client/I2PTunnel.h index d21b2e11..dfa23fb2 100644 --- a/libi2pd_client/I2PTunnel.h +++ b/libi2pd_client/I2PTunnel.h @@ -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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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: @@ -154,11 +154,11 @@ namespace client 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); @@ -174,8 +174,8 @@ namespace client /** 2 minute timeout for udp sessions */ const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2; - const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds - + 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; @@ -230,25 +230,27 @@ namespace client { 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: @@ -260,6 +262,7 @@ namespace client std::vector m_Sessions; std::shared_ptr m_LocalDest; UDPSessionPtr m_LastSession; + bool m_Gzip; public: @@ -270,27 +273,36 @@ namespace client { 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: @@ -302,11 +314,12 @@ namespace client 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: @@ -330,7 +343,7 @@ namespace client 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 --git a/libi2pd_client/MatchedDestination.cpp b/libi2pd_client/MatchedDestination.cpp index 8d17632b..ce800ecc 100644 --- a/libi2pd_client/MatchedDestination.cpp +++ b/libi2pd_client/MatchedDestination.cpp @@ -33,14 +33,14 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index c2983f43..fc56b2c9 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -54,7 +54,7 @@ namespace client break; } case eSAMSocketTypeAcceptor: - case eSAMSocketTypeForward: + case eSAMSocketTypeForward: { if (Session) { @@ -101,7 +101,7 @@ namespace client { 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 @@ namespace client 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 @@ namespace client } else { - LogPrint (eLogError, "SAM: handshake mismatch"); + LogPrint (eLogError, "SAM: Handshake mismatch"); Terminate ("SAM: handshake mismatch"); } } @@ -183,7 +183,7 @@ namespace client { 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 @@ namespace client { 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 @@ namespace client { 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 @@ namespace client } 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client { 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client { 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client } 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 @@ namespace client 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 @@ namespace client // 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 @@ namespace client { 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 @@ namespace client } 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 @@ namespace client { 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 @@ namespace client 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 @@ namespace client { 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 @@ namespace client { 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 @@ namespace client { 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 @@ namespace client 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 @@ namespace client } 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client localDestination (dest) { } - + SAMSingleSession::~SAMSingleSession () { i2p::client::context.DeleteLocalDestination (localDestination); - } + } void SAMSingleSession::StopLocalDestination () { @@ -1220,24 +1210,24 @@ namespace client 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 @@ namespace client { 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 @@ namespace client } catch (const std::exception& ex) { - LogPrint (eLogError, "SAM: runtime exception: ", ex.what ()); + LogPrint (eLogError, "SAM: Runtime exception: ", ex.what ()); } { @@ -1311,8 +1301,8 @@ namespace client { 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client { *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) { @@ -1510,11 +1497,11 @@ namespace client 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 --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index d92cf009..88990d7c 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2020, The PurpleI2P Project +* Copyright (c) 2013-2021, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -41,7 +41,7 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client 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 @@ namespace client // implements SAMSession std::shared_ptr GetLocalDestination (); void StopLocalDestination (); - }; - + }; + class SAMBridge: private i2p::util::RunnableService { public: @@ -249,7 +249,7 @@ namespace client 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 --git a/libi2pd_client/SOCKS.cpp b/libi2pd_client/SOCKS.cpp index c5428c86..961ff934 100644 --- a/libi2pd_client/SOCKS.cpp +++ b/libi2pd_client/SOCKS.cpp @@ -191,13 +191,13 @@ namespace proxy 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 @@ namespace proxy 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 @@ namespace proxy 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 @@ namespace proxy 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 @@ namespace proxy 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 @@ namespace proxy [[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 @@ namespace proxy 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 @@ namespace proxy 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 @@ namespace proxy 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 @@ namespace proxy 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 @@ namespace proxy 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 @@ namespace proxy } 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 @@ namespace proxy { 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 @@ namespace proxy } 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 @@ namespace proxy 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 @@ namespace proxy // 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 @@ namespace proxy 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 @@ namespace proxy 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 @@ namespace proxy } } 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 @@ namespace proxy { 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 --git a/libi2pd_wrapper/api.go b/libi2pd_wrapper/api.go index 64403aae..8c215f13 100644 --- a/libi2pd_wrapper/api.go +++ b/libi2pd_wrapper/api.go @@ -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 --git a/libi2pd_wrapper/api.swigcxx b/libi2pd_wrapper/api.swigcxx index 3ef6bd36..e1d18eef 100644 --- a/libi2pd_wrapper/api.swigcxx +++ b/libi2pd_wrapper/api.swigcxx @@ -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 --git a/libi2pd_wrapper/capi.cpp b/libi2pd_wrapper/capi.cpp index fc4df917..af4765da 100644 --- a/libi2pd_wrapper/capi.cpp +++ b/libi2pd_wrapper/capi.cpp @@ -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 --git a/libi2pd_wrapper/capi.h b/libi2pd_wrapper/capi.h index bfc4f88b..aefd89f3 100644 --- a/libi2pd_wrapper/capi.h +++ b/libi2pd_wrapper/capi.h @@ -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 @@ extern "C" { #endif // initialization start and stop -void C_InitI2P (int argc, char argv[], const char * appName); +void C_InitI2P (int argc, char *argv[], const char * appName); //void C_InitI2P (int argc, char** argv, const char * appName); void C_TerminateI2P (); void C_StartI2P ();
"<< tr("Address") << "" << tr("Type") << "" << tr("EncType") << "