Browse Source

Merge branch 'openssl' of https://github.com/PurpleI2P/i2pd into openssl

pull/1560/head
user 4 years ago
parent
commit
005581ef62
  1. 29
      .github/workflows/build-windows.yml
  2. 21
      .github/workflows/build.yml
  3. 25
      ChangeLog
  4. 2
      Makefile.linux
  5. 1
      Win32/Win32NetState.h
  6. 2
      Win32/installer.iss
  7. 4
      android/build.gradle
  8. 41
      appveyor.yml
  9. 1
      build/CMakeLists.txt
  10. 10
      build/appveyor-msys2-upgrade.bash
  11. 32
      contrib/certificates/reseed/bugme_at_mail.i2p.crt
  12. 34
      contrib/certificates/reseed/reseed_at_diva.exchange.crt
  13. 5
      contrib/rpm/i2pd-git.spec
  14. 5
      contrib/rpm/i2pd.spec
  15. 39
      daemon/Daemon.cpp
  16. 24
      daemon/HTTPServer.cpp
  17. 65
      daemon/I2PControl.cpp
  18. 51
      daemon/UPnP.cpp
  19. 6
      debian/changelog
  20. 12
      libi2pd/Config.cpp
  21. 34
      libi2pd/Destination.cpp
  22. 4
      libi2pd/Destination.h
  23. 75
      libi2pd/ECIESX25519AEADRatchetSession.cpp
  24. 26
      libi2pd/ECIESX25519AEADRatchetSession.h
  25. 33
      libi2pd/Garlic.cpp
  26. 2
      libi2pd/Garlic.h
  27. 4
      libi2pd/HTTP.cpp
  28. 18
      libi2pd/I2NPProtocol.cpp
  29. 3
      libi2pd/I2NPProtocol.h
  30. 7
      libi2pd/NTCP2.cpp
  31. 1317
      libi2pd/NTCPSession.cpp
  32. 242
      libi2pd/NTCPSession.h
  33. 44
      libi2pd/NetDb.cpp
  34. 2
      libi2pd/NetDb.hpp
  35. 128
      libi2pd/RouterContext.cpp
  36. 5
      libi2pd/RouterContext.h
  37. 40
      libi2pd/RouterInfo.cpp
  38. 3
      libi2pd/RouterInfo.h
  39. 36
      libi2pd/SSU.cpp
  40. 24
      libi2pd/SSUData.cpp
  41. 6
      libi2pd/SSUData.h
  42. 37
      libi2pd/SSUSession.cpp
  43. 2
      libi2pd/SSUSession.h
  44. 31
      libi2pd/Streaming.cpp
  45. 2
      libi2pd/Streaming.h
  46. 3
      libi2pd/TransportSession.h
  47. 144
      libi2pd/Transports.cpp
  48. 18
      libi2pd/Transports.h
  49. 3
      libi2pd/Tunnel.cpp
  50. 2
      libi2pd/Tunnel.h
  51. 49
      libi2pd/util.cpp
  52. 1
      libi2pd/util.h
  53. 6
      libi2pd/version.h
  54. 23
      libi2pd_client/AddressBook.cpp
  55. 76
      libi2pd_client/ClientContext.cpp
  56. 2
      libi2pd_client/ClientContext.h
  57. 101
      libi2pd_client/I2CP.cpp
  58. 33
      libi2pd_client/I2CP.h
  59. 116
      libi2pd_client/I2PTunnel.cpp
  60. 5
      libi2pd_client/I2PTunnel.h
  61. 1
      qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml
  62. 2
      qt/i2pd_qt/i2pd_qt.pro

29
.github/workflows/build-windows.yml

@ -0,0 +1,29 @@
name: Build on Windows
on: [push, pull_request]
defaults:
run:
shell: msys2 {0}
jobs:
build:
name: Building for ${{ matrix.arch }}
runs-on: windows-latest
strategy:
fail-fast: true
matrix:
include: [
{ msystem: MINGW64, arch: x86_64 },
{ msystem: MINGW32, arch: i686 }
]
steps:
- uses: actions/checkout@v2
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
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
run: make USE_UPNP=yes DEBUG=no -j3

21
.github/workflows/build.yml

@ -0,0 +1,21 @@
name: Build on Ubuntu with make
on: [push, pull_request]
jobs:
build:
name: Building with USE_UPNP=${{ matrix.with_upnp }} flag
runs-on: ubuntu-16.04
strategy:
fail-fast: true
matrix:
with_upnp: ['yes', 'no']
steps:
- uses: actions/checkout@v2
- name: install packages
run: |
sudo add-apt-repository ppa:mhier/libboost-latest
sudo apt-get update
sudo apt-get install build-essential libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application
run: make USE_AVX=no USE_AESNI=no USE_UPNP=${{ matrix.with_upnp }} -j3

25
ChangeLog

@ -1,6 +1,31 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.33.0] - 2020-08-24
### Added
- Shared transient addresses
- crypto.ratchet.inboundTags paramater
- 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
### 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
- 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
- Webconsole layout
- Reseed servers list
### Fixed
- Don't connect through terminated SAM destination
- 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
## [2.32.1] - 2020-06-02 ## [2.32.1] - 2020-06-02
### Added ### Added
- Read explicit peers in tunnels config - Read explicit peers in tunnels config

2
Makefile.linux

@ -15,7 +15,7 @@ ifeq ($(shell expr match $(CXX) 'clang'),5)
NEEDED_CXXFLAGS += -std=c++11 NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10 else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
NEEDED_CXXFLAGS += -std=c++11 NEEDED_CXXFLAGS += -std=c++11
else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # gcc 4.7 - 4.9 else ifeq ($(shell expr match ${CXXVER} "4\.[8-9]"),3) # gcc 4.8 - 4.9
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1 NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6 else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6
NEEDED_CXXFLAGS += -std=c++11 NEEDED_CXXFLAGS += -std=c++11

1
Win32/Win32NetState.h

@ -31,6 +31,7 @@ public:
} else { } else {
Result = E_NOINTERFACE; Result = E_NOINTERFACE;
} }
AddRef();
return Result; return Result;
} }

2
Win32/installer.iss

@ -1,5 +1,5 @@
#define I2Pd_AppName "i2pd" #define I2Pd_AppName "i2pd"
#define I2Pd_ver "2.32.1" #define I2Pd_ver "2.33.0"
#define I2Pd_Publisher "PurpleI2P" #define I2Pd_Publisher "PurpleI2P"
[Setup] [Setup]

4
android/build.gradle

@ -30,8 +30,8 @@ android {
applicationId "org.purplei2p.i2pd" applicationId "org.purplei2p.i2pd"
targetSdkVersion 29 targetSdkVersion 29
minSdkVersion 14 minSdkVersion 14
versionCode 2321 versionCode 2330
versionName "2.32.1" versionName "2.33.0"
setProperty("archivesBaseName", archivesBaseName + "-" + versionName) setProperty("archivesBaseName", archivesBaseName + "-" + versionName)
ndk { ndk {
abiFilters 'armeabi-v7a' abiFilters 'armeabi-v7a'

41
appveyor.yml

@ -1,4 +1,4 @@
version: 2.32.1.{build} version: 2.33.0.{build}
pull_requests: pull_requests:
do_not_increment_build_number: true do_not_increment_build_number: true
branches: branches:
@ -9,32 +9,49 @@ os: Visual Studio 2015
shallow_clone: true shallow_clone: true
clone_depth: 1 clone_depth: 1
# avoid building 32-bit if 64-bit failed already
matrix:
fast_finish: true
environment: environment:
APPVEYOR_SAVE_CACHE_ON_ERROR: true
MSYS2_PATH_TYPE: inherit MSYS2_PATH_TYPE: inherit
CHERE_INVOKING: enabled_from_arguments CHERE_INVOKING: enabled_from_arguments
matrix: matrix:
- MSYSTEM: MINGW64 - MSYSTEM: MINGW64
MSYS_PACKAGES: mingw-w64-x86_64-boost mingw-w64-x86_64-miniupnpc
MSYS_BITNESS: 64
- MSYSTEM: MINGW32 - MSYSTEM: MINGW32
MSYS_PACKAGES: mingw-w64-i686-boost mingw-w64-i686-miniupnpc
MSYS_BITNESS: 32 cache:
- c:\msys64\var\cache\pacman\pkg\
install: install:
# install new signing keyring
- c:\msys64\usr\bin\bash -lc "curl -O https://mirror.selfnet.de/msys2/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
- c:\msys64\usr\bin\bash -lc "curl -O https://mirror.selfnet.de/msys2/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- c:\msys64\usr\bin\bash -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -U msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
# remove packages which can break build
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc mingw-w64-{i686,x86_64}-gcc-ada mingw-w64-{i686,x86_64}-gcc-objc" - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc mingw-w64-{i686,x86_64}-gcc-ada mingw-w64-{i686,x86_64}-gcc-objc"
# TODO: revert that change when appveyor's images will be updated
- c:\msys64\usr\bin\bash -l "build/appveyor-msys2-upgrade.bash"
# update runtime # update runtime
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu" - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
# Kill bash before next try
- taskkill /T /F /IM bash.exe /IM gpg.exe /IM gpg-agent.exe | exit /B 0
# update packages and install required # update packages and install required
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu ${MSYS_PACKAGES}" - c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu $MINGW_PACKAGE_PREFIX-boost $MINGW_PACKAGE_PREFIX-miniupnpc"
build_script: build_script:
- echo MSYSTEM = %MSYSTEM%, bitness = %MSYS_BITNESS% - c:\msys64\usr\bin\bash -lc "make USE_UPNP=yes DEBUG=no -j3"
- c:\msys64\usr\bin\bash -lc "make USE_UPNP=yes -j3" # prepare archive for uploading
- 7z a -tzip -mx9 -mmt i2pd-mingw-win%MSYS_BITNESS%.zip i2pd.exe - set "FILELIST=i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates contrib/tunnels.d"
- echo This is development build, use it carefully! For running in portable mode, move all files from contrib directory here. > README.txt
- 7z a -tzip -mx9 -mmt i2pd-%APPVEYOR_BUILD_VERSION%-%APPVEYOR_REPO_COMMIT:~0,7%-mingw-win%MSYSTEM:~-2%.zip %FILELIST%
after_build:
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Sc"
test: off test: off
deploy: off
artifacts: artifacts:
- path: i2pd-mingw-win*.zip - path: i2pd-*.zip

1
build/CMakeLists.txt

@ -68,7 +68,6 @@ set(LIBI2PD_SRC
"${LIBI2PD_SRC_DIR}/NetDb.cpp" "${LIBI2PD_SRC_DIR}/NetDb.cpp"
"${LIBI2PD_SRC_DIR}/NetDbRequests.cpp" "${LIBI2PD_SRC_DIR}/NetDbRequests.cpp"
"${LIBI2PD_SRC_DIR}/NTCP2.cpp" "${LIBI2PD_SRC_DIR}/NTCP2.cpp"
"${LIBI2PD_SRC_DIR}/NTCPSession.cpp"
"${LIBI2PD_SRC_DIR}/Poly1305.cpp" "${LIBI2PD_SRC_DIR}/Poly1305.cpp"
"${LIBI2PD_SRC_DIR}/Profiling.cpp" "${LIBI2PD_SRC_DIR}/Profiling.cpp"
"${LIBI2PD_SRC_DIR}/Reseed.cpp" "${LIBI2PD_SRC_DIR}/Reseed.cpp"

10
build/appveyor-msys2-upgrade.bash

@ -1,10 +0,0 @@
set -e -x
base_url='http://repo.msys2.org/msys/x86_64/'
packages="libzstd-1.4.4-2-x86_64.pkg.tar.xz pacman-5.2.1-6-x86_64.pkg.tar.xz zstd-1.4.4-2-x86_64.pkg.tar.xz"
for p in $packages
do
curl "${base_url}$p" -o "$p"
done
pacman -U --noconfirm $packages
rm -f $packages

32
contrib/certificates/reseed/bugme_at_mail.i2p.crt

@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFezCCA2OgAwIBAgIEUQYyQjANBgkqhkiG9w0BAQ0FADBuMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOYnVnbWVAbWFpbC5p
MnAwHhcNMTQxMTA2MDkxMTE0WhcNMjQxMTA1MDkxMTE0WjBuMQswCQYDVQQGEwJY
WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt
b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOYnVnbWVAbWFpbC5p
MnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCrThOH0eSDT0VnCSBC
sqYmAydWH+O8eNttDXr2mSvZLhvAW+6/xHTkKhaWvkIvvS0Vh8hujMnD90Cgp4Fk
TKCxMj9K527o5xIZwWW05OevbjlBwIpVLO1PjmsfsoD1nIX14eEzJSEoAulKsv7V
jGUC/6hC11mmVvH9buQLSRv6sCjuAcMszmw3TAD+XYBIs+z57KuwYXtX3+OA543c
l1/ZKLYkkwY8cwzZqWDVWqTKP5TfVae58t40HhJk3bOsr21FZsaOjlmao3GO+d/3
exKuUGJRcolSqskL3sZ1ovFqko81obvvx0upI0YA0iMr/NRGl3VPuf/LJvRppYGc
LsJHgy9TIgtHvaXRi5Nt4CbKl9sZh/7WkkTTI5YGvevu00btlabAN+DSAZZqdsB3
wY8HhM1MHiA9SWsqwU65TwErcRrjNna2FiDHEu0xk5+/iAGl6CSKHZBmNcYKXSv8
cwShB0jjmciK0a05nC638RPgj0fng7KRrSglyzfjXRrljmZ40LSBL/GGMZMWpOM7
mEsBH5UZJ/2BEmjc9X9257zBdx8BK8y1TXpAligpNBsERcTw1WP1PJ35einZvlXW
qI3GwMf0sl26sn+evcK0gDl27jVDZ45MtNQEq64M4NV3Tn9zq0eg/39YvjVeqrI5
l7sxmYqYGR6BuSncwdc4x+t6swIDAQABoyEwHzAdBgNVHQ4EFgQU/REZ7NMbVZHr
Xkao6Q8Ccqv2kAMwDQYJKoZIhvcNAQENBQADggIBACc2YjLVNbl1kJUdg2klCLJt
5LjNTiIZa2Cha5GStlC/lyoRRge6+q/y9TN3tTptlzLPS9pI9EE1GfIQaE+HAk+e
/bC3KUOAHgVuETvsNAbfpaVsPCdWpFuXmp/4b9iDN7qZy4afTKUPA/Ir/cLfNp14
JULfP4z2yFOsCQZ5viNFAs1u99FrwobV2LBzUSIJQewsksuOwj96zIyau0Y629oJ
k+og88Tifd9EH3MVZNGhdpojQDDdwHQSITnCDgfRP5yER1WIA4jg6l+mM90QkvLY
5NjWTna5kJ3X6UizvgCk365yzT2sbN3R9UGXfCJa9GBcnnviJtJF3+/gC0abwY2f
NtVYp32Xky45NY/NdRhDg0bjHP3psxmX+Sc0M9NuQcDQ+fUR+CzM0IGeiszkzXOs
RG+bOou2cZ81G4oxWdAALHIRrn7VvLGlkFMxiIZyhYcTGQZzsTPT6n18dY99+DAV
yQWZfIRdm8DOnt0G+cwfeohc/9ZwDmj4jJAAi0aeTXdY6NEGIVydk6MAycEhg2Hx
9EV96kRwZNIW0AGY8CozECFL3Eyo2ClQVV4Q35SsBibsitDjM03usc2DJ/qjynXA
C8HoOSWgbddiBvqZueqK8GdhykOy3J3ysr+MNN/lbG48LqkQr1OWxev9rGGQ6RJT
wpBgPyAFAwouPy1whmnx
-----END CERTIFICATE-----

34
contrib/certificates/reseed/reseed_at_diva.exchange.crt

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
MIIF0zCCA7ugAwIBAgIQWjHyC+NRh3emuuAwcEnKSjANBgkqhkiG9w0BAQsFADB0
MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK
ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEdMBsGA1UEAwwU
cmVzZWVkQGRpdmEuZXhjaGFuZ2UwHhcNMjAwNjA5MDUzNjQ1WhcNMzAwNjA5MDUz
NjQ1WjB0MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4w
HAYDVQQKExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEdMBsG
A1UEAwwUcmVzZWVkQGRpdmEuZXhjaGFuZ2UwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQC6BJGeMEgoXk9dlzKVfmwHrT2VpwTT+wRJvh3eAM746u4uDT2y
NPHXhdGcQ9dRRZ63T98IshWCwOmWSlm1kdWkmKkVVb93GUoMQ3gziCi0apLJMAau
gEu/sPCbORS2dPsQeAPW2eIsJO7dSjTRiQAuquW//NcIXG4gnxDA52lgke1BvpKr
83SJlCrqECAy6OKtZ49yn75CqmPPWFn0b/E8bxruN5ffeipTTospvdEtT41gXUqk
hOz3k8ang+QTWiP//jOjk31KXZ2dbh0LOlNJOvRxCqQmBZafNxxCR4DH8RewfPlL
qOiOJVzbLSP9RjqPLwnny5BOjbLWXcaybN5Qv2Pyd4mKtN3EpqBwRu7VnzXpsuuG
gRbxNmfKJ/vBEGrZAHAxi0NkHHEEne3B7pPDc2dVZHOfTfCu31m9uDHZ4eHEsNOJ
SJRiGjq74l0chCSlBGLrD1Y9LPyqadjdwuB9bzM0tMFC1wPflanQCflhhnEzAfbN
BaU2GRXo/I1UCDW/dH1FIkqEe61eMW1Lwqr5tdlrUpdr5VIddTyNJRBJogbZ+HZE
8mcoJW2lXRAkYi7KEm4b4EQNe7sbRNTF0j+fAJ+3ZOZ3O3SMHss6ignlSa+giVim
VvL+Joc6wpSzxpeNPf6m82cEO/UvifFYeOC9TpiRriSt+vvgQVzQtfQ+fQIDAQAB
o2EwXzAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUF
BwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHJlc2VlZEBkaXZhLmV4Y2hh
bmdlMA0GCSqGSIb3DQEBCwUAA4ICAQCFGOb1dHlwjmgFHEER6oMiGWl1mI3Hb7GX
NNI6QUhZQ+iEWGYtsOTk3Q8xejL8t6AG/ZLXfZviLIJXZc5XZfPXk0ezDSC2cYxQ
ZAyYPw2dRP14brI86sCSqNAFIax/U5SM3zXhCbBiTfaEoBPfDpvKjx+VliaITUnc
sHTRn+C5ID5M8cZIqUSGECPEMU/bDtuRNJLTKYaJ98yXtYuS2CWsMEM4o0GGcnYQ
5HOZT/lbbwfq1Ks7IyJpeIpRaS5qckGcfgkxFY4eGujDuaFeWC+HCIh9RzBJrqZR
73Aly4Pyu7Jjg8xCCf9MswDjtqAjEHgWCmRLWL7p3H6cPipFKNMY6yomYZl5urE7
q6DUAZFKwPqlZpyeaY4/SVvaHTxuPp7484s3db4kPhdmuQS/DOB/7d+cn/S580Vy
ALqlFQjtjLEaT16upceAV0gYktDInE6Rtym/OsqilrtYks/Sc0GROSz8lJhDDWbr
W3t92muSXDh0rYrEUYWl+xl1gSTpbIP75zzU+cUr1E/qlRY9qZn66FsJpOuN0I0q
UXsQS/bPDcA+IW48Hd9LfO9gtTWZslwFTimjEvQ2nJAnUlUQP6OfuPUKHoYX/CwY
2LCN8+pv2bKPDVHvp0lf6xrbbZNvFtzfR0G3AprZjYpuu2XgjVB5nJnwmbH74b9w
LD8d2z2Lgg==
-----END CERTIFICATE-----

5
contrib/rpm/i2pd-git.spec

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.32.1 Version: 2.33.0
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@ -124,6 +124,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Aug 24 2020 orignal <i2porignal@yandex.ru> - 2.33.0
- update to 2.33.0
* Tue Jun 02 2020 r4sas <r4sas@i2pmail.org> - 2.32.1 * Tue Jun 02 2020 r4sas <r4sas@i2pmail.org> - 2.32.1
- update to 2.32.1 - update to 2.32.1

5
contrib/rpm/i2pd.spec

@ -1,5 +1,5 @@
Name: i2pd Name: i2pd
Version: 2.32.1 Version: 2.33.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@ -122,6 +122,9 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Mon Aug 24 2020 orignal <i2porignal@yandex.ru> - 2.33.0
- update to 2.33.0
* Tue Jun 02 2020 r4sas <r4sas@i2pmail.org> - 2.32.1 * Tue Jun 02 2020 r4sas <r4sas@i2pmail.org> - 2.32.1
- update to 2.32.1 - update to 2.32.1

39
daemon/Daemon.cpp

@ -17,7 +17,6 @@
#include "Base.h" #include "Base.h"
#include "version.h" #include "version.h"
#include "Transports.h" #include "Transports.h"
#include "NTCPSession.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "Tunnel.h" #include "Tunnel.h"
@ -135,8 +134,8 @@ namespace i2p
i2p::context.SetNetID (netID); i2p::context.SetNetID (netID);
i2p::context.Init (); i2p::context.Init ();
bool ipv6; i2p::config::GetOption("ipv6", ipv6); bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ipv4; i2p::config::GetOption("ipv4", ipv4); bool ipv4; i2p::config::GetOption("ipv4", ipv4);
#ifdef MESHNET #ifdef MESHNET
// manual override for meshnet // manual override for meshnet
ipv4 = false; ipv4 = false;
@ -151,8 +150,7 @@ namespace i2p
i2p::context.SetSupportsV6 (ipv6); i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4); i2p::context.SetSupportsV4 (ipv4);
bool ntcp; i2p::config::GetOption("ntcp", ntcp); i2p::context.RemoveNTCPAddress (!ipv6); // TODO: remove later
i2p::context.PublishNTCPAddress (ntcp, !ipv6);
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2) if (ntcp2)
{ {
@ -160,7 +158,7 @@ namespace i2p
if (published) if (published)
{ {
uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port); uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port);
if (!ntcp && !ntcp2port) ntcp2port = port; // use standard port if (!ntcp2port) ntcp2port = port; // use standard port
i2p::context.PublishNTCP2Address (ntcp2port, true); // publish i2p::context.PublishNTCP2Address (ntcp2port, true); // publish
if (ipv6) if (ipv6)
{ {
@ -180,13 +178,10 @@ namespace i2p
SetMaxNumTransitTunnels (transitTunnels); SetMaxNumTransitTunnels (transitTunnels);
bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill); bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill);
if (isFloodfill) if (isFloodfill) {
{
LogPrint(eLogInfo, "Daemon: router will be floodfill"); LogPrint(eLogInfo, "Daemon: router will be floodfill");
i2p::context.SetFloodfill (true); i2p::context.SetFloodfill (true);
} } else {
else
{
i2p::context.SetFloodfill (false); i2p::context.SetFloodfill (false);
} }
@ -254,8 +249,7 @@ namespace i2p
i2p::transport::transports.RestrictRoutesToFamilies(fams); i2p::transport::transports.RestrictRoutesToFamilies(fams);
restricted = fams.size() > 0; restricted = fams.size() > 0;
} }
if (routers.length() > 0) if (routers.length() > 0) {
{
std::set<i2p::data::IdentHash> idents; std::set<i2p::data::IdentHash> idents;
size_t pos = 0, comma; size_t pos = 0, comma;
do do
@ -291,8 +285,7 @@ namespace i2p
i2p::data::netdb.Start(); i2p::data::netdb.Start();
bool upnp; i2p::config::GetOption("upnp.enabled", upnp); bool upnp; i2p::config::GetOption("upnp.enabled", upnp);
if (upnp) if (upnp) {
{
d.UPnP = std::unique_ptr<i2p::transport::UPnP>(new i2p::transport::UPnP); d.UPnP = std::unique_ptr<i2p::transport::UPnP>(new i2p::transport::UPnP);
d.UPnP->Start (); d.UPnP->Start ();
} }
@ -304,14 +297,16 @@ namespace i2p
d.m_NTPSync->Start (); d.m_NTPSync->Start ();
} }
bool ntcp; i2p::config::GetOption("ntcp", ntcp); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
bool ssu; i2p::config::GetOption("ssu", ssu); bool ssu; i2p::config::GetOption("ssu", ssu);
bool checkInReserved; i2p::config::GetOption("reservedrange", checkInReserved);
LogPrint(eLogInfo, "Daemon: starting Transports"); LogPrint(eLogInfo, "Daemon: starting Transports");
if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled"); if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled");
if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled"); if(!ntcp2) LogPrint(eLogInfo, "Daemon: ntcp2 disabled");
i2p::transport::transports.Start(ntcp, ssu); i2p::transport::transports.SetCheckReserved(checkInReserved);
if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2()) i2p::transport::transports.Start(ntcp2, ssu);
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2())
LogPrint(eLogInfo, "Daemon: Transports started"); LogPrint(eLogInfo, "Daemon: Transports started");
else else
{ {
@ -323,8 +318,7 @@ namespace i2p
} }
bool http; i2p::config::GetOption("http.enabled", http); bool http; i2p::config::GetOption("http.enabled", http);
if (http) if (http) {
{
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); 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);
@ -340,6 +334,7 @@ namespace i2p
} }
} }
LogPrint(eLogInfo, "Daemon: starting Tunnels"); LogPrint(eLogInfo, "Daemon: starting Tunnels");
i2p::tunnel::tunnels.Start(); i2p::tunnel::tunnels.Start();

24
daemon/HTTPServer.cpp

@ -12,7 +12,6 @@
#include <memory> #include <memory>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include "Base.h" #include "Base.h"
@ -83,17 +82,18 @@ namespace http {
" .disabled:after { color: #D33F3F; content: \"Disabled\" }\r\n" " .disabled:after { color: #D33F3F; content: \"Disabled\" }\r\n"
" .enabled:after { color: #56B734; content: \"Enabled\" }\r\n" " .enabled:after { color: #56B734; content: \"Enabled\" }\r\n"
" @media screen and (max-width: 980px) {\r\n" /* adaptive style */ " @media screen and (max-width: 980px) {\r\n" /* adaptive style */
" body { padding: 1.5em 0 0 0; }\r\n"
" .menu { width: 100%; display: block; float: none; position: unset; font-size: 16px;\r\n" " .menu { width: 100%; display: block; float: none; position: unset; font-size: 16px;\r\n"
" text-align: center; }\r\n" " text-align: center; }\r\n"
" .menu a, .commands a { padding: 2px; }\r\n" " .menu a, .commands a { padding: 2px; }\r\n"
" .content { float: none; margin: 0; margin-top: 16px; max-width: 100%; width: 100%;\r\n" " .content { float: none; margin-left: unset; margin-top: 16px; max-width: 100%; width: 100%;\r\n"
" text-align: center; }\r\n" " text-align: center; }\r\n"
" a, .slide label { /* margin-right: 10px; */ display: block; /* font-size: 18px; */ }\r\n" " a, .slide label { /* margin-right: 10px; */ display: block; /* font-size: 18px; */ }\r\n"
" .header { margin: unset; font-size: 1.5em; } small {display: block}\r\n" " .header { margin: unset; font-size: 1.5em; } small {display: block}\r\n"
" a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n" " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
" color: initial; margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n" " color: initial; margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n"
" input { width: 35%; text-align: center; padding: 5px;\r\n" " input { width: 35%; text-align: center; padding: 5px;\r\n"
" border: 2px solid #ccc; -webkit-border-radius: 5px; border-radius: 5px; font-size: 24px; }\r\n" " border: 2px solid #ccc; -webkit-border-radius: 5px; border-radius: 5px; font-size: 18px; }\r\n"
" textarea { width: -webkit-fill-available; height: auto; padding:5px; border:2px solid #ccc;\r\n" " textarea { width: -webkit-fill-available; height: auto; padding:5px; border:2px solid #ccc;\r\n"
" -webkit-border-radius: 5px; border-radius: 5px; font-size: 12px; }\r\n" " -webkit-border-radius: 5px; border-radius: 5px; font-size: 12px; }\r\n"
" button[type=submit] { padding: 5px 15px; background: #ccc; border: 0 none; cursor: pointer;\r\n" " button[type=submit] { padding: 5px 15px; background: #ccc; border: 0 none; cursor: pointer;\r\n"
@ -257,7 +257,10 @@ namespace http {
switch (i2p::context.GetError ()) switch (i2p::context.GetError ())
{ {
case eRouterErrorClockSkew: case eRouterErrorClockSkew:
s << "<br>Clock skew"; s << " - Clock skew";
break;
case eRouterErrorOffline:
s << " - Offline";
break; break;
default: ; default: ;
} }
@ -732,13 +735,6 @@ namespace http {
void ShowTransports (std::stringstream& s) void ShowTransports (std::stringstream& s)
{ {
s << "<b>Transports:</b><br>\r\n"; s << "<b>Transports:</b><br>\r\n";
auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
if (ntcpServer)
{
auto sessions = ntcpServer->GetNTCPSessions ();
if (!sessions.empty ())
ShowNTCPTransports (s, sessions, "NTCP");
}
auto ntcp2Server = i2p::transport::transports.GetNTCP2Server (); auto ntcp2Server = i2p::transport::transports.GetNTCP2Server ();
if (ntcp2Server) if (ntcp2Server)
{ {
@ -933,7 +929,7 @@ namespace http {
time_t t = divTime.quot; time_t t = divTime.quot;
struct tm *tm = localtime(&t); struct tm *tm = localtime(&t);
char date[128]; char date[128];
snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03ld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem); snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03lld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem);
return date; return date;
} }
@ -1320,8 +1316,8 @@ namespace http {
void HTTPServer::Accept () void HTTPServer::Accept ()
{ {
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service); auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
m_Acceptor.async_accept (*newSocket, boost::bind (&HTTPServer::HandleAccept, this, m_Acceptor.async_accept (*newSocket, std::bind (&HTTPServer::HandleAccept, this,
boost::asio::placeholders::error, newSocket)); std::placeholders::_1, newSocket));
} }
void HTTPServer::HandleAccept(const boost::system::error_code& ecode, void HTTPServer::HandleAccept(const boost::system::error_code& ecode,

65
daemon/I2PControl.cpp

@ -1,11 +1,3 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <stdio.h> #include <stdio.h>
#include <sstream> #include <sstream>
#include <openssl/x509.h> #include <openssl/x509.h>
@ -14,12 +6,7 @@
#include <boost/date_time/local_time/local_time.hpp> #include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ini_parser.hpp>
// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))
#if !GCC47_BOOST149
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#endif
#include "Crypto.h" #include "Crypto.h"
#include "FS.h" #include "FS.h"
@ -67,28 +54,29 @@ namespace client
m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem); m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem);
// handlers // handlers
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler; m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler; m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler;
m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler; m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler;
m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler; m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler;
m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler; m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler;
m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler; m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler;
m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler; m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler;
// I2PControl // I2PControl
m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler; m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler;
// RouterInfo // RouterInfo
m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler; m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler;
m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler; m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler;
m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler; m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler;
m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler; m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler;
m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler; m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler;
m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S; 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.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler; m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler; m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler;
m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = &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.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes; m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
@ -104,10 +92,10 @@ namespace client
// ClientServicesInfo // ClientServicesInfo
m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlService::I2PTunnelInfoHandler; m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlService::I2PTunnelInfoHandler;
m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlService::HTTPProxyInfoHandler; m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlService::HTTPProxyInfoHandler;
m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler; m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler;
m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler; m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler;
m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler; m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler;
m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler; m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler;
} }
I2PControlService::~I2PControlService () I2PControlService::~I2PControlService ()
@ -242,12 +230,6 @@ namespace client
} }
} }
std::ostringstream response; std::ostringstream response;
#if GCC47_BOOST149
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
response << "{\"id\":null,\"error\":";
response << "{\"code\":-32603,\"message\":\"JSON requests is not supported with this version of boost\"},";
response << "\"jsonrpc\":\"2.0\"}";
#else
boost::property_tree::ptree pt; boost::property_tree::ptree pt;
boost::property_tree::read_json (ss, pt); boost::property_tree::read_json (ss, pt);
@ -267,7 +249,6 @@ namespace client
response << "{\"code\":-32601,\"message\":\"Method not found\"},"; response << "{\"code\":-32601,\"message\":\"Method not found\"},";
response << "\"jsonrpc\":\"2.0\"}"; response << "\"jsonrpc\":\"2.0\"}";
} }
#endif
SendResponse (socket, buf, response, isHtml); SendResponse (socket, buf, response, isHtml);
} }
catch (std::exception& ex) catch (std::exception& ex)
@ -507,7 +488,7 @@ namespace client
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent
m_ShutdownTimer.async_wait ( m_ShutdownTimer.async_wait (
[](const boost::system::error_code& ecode) [](const boost::system::error_code& ecode)
{ {
Daemon.running = 0; Daemon.running = 0;
}); });
} }
@ -521,7 +502,7 @@ namespace client
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second
m_ShutdownTimer.async_wait ( m_ShutdownTimer.async_wait (
[](const boost::system::error_code& ecode) [](const boost::system::error_code& ecode)
{ {
Daemon.running = 0; Daemon.running = 0;
}); });
} }

51
daemon/UPnP.cpp

@ -1,18 +1,9 @@
/*
* 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
*/
#ifdef USE_UPNP #ifdef USE_UPNP
#include <string> #include <string>
#include <thread> #include <thread>
#include <boost/thread/thread.hpp> #include <boost/thread/thread.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/bind.hpp>
#include "Log.h" #include "Log.h"
@ -88,10 +79,10 @@ namespace transport
void UPnP::Discover () void UPnP::Discover ()
{ {
bool isError; bool isError;
int err; int err;
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS)) #if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS))
err = UPNPDISCOVER_SUCCESS; err = UPNPDISCOVER_SUCCESS;
#if (MINIUPNPC_API_VERSION >= 14) #if (MINIUPNPC_API_VERSION >= 14)
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, 2, &err); m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, 2, &err);
@ -100,9 +91,9 @@ namespace transport
#endif #endif
isError = err != UPNPDISCOVER_SUCCESS; isError = err != UPNPDISCOVER_SUCCESS;
#else // MINIUPNPC_API_VERSION >= 8 #else // MINIUPNPC_API_VERSION >= 8
err = 0; err = 0;
m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0); m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0);
isError = m_Devlist == NULL; isError = m_Devlist == NULL;
#endif // MINIUPNPC_API_VERSION >= 8 #endif // MINIUPNPC_API_VERSION >= 8
{ {
@ -113,15 +104,15 @@ namespace transport
if (isError) 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; return;
} }
err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr)); err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
m_upnpUrlsInitialized = err != 0; m_upnpUrlsInitialized=err!=0;
if (err == UPNP_IGD_VALID_CONNECTED) if (err == UPNP_IGD_VALID_CONNECTED)
{ {
err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress); err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(err != UPNPCOMMAND_SUCCESS) if(err != UPNPCOMMAND_SUCCESS)
{ {
LogPrint (eLogError, "UPnP: unable to get external address: error ", err); LogPrint (eLogError, "UPnP: unable to get external address: error ", err);
@ -132,14 +123,14 @@ namespace transport
LogPrint (eLogError, "UPnP: found Internet Gateway Device ", m_upnpUrls.controlURL); LogPrint (eLogError, "UPnP: found Internet Gateway Device ", m_upnpUrls.controlURL);
if (!m_externalIPAddress[0]) 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; return;
} }
} }
} }
else 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; return;
} }
@ -190,7 +181,7 @@ namespace transport
err = CheckMapping (strPort.c_str (), strType.c_str ()); err = CheckMapping (strPort.c_str (), strType.c_str ());
if (err != UPNPCOMMAND_SUCCESS) // if mapping not found if (err != UPNPCOMMAND_SUCCESS) // if mapping not found
{ {
LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err); LogPrint (eLogDebug, "UPnP: possibly port ", strPort, " is not forwarded: return code ", err);
#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS)) #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); 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);
@ -210,7 +201,7 @@ namespace transport
} }
else 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; return;
} }
} }
@ -227,14 +218,14 @@ namespace transport
void UPnP::CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address) void UPnP::CloseMapping (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{ {
if(!m_upnpUrlsInitialized) { if(!m_upnpUrlsInitialized) {
return; return;
} }
std::string strType (GetProto (address)), strPort (std::to_string (address->port)); std::string strType (GetProto (address)), strPort (std::to_string (address->port));
int err = UPNPCOMMAND_SUCCESS; int err = UPNPCOMMAND_SUCCESS;
err = CheckMapping (strPort.c_str (), strType.c_str ()); err = CheckMapping (strPort.c_str (), strType.c_str ());
if (err == UPNPCOMMAND_SUCCESS) if (err == UPNPCOMMAND_SUCCESS)
{ {
err = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), NULL); err = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), NULL);
LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", err); LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", err);
@ -245,11 +236,11 @@ namespace transport
{ {
freeUPNPDevlist (m_Devlist); freeUPNPDevlist (m_Devlist);
m_Devlist = 0; m_Devlist = 0;
if(m_upnpUrlsInitialized){ if(m_upnpUrlsInitialized){
FreeUPNPUrls (&m_upnpUrls); FreeUPNPUrls (&m_upnpUrls);
m_upnpUrlsInitialized=false; m_upnpUrlsInitialized=false;
} }
} }
std::string UPnP::GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address) std::string UPnP::GetProto (std::shared_ptr<i2p::data::RouterInfo::Address> address)
{ {

6
debian/changelog vendored

@ -1,3 +1,9 @@
i2pd (2.33.0-1) unstable; urgency=medium
* updated to version 2.33.0/0.9.47
-- orignal <orignal@i2pmail.org> Mon, 24 Aug 2020 16:00:00 +0000
i2pd (2.32.1-1) unstable; urgency=high i2pd (2.32.1-1) unstable; urgency=high
* updated to version 2.32.1 * updated to version 2.32.1

12
libi2pd/Config.cpp

@ -51,6 +51,7 @@ namespace config {
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)") ("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
("ipv4", value<bool>()->default_value(true), "Enable communication through ipv4 (default: enabled)") ("ipv4", value<bool>()->default_value(true), "Enable communication through ipv4 (default: enabled)")
("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)") ("ipv6", bool_switch()->default_value(false), "Enable communication through ipv6 (default: disabled)")
("reservedrange", value<bool>()->default_value(true), "Check remote RI for being in blacklist of reserved IP ranges (default: enabled)")
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2") ("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)") ("daemon", bool_switch()->default_value(false), "Router will go to background after start (default: disabled)")
("service", bool_switch()->default_value(false), "Router will use system folders like '/var/lib/i2pd' (default: disabled)") ("service", bool_switch()->default_value(false), "Router will use system folders like '/var/lib/i2pd' (default: disabled)")
@ -58,9 +59,9 @@ namespace config {
("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)") ("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)")
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") ("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") ("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
("ntcp", value<bool>()->default_value(false), "Enable NTCP transport (default: disabled)") ("ntcp", value<bool>()->default_value(false), "Ignored. Always false")
("ssu", value<bool>()->default_value(true), "Enable SSU transport (default: enabled)") ("ssu", value<bool>()->default_value(true), "Enable SSU transport (default: enabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Proxy URL for NTCP transport") ("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
#ifdef _WIN32 #ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')") ("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)")
@ -96,7 +97,7 @@ namespace config {
("httpproxy.enabled", value<bool>()->default_value(true), "Enable or disable HTTP Proxy") ("httpproxy.enabled", value<bool>()->default_value(true), "Enable or disable HTTP Proxy")
("httpproxy.address", value<std::string>()->default_value("127.0.0.1"), "HTTP Proxy listen address") ("httpproxy.address", value<std::string>()->default_value("127.0.0.1"), "HTTP Proxy listen address")
("httpproxy.port", value<uint16_t>()->default_value(4444), "HTTP Proxy listen port") ("httpproxy.port", value<uint16_t>()->default_value(4444), "HTTP Proxy listen port")
("httpproxy.keys", value<std::string>()->default_value(""), "File to persist HTTP Proxy keys") ("httpproxy.keys", value<std::string>()->default_value("transient-proxy"), "File to persist HTTP Proxy keys. Transient by default")
("httpproxy.signaturetype", value<i2p::data::SigningKeyType>()-> ("httpproxy.signaturetype", value<i2p::data::SigningKeyType>()->
default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default")
("httpproxy.inbound.length", value<std::string>()->default_value("3"), "HTTP proxy inbound tunnel length") ("httpproxy.inbound.length", value<std::string>()->default_value("3"), "HTTP proxy inbound tunnel length")
@ -116,7 +117,7 @@ namespace config {
("socksproxy.enabled", value<bool>()->default_value(true), "Enable or disable SOCKS Proxy") ("socksproxy.enabled", value<bool>()->default_value(true), "Enable or disable SOCKS Proxy")
("socksproxy.address", value<std::string>()->default_value("127.0.0.1"), "SOCKS Proxy listen address") ("socksproxy.address", value<std::string>()->default_value("127.0.0.1"), "SOCKS Proxy listen address")
("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port") ("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port")
("socksproxy.keys", value<std::string>()->default_value(""), "File to persist SOCKS Proxy keys") ("socksproxy.keys", value<std::string>()->default_value("transient-proxy"), "File to persist SOCKS Proxy keys. Transient by default")
("socksproxy.signaturetype", value<i2p::data::SigningKeyType>()-> ("socksproxy.signaturetype", value<i2p::data::SigningKeyType>()->
default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default") default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default")
("socksproxy.inbound.length", value<std::string>()->default_value("3"), "SOCKS proxy inbound tunnel length") ("socksproxy.inbound.length", value<std::string>()->default_value("3"), "SOCKS proxy inbound tunnel length")
@ -152,6 +153,7 @@ namespace config {
("i2cp.enabled", value<bool>()->default_value(false), "Enable or disable I2CP") ("i2cp.enabled", value<bool>()->default_value(false), "Enable or disable I2CP")
("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address") ("i2cp.address", value<std::string>()->default_value("127.0.0.1"), "I2CP listen address")
("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port") ("i2cp.port", value<uint16_t>()->default_value(7654), "I2CP listen port")
("i2cp.singlethread", value<bool>()->default_value(true), "Destinations run in the I2CP server's thread")
; ;
options_description i2pcontrol("I2PControl options"); options_description i2pcontrol("I2PControl options");
@ -195,7 +197,7 @@ namespace config {
("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks") ("reseed.proxy", value<std::string>()->default_value(""), "url for reseed proxy, supports http/socks")
("reseed.urls", value<std::string>()->default_value( ("reseed.urls", value<std::string>()->default_value(
"https://reseed.i2p-projekt.de/," "https://reseed.i2p-projekt.de/,"
"https://i2p.mooo.com/netDb/," "https://reseed.diva.exchange/,"
"https://reseed.i2p2.no/," "https://reseed.i2p2.no/,"
"https://reseed-fr.i2pd.xyz/," "https://reseed-fr.i2pd.xyz/,"
"https://reseed.memcpy.io/," "https://reseed.memcpy.io/,"

34
libi2pd/Destination.cpp

@ -13,6 +13,7 @@
#include <vector> #include <vector>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include "Crypto.h" #include "Crypto.h"
#include "Config.h"
#include "Log.h" #include "Log.h"
#include "FS.h" #include "FS.h"
#include "Timestamp.h" #include "Timestamp.h"
@ -464,14 +465,14 @@ namespace client
if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST) if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST)
{ {
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
{
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"); // TODO: recheck this message i2p::data::IdentHash peerHash (buf + 33 + i*32);
i2p::data::netdb.RequestDestination (peerHash); if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash))
{
LogPrint (eLogInfo, "Destination: Found new floodfill, request it");
i2p::data::netdb.RequestDestination (peerHash, nullptr, false); // through exploratory
}
} }
}
auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded); auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded);
if (floodfill) if (floodfill)
@ -603,9 +604,8 @@ namespace client
return; return;
} }
auto s = shared_from_this (); auto s = shared_from_this ();
// we must capture this for gcc 4.7 due the bug
RequestLeaseSet (ls->GetStoreHash (), RequestLeaseSet (ls->GetStoreHash (),
[s, ls, this](std::shared_ptr<const i2p::data::LeaseSet> leaseSet) [s, ls](std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
{ {
if (leaseSet) if (leaseSet)
{ {
@ -744,14 +744,19 @@ namespace client
request->excluded.insert (nextFloodfill->GetIdentHash ()); request->excluded.insert (nextFloodfill->GetIdentHash ());
request->requestTimeoutTimer.cancel (); request->requestTimeoutTimer.cancel ();
bool isECIES = SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD_RATCHET) &&
nextFloodfill->GetVersion () >= MAKE_VERSION_NUMBER(0, 9, 46); // >= 0.9.46;
uint8_t replyKey[32], replyTag[32]; uint8_t replyKey[32], replyTag[32];
RAND_bytes (replyKey, 32); // random session key RAND_bytes (replyKey, 32); // random session key
RAND_bytes (replyTag, 32); // random session tag RAND_bytes (replyTag, isECIES ? 8 : 32); // random session tag
AddSessionKey (replyKey, replyTag); if (isECIES)
AddECIESx25519Key (replyKey, replyTag);
else
AddSessionKey (replyKey, replyTag);
auto msg = WrapMessage (nextFloodfill, auto msg = WrapMessage (nextFloodfill,
CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
request->replyTunnel, replyKey, replyTag)); request->replyTunnel, replyKey, replyTag, isECIES));
request->outboundTunnel->SendTunnelDataMsg ( request->outboundTunnel->SendTunnelDataMsg (
{ {
i2p::tunnel::TunnelMessageBlock i2p::tunnel::TunnelMessageBlock
@ -847,6 +852,7 @@ namespace client
bool isPublic, const std::map<std::string, std::string> * params): bool isPublic, const std::map<std::string, std::string> * params):
LeaseSetDestination (service, isPublic, params), LeaseSetDestination (service, isPublic, params),
m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY), m_Keys (keys), m_StreamingAckDelay (DEFAULT_INITIAL_ACK_DELAY),
m_IsStreamingAnswerPings (DEFAULT_ANSWER_PINGS),
m_DatagramDestination (nullptr), m_RefCounter (0), m_DatagramDestination (nullptr), m_RefCounter (0),
m_ReadyChecker(service) m_ReadyChecker(service)
{ {
@ -855,8 +861,7 @@ namespace client
// extract encryption type params for LS2 // extract encryption type params for LS2
std::set<i2p::data::CryptoKeyType> encryptionKeyTypes; std::set<i2p::data::CryptoKeyType> encryptionKeyTypes;
if ((GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2 || if (params)
GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) && params)
{ {
auto it = params->find (I2CP_PARAM_LEASESET_ENCRYPTION_TYPE); auto it = params->find (I2CP_PARAM_LEASESET_ENCRYPTION_TYPE);
if (it != params->end ()) if (it != params->end ())
@ -915,6 +920,9 @@ namespace client
auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY); auto it = params->find (I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY);
if (it != params->end ()) if (it != params->end ())
m_StreamingAckDelay = std::stoi(it->second); 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);
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
{ {

4
libi2pd/Destination.h

@ -78,6 +78,8 @@ namespace client
// streaming // streaming
const char I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY[] = "i2p.streaming.initialAckDelay"; const char I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY[] = "i2p.streaming.initialAckDelay";
const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds const int DEFAULT_INITIAL_ACK_DELAY = 200; // milliseconds
const char I2CP_PARAM_STREAMING_ANSWER_PINGS[] = "i2p.streaming.answerPings";
const int DEFAULT_ANSWER_PINGS = true;
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete; typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
@ -242,6 +244,7 @@ namespace client
bool IsAcceptingStreams () const; bool IsAcceptingStreams () const;
void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor); void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor);
int GetStreamingAckDelay () const { return m_StreamingAckDelay; } int GetStreamingAckDelay () const { return m_StreamingAckDelay; }
bool IsStreamingAnswerPings () const { return m_IsStreamingAnswerPings; }
// datagram // datagram
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
@ -275,6 +278,7 @@ namespace client
std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey; std::unique_ptr<EncryptionKey> m_ECIESx25519EncryptionKey;
int m_StreamingAckDelay; int m_StreamingAckDelay;
bool m_IsStreamingAnswerPings;
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts; std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
i2p::datagram::DatagramDestination * m_DatagramDestination; i2p::datagram::DatagramDestination * m_DatagramDestination;

75
libi2pd/ECIESX25519AEADRatchetSession.cpp

@ -88,12 +88,67 @@ namespace garlic
} }
} }
void RatchetTagSet::DeleteSymmKey (int index)
{
m_ItermediateSymmKeys.erase (index);
}
void RatchetTagSet::Expire () void RatchetTagSet::Expire ()
{ {
if (!m_ExpirationTimestamp) if (!m_ExpirationTimestamp)
m_ExpirationTimestamp = i2p::util::GetSecondsSinceEpoch () + ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT; m_ExpirationTimestamp = i2p::util::GetSecondsSinceEpoch () + ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT;
} }
bool RatchetTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index)
{
auto session = GetSession ();
if (!session) return false;
return session->HandleNextMessage (buf, len, shared_from_this (), index);
}
DatabaseLookupTagSet::DatabaseLookupTagSet (GarlicDestination * destination, const uint8_t * key):
RatchetTagSet (nullptr), m_Destination (destination)
{
memcpy (m_Key, key, 32);
Expire ();
}
bool DatabaseLookupTagSet::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
len -= 16; // poly1305
if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len - offset, buf, 8, m_Key, nonce, buf + offset, len - offset, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Lookup reply AEAD decryption failed");
return false;
}
// we assume 1 I2NP block with delivery type local
if (offset + 3 > len)
{
LogPrint (eLogWarning, "Garlic: Lookup reply is too short ", len);
return false;
}
if (buf[offset] != eECIESx25519BlkGalicClove)
{
LogPrint (eLogWarning, "Garlic: Lookup reply unexpected block ", (int)buf[offset]);
return false;
}
offset++;
auto size = bufbe16toh (buf + offset);
offset += 2;
if (offset + size > len)
{
LogPrint (eLogWarning, "Garlic: Lookup reply block is too long ", size);
return false;
}
if (m_Destination)
m_Destination->HandleECIESx25519GarlicClove (buf + offset, size);
return true;
}
ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSet): ECIESX25519AEADRatchetSession::ECIESX25519AEADRatchetSession (GarlicDestination * owner, bool attachLeaseSet):
GarlicRoutingSession (owner, attachLeaseSet) GarlicRoutingSession (owner, attachLeaseSet)
{ {
@ -657,7 +712,12 @@ namespace garlic
moreTags -= (receiveTagset->GetNextIndex () - index); moreTags -= (receiveTagset->GetNextIndex () - index);
} }
if (moreTags > 0) if (moreTags > 0)
{
GenerateMoreReceiveTags (receiveTagset, moreTags); GenerateMoreReceiveTags (receiveTagset, moreTags);
index -= (moreTags >> 1); // /2
if (index > 0)
receiveTagset->SetTrimBehind (index);
}
} }
return true; return true;
} }
@ -739,8 +799,11 @@ namespace garlic
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
size_t payloadLen = 0; size_t payloadLen = 0;
if (first) payloadLen += 7;// datatime if (first) payloadLen += 7;// datatime
if (msg && m_Destination) if (msg)
payloadLen += msg->GetPayloadLength () + 13 + 32; {
payloadLen += msg->GetPayloadLength () + 13;
if (m_Destination) payloadLen += 32;
}
auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated || auto leaseSet = (GetLeaseSetUpdateStatus () == eLeaseSetUpdated ||
(GetLeaseSetUpdateStatus () == eLeaseSetSubmitted && (GetLeaseSetUpdateStatus () == eLeaseSetSubmitted &&
ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)) ? ts > GetLeaseSetSubmissionTime () + LEASET_CONFIRMATION_TIMEOUT)) ?
@ -816,7 +879,7 @@ namespace garlic
} }
// msg // msg
if (msg && m_Destination) if (msg && m_Destination)
offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset, true); offset += CreateGarlicClove (msg, v.data () + offset, payloadLen - offset);
// ack // ack
if (m_AckRequests.size () > 0) if (m_AckRequests.size () > 0)
{ {
@ -875,16 +938,16 @@ namespace garlic
return v; return v;
} }
size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len, bool isDestination) size_t ECIESX25519AEADRatchetSession::CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len)
{ {
if (!msg) return 0; if (!msg) return 0;
uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1; uint16_t cloveSize = msg->GetPayloadLength () + 9 + 1;
if (isDestination) cloveSize += 32; if (m_Destination) cloveSize += 32;
if ((int)len < cloveSize + 3) return 0; if ((int)len < cloveSize + 3) return 0;
buf[0] = eECIESx25519BlkGalicClove; // clove type buf[0] = eECIESx25519BlkGalicClove; // clove type
htobe16buf (buf + 1, cloveSize); // size htobe16buf (buf + 1, cloveSize); // size
buf += 3; buf += 3;
if (isDestination) if (m_Destination)
{ {
*buf = (eGarlicDeliveryTypeDestination << 5); *buf = (eGarlicDeliveryTypeDestination << 5);
memcpy (buf + 1, *m_Destination, 32); buf += 32; memcpy (buf + 1, *m_Destination, 32); buf += 32;

26
libi2pd/ECIESX25519AEADRatchetSession.h

@ -40,7 +40,7 @@ namespace garlic
// - 16 /* I2NP header */ - 16 /* poly hash */ - 8 /* tag */ - 4 /* garlic length */ // - 16 /* I2NP header */ - 16 /* poly hash */ - 8 /* tag */ - 4 /* garlic length */
class ECIESX25519AEADRatchetSession; class ECIESX25519AEADRatchetSession;
class RatchetTagSet class RatchetTagSet: public std::enable_shared_from_this<RatchetTagSet>
{ {
public: public:
@ -52,13 +52,18 @@ namespace garlic
const uint8_t * GetNextRootKey () const { return m_NextRootKey; }; const uint8_t * GetNextRootKey () const { return m_NextRootKey; };
int GetNextIndex () const { return m_NextIndex; }; int GetNextIndex () const { return m_NextIndex; };
void GetSymmKey (int index, uint8_t * key); void GetSymmKey (int index, uint8_t * key);
void DeleteSymmKey (int index);
std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session.lock (); }; std::shared_ptr<ECIESX25519AEADRatchetSession> GetSession () { return m_Session.lock (); };
int GetTagSetID () const { return m_TagSetID; }; int GetTagSetID () const { return m_TagSetID; };
void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; }; void SetTagSetID (int tagsetID) { m_TagSetID = tagsetID; };
void SetTrimBehind (int index) { if (index > m_TrimBehindIndex) m_TrimBehindIndex = index; };
void Expire (); void Expire ();
bool IsExpired (uint64_t ts) const { return m_ExpirationTimestamp && ts > m_ExpirationTimestamp; }; bool IsExpired (uint64_t ts) const { return m_ExpirationTimestamp && ts > m_ExpirationTimestamp; };
virtual bool IsIndexExpired (int index) const { return m_Session.expired () || index < m_TrimBehindIndex; };
virtual bool HandleNextMessage (uint8_t * buf, size_t len, int index);
private: private:
@ -73,7 +78,7 @@ namespace garlic
} m_KeyData; } m_KeyData;
uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32]; uint8_t m_SessTagConstant[32], m_SymmKeyCK[32], m_CurrentSymmKeyCK[64], m_NextRootKey[32];
int m_NextIndex, m_NextSymmKeyIndex; int m_NextIndex, m_NextSymmKeyIndex, m_TrimBehindIndex = 0;
std::unordered_map<int, i2p::data::Tag<32> > m_ItermediateSymmKeys; std::unordered_map<int, i2p::data::Tag<32> > m_ItermediateSymmKeys;
std::weak_ptr<ECIESX25519AEADRatchetSession> m_Session; std::weak_ptr<ECIESX25519AEADRatchetSession> m_Session;
int m_TagSetID = 0; int m_TagSetID = 0;
@ -92,6 +97,21 @@ namespace garlic
std::shared_ptr<ECIESX25519AEADRatchetSession> m_DummySession; // we need a strong pointer for NS std::shared_ptr<ECIESX25519AEADRatchetSession> m_DummySession; // we need a strong pointer for NS
}; };
class DatabaseLookupTagSet: public RatchetTagSet
{
public:
DatabaseLookupTagSet (GarlicDestination * destination, const uint8_t * key);
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 enum ECIESx25519BlockType
{ {
eECIESx25519BlkDateTime = 0, eECIESx25519BlkDateTime = 0,
@ -171,7 +191,7 @@ namespace garlic
bool NewExistingSessionMessage (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);
std::vector<uint8_t> CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first); std::vector<uint8_t> CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first);
size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len, bool isDestination = false); size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len);
size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len); size_t CreateLeaseSetClove (std::shared_ptr<const i2p::data::LocalLeaseSet> ls, uint64_t ts, uint8_t * buf, size_t len);
void GenerateMoreReceiveTags (std::shared_ptr<RatchetTagSet> receiveTagset, int numTags); void GenerateMoreReceiveTags (std::shared_ptr<RatchetTagSet> receiveTagset, int numTags);

33
libi2pd/Garlic.cpp

@ -460,6 +460,14 @@ namespace garlic
} }
} }
void GarlicDestination::AddECIESx25519Key (const uint8_t * key, const uint8_t * tag)
{
uint64_t t;
memcpy (&t, tag, 8);
auto tagset = std::make_shared<DatabaseLookupTagSet>(this, key);
m_ECIESx25519Tags.emplace (t, ECIESX25519AEADRatchetIndexTagset{0, tagset});
}
bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
{ {
AddSessionKey (key, tag); AddSessionKey (key, tag);
@ -507,8 +515,7 @@ namespace garlic
if (it1 != m_ECIESx25519Tags.end ()) if (it1 != m_ECIESx25519Tags.end ())
{ {
found = true; found = true;
auto session = it1->second.tagset->GetSession (); if (!it1->second.tagset->HandleNextMessage (buf, length, it1->second.index))
if (!session || !session->HandleNextMessage (buf, length, it1->second.tagset, it1->second.index))
LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message"); LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message");
m_ECIESx25519Tags.erase (it1); m_ECIESx25519Tags.erase (it1);
} }
@ -802,24 +809,30 @@ namespace garlic
} }
} }
// ECIESx25519 // ECIESx25519
for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();) for (auto it = m_ECIESx25519Sessions.begin (); it != m_ECIESx25519Sessions.end ();)
{ {
if (it->second.tagset->IsExpired (ts) || ts > it->second.creationTime + ECIESX25519_INCOMING_TAGS_EXPIRATION_TIMEOUT) if (it->second->CheckExpired (ts))
it = m_ECIESx25519Tags.erase (it); {
it->second->SetOwner (nullptr);
it = m_ECIESx25519Sessions.erase (it);
}
else else
++it; ++it;
} }
for (auto it = m_ECIESx25519Sessions.begin (); it != m_ECIESx25519Sessions.end ();) numExpiredTags = 0;
for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();)
{ {
if (it->second->CheckExpired (ts)) if (it->second.tagset->IsExpired (ts) || it->second.tagset->IsIndexExpired (it->second.index))
{ {
it->second->SetOwner (nullptr); it->second.tagset->DeleteSymmKey (it->second.index);
it = m_ECIESx25519Sessions.erase (it); it = m_ECIESx25519Tags.erase (it);
} }
else else
++it; ++it;
} }
if (numExpiredTags > 0)
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ());
} }
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID) void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
@ -1009,7 +1022,7 @@ namespace garlic
{ {
auto index = tagset->GetNextIndex (); auto index = tagset->GetNextIndex ();
uint64_t tag = tagset->GetNextSessionTag (); uint64_t tag = tagset->GetNextSessionTag ();
m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{index, tagset, i2p::util::GetSecondsSinceEpoch ()}); m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{index, tagset});
} }
void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session) void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session)

2
libi2pd/Garlic.h

@ -220,7 +220,6 @@ namespace garlic
{ {
int index; int index;
RatchetTagSetPtr tagset; RatchetTagSetPtr tagset;
uint64_t creationTime; // seconds since epoch
}; };
class GarlicDestination: public i2p::data::LocalDestination class GarlicDestination: public i2p::data::LocalDestination
@ -242,6 +241,7 @@ namespace garlic
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet = false); std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet = false);
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID); void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
void AddECIESx25519SessionNextTag (RatchetTagSetPtr tagset); void AddECIESx25519SessionNextTag (RatchetTagSetPtr tagset);

4
libi2pd/HTTP.cpp

@ -16,8 +16,8 @@
namespace i2p { namespace i2p {
namespace http { namespace http {
const std::vector<std::string> HTTP_METHODS = { const std::vector<std::string> HTTP_METHODS = {
"GET", "HEAD", "POST", "PUT", "PATCH", "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "CONNECT", // HTTP basic methods
"DELETE", "OPTIONS", "CONNECT", "PROPFIND" "COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK", "SEARCH" // WebDAV methods, for SEARCH see rfc5323
}; };
const std::vector<std::string> HTTP_VERSIONS = { const std::vector<std::string> HTTP_VERSIONS = {
"HTTP/1.0", "HTTP/1.1" "HTTP/1.0", "HTTP/1.1"

18
libi2pd/I2NPProtocol.cpp

@ -171,7 +171,8 @@ namespace i2p
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills, const std::set<i2p::data::IdentHash>& excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag) std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey,
const uint8_t * replyTag, bool replyECIES)
{ {
int cnt = excludedFloodfills.size (); int cnt = excludedFloodfills.size ();
auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage (); auto m = cnt > 7 ? NewI2NPMessage () : NewI2NPShortMessage ();
@ -180,7 +181,8 @@ namespace i2p
buf += 32; buf += 32;
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
buf += 32; buf += 32;
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCRYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags *buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
*buf |= (replyECIES ? DATABASE_LOOKUP_ECIES_FLAG : DATABASE_LOOKUP_ENCRYPTION_FLAG);
buf ++; buf ++;
htobe32buf (buf, replyTunnel->GetNextTunnelID ()); // reply tunnel ID htobe32buf (buf, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
buf += 4; buf += 4;
@ -204,8 +206,16 @@ namespace i2p
// encryption // encryption
memcpy (buf, replyKey, 32); memcpy (buf, replyKey, 32);
buf[32] = 1; // 1 tag buf[32] = 1; // 1 tag
memcpy (buf + 33, replyTag, 32); if (replyECIES)
buf += 65; {
memcpy (buf + 33, replyTag, 8); // 8 bytes tag
buf += 41;
}
else
{
memcpy (buf + 33, replyTag, 32); // 32 bytes tag
buf += 65;
}
m->len += (buf - m->GetPayload ()); m->len += (buf - m->GetPayload ());
m->FillI2NPMessageHeader (eI2NPDatabaseLookup); m->FillI2NPMessageHeader (eI2NPDatabaseLookup);

3
libi2pd/I2NPProtocol.h

@ -252,7 +252,8 @@ namespace tunnel
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr); uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, std::shared_ptr<I2NPMessage> CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
const std::set<i2p::data::IdentHash>& excludedFloodfills, const std::set<i2p::data::IdentHash>& excludedFloodfills,
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag); std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel,
const uint8_t * replyKey, const uint8_t * replyTag, bool replyECIES = false);
std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers); std::shared_ptr<I2NPMessage> CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0); std::shared_ptr<I2NPMessage> CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);

7
libi2pd/NTCP2.cpp

@ -338,11 +338,8 @@ namespace transport
KDF3Bob (); KDF3Bob ();
if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt if (i2p::crypto::AEADChaCha20Poly1305 (m_SessionConfirmedBuffer + 48, m3p2Len - 16, GetH (), 32, GetK (), nonce, m3p2Buf, m3p2Len - 16, false)) // decrypt
{
// caclulate new h again for KDF data // caclulate new h again for KDF data
memcpy (m_SessionConfirmedBuffer + 16, m_H, 32); // h || ciphertext MixHash (m_SessionConfirmedBuffer + 48, m3p2Len); // h = SHA256(h || ciphertext)
SHA256 (m_SessionConfirmedBuffer + 16, m3p2Len + 32, m_H); //h = SHA256(h || ciphertext);
}
else else
{ {
LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed "); LogPrint (eLogWarning, "NTCP2: SessionConfirmed Part2 AEAD verification failed ");
@ -1438,7 +1435,7 @@ namespace transport
{ {
auto timer = std::make_shared<boost::asio::deadline_timer>(GetService()); auto timer = std::make_shared<boost::asio::deadline_timer>(GetService());
auto timeout = NTCP_CONNECT_TIMEOUT * 5; auto timeout = NTCP2_CONNECT_TIMEOUT * 5;
conn->SetTerminationTimeout(timeout * 2); conn->SetTerminationTimeout(timeout * 2);
timer->expires_from_now (boost::posix_time::seconds(timeout)); timer->expires_from_now (boost::posix_time::seconds(timeout));
timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) timer->async_wait ([conn, timeout](const boost::system::error_code& ecode)

1317
libi2pd/NTCPSession.cpp

File diff suppressed because it is too large Load Diff

242
libi2pd/NTCPSession.h

@ -1,242 +0,0 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#ifndef NTCP_SESSION_H__
#define NTCP_SESSION_H__
#include <inttypes.h>
#include <map>
#include <memory>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>
#include "Crypto.h"
#include "Identity.h"
#include "RouterInfo.h"
#include "I2NPProtocol.h"
#include "TransportSession.h"
#include "CryptoWorker.h"
namespace i2p
{
namespace transport
{
struct NTCPPhase1
{
uint8_t pubKey[256];
uint8_t HXxorHI[32];
};
struct NTCPPhase2
{
uint8_t pubKey[256];
struct
{
uint8_t hxy[32];
uint8_t timestamp[4];
uint8_t filler[12];
} encrypted;
};
struct NTCPWork;
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
const size_t NTCP_BUFFER_SIZE = 1028; // fits 1 tunnel data message
const int NTCP_CONNECT_TIMEOUT = 5; // 5 seconds
const int NTCP_ESTABLISH_TIMEOUT = 10; // 10 seconds
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
const int NTCP_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448
const int NTCP_CLOCK_SKEW = 60; // in seconds
const int NTCP_MAX_OUTGOING_QUEUE_SIZE = 200; // how many messages we can queue up
class NTCPServer;
class NTCPSession: public TransportSession, public std::enable_shared_from_this<NTCPSession>
{
public:
NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
~NTCPSession ();
void Terminate ();
void Done ();
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
boost::asio::io_service & GetService();
bool IsEstablished () const { return m_IsEstablished; };
bool IsTerminated () const { return m_IsTerminated; };
void ClientLogin ();
void ServerLogin ();
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
private:
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
void Connected ();
void SendTimeSyncMessage ();
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
void CreateAESKey (uint8_t * pubKey);
// client
void SendPhase3 ();
void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandlePhase2 (NTCPWork * work=nullptr);
void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
//server
void SendPhase2 (NTCPWork * work=nullptr);
void SendPhase4 (uint32_t tsA, uint32_t tsB);
void HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
void HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
void HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen);
void HandlePhase3 (uint32_t tsB, size_t paddingLen);
void HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
// common
void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
bool DecryptNextBlock (const uint8_t * encrypted);
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg);
void Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs);
private:
NTCPServer& m_Server;
boost::asio::ip::tcp::socket m_Socket;
bool m_IsEstablished, m_IsTerminated;
i2p::crypto::CBCDecryption m_Decryption;
i2p::crypto::CBCEncryption m_Encryption;
struct Establisher
{
NTCPPhase1 phase1;
NTCPPhase2 phase2;
} * m_Establisher;
i2p::crypto::AESAlignedBuffer<NTCP_BUFFER_SIZE + 16> m_ReceiveBuffer;
i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer;
int m_ReceiveBufferOffset;
std::shared_ptr<I2NPMessage> m_NextMessage;
size_t m_NextMessageOffset;
i2p::I2NPMessagesHandler m_Handler;
bool m_IsSending;
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
};
// TODO: move to NTCP.h/.cpp
class NTCPServer
{
public:
typedef i2p::worker::ThreadPool<NTCPSession> Pool;
enum RemoteAddressType
{
eIP4Address,
eIP6Address,
eHostname
};
enum ProxyType
{
eNoProxy,
eSocksProxy,
eHTTPProxy
};
NTCPServer (int workers=4);
~NTCPServer ();
void Start ();
void Stop ();
bool AddNTCPSession (std::shared_ptr<NTCPSession> session);
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr<NTCPSession> conn);
void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr<NTCPSession> conn);
bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; };
bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; };
bool NetworkIsReady() const { return IsBoundV4() || IsBoundV6() || UsingProxy(); };
bool UsingProxy() const { return m_ProxyType != eNoProxy; };
void UseProxy(ProxyType proxy, const std::string & address, uint16_t port);
boost::asio::io_service& GetService () { return m_Service; };
void SetSessionLimits(uint16_t softLimit, uint16_t hardLimit) { m_SoftLimit = softLimit; m_HardLimit = hardLimit; }
bool ShouldLimit() const { return ShouldHardLimit() || ShouldSoftLimit(); }
void Work(std::shared_ptr<NTCPSession> conn, Pool::WorkFunc work)
{
m_CryptoPool->Offer({conn, work});
}
private:
/** @brief return true for hard limit */
bool ShouldHardLimit() const { return m_HardLimit && m_NTCPSessions.size() >= m_HardLimit; }
/** @brief return true for probabalistic soft backoff */
bool ShouldSoftLimit() const
{
auto sessions = m_NTCPSessions.size();
return sessions && m_SoftLimit && m_SoftLimit < sessions && ( rand() % sessions ) <= m_SoftLimit;
}
void Run ();
void HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
void HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype);
void AfterSocksHandshake(std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype);
// timer
void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode);
private:
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
boost::asio::io_service::work m_Work;
boost::asio::deadline_timer m_TerminationTimer;
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions; // access from m_Thread only
std::list<std::shared_ptr<NTCPSession> > m_PendingIncomingSessions;
ProxyType m_ProxyType;
std::string m_ProxyAddress;
uint16_t m_ProxyPort;
boost::asio::ip::tcp::resolver m_Resolver;
boost::asio::ip::tcp::endpoint * m_ProxyEndpoint;
std::shared_ptr<Pool> m_CryptoPool;
uint16_t m_SoftLimit, m_HardLimit;
public:
// for HTTP/I2PControl
const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
};
}
}
#endif

44
libi2pd/NetDb.cpp

@ -125,6 +125,7 @@ namespace data
} }
} }
if (!m_IsRunning) break; if (!m_IsRunning) break;
if (!i2p::transport::transports.IsOnline ()) continue; // don't manage netdb when offline
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastManageRequest >= 15) // manage requests every 15 seconds if (ts - lastManageRequest >= 15) // manage requests every 15 seconds
@ -560,6 +561,9 @@ namespace data
updatedCount++; updatedCount++;
continue; continue;
} }
// make router reachable back if too few routers
if (it.second->IsUnreachable () && total - deletedCount < NETDB_MIN_ROUTERS)
it.second->SetUnreachable (false);
// find & mark expired routers // find & mark expired routers
if (it.second->UsesIntroducer ()) if (it.second->UsesIntroducer ())
{ {
@ -610,7 +614,7 @@ namespace data
} }
} }
void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete) void NetDb::RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete, bool direct)
{ {
auto dest = m_Requests.CreateRequest (destination, false, requestComplete); // non-exploratory auto dest = m_Requests.CreateRequest (destination, false, requestComplete); // non-exploratory
if (!dest) if (!dest)
@ -621,7 +625,23 @@ namespace data
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ()); auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
if (floodfill) if (floodfill)
transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); {
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;
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);
}
}
}
else else
{ {
LogPrint (eLogError, "NetDb: ", destination.ToBase64(), " destination requested, but no floodfills found"); LogPrint (eLogError, "NetDb: ", destination.ToBase64(), " destination requested, but no floodfills found");
@ -775,37 +795,21 @@ namespace data
// reply to our destination. Try other floodfills // reply to our destination. Try other floodfills
if (outbound && inbound) if (outbound && inbound)
{ {
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
auto count = dest->GetExcludedPeers ().size (); auto count = dest->GetExcludedPeers ().size ();
if (count < 7) if (count < 7)
{ {
auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
if (nextFloodfill) if (nextFloodfill)
{ {
// tell floodfill about us
msgs.push_back (i2p::tunnel::TunnelMessageBlock
{
i2p::tunnel::eDeliveryTypeRouter,
nextFloodfill->GetIdentHash (), 0,
CreateDatabaseStoreMsg ()
});
// request destination // request destination
LogPrint (eLogDebug, "NetDb: Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ()); LogPrint (eLogDebug, "NetDb: Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ());
auto msg = dest->CreateRequestMessage (nextFloodfill, inbound); outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0,
msgs.push_back (i2p::tunnel::TunnelMessageBlock dest->CreateRequestMessage (nextFloodfill, inbound));
{
i2p::tunnel::eDeliveryTypeRouter,
nextFloodfill->GetIdentHash (), 0, msg
});
deleteDest = false; deleteDest = false;
} }
} }
else else
LogPrint (eLogWarning, "NetDb: ", key, " was not found on ", count, " floodfills"); LogPrint (eLogWarning, "NetDb: ", key, " was not found on ", count, " floodfills");
if (msgs.size () > 0)
outbound->SendTunnelDataMsg (msgs);
} }
} }

2
libi2pd/NetDb.hpp

@ -70,7 +70,7 @@ namespace data
std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const; std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const; std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr); void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr, bool direct = true);
void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr); void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr);
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg); void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);

128
libi2pd/RouterContext.cpp

@ -63,7 +63,6 @@ namespace i2p
bool ipv4; i2p::config::GetOption("ipv4", ipv4); bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6); bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ssu; i2p::config::GetOption("ssu", ssu); bool ssu; i2p::config::GetOption("ssu", ssu);
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
bool nat; i2p::config::GetOption("nat", nat); bool nat; i2p::config::GetOption("nat", nat);
std::string ifname; i2p::config::GetOption("ifname", ifname); std::string ifname; i2p::config::GetOption("ifname", ifname);
@ -83,8 +82,6 @@ namespace i2p
if (ssu) if (ssu)
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
if (ntcp)
routerInfo.AddNTCPAddress (host.c_str(), port);
} }
if (ipv6) if (ipv6)
{ {
@ -99,8 +96,6 @@ namespace i2p
if (ssu) if (ssu)
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
if (ntcp)
routerInfo.AddNTCPAddress (host.c_str(), port);
} }
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
@ -115,20 +110,17 @@ namespace i2p
{ {
if (!m_NTCP2Keys) NewNTCP2Keys (); if (!m_NTCP2Keys) NewNTCP2Keys ();
UpdateNTCP2Address (true); UpdateNTCP2Address (true);
if (!ntcp) // NTCP2 should replace NTCP bool published; i2p::config::GetOption("ntcp2.published", published);
if (published)
{ {
bool published; i2p::config::GetOption("ntcp2.published", published); PublishNTCP2Address (port, true);
if (published) if (ipv6)
{ {
PublishNTCP2Address (port, true); // add NTCP2 ipv6 address
if (ipv6) std::string host = "::1";
{ if (!i2p::config::IsDefault ("ntcp2.addressv6"))
// add NTCP2 ipv6 address i2p::config::GetOption ("ntcp2.addressv6", host);
std::string host = "::1"; m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (host), port);
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
i2p::config::GetOption ("ntcp2.addressv6", host);
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address_v6::from_string (host), port);
}
} }
} }
} }
@ -381,48 +373,19 @@ namespace i2p
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable; return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
} }
void RouterContext::PublishNTCPAddress (bool publish, bool v4only) void RouterContext::RemoveNTCPAddress (bool v4only)
{ {
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
if (publish) for (auto it = addresses.begin (); it != addresses.end ();)
{
for (const auto& addr : addresses) // v4
{
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
addr->host.is_v4 ())
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
if (!v4only)
{
for (const auto& addr : addresses) // v6
{
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU &&
addr->host.is_v6 ())
{
// insert NTCP address with host/port from SSU
m_RouterInfo.AddNTCPAddress (addr->host.to_string ().c_str (), addr->port);
break;
}
}
}
}
else
{ {
for (auto it = addresses.begin (); it != addresses.end ();) if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () &&
(!v4only || (*it)->host.is_v4 ()))
{ {
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !(*it)->IsNTCP2 () && it = addresses.erase (it);
(!v4only || (*it)->host.is_v4 ())) if (v4only) break; // otherwise might be more than one address
{
it = addresses.erase (it);
if (v4only) break; // otherwise might be more than one address
}
else
++it;
} }
else
++it;
} }
} }
@ -444,16 +407,10 @@ namespace i2p
addr->ssu->introducers.clear (); addr->ssu->introducers.clear ();
port = addr->port; port = addr->port;
} }
// remove NTCP or NTCP2 v4 address // remove NTCP2 v4 address
bool ntcp; i2p::config::GetOption("ntcp", ntcp); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp) if (ntcp2)
PublishNTCPAddress (false); PublishNTCP2Address (port, false, true);
else
{
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp2)
PublishNTCP2Address (port, false, true);
}
// update // update
UpdateRouterInfo (); UpdateRouterInfo ();
} }
@ -477,23 +434,16 @@ namespace i2p
addr->ssu->introducers.clear (); addr->ssu->introducers.clear ();
port = addr->port; port = addr->port;
} }
// insert NTCP or NTCP2 back // insert NTCP2 back
bool ntcp; i2p::config::GetOption("ntcp", ntcp); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
if (ntcp) if (ntcp2)
PublishNTCPAddress (true);
else
{ {
// ntcp2 bool published; i2p::config::GetOption ("ntcp2.published", published);
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); if (published)
if (ntcp2)
{ {
bool published; i2p::config::GetOption ("ntcp2.published", published); uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
if (published) if (!ntcp2Port) ntcp2Port = port;
{ PublishNTCP2Address (ntcp2Port, true, true);
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
if (!ntcp2Port) ntcp2Port = port;
PublishNTCP2Address (ntcp2Port, true, true);
}
} }
} }
// update // update
@ -506,7 +456,7 @@ namespace i2p
{ {
m_RouterInfo.EnableV6 (); m_RouterInfo.EnableV6 ();
// insert v6 addresses if necessary // insert v6 addresses if necessary
bool foundSSU = false, foundNTCP = false, foundNTCP2 = false; bool foundSSU = false, foundNTCP2 = false;
uint16_t port = 0; uint16_t port = 0;
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr: addresses) for (auto& addr: addresses)
@ -515,12 +465,8 @@ namespace i2p
{ {
if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU) if (addr->transportStyle == i2p::data::RouterInfo::eTransportSSU)
foundSSU = true; foundSSU = true;
else if (addr->IsNTCP2 ()) else if (addr->IsPublishedNTCP2 ())
{ foundNTCP2 = true;
if (addr->IsPublishedNTCP2 ()) foundNTCP2 = true;
}
else
foundNTCP = true;
} }
port = addr->port; port = addr->port;
} }
@ -552,16 +498,6 @@ namespace i2p
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port); m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
} }
} }
// NTCP
if (!foundNTCP)
{
bool ntcp; i2p::config::GetOption("ntcp", ntcp);
if (ntcp)
{
std::string host = "::1";
m_RouterInfo.AddNTCPAddress (host.c_str (), port);
}
}
} }
else else
m_RouterInfo.DisableV6 (); m_RouterInfo.DisableV6 ();

5
libi2pd/RouterContext.h

@ -37,7 +37,8 @@ namespace i2p
enum RouterError enum RouterError
{ {
eRouterErrorNone = 0, eRouterErrorNone = 0,
eRouterErrorClockSkew = 1 eRouterErrorClockSkew = 1,
eRouterErrorOffline = 2
}; };
class RouterContext: public i2p::garlic::GarlicDestination class RouterContext: public i2p::garlic::GarlicDestination
@ -89,7 +90,7 @@ namespace i2p
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
void PublishNTCP2Address (int port, bool publish = true, bool v4only = false); void PublishNTCP2Address (int port, bool publish = true, bool v4only = false);
void UpdateNTCP2Address (bool enable); void UpdateNTCP2Address (bool enable);
void PublishNTCPAddress (bool publish, bool v4only = true); void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer); bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool IsUnreachable () const; bool IsUnreachable () const;

40
libi2pd/RouterInfo.cpp

@ -316,7 +316,7 @@ namespace data
} }
if (introducers) supportedTransports |= eSSUV4; // in case if host is not presented if (introducers) supportedTransports |= eSSUV4; // in case if host is not presented
if (isNTCP2Only && address->ntcp2) address->ntcp2->isNTCP2Only = true; if (isNTCP2Only && address->ntcp2) address->ntcp2->isNTCP2Only = true;
if (supportedTransports) if (supportedTransports & ~(eNTCPV4 | eNTCPV6)) // exclude NTCP only
{ {
addresses->push_back(address); addresses->push_back(address);
m_SupportedTransports |= supportedTransports; m_SupportedTransports |= supportedTransports;
@ -477,7 +477,12 @@ namespace data
s.write ((const char *)&address.date, sizeof (address.date)); s.write ((const char *)&address.date, sizeof (address.date));
std::stringstream properties; std::stringstream properties;
if (address.transportStyle == eTransportNTCP) if (address.transportStyle == eTransportNTCP)
WriteString (address.IsNTCP2 () ? "NTCP2" : "NTCP", s); {
if (address.IsNTCP2 ())
WriteString ("NTCP2", s);
else
continue; // don't write NTCP address
}
else if (address.transportStyle == eTransportSSU) else if (address.transportStyle == eTransportSSU)
{ {
WriteString ("SSU", s); WriteString ("SSU", s);
@ -704,20 +709,6 @@ namespace data
s.write (str.c_str (), len); s.write (str.c_str (), len);
} }
void RouterInfo::AddNTCPAddress (const char * host, int port)
{
auto addr = std::make_shared<Address>();
addr->host = boost::asio::ip::address::from_string (host);
addr->port = port;
addr->transportStyle = eTransportNTCP;
addr->cost = 6;
addr->date = 0;
for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return;
m_SupportedTransports |= addr->host.is_v6 () ? eNTCPV6 : eNTCPV4;
m_Addresses->push_front(std::move(addr)); // always make NTCP first
}
void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu) void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu)
{ {
auto addr = std::make_shared<Address>(); auto addr = std::make_shared<Address>();
@ -817,14 +808,6 @@ namespace data
return ""; return "";
} }
bool RouterInfo::IsNTCP (bool v4only) const
{
if (v4only)
return m_SupportedTransports & eNTCPV4;
else
return m_SupportedTransports & (eNTCPV4 | eNTCPV6);
}
bool RouterInfo::IsSSU (bool v4only) const bool RouterInfo::IsSSU (bool v4only) const
{ {
if (v4only) if (v4only)
@ -907,15 +890,6 @@ namespace data
return m_Caps & Caps::eUnreachable; // non-reachable return m_Caps & Caps::eUnreachable; // non-reachable
} }
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetNTCPAddress (bool v4only) const
{
return GetAddress (
[v4only](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return (address->transportStyle == eTransportNTCP) && !address->IsNTCP2Only () && (!v4only || address->host.is_v4 ());
});
}
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUAddress (bool v4only) const std::shared_ptr<const RouterInfo::Address> RouterInfo::GetSSUAddress (bool v4only) const
{ {
return GetAddress ( return GetAddress (

3
libi2pd/RouterInfo.h

@ -153,12 +153,10 @@ namespace data
uint64_t GetTimestamp () const { return m_Timestamp; }; uint64_t GetTimestamp () const { return m_Timestamp; };
int GetVersion () const { return m_Version; }; int GetVersion () const { return m_Version; };
Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr Addresses& GetAddresses () { return *m_Addresses; }; // should be called for local RI only, otherwise must return shared_ptr
std::shared_ptr<const Address> GetNTCPAddress (bool v4only = true) const;
std::shared_ptr<const Address> GetNTCP2Address (bool publishedOnly, bool v4only = true) const; std::shared_ptr<const Address> GetNTCP2Address (bool publishedOnly, bool v4only = true) const;
std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const; std::shared_ptr<const Address> GetSSUAddress (bool v4only = true) const;
std::shared_ptr<const Address> GetSSUV6Address () const; std::shared_ptr<const Address> GetSSUV6Address () const;
void AddNTCPAddress (const char * host, int port);
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0); void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0); void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0);
bool AddIntroducer (const Introducer& introducer); bool AddIntroducer (const Introducer& introducer);
@ -169,7 +167,6 @@ namespace data
void ClearProperties () { m_Properties.clear (); }; void ClearProperties () { m_Properties.clear (); };
bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; }; bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; };
bool IsReachable () const { return m_Caps & Caps::eReachable; }; bool IsReachable () const { return m_Caps & Caps::eReachable; };
bool IsNTCP (bool v4only = true) const;
bool IsSSU (bool v4only = true) const; bool IsSSU (bool v4only = true) const;
bool IsSSUV6 () const; bool IsSSUV6 () const;
bool IsNTCP2 (bool v4only = true) const; bool IsNTCP2 (bool v4only = true) const;

36
libi2pd/SSU.cpp

@ -7,7 +7,6 @@
*/ */
#include <string.h> #include <string.h>
#include <boost/bind.hpp>
#include "Log.h" #include "Log.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "RouterContext.h" #include "RouterContext.h"
@ -243,10 +242,16 @@ namespace transport
void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to)
{ {
boost::system::error_code ec;
if (to.protocol () == boost::asio::ip::udp::v4()) if (to.protocol () == boost::asio::ip::udp::v4())
m_Socket.send_to (boost::asio::buffer (buf, len), to); m_Socket.send_to (boost::asio::buffer (buf, len), to, 0, ec);
else else
m_SocketV6.send_to (boost::asio::buffer (buf, len), to); m_SocketV6.send_to (boost::asio::buffer (buf, len), to, 0, ec);
if (ec)
{
LogPrint (eLogError, "SSU: send exception: ", ec.message (), " while trying to send data to ", to.address (), ":", to.port (), " (length: ", len, ")");
}
} }
void SSUServer::Receive () void SSUServer::Receive ()
@ -265,7 +270,13 @@ namespace transport
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
{ {
if (!ecode) 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)
// just try continue reading when received ICMP response otherwise socket can crash,
// but better to find out which host were sent it and mark that router as unreachable
{ {
packet->len = bytes_transferred; packet->len = bytes_transferred;
std::vector<SSUPacket *> packets; std::vector<SSUPacket *> packets;
@ -287,7 +298,7 @@ namespace transport
} }
else else
{ {
LogPrint (eLogError, "SSU: receive_from error: ", ec.message ()); LogPrint (eLogError, "SSU: receive_from error: code ", ec.value(), ": ", ec.message ());
delete packet; delete packet;
break; break;
} }
@ -302,7 +313,7 @@ namespace transport
delete packet; delete packet;
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
LogPrint (eLogError, "SSU: receive error: ", ecode.message ()); LogPrint (eLogError, "SSU: receive error: code ", ecode.value(), ": ", ecode.message ());
m_Socket.close (); m_Socket.close ();
OpenSocket (); OpenSocket ();
Receive (); Receive ();
@ -312,7 +323,13 @@ namespace transport
void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet) void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
{ {
if (!ecode) 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)
// just try continue reading when received ICMP response otherwise socket can crash,
// but better to find out which host were sent it and mark that router as unreachable
{ {
packet->len = bytes_transferred; packet->len = bytes_transferred;
std::vector<SSUPacket *> packets; std::vector<SSUPacket *> packets;
@ -334,7 +351,7 @@ namespace transport
} }
else else
{ {
LogPrint (eLogError, "SSU: v6 receive_from error: ", ec.message ()); LogPrint (eLogError, "SSU: v6 receive_from error: code ", ec.value(), ": ", ec.message ());
delete packet; delete packet;
break; break;
} }
@ -349,7 +366,7 @@ namespace transport
delete packet; delete packet;
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
LogPrint (eLogError, "SSU: v6 receive error: ", ecode.message ()); LogPrint (eLogError, "SSU: v6 receive error: code ", ecode.value(), ": ", ecode.message ());
m_SocketV6.close (); m_SocketV6.close ();
OpenSocketV6 (); OpenSocketV6 ();
ReceiveV6 (); ReceiveV6 ();
@ -460,6 +477,7 @@ namespace transport
// otherwise create new session // otherwise create new session
auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest); auto session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
sessions[remoteEndpoint] = session; sessions[remoteEndpoint] = session;
// connect // connect
LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ", LogPrint (eLogDebug, "SSU: Creating new session to [", i2p::data::GetIdentHashAbbreviation (router->GetIdentHash ()), "] ",
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ()); remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());

24
libi2pd/SSUData.cpp

@ -7,7 +7,6 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <boost/bind.hpp>
#include "Log.h" #include "Log.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "NetDb.hpp" #include "NetDb.hpp"
@ -301,7 +300,7 @@ namespace transport
void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg) void SSUData::Send (std::shared_ptr<i2p::I2NPMessage> msg)
{ {
uint32_t msgID = msg->ToSSU (); uint32_t msgID = msg->ToSSU ();
if (m_SentMessages.count (msgID) > 0) if (m_SentMessages.find (msgID) != m_SentMessages.end())
{ {
LogPrint (eLogWarning, "SSU: message ", msgID, " already sent"); LogPrint (eLogWarning, "SSU: message ", msgID, " already sent");
return; return;
@ -326,8 +325,7 @@ namespace transport
{ {
Fragment * fragment = new Fragment; Fragment * fragment = new Fragment;
fragment->fragmentNum = fragmentNum; fragment->fragmentNum = fragmentNum;
uint8_t * buf = fragment->buf; uint8_t * payload = fragment->buf + sizeof (SSUHeader);
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_WANT_REPLY; // for compatibility *payload = DATA_FLAG_WANT_REPLY; // for compatibility
payload++; payload++;
*payload = 1; // always 1 message fragment per message *payload = 1; // always 1 message fragment per message
@ -346,14 +344,20 @@ namespace transport
payload += 3; payload += 3;
memcpy (payload, msgBuf, size); memcpy (payload, msgBuf, size);
size += payload - buf; size += payload - fragment->buf;
if (size & 0x0F) // make sure 16 bytes boundary uint8_t rem = size & 0x0F;
size = ((size >> 4) + 1) << 4; // (/16 + 1)*16 if (rem) // make sure 16 bytes boundary
{
auto padding = 16 - rem;
memset (fragment->buf + size, 0, padding);
size += padding;
}
fragment->len = size; fragment->len = size;
fragments.push_back (std::unique_ptr<Fragment> (fragment)); fragments.push_back (std::unique_ptr<Fragment> (fragment));
// encrypt message with session key // encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size); uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, fragment->buf, size, buf);
try try
{ {
m_Session.Send (buf, size); m_Session.Send (buf, size);
@ -432,6 +436,7 @@ namespace transport
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18];
uint32_t ts = i2p::util::GetSecondsSinceEpoch (); uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
int numResent = 0; int numResent = 0;
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();) for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
@ -445,7 +450,8 @@ namespace transport
{ {
try try
{ {
m_Session.Send (f->buf, f->len); // resend m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, f->buf, f->len, buf);
m_Session.Send (buf, f->len); // resend
numResent++; numResent++;
} }
catch (boost::system::system_error& ec) catch (boost::system::system_error& ec)

6
libi2pd/SSUData.h

@ -11,7 +11,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
#include <map> #include <unordered_map>
#include <vector> #include <vector>
#include <unordered_set> #include <unordered_set>
#include <memory> #include <memory>
@ -124,8 +124,8 @@ namespace transport
private: private:
SSUSession& m_Session; SSUSession& m_Session;
std::map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages; std::unordered_map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages;
std::map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages; std::unordered_map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages;
std::unordered_set<uint32_t> m_ReceivedMessages; std::unordered_set<uint32_t> m_ReceivedMessages;
boost::asio::deadline_timer m_ResendTimer, m_IncompleteMessagesCleanupTimer; boost::asio::deadline_timer m_ResendTimer, m_IncompleteMessagesCleanupTimer;
int m_MaxPacketSize, m_PacketSize; int m_MaxPacketSize, m_PacketSize;

37
libi2pd/SSUSession.cpp

@ -6,7 +6,6 @@
* See full license text in LICENSE file at top of project tree * See full license text in LICENSE file at top of project tree
*/ */
#include <boost/bind.hpp>
#include "version.h" #include "version.h"
#include "Crypto.h" #include "Crypto.h"
#include "Log.h" #include "Log.h"
@ -225,7 +224,11 @@ namespace transport
return; return;
} }
if (!m_DHKeysPair) if (!m_DHKeysPair)
m_DHKeysPair = transports.GetNextDHKeysPair (); {
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
pair->GenerateKeys ();
m_DHKeysPair = pair;
}
CreateAESandMacKey (buf + headerSize); CreateAESandMacKey (buf + headerSize);
SendSessionCreated (buf + headerSize, sendRelayTag); SendSessionCreated (buf + headerSize, sendRelayTag);
} }
@ -744,24 +747,30 @@ namespace transport
} }
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len) void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len)
{
FillHeaderAndEncrypt (payloadType, buf, len, buf);
}
void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * in, size_t len, uint8_t * out)
{ {
if (len < sizeof (SSUHeader)) if (len < sizeof (SSUHeader))
{ {
LogPrint (eLogError, "SSU: Unexpected packet length ", len); LogPrint (eLogError, "SSU: Unexpected packet length ", len);
return; return;
} }
SSUHeader * header = (SSUHeader *)buf; SSUHeader * header = (SSUHeader *)out;
RAND_bytes (header->iv, 16); // random iv RAND_bytes (header->iv, 16); // random iv
m_SessionKeyEncryption.SetIV (header->iv); m_SessionKeyEncryption.SetIV (header->iv);
header->flag = payloadType << 4; // MSB is 0 SSUHeader * inHeader = (SSUHeader *)in;
htobe32buf (header->time, i2p::util::GetSecondsSinceEpoch ()); inHeader->flag = payloadType << 4; // MSB is 0
uint8_t * encrypted = &header->flag; htobe32buf (inHeader->time, i2p::util::GetSecondsSinceEpoch ());
uint16_t encryptedLen = len - (encrypted - buf); uint8_t * encrypted = &header->flag, * clear = &inHeader->flag;
m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted); uint16_t encryptedLen = len - (encrypted - out);
// assume actual buffer size is 18 (16 + 2) bytes more m_SessionKeyEncryption.Encrypt (clear, encryptedLen, encrypted);
memcpy (buf + len, header->iv, 16); // assume actual out buffer size is 18 (16 + 2) bytes more
memcpy (out + len, header->iv, 16);
uint16_t netid = i2p::context.GetNetID (); uint16_t netid = i2p::context.GetNetID ();
htobe16buf (buf + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8)); htobe16buf (out + len + 16, (netid == I2PD_NET_ID) ? encryptedLen : encryptedLen ^ ((netid - 2) << 8));
i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac); i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac);
} }
@ -821,9 +830,9 @@ namespace transport
{ {
if (m_State == eSessionStateUnknown) if (m_State == eSessionStateUnknown)
{ {
// set connect timer ScheduleConnectTimer (); // set connect timer
ScheduleConnectTimer (); m_DHKeysPair = std::make_shared<i2p::crypto::DHKeys> ();
m_DHKeysPair = transports.GetNextDHKeysPair (); m_DHKeysPair->GenerateKeys ();
SendSessionRequest (); SendSessionRequest ();
} }
} }

2
libi2pd/SSUSession.h

@ -138,6 +138,7 @@ namespace transport
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey, void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey,
const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag = 0); const uint8_t * iv, const i2p::crypto::MACKey& macKey, uint8_t flag = 0);
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * in, size_t len, uint8_t * out); // with session key
void Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey); void Decrypt (uint8_t * buf, size_t len, const i2p::crypto::AESKey& aesKey);
void DecryptSessionKey (uint8_t * buf, size_t len); void DecryptSessionKey (uint8_t * buf, size_t len);
bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey); bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey);
@ -165,6 +166,7 @@ namespace transport
bool m_IsDataReceived; bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only
std::map<uint32_t, std::shared_ptr<const i2p::data::RouterInfo> > m_RelayRequests; // nonce->Charlie std::map<uint32_t, std::shared_ptr<const i2p::data::RouterInfo> > m_RelayRequests; // nonce->Charlie
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
}; };
} }
} }

31
libi2pd/Streaming.cpp

@ -351,6 +351,28 @@ namespace stream
return true; return true;
} }
void Stream::HandlePing (Packet * packet)
{
uint16_t flags = packet->GetFlags ();
if (ProcessOptions (flags, packet) && m_RemoteIdentity)
{
// send pong
Packet p;
memset (p.buf, 0, 22); // minimal header all zeroes
memcpy (p.buf + 4, packet->buf, 4); // but receiveStreamID is the sendStreamID from the ping
htobe16buf (p.buf + 18, PACKET_FLAG_ECHO); // and echo flag
ssize_t payloadLen = packet->len - (packet->GetPayload () - packet->buf);
if (payloadLen > 0)
memcpy (p.buf + 22, packet->GetPayload (), payloadLen);
else
payloadLen = 0;
p.len = payloadLen + 22;
SendPackets (std::vector<Packet *> { &p });
LogPrint (eLogDebug, "Streaming: Pong of ", p.len, " bytes sent");
}
m_LocalDestination.DeletePacket (packet);
}
void Stream::ProcessAck (Packet * packet) void Stream::ProcessAck (Packet * packet)
{ {
bool acknowledged = false; bool acknowledged = false;
@ -609,6 +631,7 @@ namespace stream
packet[size] = 0; packet[size] = 0;
size++; // NACK count size++; // NACK count
} }
packet[size] = 0;
size++; // resend delay size++; // resend delay
htobuf16 (packet + size, 0); // no flags set htobuf16 (packet + size, 0); // no flags set
size += 2; // flags size += 2; // flags
@ -666,6 +689,7 @@ namespace stream
size += 4; // ack Through size += 4; // ack Through
packet[size] = 0; packet[size] = 0;
size++; // NACK count size++; // NACK count
packet[size] = 0;
size++; // resend delay size++; // resend delay
htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED);
size += 2; // flags size += 2; // flags
@ -1016,6 +1040,13 @@ namespace stream
auto it = m_Streams.find (sendStreamID); auto it = m_Streams.find (sendStreamID);
if (it != m_Streams.end ()) if (it != m_Streams.end ())
it->second->HandleNextPacket (packet); it->second->HandleNextPacket (packet);
else if (packet->IsEcho () && m_Owner->IsStreamingAnswerPings ())
{
// ping
LogPrint (eLogInfo, "Streaming: Ping received sSID=", sendStreamID);
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
s->HandlePing (packet);
}
else else
{ {
LogPrint (eLogInfo, "Streaming: Unknown stream sSID=", sendStreamID); LogPrint (eLogInfo, "Streaming: Unknown stream sSID=", sendStreamID);

2
libi2pd/Streaming.h

@ -87,6 +87,7 @@ namespace stream
bool IsSYN () const { return GetFlags () & PACKET_FLAG_SYNCHRONIZE; }; bool IsSYN () const { return GetFlags () & PACKET_FLAG_SYNCHRONIZE; };
bool IsNoAck () const { return GetFlags () & PACKET_FLAG_NO_ACK; }; bool IsNoAck () const { return GetFlags () & PACKET_FLAG_NO_ACK; };
bool IsEcho () const { return GetFlags () & PACKET_FLAG_ECHO; };
}; };
struct PacketCmp struct PacketCmp
@ -168,6 +169,7 @@ namespace stream
StreamingDestination& GetLocalDestination () { return m_LocalDestination; }; StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
void HandleNextPacket (Packet * packet); void HandleNextPacket (Packet * packet);
void HandlePing (Packet * packet);
size_t Send (const uint8_t * buf, size_t len); size_t Send (const uint8_t * buf, size_t len);
void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler); void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler);

3
libi2pd/TransportSession.h

@ -64,7 +64,7 @@ namespace transport
public: public:
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout): TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout):
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout),
m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()) m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ())
{ {
if (router) if (router)
@ -103,7 +103,6 @@ namespace transport
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity; std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
mutable std::mutex m_RemoteIdentityMutex; mutable std::mutex m_RemoteIdentityMutex;
std::shared_ptr<i2p::crypto::DHKeys> m_DHKeysPair; // X - for client and Y - for server
size_t m_NumSentBytes, m_NumReceivedBytes; size_t m_NumSentBytes, m_NumReceivedBytes;
bool m_IsOutgoing; bool m_IsOutgoing;
int m_TerminationTimeout; int m_TerminationTimeout;

144
libi2pd/Transports.cpp

@ -131,10 +131,10 @@ namespace transport
Transports transports; Transports transports;
Transports::Transports (): Transports::Transports ():
m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_Thread (nullptr), m_Service (nullptr), m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_CheckReserved(true), m_Thread (nullptr),
m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr), m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr),
m_NTCPServer (nullptr), m_SSUServer (nullptr), m_NTCP2Server (nullptr), m_SSUServer (nullptr), m_NTCP2Server (nullptr),
m_DHKeysPairSupplier (5), m_X25519KeysPairSupplier (5), // 5 pre-generated keys m_X25519KeysPairSupplier (5), // 5 pre-generated keys
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0), m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0),
m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0), m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0),
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0),
@ -154,7 +154,7 @@ namespace transport
} }
} }
void Transports::Start (bool enableNTCP, bool enableSSU) void Transports::Start (bool enableNTCP2, bool enableSSU)
{ {
if (!m_Service) if (!m_Service)
{ {
@ -165,54 +165,13 @@ namespace transport
} }
i2p::config::GetOption("nat", m_IsNAT); i2p::config::GetOption("nat", m_IsNAT);
m_DHKeysPairSupplier.Start ();
m_X25519KeysPairSupplier.Start (); m_X25519KeysPairSupplier.Start ();
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&Transports::Run, this)); m_Thread = new std::thread (std::bind (&Transports::Run, this));
std::string ntcpproxy; i2p::config::GetOption("ntcpproxy", ntcpproxy);
std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy);
i2p::http::URL proxyurl; i2p::http::URL proxyurl;
uint16_t softLimit, hardLimit, threads;
i2p::config::GetOption("limits.ntcpsoft", softLimit);
i2p::config::GetOption("limits.ntcphard", hardLimit);
i2p::config::GetOption("limits.ntcpthreads", threads);
if(softLimit > 0 && hardLimit > 0 && softLimit >= hardLimit)
{
LogPrint(eLogError, "ntcp soft limit must be less than ntcp hard limit");
return;
}
if(ntcpproxy.size() && enableNTCP)
{
if(proxyurl.parse(ntcpproxy))
{
if(proxyurl.schema == "socks" || proxyurl.schema == "http")
{
m_NTCPServer = new NTCPServer(threads);
m_NTCPServer->SetSessionLimits(softLimit, hardLimit);
NTCPServer::ProxyType proxytype = NTCPServer::eSocksProxy;
if (proxyurl.schema == "http")
proxytype = NTCPServer::eHTTPProxy;
m_NTCPServer->UseProxy(proxytype, proxyurl.host, proxyurl.port);
m_NTCPServer->Start();
if(!m_NTCPServer->NetworkIsReady())
{
LogPrint(eLogError, "Transports: NTCP failed to start with proxy");
m_NTCPServer->Stop();
delete m_NTCPServer;
m_NTCPServer = nullptr;
}
}
else
LogPrint(eLogError, "Transports: unsupported NTCP proxy URL ", ntcpproxy);
}
else
LogPrint(eLogError, "Transports: invalid NTCP proxy url ", ntcpproxy);
return;
}
// create NTCP2. TODO: move to acceptor // create NTCP2. TODO: move to acceptor
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); if (enableNTCP2)
if (ntcp2)
{ {
if(!ntcp2proxy.empty()) if(!ntcp2proxy.empty())
{ {
@ -248,20 +207,6 @@ namespace transport
for (const auto& address : addresses) for (const auto& address : addresses)
{ {
if (!address) continue; if (!address) continue;
if (m_NTCPServer == nullptr && enableNTCP)
{
m_NTCPServer = new NTCPServer (threads);
m_NTCPServer->SetSessionLimits(softLimit, hardLimit);
m_NTCPServer->Start ();
if (!(m_NTCPServer->IsBoundV6() || m_NTCPServer->IsBoundV4())) {
/** failed to bind to NTCP */
LogPrint(eLogError, "Transports: failed to bind to TCP");
m_NTCPServer->Stop();
delete m_NTCPServer;
m_NTCPServer = nullptr;
}
}
if (address->transportStyle == RouterInfo::eTransportSSU) if (address->transportStyle == RouterInfo::eTransportSSU)
{ {
if (m_SSUServer == nullptr && enableSSU) if (m_SSUServer == nullptr && enableSSU)
@ -306,12 +251,6 @@ namespace transport
delete m_SSUServer; delete m_SSUServer;
m_SSUServer = nullptr; m_SSUServer = nullptr;
} }
if (m_NTCPServer)
{
m_NTCPServer->Stop ();
delete m_NTCPServer;
m_NTCPServer = nullptr;
}
if (m_NTCP2Server) if (m_NTCP2Server)
{ {
@ -320,7 +259,6 @@ namespace transport
m_NTCP2Server = nullptr; m_NTCP2Server = nullptr;
} }
m_DHKeysPairSupplier.Stop ();
m_X25519KeysPairSupplier.Stop (); m_X25519KeysPairSupplier.Stop ();
m_IsRunning = false; m_IsRunning = false;
if (m_Service) m_Service->stop (); if (m_Service) m_Service->stop ();
@ -381,7 +319,8 @@ namespace transport
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg) void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
{ {
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg }); if (m_IsOnline)
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
} }
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs) void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
@ -452,7 +391,7 @@ namespace transport
{ {
// NTCP2 have priority over NTCP // NTCP2 have priority over NTCP
auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only auto address = peer.router->GetNTCP2Address (true, !context.SupportsV6 ()); // published only
if (address && !peer.router->IsUnreachable ()) if (address && !peer.router->IsUnreachable () && (!m_CheckReserved || !i2p::util::net::IsInReservedRange(address->host)))
{ {
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router); auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router);
@ -472,48 +411,17 @@ namespace transport
} }
} }
} }
if (peer.numAttempts == 1) // NTCP1 if (peer.numAttempts == 1)// SSU
{
peer.numAttempts++;
auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ());
if (address && m_NTCPServer)
{
if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ())
{
if(!m_NTCPServer->ShouldLimit())
{
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
if(m_NTCPServer->UsingProxy())
{
NTCPServer::RemoteAddressType remote = NTCPServer::eIP4Address;
std::string addr = address->host.to_string();
if(address->host.is_v6())
remote = NTCPServer::eIP6Address;
m_NTCPServer->ConnectWithProxy(addr, address->port, remote, s);
}
else
m_NTCPServer->Connect (address->host, address->port, s);
return true;
}
else
{
LogPrint(eLogWarning, "Transports: NTCP Limit hit falling back to SSU");
}
}
}
else
LogPrint (eLogDebug, "Transports: NTCP address is not present for ", i2p::data::GetIdentHashAbbreviation (ident), ", trying SSU");
}
if (peer.numAttempts == 2)// SSU
{ {
peer.numAttempts++; peer.numAttempts++;
if (m_SSUServer && peer.router->IsSSU (!context.SupportsV6 ())) if (m_SSUServer && peer.router->IsSSU (!context.SupportsV6 ()))
{ {
auto address = peer.router->GetSSUAddress (!context.SupportsV6 ()); auto address = peer.router->GetSSUAddress (!context.SupportsV6 ());
m_SSUServer->CreateSession (peer.router, address->host, address->port); if (!m_CheckReserved || !i2p::util::net::IsInReservedRange(address->host))
return true; {
m_SSUServer->CreateSession (peer.router, address->host, address->port);
return true;
}
} }
} }
LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available"); LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available");
@ -630,16 +538,6 @@ namespace transport
} }
} }
std::shared_ptr<i2p::crypto::DHKeys> Transports::GetNextDHKeysPair ()
{
return m_DHKeysPairSupplier.Acquire ();
}
void Transports::ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair)
{
m_DHKeysPairSupplier.Return (pair);
}
std::shared_ptr<i2p::crypto::X25519Keys> Transports::GetNextX25519KeysPair () std::shared_ptr<i2p::crypto::X25519Keys> Transports::GetNextX25519KeysPair ()
{ {
return m_X25519KeysPairSupplier.Acquire (); return m_X25519KeysPairSupplier.Acquire ();
@ -850,5 +748,17 @@ namespace transport
} }
return false; return false;
} }
void Transports::SetOnline (bool online)
{
if (m_IsOnline != online)
{
m_IsOnline = online;
if (online)
PeerTest ();
else
i2p::context.SetError (eRouterErrorOffline);
}
}
} }
} }

18
libi2pd/Transports.h

@ -21,7 +21,6 @@
#include <atomic> #include <atomic>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "TransportSession.h" #include "TransportSession.h"
#include "NTCPSession.h"
#include "SSU.h" #include "SSU.h"
#include "NTCP2.h" #include "NTCP2.h"
#include "RouterInfo.h" #include "RouterInfo.h"
@ -60,7 +59,6 @@ namespace transport
std::condition_variable m_Acquired; std::condition_variable m_Acquired;
std::mutex m_AcquiredMutex; std::mutex m_AcquiredMutex;
}; };
typedef EphemeralKeysSupplier<i2p::crypto::DHKeys> DHKeysPairSupplier;
typedef EphemeralKeysSupplier<i2p::crypto::X25519Keys> X25519KeysPairSupplier; typedef EphemeralKeysSupplier<i2p::crypto::X25519Keys> X25519KeysPairSupplier;
struct Peer struct Peer
@ -88,19 +86,16 @@ namespace transport
Transports (); Transports ();
~Transports (); ~Transports ();
void Start (bool enableNTCP=true, bool enableSSU=true); void Start (bool enableNTCP2=true, bool enableSSU=true);
void Stop (); void Stop ();
bool IsBoundNTCP() const { return m_NTCPServer != nullptr; }
bool IsBoundSSU() const { return m_SSUServer != nullptr; } bool IsBoundSSU() const { return m_SSUServer != nullptr; }
bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; } bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; }
bool IsOnline() const { return m_IsOnline; }; bool IsOnline() const { return m_IsOnline; };
void SetOnline (bool online) { m_IsOnline = online; }; void SetOnline (bool online);
boost::asio::io_service& GetService () { return *m_Service; }; boost::asio::io_service& GetService () { return *m_Service; };
std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair ();
void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair);
std::shared_ptr<i2p::crypto::X25519Keys> GetNextX25519KeysPair (); std::shared_ptr<i2p::crypto::X25519Keys> GetNextX25519KeysPair ();
void ReuseX25519KeysPair (std::shared_ptr<i2p::crypto::X25519Keys> pair); void ReuseX25519KeysPair (std::shared_ptr<i2p::crypto::X25519Keys> pair);
@ -138,6 +133,9 @@ namespace transport
void PeerTest (); void PeerTest ();
void SetCheckReserved (bool check) { m_CheckReserved = check; };
bool IsCheckReserved () { return m_CheckReserved; };
private: private:
void Run (); void Run ();
@ -153,19 +151,18 @@ namespace transport
private: private:
bool m_IsOnline, m_IsRunning, m_IsNAT; volatile bool m_IsOnline;
bool m_IsRunning, m_IsNAT, m_CheckReserved;
std::thread * m_Thread; std::thread * m_Thread;
boost::asio::io_service * m_Service; boost::asio::io_service * m_Service;
boost::asio::io_service::work * m_Work; boost::asio::io_service::work * m_Work;
boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer; boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer;
NTCPServer * m_NTCPServer;
SSUServer * m_SSUServer; SSUServer * m_SSUServer;
NTCP2Server * m_NTCP2Server; NTCP2Server * m_NTCP2Server;
mutable std::mutex m_PeersMutex; mutable std::mutex m_PeersMutex;
std::unordered_map<i2p::data::IdentHash, Peer> m_Peers; std::unordered_map<i2p::data::IdentHash, Peer> m_Peers;
DHKeysPairSupplier m_DHKeysPairSupplier;
X25519KeysPairSupplier m_X25519KeysPairSupplier; X25519KeysPairSupplier m_X25519KeysPairSupplier;
std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes, m_TotalTransitTransmittedBytes; std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes, m_TotalTransitTransmittedBytes;
@ -186,7 +183,6 @@ namespace transport
public: public:
// for HTTP only // for HTTP only
const NTCPServer * GetNTCPServer () const { return m_NTCPServer; };
const SSUServer * GetSSUServer () const { return m_SSUServer; }; const SSUServer * GetSSUServer () const { return m_SSUServer; };
const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; }; const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; };
const decltype(m_Peers)& GetPeers () const { return m_Peers; }; const decltype(m_Peers)& GetPeers () const { return m_Peers; };

3
libi2pd/Tunnel.cpp

@ -303,6 +303,7 @@ namespace tunnel
{ {
for (auto& msg : msgs) for (auto& msg : msgs)
{ {
if (!msg.data) continue;
switch (msg.deliveryType) switch (msg.deliveryType)
{ {
case eDeliveryTypeLocal: case eDeliveryTypeLocal:
@ -521,7 +522,7 @@ namespace tunnel
} }
uint64_t ts = i2p::util::GetSecondsSinceEpoch (); uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
if (ts - lastTs >= 15) // manage tunnels every 15 seconds if (ts - lastTs >= 15 && i2p::transport::transports.IsOnline()) // manage tunnels every 15 seconds
{ {
ManageTunnels (); ManageTunnels ();
lastTs = ts; lastTs = ts;

2
libi2pd/Tunnel.h

@ -36,7 +36,7 @@ namespace tunnel
const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute
const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes
const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds
const int STANDARD_NUM_RECORDS = 5; // in VariableTunnelBuild message const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message
enum TunnelState enum TunnelState
{ {

49
libi2pd/util.cpp

@ -61,6 +61,9 @@ int inet_pton_xp(int af, const char *src, void *dst)
#include <ifaddrs.h> #include <ifaddrs.h>
#endif #endif
#define address_pair_v4(a,b) { boost::asio::ip::address_v4::from_string (a).to_ulong (), boost::asio::ip::address_v4::from_string (b).to_ulong () }
#define address_pair_v6(a,b) { boost::asio::ip::address_v6::from_string (a).to_bytes (), boost::asio::ip::address_v6::from_string (b).to_bytes () }
namespace i2p namespace i2p
{ {
namespace util namespace util
@ -391,6 +394,50 @@ namespace net
return boost::asio::ip::address::from_string(fallback); return boost::asio::ip::address::from_string(fallback);
#endif #endif
} }
}
bool IsInReservedRange(const boost::asio::ip::address& host) {
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
if(host.is_v4())
{
static const std::vector< std::pair<uint32_t, uint32_t> > reservedIPv4Ranges {
address_pair_v4("0.0.0.0", "0.255.255.255"),
address_pair_v4("10.0.0.0", "10.255.255.255"),
address_pair_v4("100.64.0.0", "100.127.255.255"),
address_pair_v4("127.0.0.0", "127.255.255.255"),
address_pair_v4("169.254.0.0", "169.254.255.255"),
address_pair_v4("172.16.0.0", "172.31.255.255"),
address_pair_v4("192.0.0.0", "192.0.0.255"),
address_pair_v4("192.0.2.0", "192.0.2.255"),
address_pair_v4("192.88.99.0", "192.88.99.255"),
address_pair_v4("192.168.0.0", "192.168.255.255"),
address_pair_v4("198.18.0.0", "192.19.255.255"),
address_pair_v4("198.51.100.0", "198.51.100.255"),
address_pair_v4("203.0.113.0", "203.0.113.255"),
address_pair_v4("224.0.0.0", "255.255.255.255")
};
uint32_t ipv4_address = host.to_v4 ().to_ulong ();
for(const auto& it : reservedIPv4Ranges) {
if (ipv4_address >= it.first && ipv4_address <= it.second)
return true;
}
}
if(host.is_v6())
{
static const std::vector< std::pair<boost::asio::ip::address_v6::bytes_type, boost::asio::ip::address_v6::bytes_type> > reservedIPv6Ranges {
address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"),
address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"),
address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
};
boost::asio::ip::address_v6::bytes_type ipv6_address = host.to_v6 ().to_bytes ();
for(const auto& it : reservedIPv6Ranges) {
if (ipv6_address >= it.first && ipv6_address <= it.second)
return true;
}
}
return false;
}
} // net
} // util } // util
} // i2p } // i2p

1
libi2pd/util.h

@ -172,6 +172,7 @@ namespace util
{ {
int GetMTU (const boost::asio::ip::address& localAddress); int GetMTU (const boost::asio::ip::address& localAddress);
const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6=false); const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6=false);
bool IsInReservedRange(const boost::asio::ip::address& host);
} }
} }
} }

6
libi2pd/version.h

@ -16,8 +16,8 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 32 #define I2PD_VERSION_MINOR 33
#define I2PD_VERSION_MICRO 1 #define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)
#define VERSION I2PD_VERSION #define VERSION I2PD_VERSION
@ -30,7 +30,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 46 #define I2P_VERSION_MICRO 47
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

23
libi2pd_client/AddressBook.cpp

@ -198,13 +198,18 @@ namespace client
for (const auto& it: addresses) for (const auto& it: addresses)
{ {
f << it.first << ","; if (it.second->IsValid ())
if (it.second->IsIdentHash ()) {
f << it.second->identHash.ToBase32 (); f << it.first << ",";
if (it.second->IsIdentHash ())
f << it.second->identHash.ToBase32 ();
else
f << it.second->blindedPublicKey->ToB33 ();
f << std::endl;
num++;
}
else else
f << it.second->blindedPublicKey->ToB33 (); LogPrint (eLogWarning, "Addressbook: invalid address ", it.first);
f << std::endl;
num++;
} }
LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved"); LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved");
return num; return num;
@ -801,6 +806,7 @@ namespace client
i2p::http::HTTPReq req; i2p::http::HTTPReq req;
req.AddHeader("Host", dest_host); req.AddHeader("Host", dest_host);
req.AddHeader("User-Agent", "Wget/1.11.4"); req.AddHeader("User-Agent", "Wget/1.11.4");
req.AddHeader("Accept-Encoding", "gzip");
req.AddHeader("X-Accept-Encoding", "x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0"); req.AddHeader("X-Accept-Encoding", "x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0");
req.AddHeader("Connection", "close"); req.AddHeader("Connection", "close");
if (!m_Etag.empty()) if (!m_Etag.empty())
@ -811,6 +817,7 @@ namespace client
url.schema = ""; url.schema = "";
url.host = ""; url.host = "";
req.uri = url.to_string(); req.uri = url.to_string();
req.version = "HTTP/1.1";
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port); auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port);
std::string request = req.to_string(); std::string request = req.to_string();
stream->Send ((const uint8_t *) request.data(), request.length()); stream->Send ((const uint8_t *) request.data(), request.length());
@ -882,7 +889,7 @@ namespace client
/* assert: res.code == 200 */ /* assert: res.code == 200 */
auto it = res.headers.find("ETag"); auto it = res.headers.find("ETag");
if (it != res.headers.end()) m_Etag = it->second; if (it != res.headers.end()) m_Etag = it->second;
it = res.headers.find("If-Modified-Since"); it = res.headers.find("Last-Modified");
if (it != res.headers.end()) m_LastModified = it->second; if (it != res.headers.end()) m_LastModified = it->second;
if (res.is_chunked()) if (res.is_chunked())
{ {
@ -890,7 +897,7 @@ namespace client
i2p::http::MergeChunkedResponse (in, out); i2p::http::MergeChunkedResponse (in, out);
response = out.str(); response = out.str();
} }
else if (res.is_gzipped()) if (res.is_gzipped())
{ {
std::stringstream out; std::stringstream out;
i2p::data::GzipInflator inflator; i2p::data::GzipInflator inflator;

76
libi2pd_client/ClientContext.cpp

@ -102,10 +102,11 @@ namespace client
{ {
std::string i2cpAddr; i2p::config::GetOption("i2cp.address", i2cpAddr); std::string i2cpAddr; i2p::config::GetOption("i2cp.address", i2cpAddr);
uint16_t i2cpPort; i2p::config::GetOption("i2cp.port", i2cpPort); 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 try
{ {
m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort); m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort, singleThread);
m_I2CPServer->Start (); m_I2CPServer->Start ();
} }
catch (std::exception& e) catch (std::exception& e)
@ -253,7 +254,8 @@ namespace client
bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename,
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType) i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType)
{ {
if (filename == "transient") static const std::string transient("transient");
if (!filename.compare (0, transient.length (), transient)) // starts with transient
{ {
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created"); LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
@ -400,7 +402,14 @@ namespace client
void ClientContext::CreateNewSharedLocalDestination () void ClientContext::CreateNewSharedLocalDestination ()
{ {
m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, EDDSA std::map<std::string, std::string> params
{
{ I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, "2" },
{ I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, "2" },
{ I2CP_PARAM_LEASESET_TYPE, "3" }
};
m_SharedLocalDestination = CreateNewLocalDestination (false, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, &params); // non-public, EDDSA
m_SharedLocalDestination->Acquire (); m_SharedLocalDestination->Acquire ();
} }
@ -435,7 +444,7 @@ namespace client
} }
template<typename Section> template<typename Section>
void ClientContext::ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const void ClientContext::ReadI2CPOptions (const Section& section, bool isServer, std::map<std::string, std::string>& options) const
{ {
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNEL_LENGTH, DEFAULT_INBOUND_TUNNEL_LENGTH); options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNEL_LENGTH, DEFAULT_INBOUND_TUNNEL_LENGTH);
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH); options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, DEFAULT_OUTBOUND_TUNNEL_LENGTH);
@ -445,6 +454,7 @@ namespace client
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY); options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY);
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY); options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY);
options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY); options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY);
options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false);
options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE); options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE);
std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, ""); std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, "");
if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType; if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType;
@ -533,6 +543,7 @@ namespace client
return; return;
} }
std::map<std::string, std::shared_ptr<ClientDestination> > destinations; // keys -> destination
for (auto& section: pt) for (auto& section: pt)
{ {
std::string name = section.first; std::string name = section.first;
@ -559,21 +570,28 @@ namespace client
i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
// I2CP // I2CP
std::map<std::string, std::string> options; std::map<std::string, std::string> options;
ReadI2CPOptions (section, options); ReadI2CPOptions (section, false, options);
std::shared_ptr<ClientDestination> localDestination = nullptr; std::shared_ptr<ClientDestination> localDestination = nullptr;
if (keys.length () > 0) if (keys.length () > 0)
{ {
i2p::data::PrivateKeys k; auto it = destinations.find (keys);
if(LoadPrivateKeys (k, keys, sigType, cryptoType)) if (it != destinations.end ())
localDestination = it->second;
else
{ {
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); i2p::data::PrivateKeys k;
if (!localDestination) if(LoadPrivateKeys (k, keys, sigType, cryptoType))
{ {
if(matchTunnels) localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
localDestination = CreateNewMatchedTunnelDestination(k, dest, &options); if (!localDestination)
else {
localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options); if(matchTunnels)
localDestination = CreateNewMatchedTunnelDestination(k, dest, &options);
else
localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options);
destinations[keys] = localDestination;
}
} }
} }
} }
@ -676,15 +694,24 @@ namespace client
// I2CP // I2CP
std::map<std::string, std::string> options; std::map<std::string, std::string> options;
ReadI2CPOptions (section, options); ReadI2CPOptions (section, true, options);
std::shared_ptr<ClientDestination> localDestination = nullptr; std::shared_ptr<ClientDestination> localDestination = nullptr;
i2p::data::PrivateKeys k; auto it = destinations.find (keys);
if(!LoadPrivateKeys (k, keys, sigType, cryptoType)) if (it != destinations.end ())
continue; localDestination = it->second;
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); else
if (!localDestination) {
localDestination = CreateNewLocalDestination (k, true, &options); i2p::data::PrivateKeys k;
if(!LoadPrivateKeys (k, keys, sigType, cryptoType))
continue;
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
if (!localDestination)
{
localDestination = CreateNewLocalDestination (k, true, &options);
destinations[keys] = localDestination;
}
}
if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
{ {
// udp server tunnel // udp server tunnel
@ -819,6 +846,8 @@ namespace client
bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy); bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy);
if (socksproxy) if (socksproxy)
{ {
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 socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys);
std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr); std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr);
uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort); uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort);
@ -827,7 +856,12 @@ namespace client
uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort); uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort);
i2p::data::SigningKeyType sigType; i2p::config::GetOption("socksproxy.signaturetype", sigType); 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 (socksProxyKeys.length () > 0) if (httpProxyKeys == socksProxyKeys && m_HttpProxy)
{
localDestination = m_HttpProxy->GetLocalDestination ();
localDestination->Acquire ();
}
else if (socksProxyKeys.length () > 0)
{ {
i2p::data::PrivateKeys keys; i2p::data::PrivateKeys keys;
if (LoadPrivateKeys (keys, socksProxyKeys, sigType)) if (LoadPrivateKeys (keys, socksProxyKeys, sigType))

2
libi2pd_client/ClientContext.h

@ -115,7 +115,7 @@ namespace client
template<typename Section> template<typename Section>
void ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map<std::string, std::string>& options) const; void ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map<std::string, std::string>& options) const;
template<typename Section> template<typename Section>
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const; // for tunnels void ReadI2CPOptions (const Section& section, bool isServer, std::map<std::string, std::string>& options) const; // for tunnels
void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const; // for HTTP and SOCKS proxy void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const; // for HTTP and SOCKS proxy
void CleanupUDP(const boost::system::error_code & ecode); void CleanupUDP(const boost::system::error_code & ecode);

101
libi2pd_client/I2CP.cpp

@ -23,36 +23,13 @@ namespace i2p
namespace client namespace client
{ {
I2CPDestination::I2CPDestination (std::shared_ptr<I2CPSession> owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params): I2CPDestination::I2CPDestination (boost::asio::io_service& service, std::shared_ptr<I2CPSession> owner,
RunnableService ("I2CP"), LeaseSetDestination (GetIOService (), isPublic, &params), std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params):
LeaseSetDestination (service, isPublic, &params),
m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()) m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ())
{ {
} }
I2CPDestination::~I2CPDestination ()
{
if (IsRunning ())
Stop ();
}
void I2CPDestination::Start ()
{
if (!IsRunning ())
{
LeaseSetDestination::Start ();
StartIOService ();
}
}
void I2CPDestination::Stop ()
{
if (IsRunning ())
{
LeaseSetDestination::Stop ();
StopIOService ();
}
}
void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key) void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key)
{ {
m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_Identity->GetCryptoKeyType (), key); m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_Identity->GetCryptoKeyType (), key);
@ -217,6 +194,37 @@ namespace client
} }
} }
RunnableI2CPDestination::RunnableI2CPDestination (std::shared_ptr<I2CPSession> owner,
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params):
RunnableService ("I2CP"),
I2CPDestination (GetIOService (), owner, identity, isPublic, params)
{
}
RunnableI2CPDestination::~RunnableI2CPDestination ()
{
if (IsRunning ())
Stop ();
}
void RunnableI2CPDestination::Start ()
{
if (!IsRunning ())
{
I2CPDestination::Start ();
StartIOService ();
}
}
void RunnableI2CPDestination::Stop ()
{
if (IsRunning ())
{
I2CPDestination::Stop ();
StopIOService ();
}
}
I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket): I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr<proto::socket> socket):
m_Owner (owner), m_Socket (socket), m_Payload (nullptr), m_Owner (owner), m_Socket (socket), m_Payload (nullptr),
m_SessionID (0xFFFF), m_MessageID (0), m_IsSendAccepted (true) m_SessionID (0xFFFF), m_MessageID (0), m_IsSendAccepted (true)
@ -451,7 +459,9 @@ namespace client
if (params[I2CP_PARAM_DONT_PUBLISH_LEASESET] == "true") isPublic = false; if (params[I2CP_PARAM_DONT_PUBLISH_LEASESET] == "true") isPublic = false;
if (!m_Destination) if (!m_Destination)
{ {
m_Destination = std::make_shared<I2CPDestination>(shared_from_this (), identity, isPublic, params); m_Destination = m_Owner.IsSingleThread () ?
std::make_shared<I2CPDestination>(m_Owner.GetService (), shared_from_this (), identity, isPublic, params):
std::make_shared<RunnableI2CPDestination>(shared_from_this (), identity, isPublic, params);
SendSessionStatusMessage (1); // created SendSessionStatusMessage (1); // created
LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created"); LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created");
m_Destination->Start (); m_Destination->Start ();
@ -800,9 +810,9 @@ namespace client
std::placeholders::_1, std::placeholders::_2, buf)); std::placeholders::_1, std::placeholders::_2, buf));
} }
I2CPServer::I2CPServer (const std::string& interface, int port): I2CPServer::I2CPServer (const std::string& interface, int port, bool isSingleThread):
m_IsRunning (false), m_Thread (nullptr), RunnableService ("I2CP"), m_IsSingleThread (isSingleThread),
m_Acceptor (m_Service, m_Acceptor (GetIOService (),
#ifdef ANDROID #ifdef ANDROID
I2CPSession::proto::endpoint(std::string (1, '\0') + interface)) // leading 0 for abstract address I2CPSession::proto::endpoint(std::string (1, '\0') + interface)) // leading 0 for abstract address
#else #else
@ -825,20 +835,18 @@ namespace client
I2CPServer::~I2CPServer () I2CPServer::~I2CPServer ()
{ {
if (m_IsRunning) if (IsRunning ())
Stop (); Stop ();
} }
void I2CPServer::Start () void I2CPServer::Start ()
{ {
Accept (); Accept ();
m_IsRunning = true; StartIOService ();
m_Thread = new std::thread (std::bind (&I2CPServer::Run, this));
} }
void I2CPServer::Stop () void I2CPServer::Stop ()
{ {
m_IsRunning = false;
m_Acceptor.cancel (); m_Acceptor.cancel ();
{ {
auto sessions = m_Sessions; auto sessions = m_Sessions;
@ -846,33 +854,12 @@ namespace client
it.second->Stop (); it.second->Stop ();
} }
m_Sessions.clear (); m_Sessions.clear ();
m_Service.stop (); StopIOService ();
if (m_Thread)
{
m_Thread->join ();
delete m_Thread;
m_Thread = nullptr;
}
}
void I2CPServer::Run ()
{
while (m_IsRunning)
{
try
{
m_Service.run ();
}
catch (std::exception& ex)
{
LogPrint (eLogError, "I2CP: runtime exception: ", ex.what ());
}
}
} }
void I2CPServer::Accept () void I2CPServer::Accept ()
{ {
auto newSocket = std::make_shared<I2CPSession::proto::socket> (m_Service); auto newSocket = std::make_shared<I2CPSession::proto::socket> (GetIOService ());
m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this, m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this,
std::placeholders::_1, newSocket)); std::placeholders::_1, newSocket));
} }

33
libi2pd_client/I2CP.h

@ -63,15 +63,13 @@ namespace client
const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability"; const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability";
class I2CPSession; class I2CPSession;
class I2CPDestination: private i2p::util::RunnableService, public LeaseSetDestination class I2CPDestination: public LeaseSetDestination
{ {
public: public:
I2CPDestination (std::shared_ptr<I2CPSession> owner, std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params); I2CPDestination (boost::asio::io_service& service, std::shared_ptr<I2CPSession> owner,
~I2CPDestination (); std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params);
~I2CPDestination () {};
void Start ();
void Stop ();
void SetEncryptionPrivateKey (const uint8_t * key); void SetEncryptionPrivateKey (const uint8_t * key);
void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; }; void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; };
@ -109,6 +107,18 @@ namespace client
uint64_t m_LeaseSetExpirationTime; uint64_t m_LeaseSetExpirationTime;
}; };
class RunnableI2CPDestination: private i2p::util::RunnableService, public I2CPDestination
{
public:
RunnableI2CPDestination (std::shared_ptr<I2CPSession> owner, std::shared_ptr<const i2p::data::IdentityEx> identity,
bool isPublic, const std::map<std::string, std::string>& params);
~RunnableI2CPDestination ();
void Start ();
void Stop ();
};
class I2CPServer; class I2CPServer;
class I2CPSession: public std::enable_shared_from_this<I2CPSession> class I2CPSession: public std::enable_shared_from_this<I2CPSession>
{ {
@ -179,16 +189,17 @@ namespace client
}; };
typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len); typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len);
class I2CPServer class I2CPServer: private i2p::util::RunnableService
{ {
public: public:
I2CPServer (const std::string& interface, int port); I2CPServer (const std::string& interface, int port, bool isSingleThread);
~I2CPServer (); ~I2CPServer ();
void Start (); void Start ();
void Stop (); void Stop ();
boost::asio::io_service& GetService () { return m_Service; }; boost::asio::io_service& GetService () { return GetIOService (); };
bool IsSingleThread () const { return m_IsSingleThread; };
bool InsertSession (std::shared_ptr<I2CPSession> session); bool InsertSession (std::shared_ptr<I2CPSession> session);
void RemoveSession (uint16_t sessionID); void RemoveSession (uint16_t sessionID);
@ -203,12 +214,10 @@ namespace client
private: private:
bool m_IsSingleThread;
I2CPMessageHandler m_MessagesHandlers[256]; I2CPMessageHandler m_MessagesHandlers[256];
std::map<uint16_t, std::shared_ptr<I2CPSession> > m_Sessions; std::map<uint16_t, std::shared_ptr<I2CPSession> > m_Sessions;
bool m_IsRunning;
std::thread * m_Thread;
boost::asio::io_service m_Service;
I2CPSession::proto::acceptor m_Acceptor; I2CPSession::proto::acceptor m_Acceptor;
public: public:

116
libi2pd_client/I2PTunnel.cpp

@ -139,20 +139,23 @@ namespace client
} }
} }
else else
WriteToStream (m_Buffer, bytes_transferred);
}
void I2PTunnelConnection::WriteToStream (const uint8_t * buf, size_t len)
{
if (m_Stream)
{ {
if (m_Stream) auto s = shared_from_this ();
{ m_Stream->AsyncSend (buf, len,
auto s = shared_from_this (); [s](const boost::system::error_code& ecode)
m_Stream->AsyncSend (m_Buffer, bytes_transferred, {
[s](const boost::system::error_code& ecode) if (!ecode)
{ s->Receive ();
if (!ecode) else
s->Receive (); s->Terminate ();
else });
s->Terminate ();
});
} }
}
} }
void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode) void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
@ -302,7 +305,8 @@ namespace client
I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& host): const boost::asio::ip::tcp::endpoint& target, const std::string& host):
I2PTunnelConnection (owner, stream, socket, target), m_Host (host), m_HeaderSent (false), m_From (stream->GetRemoteIdentity ()) I2PTunnelConnection (owner, stream, socket, target), m_Host (host),
m_HeaderSent (false), m_ResponseHeaderSent (false), m_From (stream->GetRemoteIdentity ())
{ {
} }
@ -324,7 +328,7 @@ namespace client
if (line == "\r") endOfHeader = true; if (line == "\r") endOfHeader = true;
else else
{ {
if (m_Host.length () > 0 && line.find ("Host:") != std::string::npos) if (m_Host.length () > 0 && !line.compare(0, 5, "Host:"))
m_OutHeader << "Host: " << m_Host << "\r\n"; // override host m_OutHeader << "Host: " << m_Host << "\r\n"; // override host
else else
m_OutHeader << line << "\n"; m_OutHeader << line << "\n";
@ -333,25 +337,79 @@ namespace client
else else
break; break;
} }
// add X-I2P fields
if (m_From)
{
m_OutHeader << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(m_From->GetIdentHash ()) << "\r\n";
m_OutHeader << X_I2P_DEST_HASH << ": " << m_From->GetIdentHash ().ToBase64 () << "\r\n";
m_OutHeader << X_I2P_DEST_B64 << ": " << m_From->ToBase64 () << "\r\n";
}
if (endOfHeader) if (endOfHeader)
{ {
// add X-I2P fields
if (m_From)
{
m_OutHeader << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(m_From->GetIdentHash ()) << "\r\n";
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 << "\r\n"; // end of header
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
m_InHeader.str (""); m_InHeader.str ("");
m_From = nullptr;
m_HeaderSent = true; m_HeaderSent = true;
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
} }
} }
} }
void I2PServerTunnelConnectionHTTP::WriteToStream (const uint8_t * buf, size_t len)
{
if (m_ResponseHeaderSent)
I2PTunnelConnection::WriteToStream (buf, len);
else
{
m_InHeader.clear ();
if (m_InHeader.str ().empty ()) m_OutHeader.str (""); // start of response
m_InHeader.write ((const char *)buf, len);
std::string line;
bool endOfHeader = false;
while (!endOfHeader)
{
std::getline(m_InHeader, line);
if (!m_InHeader.fail ())
{
if (line == "\r") endOfHeader = true;
else
{
static const std::vector<std::string> excluded // list of excluded headers
{
"Server:", "Date:", "X-Runtime:", "X-Powered-By:", "Proxy"
};
bool matched = false;
for (const auto& it: excluded)
if (!line.compare(0, it.length (), it))
{
matched = true;
break;
}
if (!matched)
m_OutHeader << line << "\n";
}
}
else
break;
}
if (endOfHeader)
{
m_OutHeader << "\r\n"; // end of header
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
m_InHeader.str ("");
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<i2p::stream::Stream> stream, I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass): const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass):
@ -708,9 +766,12 @@ namespace client
if(!ecode) if(!ecode)
{ {
LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint); LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint);
LastActivity = i2p::util::GetMillisecondsSinceEpoch(); auto ts = i2p::util::GetMillisecondsSinceEpoch();
auto session = m_Destination->GetSession (Identity); auto session = m_Destination->GetSession (Identity);
m_Destination->SendDatagram(session, m_Buffer, len, LocalPort, RemotePort); if (ts > LastActivity + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
m_Destination->SendDatagram(session, m_Buffer, len, LocalPort, RemotePort);
else
m_Destination->SendRawDatagram(session, m_Buffer, len, LocalPort, RemotePort);
size_t numPackets = 0; size_t numPackets = 0;
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE) while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
{ {
@ -724,6 +785,7 @@ namespace client
if (numPackets > 0) 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); m_Destination->FlushSendQueue (session);
LastActivity = ts;
Receive(); Receive();
} }
else else
@ -841,9 +903,13 @@ namespace client
m_LastPort = remotePort; m_LastPort = remotePort;
} }
// send off to remote i2p destination // send off to remote i2p destination
auto ts = i2p::util::GetMillisecondsSinceEpoch();
LogPrint(eLogDebug, "UDP Client: send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort); LogPrint(eLogDebug, "UDP Client: send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort);
auto session = m_LocalDest->GetDatagramDestination()->GetSession (*m_RemoteIdent); auto session = m_LocalDest->GetDatagramDestination()->GetSession (*m_RemoteIdent);
m_LocalDest->GetDatagramDestination()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
m_LocalDest->GetDatagramDestination()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
else
m_LocalDest->GetDatagramDestination()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
size_t numPackets = 0; size_t numPackets = 0;
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE) while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
{ {
@ -862,7 +928,7 @@ namespace client
// mark convo as active // mark convo as active
if (m_LastSession) if (m_LastSession)
m_LastSession->second = i2p::util::GetMillisecondsSinceEpoch(); m_LastSession->second = ts;
RecvFromLocal(); RecvFromLocal();
} }

5
libi2pd_client/I2PTunnel.h

@ -57,6 +57,7 @@ namespace client
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
virtual void Write (const uint8_t * buf, size_t len); // can be overloaded virtual void Write (const uint8_t * buf, size_t len); // can be overloaded
void HandleWrite (const boost::system::error_code& ecode); void HandleWrite (const boost::system::error_code& ecode);
virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded
void StreamReceive (); void StreamReceive ();
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
@ -103,12 +104,13 @@ namespace client
protected: protected:
void Write (const uint8_t * buf, size_t len); void Write (const uint8_t * buf, size_t len);
void WriteToStream (const uint8_t * buf, size_t len);
private: private:
std::string m_Host; std::string m_Host;
std::stringstream m_InHeader, m_OutHeader; std::stringstream m_InHeader, m_OutHeader;
bool m_HeaderSent; bool m_HeaderSent, m_ResponseHeaderSent;
std::shared_ptr<const i2p::data::IdentityEx> m_From; std::shared_ptr<const i2p::data::IdentityEx> m_From;
}; };
@ -165,6 +167,7 @@ namespace client
/** 2 minute timeout for udp sessions */ /** 2 minute timeout for udp sessions */
const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2; const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2;
const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds
/** max size for i2p udp */ /** max size for i2p udp */
const size_t I2P_UDP_MAX_MTU = 64*1024; const size_t I2P_UDP_MAX_MTU = 64*1024;

1
qt/i2pd_qt/data/website.i2pd.i2pd.appdata.xml

@ -35,6 +35,7 @@
<translation type="qt" /> <translation type="qt" />
<releases> <releases>
<release version="2.33.0" date="2020-08-24" />
<release version="2.32.1" date="2020-06-02" /> <release version="2.32.1" date="2020-06-02" />
<release version="2.32.0" date="2020-05-25" /> <release version="2.32.0" date="2020-05-25" />
<release version="2.31.0" date="2020-04-10" /> <release version="2.31.0" date="2020-04-10" />

2
qt/i2pd_qt/i2pd_qt.pro

@ -42,7 +42,6 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \
../../libi2pd/NetDb.cpp \ ../../libi2pd/NetDb.cpp \
../../libi2pd/NetDbRequests.cpp \ ../../libi2pd/NetDbRequests.cpp \
../../libi2pd/NTCP2.cpp \ ../../libi2pd/NTCP2.cpp \
../../libi2pd/NTCPSession.cpp \
../../libi2pd/Poly1305.cpp \ ../../libi2pd/Poly1305.cpp \
../../libi2pd/Profiling.cpp \ ../../libi2pd/Profiling.cpp \
../../libi2pd/Reseed.cpp \ ../../libi2pd/Reseed.cpp \
@ -123,7 +122,6 @@ HEADERS += DaemonQT.h mainwindow.h \
../../libi2pd/NetDb.hpp \ ../../libi2pd/NetDb.hpp \
../../libi2pd/NetDbRequests.h \ ../../libi2pd/NetDbRequests.h \
../../libi2pd/NTCP2.h \ ../../libi2pd/NTCP2.h \
../../libi2pd/NTCPSession.h \
../../libi2pd/Poly1305.h \ ../../libi2pd/Poly1305.h \
../../libi2pd/Profiling.h \ ../../libi2pd/Profiling.h \
../../libi2pd/Queue.h \ ../../libi2pd/Queue.h \

Loading…
Cancel
Save