diff --git a/.dir-locals.el b/.dir-locals.el deleted file mode 100644 index 2ef6066c..00000000 --- a/.dir-locals.el +++ /dev/null @@ -1,2 +0,0 @@ -((c++-mode . ((indent-tabs-mode . t))) - (c-mode . ((mode . c++)))) diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 0f57a84a..00000000 --- a/.editorconfig +++ /dev/null @@ -1,39 +0,0 @@ -# editorconfig.org - -root = true - -[*] -# Unix style files -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[Makefile,Makefile.*] -indent_style = tab -indent_size = 4 - -[*.cmd] -indent_style = space -indent_size = 2 -end_of_line = crlf - -[*.{h,cpp}] -indent_style = tab -indent_size = 4 - -[*.rc] -indent_style = space -indent_size = 4 - -[*.{md,markdown}] -indent_style = space -indent_size = 2 -trim_trailing_whitespace = false - -[*.yml] -indent_style = space -indent_size = 2 - -[*.patch] -trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 488400a3..00000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -/build/build_mingw.cmd eol=crlf \ No newline at end of file diff --git a/.github/workflows/build-deb.yml b/.github/workflows/build-deb.yml deleted file mode 100644 index 597dc211..00000000 --- a/.github/workflows/build-deb.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Build Debian packages - -on: - push: - branches: - - '*' - paths: - - .github/workflows/build-deb.yml - - contrib/** - - daemon/** - - debian/** - - i18n/** - - libi2pd/** - - libi2pd_client/** - - Makefile - - Makefile.linux - tags: - - '*' - pull_request: - branches: - - '*' - -jobs: - build: - name: ${{ matrix.dist }} - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - dist: ['buster', 'bullseye', 'bookworm'] - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Commit Hash - id: commit - uses: prompt/actions-commit-hash@v3.0.0 - - - name: Build package - uses: jtdor/build-deb-action@v1 - with: - docker-image: debian:${{ matrix.dist }}-slim - buildpackage-opts: --build=binary --no-sign - before-build-hook: debchange --controlmaint --local "+${{ steps.commit.outputs.short }}~${{ matrix.dist }}" -b --distribution ${{ matrix.dist }} "CI build" - extra-build-deps: devscripts git - - - name: Upload package - uses: actions/upload-artifact@v4 - with: - name: i2pd_${{ matrix.dist }} - path: debian/artifacts/i2pd_*.deb - - - name: Upload debugging symbols - uses: actions/upload-artifact@v4 - with: - name: i2pd-dbgsym_${{ matrix.dist }} - path: debian/artifacts/i2pd-dbgsym_*.deb diff --git a/.github/workflows/build-freebsd.yml b/.github/workflows/build-freebsd.yml deleted file mode 100644 index a4a7566a..00000000 --- a/.github/workflows/build-freebsd.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Build on FreeBSD - -on: - push: - branches: - - '*' - paths: - - .github/workflows/build-freebsd.yml - - build/CMakeLists.txt - - build/cmake_modules/** - - daemon/** - - i18n/** - - libi2pd/** - - libi2pd_client/** - - Makefile - - Makefile.bsd - tags: - - '*' - pull_request: - branches: - - '*' - -jobs: - build: - runs-on: ubuntu-latest - name: with UPnP - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Test in FreeBSD - id: test - uses: vmactions/freebsd-vm@v1 - with: - usesh: true - mem: 2048 - sync: rsync - copyback: true - prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc - run: | - cd build - cmake -DWITH_UPNP=ON -DCMAKE_BUILD_TYPE=Release . - gmake -j2 - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: i2pd-freebsd - path: build/i2pd diff --git a/.github/workflows/build-osx.yml b/.github/workflows/build-osx.yml deleted file mode 100644 index 31f0b90d..00000000 --- a/.github/workflows/build-osx.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Build on OSX - -on: - push: - branches: - - '*' - paths: - - .github/workflows/build-osx.yml - - daemon/** - - i18n/** - - libi2pd/** - - libi2pd_client/** - - Makefile - - Makefile.homebrew - tags: - - '*' - pull_request: - branches: - - '*' - -jobs: - build: - name: With USE_UPNP=${{ matrix.with_upnp }} - runs-on: macOS-latest - - strategy: - fail-fast: true - matrix: - with_upnp: ['yes', 'no'] - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install required formulae - run: | - find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete - brew update - brew install boost miniupnpc openssl@1.1 - - - name: List installed formulae - run: brew list - - - name: Build application - run: make HOMEBREW=1 USE_UPNP=${{ matrix.with_upnp }} PREFIX=$GITHUB_WORKSPACE/output -j3 diff --git a/.github/workflows/build-windows-msvc.yml-disabled b/.github/workflows/build-windows-msvc.yml-disabled deleted file mode 100644 index 922ebd0d..00000000 --- a/.github/workflows/build-windows-msvc.yml-disabled +++ /dev/null @@ -1,80 +0,0 @@ -name: Build on Windows with MSVC - -on: - push: - branches: - - '*' - paths: - - .github/workflows/build-windows-msvc.yml - - build/CMakeLists.txt - - build/cmake_modules/** - - daemon/** - - i18n/** - - libi2pd/** - - libi2pd_client/** - - Win32/** - tags: - - '*' - pull_request: - branches: - - '*' - -jobs: - build: - name: Build - runs-on: windows-latest - env: - boost_path: ${{ github.workspace }}\boost_1_83_0 - openssl_path: ${{ github.workspace }}\openssl_3_2_1 - - strategy: - fail-fast: false - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Build and install zlib - run: | - powershell -Command "(Invoke-WebRequest -Uri https://raw.githubusercontent.com/r4sas/zlib.install/master/install.bat -OutFile install_zlib.bat)" - powershell -Command "(Get-Content install_zlib.bat) | Set-Content install_zlib.bat" # fixing line endings - set BUILD_TYPE=Debug - ./install_zlib.bat - set BUILD_TYPE=Release - ./install_zlib.bat - del install_zlib.bat - - - name: Install Boost - run: | - powershell -Command "(Start-BitsTransfer -Source https://sourceforge.net/projects/boost/files/boost-binaries/1.83.0/boost_1_83_0-msvc-14.3-64.exe/download -Destination boost_1_83_0-msvc-14.3-64.exe)" - ./boost_1_83_0-msvc-14.3-64.exe /DIR="${{env.boost_path}}" /VERYSILENT /SUPPRESSMSGBOXES /SP- - - - name: Install OpenSSL - run: | - powershell -Command "(Start-BitsTransfer -Source https://slproweb.com/download/Win64OpenSSL-3_2_1.exe -Destination Win64OpenSSL-3_2_1.exe)" - ./Win64OpenSSL-3_2_1.exe /DIR="${{env.openssl_path}}" /TASKS="copytobin" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- - - - name: Make copy of the OpenSSL libraries for CMake - run: | - dir ${{ github.workspace }} - dir ${{env.openssl_path}}\lib\VC - dir ${{env.openssl_path}}\lib\VC\x64\ - dir ${{env.openssl_path}}\lib\VC\x64\MTd\ - xcopy /s /y "${{env.openssl_path}}\lib\VC\x64\MTd" "${{env.openssl_path}}\lib" - - - name: Configure - working-directory: build - run: cmake -DBoost_ROOT="${{env.boost_path}}" -DOPENSSL_ROOT_DIR="${{env.openssl_path}}" -DWITH_STATIC=ON . - - - name: Build - working-directory: build - run: cmake --build . --config Debug -- -m - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: i2pd-msvc - path: build/Debug/i2pd.* - diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml deleted file mode 100644 index 6f10e62b..00000000 --- a/.github/workflows/build-windows.yml +++ /dev/null @@ -1,250 +0,0 @@ -name: Build on Windows - -on: - push: - branches: - - '*' - paths: - - .github/workflows/build-windows.yml - - build/CMakeLists.txt - - build/cmake_modules/** - - daemon/** - - i18n/** - - libi2pd/** - - libi2pd_client/** - - Win32/** - - Makefile - - Makefile.mingw - tags: - - '*' - pull_request: - branches: - - '*' - -defaults: - run: - shell: msys2 {0} - -jobs: - build: - name: ${{ matrix.arch }} - runs-on: windows-latest - - strategy: - fail-fast: false - matrix: - include: [ - { msystem: UCRT64, arch: ucrt-x86_64, arch_short: x64-ucrt, compiler: gcc }, - { msystem: CLANG64, arch: clang-x86_64, arch_short: x64-clang, compiler: clang }, - { msystem: MINGW64, arch: x86_64, arch_short: x64, compiler: gcc }, - { msystem: MINGW32, arch: i686, arch_short: x86, compiler: gcc } - ] - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup MSYS2 - uses: msys2/setup-msys2@v2 - with: - msystem: ${{ matrix.msystem }} - install: base-devel git mingw-w64-${{ matrix.arch }}-${{ matrix.compiler }} mingw-w64-${{ matrix.arch }}-boost mingw-w64-${{ matrix.arch }}-openssl mingw-w64-${{ matrix.arch }}-miniupnpc - update: true - - - name: Install additional clang packages - if: ${{ matrix.msystem == 'CLANG64' }} - run: pacman --noconfirm -S mingw-w64-${{ matrix.arch }}-gcc-compat - - - name: Build application - run: | - mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon - make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes -j3 - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: i2pd-${{ matrix.arch_short }}.exe - path: i2pd.exe - - build-cmake: - name: CMake ${{ matrix.arch }} - runs-on: windows-latest - - strategy: - fail-fast: false - matrix: - include: [ - { msystem: UCRT64, arch: ucrt-x86_64, arch_short: x64-ucrt, compiler: gcc }, - { msystem: CLANG64, arch: clang-x86_64, arch_short: x64-clang, compiler: clang }, - { msystem: MINGW64, arch: x86_64, arch_short: x64, compiler: gcc }, - { msystem: MINGW32, arch: i686, arch_short: x86, compiler: gcc } - ] - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup MSYS2 - uses: msys2/setup-msys2@v2 - with: - msystem: ${{ matrix.msystem }} - install: base-devel git mingw-w64-${{ matrix.arch }}-cmake mingw-w64-${{ matrix.arch }}-ninja mingw-w64-${{ matrix.arch }}-${{ matrix.compiler }} mingw-w64-${{ matrix.arch }}-boost mingw-w64-${{ matrix.arch }}-openssl mingw-w64-${{ matrix.arch }}-miniupnpc - update: true - - - name: Build application - run: | - cd build - cmake -DWITH_GIT_VERSION=ON -DWITH_STATIC=ON -DWITH_UPNP=ON -DCMAKE_BUILD_TYPE=Release . - cmake --build . -- -j3 - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: i2pd-cmake-${{ matrix.arch_short }}.exe - path: build/i2pd.exe - - build-xp: - name: XP - runs-on: windows-latest - - strategy: - fail-fast: false - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup MSYS2 - uses: msys2/setup-msys2@v2 - with: - msystem: MINGW32 - install: base-devel git mingw-w64-i686-gcc mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-miniupnpc - cache: true - update: true - - - name: Clone MinGW packages repository and revert boost to 1.85.0 - run: | - git clone https://github.com/msys2/MINGW-packages - cd MINGW-packages - git checkout 4cbb366edf2f268ac3146174b40ce38604646fc5 mingw-w64-boost - cd mingw-w64-boost - sed -i 's/boostorg.jfrog.io\/artifactory\/main/archives.boost.io/' PKGBUILD - - # headers - - name: Get headers package version - id: version-headers - run: | - echo "version=$(pacman -Si mingw-w64-i686-headers-git | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT - - name: Cache headers package - uses: actions/cache@v4 - id: cache-headers - with: - path: MINGW-packages/mingw-w64-headers-git/*.zst - key: winxp-headers-${{ steps.version-headers.outputs.version }} - - name: Build WinXP-capable headers package - if: steps.cache-headers.outputs.cache-hit != 'true' - run: | - cd MINGW-packages/mingw-w64-headers-git - sed -i 's/0x601/0x501/' PKGBUILD - MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck - - name: Install headers package - run: pacman --noconfirm -U MINGW-packages/mingw-w64-headers-git/mingw-w64-i686-*-any.pkg.tar.zst - - # CRT - - name: Get crt package version - id: version-crt - run: | - echo "version=$(pacman -Si mingw-w64-i686-crt-git | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT - - name: Cache crt package - uses: actions/cache@v4 - id: cache-crt - with: - path: MINGW-packages/mingw-w64-crt-git/*.zst - key: winxp-crt-${{ steps.version-crt.outputs.version }} - - name: Build WinXP-capable crt package - if: steps.cache-crt.outputs.cache-hit != 'true' - run: | - cd MINGW-packages/mingw-w64-crt-git - MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck - - name: Install crt package - run: pacman --noconfirm -U MINGW-packages/mingw-w64-crt-git/mingw-w64-i686-*-any.pkg.tar.zst - - # winpthreads - - name: Get winpthreads package version - id: version-winpthreads - run: | - echo "version=$(pacman -Si mingw-w64-i686-winpthreads-git | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT - - name: Cache winpthreads package - uses: actions/cache@v4 - id: cache-winpthreads - with: - path: MINGW-packages/mingw-w64-winpthreads-git/*.zst - key: winxp-winpthreads-${{ steps.version-winpthreads.outputs.version }} - - name: Build WinXP-capable winpthreads package - if: steps.cache-winpthreads.outputs.cache-hit != 'true' - run: | - cd MINGW-packages/mingw-w64-winpthreads-git - MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck - - name: Install winpthreads package - run: pacman --noconfirm -U MINGW-packages/mingw-w64-winpthreads-git/mingw-w64-i686-*-any.pkg.tar.zst - - # OpenSSL - - name: Get openssl package version - id: version-openssl - run: | - echo "version=$(pacman -Si mingw-w64-i686-openssl | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT - - name: Cache openssl package - uses: actions/cache@v4 - id: cache-openssl - with: - path: MINGW-packages/mingw-w64-openssl/*.zst - key: winxp-openssl-${{ steps.version-openssl.outputs.version }} - - name: Build WinXP-capable openssl package - if: steps.cache-openssl.outputs.cache-hit != 'true' - run: | - cd MINGW-packages/mingw-w64-openssl - gpg --recv-keys D894E2CE8B3D79F5 - gpg --recv-keys 216094DFD0CB81EF - MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck - - name: Install openssl package - run: pacman --noconfirm -U MINGW-packages/mingw-w64-openssl/mingw-w64-i686-*-any.pkg.tar.zst - - # Boost - #- name: Get boost package version - # id: version-boost - # run: | - # echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT - - name: Cache boost package - uses: actions/cache@v4 - id: cache-boost - with: - path: MINGW-packages/mingw-w64-boost/*.zst - key: winxp-boost-1.85.0+crt-${{ steps.version-headers.outputs.version }}+ossl-${{ steps.version-openssl.outputs.version }} - # Rebuild package if packages above has changed - - name: Build WinXP-capable boost package - if: steps.cache-boost.outputs.cache-hit != 'true' - run: | - cd MINGW-packages/mingw-w64-boost - MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck - - name: Remove boost packages - run: pacman --noconfirm -R mingw-w64-i686-boost mingw-w64-i686-boost-libs - - name: Install boost package - run: pacman --noconfirm -U MINGW-packages/mingw-w64-boost/mingw-w64-i686-*-any.pkg.tar.zst - - # Building i2pd - - name: Build application - run: | - mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon - make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes USE_WINXP_FLAGS=yes -j3 - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: i2pd-xp.exe - path: i2pd.exe diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 0b65ec9d..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Build on Ubuntu - -on: - push: - branches: - - '*' - paths: - - .github/workflows/build.yml - - build/CMakeLists.txt - - build/cmake_modules/** - - daemon/** - - i18n/** - - libi2pd/** - - libi2pd_client/** - - Makefile - - Makefile.linux - tags: - - '*' - pull_request: - branches: - - '*' - -jobs: - build-make: - name: Make with USE_UPNP=${{ matrix.with_upnp }} - runs-on: ubuntu-latest - - strategy: - fail-fast: true - matrix: - with_upnp: ['yes', 'no'] - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: install packages - run: | - sudo apt-get update - sudo apt-get install build-essential libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev - - - name: build application - run: make USE_UPNP=${{ matrix.with_upnp }} -j3 - - build-cmake: - name: CMake with -DWITH_UPNP=${{ matrix.with_upnp }} - runs-on: ubuntu-latest - - strategy: - fail-fast: true - matrix: - with_upnp: ['ON', 'OFF'] - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: install packages - run: | - sudo apt-get update - sudo apt-get install build-essential cmake libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev - - - name: build application - run: | - cd build - cmake -DWITH_UPNP=${{ matrix.with_upnp }} . - make -j3 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index c6d55664..00000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,140 +0,0 @@ -name: Build containers - -on: - push: - branches: - - openssl - - docker - paths: - - .github/workflows/docker.yml - - contrib/docker/** - - contrib/certificates/** - - daemon/** - - i18n/** - - libi2pd/** - - libi2pd_client/** - - Makefile - - Makefile.linux - tags: - - '*' - -jobs: - build: - name: Building container for ${{ matrix.platform }} - runs-on: ubuntu-latest - permissions: - packages: write - contents: read - - strategy: - matrix: - include: [ - { platform: 'linux/amd64', archname: 'amd64' }, - { platform: 'linux/386', archname: 'i386' }, - { platform: 'linux/arm64', archname: 'arm64' }, - { platform: 'linux/arm/v7', archname: 'armv7' }, - ] - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to GitHub Container registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build container for ${{ matrix.archname }} - uses: docker/build-push-action@v5 - with: - context: ./contrib/docker - file: ./contrib/docker/Dockerfile - platforms: ${{ matrix.platform }} - push: true - tags: | - purplei2p/i2pd:latest-${{ matrix.archname }} - ghcr.io/purplei2p/i2pd:latest-${{ matrix.archname }} - provenance: false - - push: - name: Pushing merged manifest - runs-on: ubuntu-latest - - permissions: - packages: write - contents: read - - needs: build - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Login to GitHub Container registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Create and push latest manifest image to Docker Hub - if: ${{ !startsWith(github.ref, 'refs/tags/') }} - uses: Noelware/docker-manifest-action@master - with: - inputs: purplei2p/i2pd:latest - tags: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7 - push: true - - - name: Create and push latest manifest image to GHCR - if: ${{ !startsWith(github.ref, 'refs/tags/') }} - uses: Noelware/docker-manifest-action@master - with: - inputs: ghcr.io/purplei2p/i2pd:latest - tags: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7 - push: true - - - name: Store release version to env - if: ${{ startsWith(github.ref, 'refs/tags/') }} - run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV - - - name: Create and push release manifest to Docker Hub - if: ${{ startsWith(github.ref, 'refs/tags/') }} - uses: Noelware/docker-manifest-action@master - with: - inputs: purplei2p/i2pd:latest,purplei2p/i2pd:latest-release,purplei2p/i2pd:release-${{ env.RELEASE_VERSION }} - tags: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7 - push: true - - - name: Create and push release manifest to GHCR - if: ${{ startsWith(github.ref, 'refs/tags/') }} - uses: Noelware/docker-manifest-action@master - with: - inputs: ghcr.io/purplei2p/i2pd:latest,ghcr.io/purplei2p/i2pd:latest-release,ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }} - tags: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7 - push: true diff --git a/.gitignore b/.gitignore index 75bd6abb..a059c66e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,15 @@ # i2pd -*.o +obj/*.o router.info router.keys i2p netDb -/i2pd -/libi2pd.a -/libi2pdclient.a -/libi2pdlang.a -/libi2pd.so -/libi2pdclient.so -/libi2pdlang.so -/libi2pd.dll -/libi2pdclient.dll -/libi2pdlang.dll -*.exe - # Autotools autom4te.cache .deps stamp-h1 -#Makefile +Makefile config.h config.h.in~ config.log @@ -241,39 +229,3 @@ pip-log.txt #Mr Developer .mr.developer.cfg - -# Sphinx -docs/_build -/androidIdea/ - -# Doxygen -docs/generated - -# emacs files -*~ -*\#* - -# gdb files -.gdb_history - -# cmake makefile -build/Makefile - -# debian stuff -debian/i2pd.1.gz -.pc/ - -# qt - -qt/i2pd_qt/*.autosave -qt/i2pd_qt/*.ui.bk* -qt/i2pd_qt/*.ui_* - -#unknown android stuff -android/libs/ - -#various logs -*LOGS/ - -qt/build-*.sh* - diff --git a/AddressBook.cpp b/AddressBook.cpp new file mode 100644 index 00000000..f78666a0 --- /dev/null +++ b/AddressBook.cpp @@ -0,0 +1,547 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "base64.h" +#include "util.h" +#include "Identity.h" +#include "Log.h" +#include "NetDb.h" +#include "ClientContext.h" +#include "AddressBook.h" + +namespace i2p +{ +namespace client +{ + + class AddressBookFilesystemStorage: public AddressBookStorage + { + public: + + AddressBookFilesystemStorage (); + bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const; + void AddAddress (const i2p::data::IdentityEx& address); + void RemoveAddress (const i2p::data::IdentHash& ident); + + int Load (std::map& addresses); + int Save (const std::map& addresses); + + private: + + boost::filesystem::path GetPath () const { return i2p::util::filesystem::GetDefaultDataDir() / "addressbook"; }; + + }; + + AddressBookFilesystemStorage::AddressBookFilesystemStorage () + { + auto path = GetPath (); + if (!boost::filesystem::exists (path)) + { + // Create directory is necessary + if (!boost::filesystem::create_directory (path)) + LogPrint (eLogError, "Failed to create addressbook directory"); + } + } + + bool AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const + { + auto filename = GetPath () / (ident.ToBase32() + ".b32"); + std::ifstream f(filename.c_str (), std::ifstream::binary); + if (f.is_open ()) + { + f.seekg (0,std::ios::end); + size_t len = f.tellg (); + if (len < i2p::data::DEFAULT_IDENTITY_SIZE) + { + LogPrint (eLogError, "File ", filename, " is too short. ", len); + return false; + } + f.seekg(0, std::ios::beg); + uint8_t * buf = new uint8_t[len]; + f.read((char *)buf, len); + address.FromBuffer (buf, len); + delete[] buf; + return true; + } + else + return false; + } + + void AddressBookFilesystemStorage::AddAddress (const i2p::data::IdentityEx& address) + { + auto filename = GetPath () / (address.GetIdentHash ().ToBase32() + ".b32"); + std::ofstream f (filename.c_str (), std::ofstream::binary | std::ofstream::out); + if (f.is_open ()) + { + size_t len = address.GetFullLen (); + uint8_t * buf = new uint8_t[len]; + address.ToBuffer (buf, len); + f.write ((char *)buf, len); + delete[] buf; + } + else + LogPrint (eLogError, "Can't open file ", filename); + } + + void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident) + { + auto filename = GetPath () / (ident.ToBase32() + ".b32"); + if (boost::filesystem::exists (filename)) + boost::filesystem::remove (filename); + } + + int AddressBookFilesystemStorage::Load (std::map& addresses) + { + int num = 0; + auto filename = GetPath () / "addresses.csv"; + std::ifstream f (filename.c_str (), std::ofstream::in); // in text mode + if (f.is_open ()) + { + addresses.clear (); + while (!f.eof ()) + { + std::string s; + getline(f, s); + if (!s.length()) + continue; // skip empty line + + size_t pos = s.find(','); + if (pos != std::string::npos) + { + std::string name = s.substr(0, pos++); + std::string addr = s.substr(pos); + + i2p::data::IdentHash ident; + ident.FromBase32 (addr); + addresses[name] = ident; + num++; + } + } + LogPrint (eLogInfo, num, " addresses loaded"); + } + else + LogPrint (eLogWarning, filename, " not found"); + return num; + } + + int AddressBookFilesystemStorage::Save (const std::map& addresses) + { + int num = 0; + auto filename = GetPath () / "addresses.csv"; + std::ofstream f (filename.c_str (), std::ofstream::out); // in text mode + if (f.is_open ()) + { + for (auto it: addresses) + { + f << it.first << "," << it.second.ToBase32 () << std::endl; + num++; + } + LogPrint (eLogInfo, num, " addresses saved"); + } + else + LogPrint (eLogError, "Can't open file ", filename); + return num; + } + +//--------------------------------------------------------------------- + AddressBook::AddressBook (): m_IsLoaded (false), m_IsDownloading (false), + m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr) + { + } + + AddressBook::~AddressBook () + { + if (m_IsDownloading) + { + LogPrint (eLogInfo, "Subscription is downloading. Waiting for temination..."); + for (int i = 0; i < 30; i++) + { + if (!m_IsDownloading) + { + LogPrint (eLogInfo, "Subscription download complete"); + break; + } + std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds + } + LogPrint (eLogError, "Subscription download hangs"); + } + if (m_Storage) + { + m_Storage->Save (m_Addresses); + delete m_Storage; + } + delete m_DefaultSubscription; + for (auto it: m_Subscriptions) + delete it; + delete m_SubscriptionsUpdateTimer; + } + + AddressBookStorage * AddressBook::CreateStorage () + { + return new AddressBookFilesystemStorage (); + } + + bool AddressBook::GetIdentHash (const std::string& address, i2p::data::IdentHash& ident) + { + auto pos = address.find(".b32.i2p"); + if (pos != std::string::npos) + { + Base32ToByteStream (address.c_str(), pos, ident, 32); + return true; + } + else + { + pos = address.find (".i2p"); + if (pos != std::string::npos) + { + auto identHash = FindAddress (address); + if (identHash) + { + ident = *identHash; + return true; + } + else + return false; + } + } + // if not .b32 we assume full base64 address + i2p::data::IdentityEx dest; + if (!dest.FromBase64 (address)) + return false; + ident = dest.GetIdentHash (); + return true; + } + + const i2p::data::IdentHash * AddressBook::FindAddress (const std::string& address) + { + if (!m_IsLoaded) + LoadHosts (); + if (m_IsLoaded) + { + auto it = m_Addresses.find (address); + if (it != m_Addresses.end ()) + return &it->second; + } + return nullptr; + } + + void AddressBook::InsertAddress (const std::string& address, const std::string& base64) + { + i2p::data::IdentityEx ident; + ident.FromBase64 (base64); + if (!m_Storage) + m_Storage = CreateStorage (); + m_Storage->AddAddress (ident); + m_Addresses[address] = ident.GetIdentHash (); + LogPrint (address,"->", ToAddress(ident.GetIdentHash ()), " added"); + } + + void AddressBook::InsertAddress (const i2p::data::IdentityEx& address) + { + if (!m_Storage) + m_Storage = CreateStorage (); + m_Storage->AddAddress (address); + } + + bool AddressBook::GetAddress (const std::string& address, i2p::data::IdentityEx& identity) + { + if (!m_Storage) + m_Storage = CreateStorage (); + i2p::data::IdentHash ident; + if (!GetIdentHash (address, ident)) return false; + return m_Storage->GetAddress (ident, identity); + } + + void AddressBook::LoadHosts () + { + if (!m_Storage) + m_Storage = CreateStorage (); + if (m_Storage->Load (m_Addresses) > 0) + { + m_IsLoaded = true; + return; + } + + // try hosts.txt first + std::ifstream f (i2p::util::filesystem::GetFullPath ("hosts.txt").c_str (), std::ofstream::in); // in text mode + if (f.is_open ()) + { + LoadHostsFromStream (f); + m_IsLoaded = true; + } + else + { + // if not found download it from http://i2p-projekt.i2p/hosts.txt + LogPrint (eLogInfo, "hosts.txt not found. Try to download it from default subscription..."); + if (!m_IsDownloading) + { + m_IsDownloading = true; + if (!m_DefaultSubscription) + m_DefaultSubscription = new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS); + m_DefaultSubscription->CheckSubscription (); + } + } + + } + + void AddressBook::LoadHostsFromStream (std::istream& f) + { + std::unique_lock l(m_AddressBookMutex); + int numAddresses = 0; + std::string s; + while (!f.eof ()) + { + getline(f, s); + + if (!s.length()) + continue; // skip empty line + + size_t pos = s.find('='); + + if (pos != std::string::npos) + { + std::string name = s.substr(0, pos++); + std::string addr = s.substr(pos); + + i2p::data::IdentityEx ident; + if (ident.FromBase64(addr)) + { + m_Addresses[name] = ident.GetIdentHash (); + m_Storage->AddAddress (ident); + numAddresses++; + } + else + LogPrint (eLogError, "Malformed address ", addr, " for ", name); + } + } + LogPrint (eLogInfo, numAddresses, " addresses processed"); + if (numAddresses > 0) + { + m_IsLoaded = true; + m_Storage->Save (m_Addresses); + } + } + + void AddressBook::LoadSubscriptions () + { + if (!m_Subscriptions.size ()) + { + std::ifstream f (i2p::util::filesystem::GetFullPath ("subscriptions.txt").c_str (), std::ofstream::in); // in text mode + if (f.is_open ()) + { + std::string s; + while (!f.eof ()) + { + getline(f, s); + if (!s.length()) continue; // skip empty line + m_Subscriptions.push_back (new AddressBookSubscription (*this, s)); + } + LogPrint (eLogInfo, m_Subscriptions.size (), " subscriptions loaded"); + } + else + LogPrint (eLogWarning, "subscriptions.txt not found"); + } + else + LogPrint (eLogError, "Subscriptions already loaded"); + } + + void AddressBook::DownloadComplete (bool success) + { + m_IsDownloading = false; + m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes( + success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT)); + m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } + + void AddressBook::StartSubscriptions () + { + LoadSubscriptions (); + if (!m_Subscriptions.size ()) return; + + auto dest = i2p::client::context.GetSharedLocalDestination (); + if (dest) + { + m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer (dest->GetService ()); + m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT)); + m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } + else + LogPrint (eLogError, "Can't start subscriptions: missing shared local destination"); + } + + void AddressBook::StopSubscriptions () + { + if (m_SubscriptionsUpdateTimer) + m_SubscriptionsUpdateTimer->cancel (); + } + + void AddressBook::HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto dest = i2p::client::context.GetSharedLocalDestination (); + if (!dest) return; + if (m_IsLoaded && !m_IsDownloading && dest->IsReady ()) + { + // pick random subscription + CryptoPP::AutoSeededRandomPool rnd; + auto ind = rnd.GenerateWord32 (0, m_Subscriptions.size() - 1); + m_IsDownloading = true; + m_Subscriptions[ind]->CheckSubscription (); + } + else + { + if (!m_IsLoaded) + LoadHosts (); + // try it again later + m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_RETRY_TIMEOUT)); + m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, + this, std::placeholders::_1)); + } + } + } + + AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link): + m_Book (book), m_Link (link) + { + } + + void AddressBookSubscription::CheckSubscription () + { + std::thread load_hosts(&AddressBookSubscription::Request, this); + load_hosts.detach(); // TODO: use join + } + + void AddressBookSubscription::Request () + { + // must be run in separate thread + LogPrint (eLogInfo, "Downloading hosts from ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); + bool success = false; + i2p::util::http::url u (m_Link); + i2p::data::IdentHash ident; + if (m_Book.GetIdentHash (u.host_, ident)) + { + std::condition_variable newDataReceived; + std::mutex newDataReceivedMutex; + const i2p::data::LeaseSet * leaseSet = i2p::data::netdb.FindLeaseSet (ident); + if (!leaseSet) + { + bool found = false; + std::unique_lock l(newDataReceivedMutex); + i2p::client::context.GetSharedLocalDestination ()->RequestDestination (ident, + [&newDataReceived, &found](bool success) + { + found = success; + newDataReceived.notify_all (); + }); + if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) + LogPrint (eLogError, "Subscription LeseseSet request timeout expired"); + if (found) + leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident); + } + if (leaseSet) + { + std::stringstream request, response; + // standard header + request << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_ + << "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n"; + if (m_Etag.length () > 0) // etag + request << i2p::util::http::IF_NONE_MATCH << ": \"" << m_Etag << "\"\r\n"; + if (m_LastModified.length () > 0) // if-modfief-since + request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n"; + request << "\r\n"; // end of header + auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (*leaseSet, u.port_); + stream->Send ((uint8_t *)request.str ().c_str (), request.str ().length ()); + + uint8_t buf[4095]; + bool end = false; + while (!end) + { + stream->AsyncReceive (boost::asio::buffer (buf, 4096), + [&](const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (bytes_transferred) + response.write ((char *)buf, bytes_transferred); + if (ecode == boost::asio::error::timed_out || !stream->IsOpen ()) + end = true; + newDataReceived.notify_all (); + }, + 30); // wait for 30 seconds + std::unique_lock l(newDataReceivedMutex); + if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) + LogPrint (eLogError, "Subscription timeout expired"); + } + // process remaining buffer + while (size_t len = stream->ReadSome (buf, 4096)) + response.write ((char *)buf, len); + + // parse response + std::string version; + response >> version; // HTTP version + int status = 0; + response >> status; // status + if (status == 200) // OK + { + bool isChunked = false; + std::string header, statusMessage; + std::getline (response, statusMessage); + // read until new line meaning end of header + while (!response.eof () && header != "\r") + { + std::getline (response, header); + auto colon = header.find (':'); + if (colon != std::string::npos) + { + std::string field = header.substr (0, colon); + header.resize (header.length () - 1); // delete \r + if (field == i2p::util::http::ETAG) + m_Etag = header.substr (colon + 1); + else if (field == i2p::util::http::LAST_MODIFIED) + m_LastModified = header.substr (colon + 1); + else if (field == i2p::util::http::TRANSFER_ENCODING) + isChunked = !header.compare (colon + 1, std::string::npos, "chunked"); + } + } + LogPrint (eLogInfo, m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified); + if (!response.eof ()) + { + success = true; + if (!isChunked) + m_Book.LoadHostsFromStream (response); + else + { + // merge chunks + std::stringstream merged; + i2p::util::http::MergeChunkedResponse (response, merged); + m_Book.LoadHostsFromStream (merged); + } + } + } + else if (status == 304) + { + success = true; + LogPrint (eLogInfo, "No updates from ", m_Link); + } + else + LogPrint (eLogWarning, "Adressbook HTTP response ", status); + } + else + LogPrint (eLogError, "Address ", u.host_, " not found"); + } + else + LogPrint (eLogError, "Can't resolve ", u.host_); + LogPrint (eLogInfo, "Download complete ", success ? "Success" : "Failed"); + m_Book.DownloadComplete (success); + } +} +} + diff --git a/AddressBook.h b/AddressBook.h new file mode 100644 index 00000000..5b24c017 --- /dev/null +++ b/AddressBook.h @@ -0,0 +1,100 @@ +#ifndef ADDRESS_BOOK_H__ +#define ADDRESS_BOOK_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "base64.h" +#include "util.h" +#include "Identity.h" +#include "Log.h" + +namespace i2p +{ +namespace client +{ + const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt"; + const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes + const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes + const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 240; // in minutes + const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes + const int SUBSCRIPTION_REQUEST_TIMEOUT = 60; //in second + + class AddressBookStorage // interface for storage + { + public: + + virtual ~AddressBookStorage () {}; + virtual bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const = 0; + virtual void AddAddress (const i2p::data::IdentityEx& address) = 0; + virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0; + + virtual int Load (std::map& addresses) = 0; + virtual int Save (const std::map& addresses) = 0; + }; + + class AddressBookSubscription; + class AddressBook + { + public: + + AddressBook (); + ~AddressBook (); + bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident); + bool GetAddress (const std::string& address, i2p::data::IdentityEx& identity); + const i2p::data::IdentHash * FindAddress (const std::string& address); + void InsertAddress (const std::string& address, const std::string& base64); // for jump service + void InsertAddress (const i2p::data::IdentityEx& address); + + void StartSubscriptions (); + void StopSubscriptions (); + void LoadHostsFromStream (std::istream& f); + void DownloadComplete (bool success); + //This method returns the ".b32.i2p" address + std::string ToAddress(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); } + std::string ToAddress(const i2p::data::IdentityEx& ident) { return ToAddress(ident.GetIdentHash ()); } + private: + + AddressBookStorage * CreateStorage (); + void LoadHosts (); + void LoadSubscriptions (); + + void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode); + + private: + + std::mutex m_AddressBookMutex; + std::map m_Addresses; + AddressBookStorage * m_Storage; + volatile bool m_IsLoaded, m_IsDownloading; + std::vector m_Subscriptions; + AddressBookSubscription * m_DefaultSubscription; // in case if we don't know any addresses yet + boost::asio::deadline_timer * m_SubscriptionsUpdateTimer; + }; + + class AddressBookSubscription + { + public: + + AddressBookSubscription (AddressBook& book, const std::string& link); + void CheckSubscription (); + + private: + + void Request (); + + private: + + AddressBook& m_Book; + std::string m_Link, m_Etag, m_LastModified; + }; +} +} + +#endif + + diff --git a/BOB.cpp b/BOB.cpp new file mode 100644 index 00000000..afbb00b0 --- /dev/null +++ b/BOB.cpp @@ -0,0 +1,655 @@ +#include +#include +#include "Log.h" +#include "ClientContext.h" +#include "BOB.h" + +namespace i2p +{ +namespace client +{ + BOBI2PInboundTunnel::BOBI2PInboundTunnel (int port, ClientDestination * localDestination): + BOBI2PTunnel (localDestination), + m_Acceptor (localDestination->GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), m_Timer (localDestination->GetService ()) + { + } + + BOBI2PInboundTunnel::~BOBI2PInboundTunnel () + { + Stop (); + } + + void BOBI2PInboundTunnel::Start () + { + m_Acceptor.listen (); + Accept (); + } + + void BOBI2PInboundTunnel::Stop () + { + m_Acceptor.close(); + ClearConnections (); + } + + void BOBI2PInboundTunnel::Accept () + { + auto receiver = new AddressReceiver (); + receiver->socket = new boost::asio::ip::tcp::socket (GetService ()); + m_Acceptor.async_accept (*receiver->socket, std::bind (&BOBI2PInboundTunnel::HandleAccept, this, + std::placeholders::_1, receiver)); + } + + void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, AddressReceiver * receiver) + { + if (!ecode) + { + Accept (); + ReceiveAddress (receiver); + } + else + { + delete receiver->socket; + delete receiver; + } + } + + void BOBI2PInboundTunnel::ReceiveAddress (AddressReceiver * receiver) + { + receiver->socket->async_read_some (boost::asio::buffer( + receiver->buffer + receiver->bufferOffset, + BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset), + std::bind(&BOBI2PInboundTunnel::HandleReceivedAddress, this, + std::placeholders::_1, std::placeholders::_2, receiver)); + } + + void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred, + AddressReceiver * receiver) + { + if (ecode) + { + LogPrint ("BOB inbound tunnel read error: ", ecode.message ()); + delete receiver->socket; + delete receiver; + } + else + { + receiver->bufferOffset += bytes_transferred; + receiver->buffer[receiver->bufferOffset] = 0; + char * eol = strchr (receiver->buffer, '\n'); + if (eol) + { + *eol = 0; + + receiver->data = (uint8_t *)eol + 1; + receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1); + i2p::data::IdentHash ident; + if (!context.GetAddressBook ().GetIdentHash (receiver->buffer, ident)) + { + LogPrint (eLogError, "BOB address ", receiver->buffer, " not found"); + delete receiver->socket; + delete receiver; + return; + } + auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident); + if (leaseSet) + CreateConnection (receiver, leaseSet); + else + { + GetLocalDestination ()->RequestDestination (ident); + m_Timer.expires_from_now (boost::posix_time::seconds (I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT)); + m_Timer.async_wait (std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestTimer, + this, std::placeholders::_1, receiver, ident)); + } + } + else + { + if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE) + ReceiveAddress (receiver); + else + { + LogPrint ("BOB missing inbound address "); + delete receiver->socket; + delete receiver; + } + } + } + } + + void BOBI2PInboundTunnel::HandleDestinationRequestTimer (const boost::system::error_code& ecode, AddressReceiver * receiver, i2p::data::IdentHash ident) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident); + if (leaseSet) + { + CreateConnection (receiver, leaseSet); + return; + } + else + LogPrint ("LeaseSet for BOB inbound destination not found"); + } + delete receiver->socket; + delete receiver; + } + + void BOBI2PInboundTunnel::CreateConnection (AddressReceiver * receiver, const i2p::data::LeaseSet * leaseSet) + { + LogPrint ("New BOB inbound connection"); + auto connection = std::make_shared(this, receiver->socket, leaseSet); + AddConnection (connection); + connection->I2PConnect (receiver->data, receiver->dataLen); + delete receiver; + } + + BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& address, int port, + ClientDestination * localDestination, bool quiet): BOBI2PTunnel (localDestination), + m_Endpoint (boost::asio::ip::address::from_string (address), port), m_IsQuiet (quiet) + { + } + + void BOBI2POutboundTunnel::Start () + { + Accept (); + } + + void BOBI2POutboundTunnel::Stop () + { + ClearConnections (); + } + + void BOBI2POutboundTunnel::Accept () + { + auto localDestination = GetLocalDestination (); + if (localDestination) + localDestination->AcceptStreams (std::bind (&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1)); + else + LogPrint ("Local destination not set for server tunnel"); + } + + void BOBI2POutboundTunnel::HandleAccept (std::shared_ptr stream) + { + if (stream) + { + auto conn = std::make_shared (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint, m_IsQuiet); + AddConnection (conn); + conn->Connect (); + } + } + + BOBDestination::BOBDestination (ClientDestination& localDestination): + m_LocalDestination (localDestination), + m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr) + { + } + + BOBDestination::~BOBDestination () + { + delete m_OutboundTunnel; + delete m_InboundTunnel; + i2p::client::context.DeleteLocalDestination (&m_LocalDestination); + } + + void BOBDestination::Start () + { + if (m_OutboundTunnel) m_OutboundTunnel->Start (); + if (m_InboundTunnel) m_InboundTunnel->Start (); + } + + void BOBDestination::Stop () + { + StopTunnels (); + m_LocalDestination.Stop (); + } + + void BOBDestination::StopTunnels () + { + if (m_OutboundTunnel) + { + m_OutboundTunnel->Stop (); + delete m_OutboundTunnel; + m_OutboundTunnel = nullptr; + } + if (m_InboundTunnel) + { + m_InboundTunnel->Stop (); + delete m_InboundTunnel; + m_InboundTunnel = nullptr; + } + } + + void BOBDestination::CreateInboundTunnel (int port) + { + if (!m_InboundTunnel) + m_InboundTunnel = new BOBI2PInboundTunnel (port, &m_LocalDestination); + } + + void BOBDestination::CreateOutboundTunnel (const std::string& address, int port, bool quiet) + { + if (!m_OutboundTunnel) + m_OutboundTunnel = new BOBI2POutboundTunnel (address, port, &m_LocalDestination, quiet); + } + + BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner): + m_Owner (owner), m_Socket (m_Owner.GetService ()), m_ReceiveBufferOffset (0), + m_IsOpen (true), m_IsQuiet (false), m_InPort (0), m_OutPort (0), + m_CurrentDestination (nullptr) + { + } + + BOBCommandSession::~BOBCommandSession () + { + } + + void BOBCommandSession::Terminate () + { + m_Socket.close (); + m_IsOpen = false; + } + + void BOBCommandSession::Receive () + { + m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, BOB_COMMAND_BUFFER_SIZE - m_ReceiveBufferOffset), + std::bind(&BOBCommandSession::HandleReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void BOBCommandSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("BOB command channel read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + size_t size = m_ReceiveBufferOffset + bytes_transferred; + m_ReceiveBuffer[size] = 0; + char * eol = strchr (m_ReceiveBuffer, '\n'); + if (eol) + { + *eol = 0; + char * operand = strchr (m_ReceiveBuffer, ' '); + if (operand) + { + *operand = 0; + operand++; + } + else + operand = eol; + // process command + auto& handlers = m_Owner.GetCommandHandlers (); + auto it = handlers.find (m_ReceiveBuffer); + if (it != handlers.end ()) + (this->*(it->second))(operand, eol - operand); + else + { + LogPrint (eLogError, "BOB unknown command ", m_ReceiveBuffer); + SendReplyError ("unknown command"); + } + + m_ReceiveBufferOffset = size - (eol - m_ReceiveBuffer) - 1; + memmove (m_ReceiveBuffer, eol + 1, m_ReceiveBufferOffset); + } + else + { + if (size < BOB_COMMAND_BUFFER_SIZE) + m_ReceiveBufferOffset = size; + else + { + LogPrint (eLogError, "Malformed input of the BOB command channel"); + Terminate (); + } + } + } + } + + void BOBCommandSession::Send (size_t len) + { + boost::asio::async_write (m_Socket, boost::asio::buffer (m_SendBuffer, len), + boost::asio::transfer_all (), + std::bind(&BOBCommandSession::HandleSent, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void BOBCommandSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("BOB command channel send error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + if (m_IsOpen) + Receive (); + else + Terminate (); + } + } + + void BOBCommandSession::SendReplyOK (const char * msg) + { +#ifdef _MSC_VER + size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg); +#else + size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg); +#endif + Send (len); + } + + void BOBCommandSession::SendReplyError (const char * msg) + { +#ifdef _MSC_VER + size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg); +#else + size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg); +#endif + Send (len); + } + + void BOBCommandSession::SendVersion () + { + size_t len = strlen (BOB_VERSION); + memcpy (m_SendBuffer, BOB_VERSION, len); + Send (len); + } + + void BOBCommandSession::SendData (const char * nickname) + { +#ifdef _MSC_VER + size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname); +#else + size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname); +#endif + Send (len); + } + + void BOBCommandSession::ZapCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: zap"); + Terminate (); + } + + void BOBCommandSession::QuitCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: quit"); + m_IsOpen = false; + SendReplyOK ("Bye!"); + } + + void BOBCommandSession::StartCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: start ", m_Nickname); + if (!m_CurrentDestination) + { + m_CurrentDestination = new BOBDestination (*i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options)); + m_Owner.AddDestination (m_Nickname, m_CurrentDestination); + } + if (m_InPort) + m_CurrentDestination->CreateInboundTunnel (m_InPort); + if (m_OutPort && !m_Address.empty ()) + m_CurrentDestination->CreateOutboundTunnel (m_Address, m_OutPort, m_IsQuiet); + m_CurrentDestination->Start (); + SendReplyOK ("tunnel starting"); + } + + void BOBCommandSession::StopCommandHandler (const char * operand, size_t len) + { + auto dest = m_Owner.FindDestination (m_Nickname); + if (dest) + { + dest->StopTunnels (); + SendReplyOK ("tunnel stopping"); + } + else + SendReplyError ("tunnel not found"); + } + + void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: setnick ", operand); + m_Nickname = operand; + std::string msg ("Nickname set to "); + msg += operand; + SendReplyOK (msg.c_str ()); + } + + void BOBCommandSession::GetNickCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: getnick ", operand); + m_CurrentDestination = m_Owner.FindDestination (operand); + if (m_CurrentDestination) + { + m_Keys = m_CurrentDestination->GetKeys (); + m_Nickname = operand; + std::string msg ("Nickname set to "); + msg += operand; + SendReplyOK (msg.c_str ()); + } + else + SendReplyError ("tunnel not found"); + } + + void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: newkeys"); + m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (); + SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); + } + + void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: setkeys ", operand); + m_Keys.FromBase64 (operand); + SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); + } + + void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: getkeys"); + SendReplyOK (m_Keys.ToBase64 ().c_str ()); + } + + void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: getdest"); + SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ()); + } + + void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: outhost ", operand); + m_Address = operand; + SendReplyOK ("outhost set"); + } + + void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: outport ", operand); + m_OutPort = boost::lexical_cast(operand); + SendReplyOK ("outbound port set"); + } + + void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: inhost ", operand); + m_Address = operand; + SendReplyOK ("inhost set"); + } + + void BOBCommandSession::InportCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: inport ", operand); + m_InPort = boost::lexical_cast(operand); + SendReplyOK ("inbound port set"); + } + + void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: quiet"); + m_IsQuiet = true; + SendReplyOK ("quiet"); + } + + void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: lookup ", operand); + i2p::data::IdentityEx addr; + if (!context.GetAddressBook ().GetAddress (operand, addr)) + { + SendReplyError ("Address Not found"); + return; + } + SendReplyOK (addr.ToBase64 ().c_str ()); + } + + void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: clear"); + m_Owner.DeleteDestination (m_Nickname); + SendReplyOK ("cleared"); + } + + void BOBCommandSession::ListCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: list"); + auto& destinations = m_Owner.GetDestinations (); + for (auto it: destinations) + SendData (it.first.c_str ()); + SendReplyOK ("Listing done"); + } + + void BOBCommandSession::OptionCommandHandler (const char * operand, size_t len) + { + LogPrint (eLogDebug, "BOB: option ", operand); + const char * value = strchr (operand, '='); + if (value) + { + *(const_cast(value)) = 0; + m_Options[operand] = value + 1; + *(const_cast(value)) = '='; + SendReplyOK ("option"); + } + else + SendReplyError ("malformed"); + } + + BOBCommandChannel::BOBCommandChannel (int port): + m_IsRunning (false), m_Thread (nullptr), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) + { + // command -> handler + m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler; + m_CommandHandlers[BOB_COMMAND_QUIT] = &BOBCommandSession::QuitCommandHandler; + m_CommandHandlers[BOB_COMMAND_START] = &BOBCommandSession::StartCommandHandler; + m_CommandHandlers[BOB_COMMAND_STOP] = &BOBCommandSession::StopCommandHandler; + m_CommandHandlers[BOB_COMMAND_SETNICK] = &BOBCommandSession::SetNickCommandHandler; + m_CommandHandlers[BOB_COMMAND_GETNICK] = &BOBCommandSession::GetNickCommandHandler; + m_CommandHandlers[BOB_COMMAND_NEWKEYS] = &BOBCommandSession::NewkeysCommandHandler; + m_CommandHandlers[BOB_COMMAND_GETKEYS] = &BOBCommandSession::GetkeysCommandHandler; + m_CommandHandlers[BOB_COMMAND_SETKEYS] = &BOBCommandSession::SetkeysCommandHandler; + m_CommandHandlers[BOB_COMMAND_GETDEST] = &BOBCommandSession::GetdestCommandHandler; + m_CommandHandlers[BOB_COMMAND_OUTHOST] = &BOBCommandSession::OuthostCommandHandler; + m_CommandHandlers[BOB_COMMAND_OUTPORT] = &BOBCommandSession::OutportCommandHandler; + m_CommandHandlers[BOB_COMMAND_INHOST] = &BOBCommandSession::InhostCommandHandler; + m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler; + m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler; + m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler; + m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler; + m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler; + m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler; + } + + BOBCommandChannel::~BOBCommandChannel () + { + Stop (); + for (auto it: m_Destinations) + delete it.second; + } + + void BOBCommandChannel::Start () + { + Accept (); + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&BOBCommandChannel::Run, this)); + } + + void BOBCommandChannel::Stop () + { + m_IsRunning = false; + for (auto it: m_Destinations) + it.second->Stop (); + m_Acceptor.cancel (); + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + + void BOBCommandChannel::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "BOB: ", ex.what ()); + } + } + } + + void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest) + { + m_Destinations[name] = dest; + } + + void BOBCommandChannel::DeleteDestination (const std::string& name) + { + auto it = m_Destinations.find (name); + if (it != m_Destinations.end ()) + { + it->second->Stop (); + delete it->second; + m_Destinations.erase (it); + } + } + + BOBDestination * BOBCommandChannel::FindDestination (const std::string& name) + { + auto it = m_Destinations.find (name); + if (it != m_Destinations.end ()) + return it->second; + return nullptr; + } + + void BOBCommandChannel::Accept () + { + auto newSession = std::make_shared (*this); + m_Acceptor.async_accept (newSession->GetSocket (), std::bind (&BOBCommandChannel::HandleAccept, this, + std::placeholders::_1, newSession)); + } + + void BOBCommandChannel::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session) + { + if (ecode != boost::asio::error::operation_aborted) + Accept (); + + if (!ecode) + { + LogPrint (eLogInfo, "New BOB command connection from ", session->GetSocket ().remote_endpoint ()); + session->SendVersion (); + } + else + LogPrint (eLogError, "BOB accept error: ", ecode.message ()); + } +} +} + diff --git a/BOB.h b/BOB.h new file mode 100644 index 00000000..fc961137 --- /dev/null +++ b/BOB.h @@ -0,0 +1,236 @@ +#ifndef BOB_H__ +#define BOB_H__ + +#include +#include +#include +#include +#include +#include +#include "I2PTunnel.h" +#include "Identity.h" +#include "LeaseSet.h" + +namespace i2p +{ +namespace client +{ + const size_t BOB_COMMAND_BUFFER_SIZE = 1024; + const char BOB_COMMAND_ZAP[] = "zap"; + const char BOB_COMMAND_QUIT[] = "quit"; + const char BOB_COMMAND_START[] = "start"; + const char BOB_COMMAND_STOP[] = "stop"; + const char BOB_COMMAND_SETNICK[] = "setnick"; + const char BOB_COMMAND_GETNICK[] = "getnick"; + const char BOB_COMMAND_NEWKEYS[] = "newkeys"; + const char BOB_COMMAND_GETKEYS[] = "getkeys"; + const char BOB_COMMAND_SETKEYS[] = "setkeys"; + const char BOB_COMMAND_GETDEST[] = "getdest"; + const char BOB_COMMAND_OUTHOST[] = "outhost"; + const char BOB_COMMAND_OUTPORT[] = "outport"; + const char BOB_COMMAND_INHOST[] = "inhost"; + const char BOB_COMMAND_INPORT[] = "inport"; + const char BOB_COMMAND_QUIET[] = "quiet"; + const char BOB_COMMAND_LOOKUP[] = "lookup"; + const char BOB_COMMAND_CLEAR[] = "clear"; + const char BOB_COMMAND_LIST[] = "list"; + const char BOB_COMMAND_OPTION[] = "option"; + + const char BOB_VERSION[] = "BOB 00.00.10\nOK\n"; + const char BOB_REPLY_OK[] = "OK %s\n"; + const char BOB_REPLY_ERROR[] = "ERROR %s\n"; + const char BOB_DATA[] = "NICKNAME %s\n"; + + class BOBI2PTunnel: public I2PTunnel + { + public: + + BOBI2PTunnel (ClientDestination * localDestination): + I2PTunnel (localDestination) {}; + + virtual void Start () {}; + virtual void Stop () {}; + }; + + class BOBI2PInboundTunnel: public BOBI2PTunnel + { + struct AddressReceiver + { + boost::asio::ip::tcp::socket * socket; + char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address + uint8_t * data; + size_t dataLen, bufferOffset; + + AddressReceiver (): data (nullptr), dataLen (0), bufferOffset (0) {}; + }; + + public: + + BOBI2PInboundTunnel (int port, ClientDestination * localDestination); + ~BOBI2PInboundTunnel (); + + void Start (); + void Stop (); + + private: + + void Accept (); + void HandleAccept (const boost::system::error_code& ecode, AddressReceiver * receiver); + + void ReceiveAddress (AddressReceiver * receiver); + void HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred, + AddressReceiver * receiver); + + void HandleDestinationRequestTimer (const boost::system::error_code& ecode, AddressReceiver * receiver, i2p::data::IdentHash ident); + + void CreateConnection (AddressReceiver * receiver, const i2p::data::LeaseSet * leaseSet); + + private: + + boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::deadline_timer m_Timer; + }; + + class BOBI2POutboundTunnel: public BOBI2PTunnel + { + public: + + BOBI2POutboundTunnel (const std::string& address, int port, ClientDestination * localDestination, bool quiet); + + void Start (); + void Stop (); + + void SetQuiet () { m_IsQuiet = true; }; + + private: + + void Accept (); + void HandleAccept (std::shared_ptr stream); + + private: + + boost::asio::ip::tcp::endpoint m_Endpoint; + bool m_IsQuiet; + }; + + + class BOBDestination + { + public: + + BOBDestination (ClientDestination& localDestination); + ~BOBDestination (); + + void Start (); + void Stop (); + void StopTunnels (); + void CreateInboundTunnel (int port); + void CreateOutboundTunnel (const std::string& address, int port, bool quiet); + const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination.GetPrivateKeys (); }; + + private: + + ClientDestination& m_LocalDestination; + BOBI2POutboundTunnel * m_OutboundTunnel; + BOBI2PInboundTunnel * m_InboundTunnel; + }; + + class BOBCommandChannel; + class BOBCommandSession: public std::enable_shared_from_this + { + public: + + BOBCommandSession (BOBCommandChannel& owner); + ~BOBCommandSession (); + void Terminate (); + + boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; + void SendVersion (); + + // command handlers + void ZapCommandHandler (const char * operand, size_t len); + void QuitCommandHandler (const char * operand, size_t len); + void StartCommandHandler (const char * operand, size_t len); + void StopCommandHandler (const char * operand, size_t len); + void SetNickCommandHandler (const char * operand, size_t len); + void GetNickCommandHandler (const char * operand, size_t len); + void NewkeysCommandHandler (const char * operand, size_t len); + void SetkeysCommandHandler (const char * operand, size_t len); + void GetkeysCommandHandler (const char * operand, size_t len); + void GetdestCommandHandler (const char * operand, size_t len); + void OuthostCommandHandler (const char * operand, size_t len); + void OutportCommandHandler (const char * operand, size_t len); + void InhostCommandHandler (const char * operand, size_t len); + void InportCommandHandler (const char * operand, size_t len); + void QuietCommandHandler (const char * operand, size_t len); + void LookupCommandHandler (const char * operand, size_t len); + void ClearCommandHandler (const char * operand, size_t len); + void ListCommandHandler (const char * operand, size_t len); + void OptionCommandHandler (const char * operand, size_t len); + + private: + + void Receive (); + void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + + void Send (size_t len); + void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void SendReplyOK (const char * msg); + void SendReplyError (const char * msg); + void SendData (const char * nickname); + + private: + + BOBCommandChannel& m_Owner; + boost::asio::ip::tcp::socket m_Socket; + char m_ReceiveBuffer[BOB_COMMAND_BUFFER_SIZE + 1], m_SendBuffer[BOB_COMMAND_BUFFER_SIZE + 1]; + size_t m_ReceiveBufferOffset; + bool m_IsOpen, m_IsQuiet; + std::string m_Nickname, m_Address; + int m_InPort, m_OutPort; + i2p::data::PrivateKeys m_Keys; + std::map m_Options; + BOBDestination * m_CurrentDestination; + }; + typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len); + + class BOBCommandChannel + { + public: + + BOBCommandChannel (int port); + ~BOBCommandChannel (); + + void Start (); + void Stop (); + + boost::asio::io_service& GetService () { return m_Service; }; + void AddDestination (const std::string& name, BOBDestination * dest); + void DeleteDestination (const std::string& name); + BOBDestination * FindDestination (const std::string& name); + + private: + + void Run (); + void Accept (); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session); + + private: + + bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::ip::tcp::acceptor m_Acceptor; + std::map m_Destinations; + std::map m_CommandHandlers; + + public: + + const decltype(m_CommandHandlers)& GetCommandHandlers () const { return m_CommandHandlers; }; + const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; + }; +} +} + +#endif + diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index 23864c0e..00000000 --- a/ChangeLog +++ /dev/null @@ -1,1255 +0,0 @@ -# for this file format description, -# see https://github.com/olivierlacan/keep-a-changelog - -## [2.56.0] - 2025-02-11 -### Added -- Config params for shared local destination -- AddressBook full addresses cache -- Decline transit tunnel to duplicated router -- Recreate tunnels in random order -### Changed -- Exclude disk operations from SSU2 and NTCP2 threads -- Set minimal version for peer test to 0.9.62 -- Send ack requested flag after second SSU2 resend attempt -- Shorter ECIESx25519 ack request interval for datagram and I2CP sessions -- Don't change datagram routing path too often if unidirectional data stream -- Reduce LeaseSet and local RouterInfo publishing confirmation intervals -- Don't delete buffer of connected routers or if an update received -- Smaller RouterInfo request timeout if sent directly -- Persist local RouterInfo in separate thread -- Don't recalculate and process ranges for every SSU2 Ack block -- Reseeds list -### Fixed -- Termination deadlock if SAM session is active -- Race condition at tunnel endpoint -- Inbound tunnel build encryption - -## [2.55.0] - 2024-12-30 -### Added -- Support boost 1.87 -- "i2p.streaming.maxConcurrentStreams" tunnel's param to limit number of simultaneous streams -- Separate thread for tunnel build requests -- Show next peer and connectivity on "Transit tunnels" page -- Tunnel name for local destination thread -- Throttle incoming ECIESx25519 sessions -- Send tunnel data to transport session directly if possible -- Publish 'R' cap for yggdrasil-only routers, and 'U' cap for routers through proxy -- Random tunnel rejection when medium congestion -- Save unreachable router's endpoint to use it next time without introducers -- Recognize symmetric NAT from peer test message 7 -- Resend HolePunch and RelayResponse messages -### Changed -- Removed own implementation of AESNI and always use one from openssl -- Renamed main thread to i2pd-daemon -- Set i2p.streaming.profile=2 for shared local destination -- Reduced LeaseSet and RouterInfo lookup timeouts -- Cleanup ECIES sessions and tags more often -- Check LeaseSet expiration time -- Handle NTCP2 session handshakes in separate thread -- Limit last decline time by 1.5 hours in router's profile -- Don't handle RelayRequest and RelayIntro with same nonce twice -- Increased hole punch expiration interval -- Send peer test message 6 with delay if message 4 was received before message 5 -- Pre-calculate more x25519 keys for transports in runtime -- Don't request LeaseSet for incoming stream -- Terminate incoming stream right away if no remote LeaseSet -- Handle choked, new RTO and window size calculation and resetting algorithm for streams -### Fixed -- Empty string in addressbook subscriptions -- ECIESx25519 sessions without destination -- Missing RouterInfo buffer in NetDb -- Invalid I2PControl certificate -- Routers disappear from NetDb when offline -- Peer test message 6 sent to unknown endpoint -- Race condition with LeaseSet update -- Excessive CPU usage by streams -- Crash on shutdown - -## [2.54.0] - 2024-10-06 -### Added -- Maintain recently connected routers list to avoid false-positive peer test -- Limited connectivity mode(through proxy) -- "i2p.streaming.profile" tunnel's param to let tunnel select also low-bandwidth routers -- Limit stream's inbound speed -- Periodic ack requests in ratchets session -- Set congestion cap G immediately if through proxy -- Show tunnel's routers bandwidth caps in web console -- Handle immediate ack requested flag in SSU2 data packets -- Resend and ack peer test and relay messages -- "senduseragent" HTTP proxy's param to pass through user's User-Agent -### Changed -- Exclude 'N' routers from high-bandwidth routers for client tunnels -- C++11 support has been dropped, the minimal requirement is C++17 now, C++20 for some compilers -- Removed dependency from boost::date_time and boost::filesystem -- Set default i2cp.leaseSetEncType to 0,4 and to 4 for server tunnels -- Handle i2cp.inboundlimit and i2cp.outboundlimit params in I2CP -- Publish LeaseSet with new timestamp update if tunnel was replaced in the same second -- Increase max number of generated tags to 800 per tagset -- Routing path expiration by time instead num attempts -- Save timestamp from epoch instead local time to profiles -- Update introducer's iTag if session to introducer was replaced to new one -- RTT, window size and number of NACKs calculation for streaming -- Don't select same peer for tunnel too often -- Use WinApi for data path UTF-8 conversion for Windows -### Fixed -- Jump link crash if address book is disabled -- Race condition if connect through an introducer -- "Date" header in I2PControl response -- Incomplete response from web console -- AEAD verification with LibreSSL -- Number of generated tags and new keys for follow-on tagsets -- Expired leases in LeaseSet -- Attempts to send HolePunch to 0.0.0.0 -- Incorrect options size in quick ack streaming packet -- Low bandwidth router appeared as first peer in high-bandwidth client tunnel - -## [2.53.1] - 2024-07-29 -### Changed -- I2CP performance improvement -### Fixed -- 100% CPU usage after I2CP/SAM/BOB session termination -- Incorrect client limits returned through I2CP -- Build with LibreSSL - -## [2.53.0] - 2024-07-19 -### Added -- New congestion control algorithm for streaming -- Support miniupnp-2.2.8 -- Limit stream's outbound speed -- Flood to next day closest floodfills before UTC midnight -- Recognize duplicated routers and bypass them -- Random SSU2 resend interval -### Changed -- Set minimal version to 0.9.69 for floodfills and 0.9.58 for client tunnels -- Removed openssl 1.0.2 support -- Move unsent I2NP messages to the new session if replaced -- Use mt19937 RNG instead rand() -- Update router's congestion caps before initial publishing -- Don't try introducer with invalid address -- Select newest introducers to publish -- Don't request relay tag for every session if we have enough introducers -- Update timestamp for non-reachable or hidden router -- Reset streaming routing path if duplicated SYN received -- Update LeaseSet if inbound tunnel failed -- Reseeds list -### Fixed -- Crash when a destination gets terminated -- Expired offline signature upon destination creation -- Race condition between local RouterInfo buffer creation and sending it through the transports - -## [2.52.0] - 2024-05-12 -### Added -- Separate threads for persisting RouterInfos and profiles to disk -- Give preference to address with direct connection -- Exclude addresses with incorrect static or intro key -- Avoid two firewalled routers in the row in tunnel -- Drop unsolicited database search replies -### Changed -- Increase number of hashes to 16 in exploratory lookup reply -- Reduce number of a RouterInfo lookup attempts to 5 -- Reset stream RTO if outbound tunnel was changed -- Insert previously excluded floodfill back when successfully connected -- Increase maximum stream resend attempts to 9 -- Reply to exploratory lookups with only confirmed routers if low tunnel build rate -- Don't accept too old RouterInfo -- Build client tunnels through confirmed routers only if low tunnel build rate -- Manage netDb requests more frequently -- Don't reply with closer than us only floodfills for lookup -### Fixed -- Crash on router lookup if exploratory pool is not ready -- Race condition in excluded peers for next lookup -- Excessive number of lookups for same destination -- Race condition with transport peers during shutdown -- Corrupted RouterInfo files - -## [2.51.0] - 2024-04-06 -### Added -- Non-blocking mode for UDP sockets -- Set SSU2 socket buffer size based on bandwidth limit -- Encrypted tunnel tests -- Support for multiple UDP server tunnels on one destination -- Publish medium congestion indication -- Local domain sockets for SOCKS proxy upstream -- Tunnel status "declined" in web console -- SAM error reply "Incompatible crypto" if remote destination has incompatible crypto -- Reduce amount of traffic by handling local message drops -- Keep SSU2 socket open even if it fails to bind -- Lower SSU2 resend traffic spikes -- Expiration for messages in SSU2 send queue -- Use EWMA for stream RTT estimation -- Request choking delay if too many NACKs in stream -- Allow 0ms latency for tunnel -- Randomize tunnels selection for tests -### Changed -- Upstream SOCKS proxy from SOCKS4 to SOCKS5 -- Transit tunnels limit to 4 bytes. Default value to 10K -- Reply CANT_REACH_PEER if connect to ourselves in SAM -- Don't send already expired I2NP messages -- Use monotonic timer to measure tunnel test latency -- Standard NTCP2 frame doesn't exceed 16K -- Always send request through tunnels in case of restricted routes -- Don't delete connected routers from NetDb -- Send lookup reply directly to reply tunnel gateway if possible -- Reduce unreachable router ban interval to 8 minutes -- Don't request banned routers / don't try to connect to unreachable router -- Consider 'M' routers as low bandwidth -- Limit minimal received SSU2 packet size to 40 bytes -- Bob picks peer test session only if Charlie's address supports peer testing -- Reject peer test msg 2 if peer testing is not supported -- Don't request termination if SSU2 session was not established -- Set maximum SSU2 queue size depending on RTT value -- New streaming RTT calculation algorithm -- Don't double initial RTO for streams when changing tunnels -- Restore failed tunnel if test or data for inbound tunnel received -- Don't fail last remaining tunnel in pool -- Publish LeasetSet again if local destination was not ready or no tunnels -- Make more attempts to pick high bandwidth hop for client tunnel -- Reduced SSU2 session termination timeout to 165 seconds -- Reseeds list -### Fixed -- ECIESx25519 symmetric key tagset early expiration -- Encrypted LeaseSet lookup -- Outbound tunnel build fails if it's endpoint is the same as reply tunnel gateway -- I2PControl RouterManager returns invalid JSON when unknown params are passed -- Mix of data between different UDP sessions on the same server -- TARGET_OS_SIMULATOR check -- Handling of "reservedrange" param -- New NTCP2 session gets teminated upon termination of old one -- New SSU2 session gets teminated upon termination of old one -- Peer test to non-supporting router -- Streaming ackThrough off 1 if number of NACKs exceeds 255 -- Race condition in ECIESx25519 tags table -- Good tunnel becomes failed -- Crash when packet comes to terminated stream -- Stream hangs during LeaseSet update - -## [2.50.2] - 2024-01-06 -###Fixed -- Crash with OpenSSL 3.2.0 -- False positive clock skew detection - -## [2.50.1] - 2023-12-23 -###Fixed -- Support for new EdDSA usage behavior in OpenSSL 3.2.0 - -## [2.50.0] - 2023-12-18 -### Added -- Support of concurrent ACCEPTs on SAM 3.1 -- Haiku OS support -- Low bandwidth and far routers can expire before 1 hour -### Changed -- Don't pick too active peer for first hop -- Try peer test again if status is Unknown -- Send peer tests with random delay -- Reseeds list -### Fixed -- XSS vulnerability in addresshelper -- Publishing NAT64 ipv6 addresses -- Deadlock in AsyncSend callback - -## [2.49.0] - 2023-09-18 -### Added -- Handle SOCK5 authorization with empty user/password -- Drop incoming transport sessions from too old or from future routers -- Memory pool for router profiles -- Allow 0 hops in explicitPeers -### Changed -- Separate network and testing status -- Remove AVX code -- Improve NTCP2 transport session logging -- Select router with ipv4 for tunnel endpoint -- Consider all addresses non-published for U and H routers even if they have host/port -- Don't pick completely unreachable routers for tunnels -- Exclude SSU1 introducers from SSU2 addresses -- Don't create paired inbound tunnel if length is different -- Remove introducer from RouterInfo after 60 minutes -- Reduce SSU2 keep alive interval and add keep alive interval variance -- Don't pick too old sessions for introducer -### Fixed -- Version of the subnegotiation in user/password SOCKS5 response -- Send keepalive for existing session with introducer -- Buffer offset for EVP_EncryptFinal_ex() to include outlen -- Termination block size processing for transport sessions -- Crash if deleted BOB destination was shared between few BOB sessions -- Introducers with zero tag -- Padding for SSU2 path response - -## [2.48.0] - 2023-06-12 -### Added -- Allow user/password authentication method for SOCK5 proxy -- Publish reject all congestion cap 'G' if transit is not accepted -- 'critical' log level -- Print b32 on webconsole destination page -- Webconsole button to drop a remote LeaseSet -- limits.zombies param - minimum percentage of successfully created tunnels for routers cleanup -- Recognize real routers if successfully connected or responded to tunnel build request -### Changed -- Bypass slow transport sessions for first hop selection -- Limit AESNI inline asm to x86/x64 -- Create smaller I2NP packets if possible -- Make router unreachable if AEAD tag verification fails in SessionCreated -- Don't include a router to floodfills list until it's confirmed as real -- Drop LeaseSet store request if not floodfill -- Bypass medium congestion('D') routers for client tunnels -- Publish encrypted RouterInfo through tunnels -- Check if s is valid x25519 public key -- Check if socket is open before sending data in SSU2 -### Fixed -- Webconsole empty page if destination is not found -- i2p.streaming.answerPings param -- Reload tunnels -- Address caps for unspecified ipv6 address -- Incomplete HTTP headers in I2P tunnels -- SSU2 socket network exceptions on Windows -- Use of 'server' type tunnel port as inport (#1936) - -## [2.47.0] - 2023-03-11 -### Added -- Congestion caps -- SAM UDP port parameter -- Support domain addresses for yggdrasil reseeds -### Changed -- DHT for floodfills instead plain list -- Process router's messages in separate thread -- Don't publish non-reachable router -- Send and check target destination in first streaming SYN packet -- Reseeds list -### Fixed -- Memory leak in windows network state detection -- Reseed attempts from invalid address - -## [2.46.1] - 2023-02-20 -### Fixed -- Race condition while getting router's peer profile -- Creation of new router.info -- Displaying LeaseSets in the webconsole -- Crash when processing ACK request - -## [2.46.0] - 2023-02-15 -### Added -- Limit number of acked SSU2 packets to 511 -- Localization to Swedish, Portuguese, Turkish, Polish -- Periodically send Datetime block in NTCP2 and SSU2 -- Don't select random port from reserved -- In memory table for peer profiles -- Store if router was unreachable in it's peer profile -- Show IPv6 addresses in square brackets in webconsole -- Check referer when processing Addresshelper -### Changed -- Algorithm for tunnel creation success rate calculation -- Drop incoming NTCP2 and SSU2 connection if published IP doesn't match actual endpoint -- Exclude actually unreachable router from netdb for 2 hours -- Select first hop from high bandwidth peers for client tunnels -- Drop too long or too short LeaseSet -- Delete router from netdb if became invalid after update -- Terminate existing session if clock skew detected -- Close previous UDP socket if open before reopening -- Minimal version for floodfill is 0.9.51 -- Sort transports by endpoints in webconsole -### Fixed -- Deadlock during processing I2NP block with Garlic in ECIES encrypted message to router -- Race condition with encrypted LeaseSets -- HTTP query detection -- Connection attempts to IPs from invalid ranges -- Publish "0.0.0.0" in RouterInfo -- Crash upon receiving PeerTest 7 -- Tunnels for closed SAM session socket -- Missing NTCP2 address in RouterInfo if enabled back - -## [2.45.1] - 2023-01-11 -### Added -- Full Cone NAT status error -### Changed -- Drop duplicated I2NP messages in SSU2 -- Set rejection code 30 if tunnel with id already exists -- Network status is always OK if peer test msg 5 received -### Fixed -- UPnP crash if SSU2 or NTCP2 is disabled -- Crash on termination for some platforms - -## [2.45.0] - 2023-01-03 -### Added -- Test for Symmetric NAT with peer test msgs 6 and 7 -- Webconsole "No Descriptors" router error state -- 1 and 15 seconds bandwidth calculation for i2pcontrol -- Show non-zero send queue size for transports in web console -- Compressible padding for I2P addresses -- Localization to Czech -- Don't accept incoming session from invalid/reserved addresses for NTCP2 and SSU2 -- Limit simultaneous tunnel build requests by 4 per pool -### Changed -- Removed SSU support -- Reduced bandwidth calculation interval from 60 to 15 seconds -- Increased default max transit tunnels number from 2500 to 5000 or 10000 for floodfill -- Transit tunnels limit is doubled if floodfill mode is enabled -- NTCP2 and SSU2 timestamps are rounded to seconds -- Drop RouterInfos and LeaseSets with timestamp from future -- Don't delete unreachable routers if tunnel creation success rate is too low -- Refuse duplicated incoming pending NTCP2 session from same IP -- Don't send SSU2 termination again if termination received block received -- Handle standard network error for SSU2 without throwing an exception -- Don't select overloaded peer for next tunnel -- Remove "X-Requested-With" in HTTP Proxy for non-AJAX requests -### Fixed -- File descriptors leak -- Random crash on AddressBook update -- Crash if incorrect LeaseSet size -- Spamming to log if no descriptors -- ::1 address in RouterInfo -- SSU2 network error handling (especially for Windows) -- Race condition with pending outgoing SSU2 sessions -- RTT self-reduction for long-live streams - -## [2.44.0] - 2022-11-20 -### Added -- SSL connection for server I2P tunnels -- Localization to Italian and Spanish -- SSU2 through SOCKS5 UDP proxy -- Reload tunnels through web console -- SSU2 send immediate ack request flag -- SSU2 send and verify path challenge -- Configurable ssu2.mtu4 and ssu2.mtu6 -### Changed -- SSU2 is enabled and SSU is disabled by default -- Separate network status and error -- Random selection between NTCP2 and SSU2 priority -- Added notbob.i2p to jump services -- Remove DoNotTrack flag from HTTP Request header -- Skip addresshelper page if destination was not changed -- SSU2 allow different ports from RelayReponse and HolePunch -- SSU2 resend PeerTest msg 1 and msg 2 -- SSU2 Send Retry instead SessionCreated if clock skew detected -### Fixed -- Long HTTP headers for HTTP proxy and HTTP server tunnel -- SSU2 resends and resend limits -- Crash at startup if addressbook is disabled -- NTCP2 ipv6 connection through SOCKS5 proxy -- SSU2 SessionRequest with zero token -- SSU2 MTU less than 1280 -- SSU2 port=1 -- Incorrect addresses from network interfaces -- Definitions for Darwin PPC; do not use pthread_setname_np - -## [2.43.0] - 2022-08-22 -### Added -- Complete SSU2 implementation -- Localization to Chinese -- Send RouterInfo update for long live sessions -- Explicit ipv6 ranges of known tunnel brokers for MTU detection -- Always send "Connection: close" and strip out Keep-Alive for server HTTP tunnel -- Show ports for all transports in web console -- Translation of webconsole site title -- Support for Windows ProgramData path when running as service -- Ability to turn off address book -- Handle signals TSTP and CONT to stop and resume network -### Changed -- Case insensitive headers for server HTTP tunnel -- Do not show 'Address registration' line if LeaseSet is encrypted -- SSU2 transports have higher priority than SSU -- Disable ElGamal precalculated table if no SSU -- Deprecate limits.ntcpsoft, limits.ntcphard and limits.ntcpthreads config options -- SSU2 is enabled and SSU is disabled by default for new installations -### Fixed -- Typo with Referer header name in HTTP proxy -- Can't handle garlic message from an exploratory tunnel -- Incorrect encryption key for exploratory lookup reply -- Bound checks issues in LeaseSets code -- MTU detection on Windows -- Crash on stop of active server tunnel -- Send datagram to wrong destination in SAM -- Incorrect static key in RouterInfo if the keys were regenerated -- Duplicated sessions in BOB - -## [2.42.1] - 2022-05-24 -### Fixed -- Incorrect jump link in HTTP Proxy - -## [2.42.0] - 2022-05-22 -### Added -- Preliminary SSU2 implementation -- Tunnel length variance -- Localization to French -- Daily cleanup of obsolete peer profiles -- Ordered jump services list in HTTP proxy -- Win32 service -- Show port for local non-published SSU addresses in web console -### Changed -- Maximum RouterInfo length increased to 3K -- Skip unknown addresses in RouterInfo -- Don't pick own router for peer test -- Reseeds list -- Internal numeric id for families -- Use ipv6 preference only when netinet headers not used -- Close stream if delete requested -- Remove version from title in web console -- Drop MESHNET build option -- Set data path before initialization -- Don't show registration block in web console if token is not provided -### Fixed -- Encrypted LeaseSet for EdDSA signature -- Clients tunnels are not built if clock is not synced on start -- Incorrect processing of i2cp.dontPublishLeaseSet param -- UDP tunnels reload -- Build for LibreSSL 3.5.2 -- Race condition in short tunnel build message -- Race condition in local RouterInfo buffer allocation - -## [2.41.0] - 2022-02-20 -### Added -- Clock syncronization through SSU -- Drop routers older than 6 months on start -- Localization to German -- Don't send streaming ack too frequently -- Select compatible outbound tunnel for I2CP messages -- Restart webconsole's acceptor in case of exception -### Changed -- Use builtin bitswap for endian on windows -- Send SessionCreated before connection close if clock skew -- Try another floodfill for publishing if no compatible tunnels found -- Reduce memory usage for RouterInfo structures -- Avoid duplicated addresses in RouterInfo. Check presence of netId and version -- Use TCP/IP sockets for I2CP on Android instead local sockets -- Return uptime as integer in I2PControl -- Reseed servers list/cerificates -- Webconsole's dark style colors -### Fixed -- Attempt to use Yggdrasil on start on Android -- Attempts to send peer tests to itself -- Severe packets drop in SSU -- Crash on tunnel tests -- Loading addressbook subscriptions from config -- Multiple I2CP session to the same destination -- Build on Apple Silicon - -## [2.40.0] - 2021-11-29 -### Added -- Keep alive parameter for client tunnels -- Support openssl 3.0.0 -- Localization to Armenian -- Show git commit info in version -- Windows menu item for opening datadir -- Reseed if too few floodfills -- Don't publish old and replacing tunnel in LeaseSet -- Webconsole light/dark theme depending on system settings (via CSS) -### Changed -- Set gzip compression to false by default -- Build tunnel through ECIES routers only -- Removed ElGamal support for tunnels -- Moved webconsole resources to separate file -- Pick tunnels with compatible transport with another tunnel of floodfill -- Use common cleanup timer for all SSU sessions -- Reduced memory usage -- Reseed servers list -- i18n code called from ClientContext -### Fixed -- Tunnels reload -- Some typos in log messages -- Cleanup relay requests table -- Server tunnel is not published -- Build on GNU/Hurd. Disable pthread_setname_np -- Crash when incorrect sigtype used with blinding - -## [2.39.0] - 2021-08-23 -### Added -- Short tunnel build messages -- Localization. To: Russian, Ukrainian, Turkmen, Uzbek and Afrikaans -- Custom CSS styles for webconsole -- Avoid slow tunnels with more than 250 ms per hop -- Process DELAY_REQUESTED streaming option -- "certsdir" options for certificates location -- Keep own RouterInfo in NetBb -- Pick ECIES routers only for tunnels on non-x64 -- NTP sync through ipv6 -- Allow ipv6 addresses for UDP server tunnels -### Changed -- Rekey of all routers to ECIES -- Better distribution for random tunnel's peer selection -- Yggdrasil reseed for v0.4, added two more -- Encryption type 0,4 by default for server tunnels -- Handle i2cp.dontPublishLeaseSet param for all destinations -- reg.i2p for subscriptions -- LeaseSet type 3 by default -- Don't allocate payload buffer for every single ECIESx25519 message -- Prefer public ipv6 instead rfc4941 -- Optimal padding for one-time ECIESx25519 message -- Don't send datetime block for one-time ECIESx25519 message with one-time key -- Router with expired introducer is still valid -- Don't disable floodfill if still reachable by ipv6 -- Set minimal version for floodfill to 0.9.38 -- Eliminate extra lookups for sequential fragments on tunnel endpoint -- Consistent path for explicit peers -- Always create new tunnel from exploratory pool -- Don't try to connect to a router not reachable from us -- Mark additional ipv6 addresses/nets as reserved (#1679) -### Fixed -- Zero-hop tunnels -- Crash upon SAM session termination -- Build with boost < 1.55.0 -- Address type for NTCP2 acceptors -- Check of ipv4/ipv6 address -- Request router to send to if not in NetDb -- Count outbound traffic for zero-hop tunnels -- URLdecode domain for registration string generator in webconsole - -## [2.38.0] - 2021-05-17 -### Added -- Publish ipv6 introducers -- Bind ipv6 or yggdrasil NTCP2 acceptor to specified address -- Support .b32.i2p addresses and hostnames for SAM STREAM CREATE -- ipv6 peer tests -- Publish iexp param for introducers -- Show ipv6 network status on the webconsole -- EdDSA signing keys can also be blinded -- Show router version on the webconsole -### Changed -- Rekey of all routers but floodfills to ECIES -- Increased number of precalculated x25519 keys to 15 -- Don't publish LeaseSet without inbound tunnels -- Reseed from compatible address(ipv4 or ipv6) -- Recongnize v4 and v6 SSU addresses without host -- Inbound tunnel gateway must be ipv4 compatible -- Don't select next introducers from existing sessions -- Set X bandwidth for floodfill by default -### Fixed -- Incoming ECIES-x25519 session doesn't send updated LeaseSet -- Unique local address for server tunnels -- Race condition for LeaseSet creation in I2CP -- Relay tag for ipv6 introducer -- Already expired introducers -- Find connected router for first peer in tunnel -- Failed outgoing ECIES-x25519 session's tagset stays forever -- Yggdrasil address disappears if router becomes unreachable through ipv6 -- Ignore SSU address/introducers if port is not specified -- Check identity and signature length for SSU SessionConfirmed - -## [2.37.0] - 2021-03-15 -### Added -- Address registration line for reg.i2p and stats.i2p through the web console -- "4" and "6" caps for addresses without published IP address -- Mesh and Proxy network statuses -- Symmetric NAT network status error -- Bind server tunnel connection to specified address -- lookuplocal BOB extended command -- address4 and address6 parameters to bind outgoing connections to -- Rekey of low-bandwidth routers to ECIES -- Popup notification windows when unable to parse config for Windows -### Changed -- Floodfills with "U" cap are not ignored anymore -- Check transports reachability between tunnel peers and between router and floodfill -- NTCP2 and reseed HTTP proxy support authorization now -- Show actual IP addresses for proxy connections -- Publish and handle SSU addreses without host -- Outbound tunnel endpoint must be ipv4 compatible -- Logging optimization -- Removed Windows service -### Fixed -- Incoming SSU session terminates after 5 seconds -- Outgoing NTCP2 ipv4 session even if ipv4 is disabled -- No incoming Yggdrasil connection if connected through NTCP2 proxy -- Race condition between tunnel build and floodfill requests decryption for ECIES routers -- Numeric bandwidth limitation -- Yggdrasil for Android - -## [2.36.0] - 2021-02-15 -### Added -- Encrypted lookup and publications to ECIES-x25519 floodfiils -- Yggdrasil transports and reseeds -- Dump addressbook in hosts.txt format -- Request RouterInfo through exploratory tunnels if direct connection to fllodfill is not possible -- Threads naming -- Check if public x25519 key is valid -- ECIES-X25519-AEAD-Ratchet for shared local destination -- LeaseSet creation timeout for I2CP session -- Resend RouterInfo after some interval for longer NTCP2 sessions -- Select reachable router of inbound tunnel gateway -- Reseed if no compatible routers in netdb -- Refresh on swipe in Android webconsole -### Changed -- reg.i2p for default addressbook instead inr.i2p -- ECIES-x25519 (crypto type 4) for new routers -- Try to connect to all compatible addresses from peer's RouterInfo -- Replace LeaseSet completely if store type changes -- Try ECIES-X25519-AEAD-Ratchet tag before ElGamal -- Don't detach ECIES-X25519-AEAD-Ratchet session from destination immediately -- Viewport and styles on error in HTTP proxy -- Don't create notification when Windows taskbar restarted -- Cumulative SSU ACK bitfields -- limit tunnel length to 8 hops -- Limit tunnels quantity to 16 -### Fixed -- Handling chunked HTTP response in addressbook -- Missing ECIES-X25519-AEAD-Ratchet tags for multiple streams with the same destination -- Correct NAME for NAMING REPLY in SAM -- SSU crash on termination -- Offline signature length for stream close packet -- Don't send updated LeaseSet through a terminated session -- Decryption of follow-on ECIES-X25519-AEAD-Ratchet NSR messages -- Non-confirmed LeaseSet is resent too late for ECIES-X25519-AEAD-Ratchet session - -## [2.35.0] - 2020-11-30 -### Added -- ECIES-x25519 routers -- Random intro keys for SSU -- Graceful shutdown timer for windows -- Send queue for I2CP messages -- Update DSA router keys to EdDSA -- TCP_QUICKACK for NTCP2 sockets on Linux -### Changed -- Exclude floodfills with DSA signatures and < 0.9.28 -- Random intervals between tunnel tests and manage for tunnel pools -- Don't replace an addressbook record by one with DSA signature -- Publish RouterInfo after update -- Create paired inbound tunnels if no inbound tunnels yet -- Reseed servers list -### Fixed -- Transient signature length, if different from identity -- Terminate I2CP session if destroyed -- RouterInfo publishing confirmation -- Check if ECIES-X25519-AEAD-Ratchet session expired before generating more tags -- Correct block size for delivery type local for ECIES-X25519-AEAD-Ratchet - -## [2.34.0] - 2020-10-27 -### Added -- Ping responses for streaming -- STREAM FORWARD for SAM -- Tunnels through ECIES-x25519 routers -- Single thread for I2CP -- Shared transient destination between proxies -- Database lookups from ECIES destinations with ratchets response -- Handle WebDAV HTTP methods -- Don't try to connect or build tunnels if offline -- Validate IP when trying connect to remote peer -- Handle ICMP responses and WinAPI errors for SSU -### Changed -- Removed NTCP -- Dropped gcc 4.7 support -- Encyption type 0,4 by default for client tunnels -- Stripped out some HTTP header for HTTP server response -- HTTP 1.1 addressbook requests -- Set LeaseSet type to 3 for ratchets if not specified -- Handle SSU v4 and v6 messages in one thread -- Eliminate DH keys thread -### Fixed -- Random crashes on I2CP session disconnect -- Stream through racthets hangs if first SYN was not acked -- Check "Last-Modified" instead "If-Modified-Since" for addressbook reponse -- Trim behind ECIESx25519 tags -- Few bugs with Android main activity -- QT visual and layout issues - -## [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 -### Added -- Read explicit peers in tunnels config -### Fixed -- Generation of tags for detached sessions -- Non-updating LeaseSet1 -- Start when deprecated websocket options present in i2pd.conf - -## [2.32.0] - 2020-05-25 -### Added -- Multiple encryption types for local destinations -- Next key and tagset for ECIES-X25519-AEAD-Ratchet -- NTCP2 through SOCKS proxy -- Throw error message if any port to bind is occupied -- gzip parameter for UDP tunnels -- Show ECIES-X25519-AEAD-Ratchet sessions and tags on the web console -- Simplified implementation of gzip for no compression mode -- Allow ECIES-X25519-AEAD-Ratchet session restart after 2 minutes -- Added logrotate config for rpm package -### Changed -- Select peers for client tunnels among routers >= 0.9.36 -- Check ECIES flag for encrypted lookup reply -- Streaming MTU size 1812 for ECIES-X25519-AEAD-Ratchet -- Don't calculate checksum for Data message send through ECIES-X25519-AEAD-Ratchet -- Catch network connectivity status for Windows -- Stop as soon as no more transit tunnels during graceful shutdown for Android -- RouterInfo gzip compression level depends on size -- Send response to received datagram from ECIES-X25519-AEAD-Ratchet session -- Update webconsole functional -- Increased max transit tunnels limit -- Reseeds list -- Dropped windows support in cmake -### Fixed -- Correct timestamp check for LeaseSet2 -- Encrypted leaseset without authentication -- Change SOCKS proxy connection response for clients without socks5h support (#1336) - -## [2.31.0] - 2020-04-10 -### Added -- NTCP2 through HTTP proxy -- Publish LeaseSet2 for I2CP destinations -- Show status page on main activity for android -- Handle ECIESFlag in DatabaseLookup at floodfill -- C++17 features for eligible compilers -### Changed -- Droped Websockets and Lua support -- Send DeliveryStatusMsg for LeaseSet for ECIES-X25519-AEAD-Ratchet -- Keep sending new session reply until established for ECIES-X25519-AEAD-Ratchet -- Updated SSU log messages -- Reopen SSU socket on exception -- Security hardening headers in web console -- Various web console changes -- Various QT changes -### Fixed -- NTCP2 socket descriptors leak -- Race condition with router's identity in transport sessions -- Not terminated streams remain forever - -## [2.30.0] - 2020-02-25 -### Added -- Single threaded SAM -- Experimental support of ECIES-X25519-AEAD-Ratchet crypto type -### Changed -- Minimal MTU size is 1280 for ipv6 -- Use unordered_map instead map for destination's sessions and tags list -- Use std::shuffle instead std::random_shuffle -- SAM is single threaded by default -- Reseeds list -### Fixed -- Correct termination of streaming destination -- Extra ',' in RouterInfo response in I2PControl -- SAM crash on session termination -- Storage for Android 10 - -## [2.29.0] - 2019-10-21 -### Added -- Client auth flag for b33 address -### Changed -- Remove incoming NTCP2 session from pending list when established -- Handle errors for NTCP2 SessionConfrimed send -### Fixed -- Failure to start on Windows XP -- SAM crash if invalid lookup address -- Possible crash when UPnP enabled on shutdown - -## [2.28.0] - 2019-08-27 -### Added -- RAW datagrams in SAM -- Publishing encrypted LeaseSet2 with DH or PSH authentication -- Ability to disable battery optimization for Android -- Transport Network ID Check -### Changed -- Set and handle published encrypted flag for LeaseSet2 -### Fixed -- ReceiveID changes in the same stream -- "\r\n" command terminator in SAM -- Addressbook lines with signatures - -## [2.27.0] - 2019-07-03 -### Added -- Support of PSK and DH authentication for encrypted LeaseSet2 -### Changed -- Uptime is based on monotonic timer -### Fixed -- BOB status command response -- Correct NTCP2 port if NTCP is disabled -- Flood encrypted LeaseSet2 with store hash - -## [2.26.0] - 2019-06-07 -### Added -- HTTP method "PROPFIND" -- Detection of external ipv6 address through the SSU -- NTCP2 publishing depends on network status -### Changed -- ntcp is disabled by default, ntcp2 is published by default -- Response to BOB's "list" command -- ipv6 address is not longer NTCP's local endpoint's address -- Reseeds list -- HTTP_REFERER stripping in httpproxy (#823) -### Fixed -- Check and handle incorrect BOB input -- Ignore introducers for NTCP or NTCP2 addresses -- RouterInfo check from NTCP2 - -## [2.25.0] - 2019-05-09 -### Added -- Create, publish and handle encrypted LeaseSet2 -- Support of b33 addresses -- RedDSA key blinding -- .b32.i2p addresses in jump links -- ntcp2.addressv6 parameter -### Changed -- Allow HTTP headers without value -- Set data directory from external storage path for Android -- addresshelper support is configurable per tunnel -- gradlew script for android build -### Fixed -- Deletion of expired encrypted LeaseSet2 on floodfills -- ipv6 fallback address -- SSU incoming packets routing - -## [2.24.0] - 2019-03-21 -### Added -- Support of transient keys for LeaseSet2 -- Support of encrypted LeaseSet2 -- Recognize signature type 11 (RedDSA) -- Support websocket connections over HTTP proxy -- Ability to disable full addressbook persist -### Changed -- Don't load peer profiles if non-persistant -- REUSE_ADDR for ipv6 acceptors -- Reset eTags if addressbook can't be loaded -### Fixed -- Build with boost 1.70 -- Filter out unspecified addresses from RouterInfo -- Check floodfill status change -- Correct SAM response for invalid key -- SAM crash on termination for Windows -- Race condition for publishing - -## [2.23.0] - 2019-01-21 -### Added -- Standard LeaseSet2 support -- Ability to adjust timestamps through the NTP -- Ability to disable peer profile persist -- Request permission for android >= 6 -- Initial addressbook to android assets -- Cancel graceful shutdown for android -- Russian translation for android -### Changed -- Chacha20 and Poly1305 implementation -- Eliminate extra copy of NTCP2 send buffers -- Extract content of tunnel.d from assets on android -- Removed name resolvers from transports -- Update reseed certificates -### Fixed -- LeaseSet published content verification -- Exclude invalid LeaseSets from the list on a floodfill -- Build for OpenWrt with openssl 1.1.1 - -## [2.22.0] - 2018-11-09 -### Added -- Multiple tunnel config files from tunnels.d folder -### Changed -- Fetch own RouterInfo upon SessionRequest for NTCP2 -- Faster XOR between AES blocks for non AVX capable CPUs -### Fixed -- Fixed NTCP2 termination send - -## [2.21.1] - 2018-10-22 -### Changed -- cost=13 for unpublished NTCP2 address -### Fixed -- Handle I2NP messages longer than 32K - -## [2.21.0] - 2018-10-04 -### Added -- EdDSA, x25519 and SipHash from openssl 1.1.1 -- NTCP2 ipv6 incoming connections -- Show total number of destination's outgoing tags in the web console -### Changed -- Android build with openssl 1.1.1/boost 1.64 -- Bandwidth classes 'P' and 'X' don't add 'O' anymore -### Fixed -- Update own RouterInfo if no SSU -- Recognize 'P' and 'X' routers as high bandwidth without 'O' -- NTCP address doesn't disappear if NTCP2 enabled -- Android with api 26+ - -## [2.20.0] - 2018-08-23 -### Added -- Full implementation of NTCP2 -- Assets for android -### Changed -- armeabi-v7a and x86 in one apk for android -- NTCP2 is enabled by default -- Show lease's expiration time in readable format in the web console -### Fixed -- Correct names for transports in the web console - -## [2.19.0] - 2018-06-26 -### Added -- ECIES support for RouterInfo -- HTTP outproxy authorization -- AVX/AESNI runtime detection -- Initial implementation of NTCP2 -- I2CP session reconfigure -- I2CP method ClientServicesInfo -- Datagrams to websocks -### Changed -- RouterInfo uses EdDSA signature by default -- Remove stream bans -- Android build system changed to gradle -- Multiple changes in QT GUI -- Dockerfile -### Fixed -- zero tunnelID issue -- tunnels reload -- headers in webconsole -- XSS in webconsole from SAM session name -- build for gcc 8 -- cmake build scripts -- systemd service files -- some netbsd issues - -## [2.18.0] - 2018-01-30 -### Added -- Show tunnel nicknames for I2CP destination in WebUI -- Re-create HTTP and SOCKS proxy by tunnel reload -- Graceful shutdown as soon as no more transit tunnels -### Changed -- Regenerate shared local destination by tunnel reload -- Use transient local destination by default if not specified -- Return correct code if pid file can't be created -- Timing and number of attempts for adressbook requests -- Certificates list -### Fixed -- Malformed addressbook subsctiption request -- Build with boost 1.66 -- Few race conditions for SAM -- Check LeaseSet's signature before update - -## [2.17.0] - 2017-12-04 -### Added -- Reseed through HTTP and SOCKS proxy -- Show status of client services through web console -- Change log level through web connsole -- transient keys for tunnels -- i2p.streaming.initialAckDelay parameter -- CRYPTO_TYPE for SAM destination -- signature and crypto type for newkeys BOB command -### Changed -- Correct publication of ECIES destinations -- Disable RSA signatures completely -### Fixed -- CVE-2017-17066 -- Possible buffer overflow for RSA-4096 -- Shutdown from web console for Windows -- Web console page layout -## [2.16.0] - 2017-11-13 -### Added -- https and "Connect" method for HTTP proxy -- outproxy for HTTP proxy -- initial support of ECIES crypto -- NTCP soft and hard descriptors limits -- Support full timestamps in logs -### Changed -- Faster implementation of GOST R 34.11 hash -- Reject routers with RSA signtures -- Reload config and shudown from Windows GUI -- Update tunnels address(destination) without restart -### Fixed -- BOB crashes if destination is not set -- Correct SAM tunnel name -- QT GUI issues - -## [2.15.0] - 2017-08-17 -### Added -- QT GUI -- Ability to add and remove I2P tunnels without restart -- Ability to disable SOCKS outproxy option -### Changed -- Strip-out Accept-* hedaers in HTTP proxy -- Don't run peer test if nat=false -- Separate output of NTCP and SSU sessions in Transports tab -### Fixed -- Handle lines with comments in hosts.txt file for address book -- Run router with empty netdb for testnet -- Skip expired introducers by iexp - -## [2.14.0] - 2017-06-01 -### Added -- Transit traffic bandwidth limitation -- NTCP connections through HTTP and SOCKS proxies -- Ability to disable address helper for HTTP proxy -### Changed -- Reseed servers list -- Minimal required version is 4.0 for Android -### Fixed -- Ignore comments in addressbook feed - -## [2.13.0] - 2017-04-06 -### Added -- Persist local destination's tags -- GOST signature types 9 and 10 -- Exploratory tunnels configuration -### Changed -- Reseed servers list -- Inactive NTCP sockets get closed faster -- Some EdDSA speed up -### Fixed -- Multiple acceptors for SAM -- Follow on data after STREAM CREATE for SAM -- Memory leaks - -## [2.12.0] - 2017-02-14 -### Added -- Additional HTTP and SOCKS proxy tunnels -- Reseed from ZIP archive -- Some stats in a main window for Windows version -### Changed -- Reseed servers list -- MTU of 1488 for ipv6 -- Android and Mac OS X versions use OpenSSL 1.1 -- New logo for Android -### Fixed -- Multiple memory leaks -- Incomptibility of some EdDSA private keys with Java -- Clock skew for Windows XP -- Occasional crashes with I2PSnark - -## [2.11.0] - 2016-12-18 -### Added -- Websockets support -- Reseed through a floodfill -- Tunnel configuration for HTTP and SOCKS proxy -- Zero-hops tunnels for destinations -- Multiple acceptors for SAM -### Changed -- Reseed servers list -- DHT uses AVX if applicable -- New logo -- LeaseSet lookups -### Fixed -- HTTP Proxy connection reset for Windows -- Crash upon SAM session termination -- Can't connect to a destination for a longer time after restart -- Mass packet loss for UDP tunnels - -## [2.10.2] - 2016-12-04 -### Fixed -- Fixes UPnP discovery bug, producing excessive CPU usage -- Fixes sudden SSU thread stop for Windows. - -## [2.10.1] - 2016-11-07 -### Fixed -- Fixed some performance issues for Windows and Android - -## [2.10.0] - 2016-10-17 -### Added -- Datagram i2p tunnels -- Unique local addresses for server tunnels -- Configurable list of reseed servers and initial addressbook -- Configurable netid -- Initial iOS support - -### Changed -- Reduced file descriptors usage -- Strict reseed checks enabled by default - -## Fixed -- Multiple fixes in I2CP and BOB implementations - -## [2.9.0] - 2016-08-12 -### Changed -- Proxy refactoring & speedup -- Transmission-I2P support -- Graceful shutdown for Windows -- Android without QT -- Reduced number of timers in SSU -- ipv6 peer test support -- Reseed from SU3 file - -## [2.8.0] - 2016-06-20 -### Added -- Basic Android support -- I2CP implementation -- 'doxygen' target - -### Changed -- I2PControl refactoring & fixes (proper jsonrpc responses on errors) -- boost::regex no more needed - -### Fixed -- initscripts: added openrc one, in sysv-ish make I2PD_PORT optional -- properly close NTCP sessions (memleak) - -## [2.7.0] - 2016-05-18 -### Added -- Precomputed El-Gamal/DH tables -- Configurable limit of transit tunnels - -### Changed -- Speed-up of asymmetric crypto for non-x64 platforms -- Refactoring of web-console - -## [2.6.0] - 2016-03-31 -### Added -- Graceful shutdown on SIGINT -- Numeric bandwidth limits (was: by router class) -- Jumpservices in web-console -- Logging to syslog -- Tray icon for windows application - -### Changed -- Logs refactoring -- Improved statistics in web-console - -### Deprecated: -- Renamed main/tunnels config files (will use old, if found, but emits warning) - -## [2.5.1] - 2016-03-10 -### Fixed -- Doesn't create ~/.i2pd dir if missing - -## [2.5.0] - 2016-03-04 -### Added -- IRC server tunnels -- SOCKS outproxy support -- Support for gzipped addressbook updates -- Support for router families - -### Changed -- Shared RTT/RTO between streams -- Filesystem work refactoring - -## [2.4.0] - 2016-02-03 -### Added -- X-I2P-* headers for server http-tunnels -- I2CP options for I2P tunnels -- Show I2P tunnels in webconsole - -### Changed -- Refactoring of cmdline/config parsing - -## [2.3.0] - 2016-01-12 -### Added -- Support for new router bandwidth class codes (P and X) -- I2PControl supports external webui -- Added --pidfile and --notransit parameters -- Ability to specify signature type for i2p tunnel - -### Changed -- Fixed multiple floodfill-related bugs -- New webconsole layout - -## [2.2.0] - 2015-12-22 -### Added -- Ability to connect to router without ip via introducer - -### Changed -- Persist temporary encryption keys for local destinations -- Performance improvements for EdDSA -- New addressbook structure - -## [2.1.0] - 2015-11-12 -### Added -- Implementation of EdDSA - -### Changed -- EdDSA is default signature type for new RouterInfos diff --git a/ClientContext.cpp b/ClientContext.cpp new file mode 100644 index 00000000..b6126fcb --- /dev/null +++ b/ClientContext.cpp @@ -0,0 +1,223 @@ +#include +#include "util.h" +#include "Log.h" +#include "Identity.h" +#include "ClientContext.h" + +namespace i2p +{ +namespace client +{ + ClientContext context; + + ClientContext::ClientContext (): m_SharedLocalDestination (nullptr), + m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_IrcTunnel (nullptr), + m_ServerTunnel (nullptr), m_SamBridge (nullptr), m_BOBCommandChannel (nullptr) + { + } + + ClientContext::~ClientContext () + { + delete m_HttpProxy; + delete m_SocksProxy; + delete m_IrcTunnel; + delete m_ServerTunnel; + delete m_SamBridge; + delete m_BOBCommandChannel; + } + + void ClientContext::Start () + { + if (!m_SharedLocalDestination) + { + m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA + m_Destinations[m_SharedLocalDestination->GetIdentity ().GetIdentHash ()] = m_SharedLocalDestination; + m_SharedLocalDestination->Start (); + } + + m_HttpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446)); + m_HttpProxy->Start(); + LogPrint("HTTP Proxy started"); + m_SocksProxy = new i2p::proxy::SOCKSProxy(i2p::util::config::GetArg("-socksproxyport", 4447)); + m_SocksProxy->Start(); + LogPrint("SOCKS Proxy Started"); + std::string ircDestination = i2p::util::config::GetArg("-ircdest", ""); + if (ircDestination.length () > 0) // ircdest is presented + { + ClientDestination * localDestination = nullptr; + std::string ircKeys = i2p::util::config::GetArg("-irckeys", ""); + if (ircKeys.length () > 0) + localDestination = LoadLocalDestination (ircKeys, false); + m_IrcTunnel = new I2PClientTunnel (ircDestination, i2p::util::config::GetArg("-ircport", 6668), localDestination); + m_IrcTunnel->Start (); + LogPrint("IRC tunnel started"); + } + std::string eepKeys = i2p::util::config::GetArg("-eepkeys", ""); + if (eepKeys.length () > 0) // eepkeys file is presented + { + auto localDestination = LoadLocalDestination (eepKeys, true); + m_ServerTunnel = new I2PServerTunnel (i2p::util::config::GetArg("-eephost", "127.0.0.1"), + i2p::util::config::GetArg("-eepport", 80), localDestination); + m_ServerTunnel->Start (); + LogPrint("Server tunnel started"); + } + int samPort = i2p::util::config::GetArg("-samport", 0); + if (samPort) + { + m_SamBridge = new SAMBridge (samPort); + m_SamBridge->Start (); + LogPrint("SAM bridge started"); + } + int bobPort = i2p::util::config::GetArg("-bobport", 0); + if (bobPort) + { + m_BOBCommandChannel = new BOBCommandChannel (bobPort); + m_BOBCommandChannel->Start (); + LogPrint("BOB command channel started"); + } + m_AddressBook.StartSubscriptions (); + } + + void ClientContext::Stop () + { + m_AddressBook.StopSubscriptions (); + m_HttpProxy->Stop(); + delete m_HttpProxy; + m_HttpProxy = nullptr; + LogPrint("HTTP Proxy stoped"); + m_SocksProxy->Stop(); + delete m_SocksProxy; + m_SocksProxy = nullptr; + LogPrint("SOCKS Proxy stoped"); + if (m_IrcTunnel) + { + m_IrcTunnel->Stop (); + delete m_IrcTunnel; + m_IrcTunnel = nullptr; + LogPrint("IRC tunnel stoped"); + } + if (m_ServerTunnel) + { + m_ServerTunnel->Stop (); + delete m_ServerTunnel; + m_ServerTunnel = nullptr; + LogPrint("Server tunnel stoped"); + } + if (m_SamBridge) + { + m_SamBridge->Stop (); + delete m_SamBridge; + m_SamBridge = nullptr; + LogPrint("SAM brdige stoped"); + } + if (m_BOBCommandChannel) + { + m_BOBCommandChannel->Stop (); + delete m_BOBCommandChannel; + m_BOBCommandChannel = nullptr; + LogPrint("BOB command channel stoped"); + } + + for (auto it: m_Destinations) + { + it.second->Stop (); + delete it.second; + } + m_Destinations.clear (); + m_SharedLocalDestination = 0; // deleted through m_Destination + } + + ClientDestination * ClientContext::LoadLocalDestination (const std::string& filename, bool isPublic) + { + i2p::data::PrivateKeys keys; + std::string fullPath = i2p::util::filesystem::GetFullPath (filename); + std::ifstream s(fullPath.c_str (), std::ifstream::binary); + if (s.is_open ()) + { + s.seekg (0, std::ios::end); + size_t len = s.tellg(); + s.seekg (0, std::ios::beg); + uint8_t * buf = new uint8_t[len]; + s.read ((char *)buf, len); + keys.FromBuffer (buf, len); + delete[] buf; + LogPrint ("Local address ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " loaded"); + } + else + { + LogPrint ("Can't open file ", fullPath, " Creating new one"); + keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_DSA_SHA1); + std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); + size_t len = keys.GetFullLen (); + uint8_t * buf = new uint8_t[len]; + len = keys.ToBuffer (buf, len); + f.write ((char *)buf, len); + delete[] buf; + + LogPrint ("New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " created"); + } + + auto localDestination = new ClientDestination (keys, isPublic); + std::unique_lock l(m_DestinationsMutex); + m_Destinations[localDestination->GetIdentHash ()] = localDestination; + localDestination->Start (); + return localDestination; + } + + ClientDestination * ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType, + const std::map * params) + { + i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); + auto localDestination = new ClientDestination (keys, isPublic, params); + std::unique_lock l(m_DestinationsMutex); + m_Destinations[localDestination->GetIdentHash ()] = localDestination; + localDestination->Start (); + return localDestination; + } + + void ClientContext::DeleteLocalDestination (ClientDestination * destination) + { + if (!destination) return; + auto it = m_Destinations.find (destination->GetIdentHash ()); + if (it != m_Destinations.end ()) + { + auto d = it->second; + { + std::unique_lock l(m_DestinationsMutex); + m_Destinations.erase (it); + } + d->Stop (); + delete d; + } + } + + ClientDestination * ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, + const std::map * params) + { + auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ()); + if (it != m_Destinations.end ()) + { + LogPrint ("Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " exists"); + if (!it->second->IsRunning ()) + { + it->second->Start (); + return it->second; + } + return nullptr; + } + auto localDestination = new ClientDestination (keys, isPublic, params); + std::unique_lock l(m_DestinationsMutex); + m_Destinations[keys.GetPublic ().GetIdentHash ()] = localDestination; + localDestination->Start (); + return localDestination; + } + + ClientDestination * ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const + { + auto it = m_Destinations.find (destination); + if (it != m_Destinations.end ()) + return it->second; + return nullptr; + } +} +} diff --git a/ClientContext.h b/ClientContext.h new file mode 100644 index 00000000..4ee81549 --- /dev/null +++ b/ClientContext.h @@ -0,0 +1,62 @@ +#ifndef CLIENT_CONTEXT_H__ +#define CLIENT_CONTEXT_H__ + +#include +#include "Destination.h" +#include "HTTPProxy.h" +#include "SOCKS.h" +#include "I2PTunnel.h" +#include "SAM.h" +#include "BOB.h" +#include "AddressBook.h" + +namespace i2p +{ +namespace client +{ + class ClientContext + { + public: + + ClientContext (); + ~ClientContext (); + + void Start (); + void Stop (); + + ClientDestination * GetSharedLocalDestination () const { return m_SharedLocalDestination; }; + ClientDestination * CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1, + const std::map * params = nullptr); // transient + ClientDestination * CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, + const std::map * params = nullptr); + void DeleteLocalDestination (ClientDestination * destination); + ClientDestination * FindLocalDestination (const i2p::data::IdentHash& destination) const; + ClientDestination * LoadLocalDestination (const std::string& filename, bool isPublic); + + AddressBook& GetAddressBook () { return m_AddressBook; }; + + private: + + std::mutex m_DestinationsMutex; + std::map m_Destinations; + ClientDestination * m_SharedLocalDestination; + + AddressBook m_AddressBook; + + i2p::proxy::HTTPProxy * m_HttpProxy; + i2p::proxy::SOCKSProxy * m_SocksProxy; + I2PClientTunnel * m_IrcTunnel; + I2PServerTunnel * m_ServerTunnel; + SAMBridge * m_SamBridge; + BOBCommandChannel * m_BOBCommandChannel; + + public: + // for HTTP + const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; + }; + + extern ClientContext context; +} +} + +#endif diff --git a/CryptoConst.cpp b/CryptoConst.cpp new file mode 100644 index 00000000..a8868988 --- /dev/null +++ b/CryptoConst.cpp @@ -0,0 +1,73 @@ +#include +#include "CryptoConst.h" + +namespace i2p +{ +namespace crypto +{ + const uint8_t elgp_[256]= + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + const uint8_t dsap_[128]= + { + 0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c, + 0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15, + 0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23, + 0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c, + 0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8, + 0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c, + 0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11, + 0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93 + }; + + const uint8_t dsaq_[20]= + { + 0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d, + 0x68, 0x40, 0x46, 0xb7 + }; + + const uint8_t dsag_[128]= + { + 0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0, + 0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81, + 0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2, + 0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52, + 0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc, + 0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a, + 0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c, + 0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82 + }; + + const CryptoConstants& GetCryptoConstants () + { + static CryptoConstants cryptoConstants = + { + {elgp_, 256}, // elgp + {2}, // elgg + {dsap_, 128}, // dsap + {dsaq_, 20}, // dsaq + {dsag_, 128} // dsag + }; + return cryptoConstants; + } + +} +} + diff --git a/CryptoConst.h b/CryptoConst.h new file mode 100644 index 00000000..ba48a35d --- /dev/null +++ b/CryptoConst.h @@ -0,0 +1,38 @@ +#ifndef CRYPTO_CONST_H__ +#define CRYPTO_CONST_H__ + +#include + +namespace i2p +{ +namespace crypto +{ + struct CryptoConstants + { + // DH/ElGamal + const CryptoPP::Integer elgp; + const CryptoPP::Integer elgg; + + // DSA + const CryptoPP::Integer dsap; + const CryptoPP::Integer dsaq; + const CryptoPP::Integer dsag; + }; + + const CryptoConstants& GetCryptoConstants (); + + // DH/ElGamal + #define elgp GetCryptoConstants ().elgp + #define elgg GetCryptoConstants ().elgg + + // DSA + #define dsap GetCryptoConstants ().dsap + #define dsaq GetCryptoConstants ().dsaq + #define dsag GetCryptoConstants ().dsag + + // RSA + const int rsae = 65537; +} +} + +#endif diff --git a/Daemon.cpp b/Daemon.cpp new file mode 100644 index 00000000..cbfaf31f --- /dev/null +++ b/Daemon.cpp @@ -0,0 +1,149 @@ +#include + +#include "Daemon.h" + +#include "Log.h" +#include "base64.h" +#include "version.h" +#include "Transports.h" +#include "NTCPSession.h" +#include "RouterInfo.h" +#include "RouterContext.h" +#include "Tunnel.h" +#include "NetDb.h" +#include "Garlic.h" +#include "util.h" +#include "Streaming.h" +#include "Destination.h" +#include "HTTPServer.h" +#include "ClientContext.h" + +#ifdef USE_UPNP +#include "UPnP.h" +#endif + + +namespace i2p +{ + namespace util + { + class Daemon_Singleton::Daemon_Singleton_Private + { + public: + Daemon_Singleton_Private() : httpServer(nullptr) + {}; + ~Daemon_Singleton_Private() + { + delete httpServer; + }; + + i2p::util::HTTPServer *httpServer; + }; + + Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {}; + Daemon_Singleton::~Daemon_Singleton() { + delete &d; + }; + + bool Daemon_Singleton::IsService () const + { +#ifndef _WIN32 + return i2p::util::config::GetArg("-service", 0); +#else + return false; +#endif + } + + bool Daemon_Singleton::init(int argc, char* argv[]) + { + i2p::util::config::OptionParser(argc, argv); + i2p::context.Init (); + + LogPrint("\n\n\n\ni2pd starting\n"); + LogPrint("Version ", VERSION); + LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string()); + i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs); + + isDaemon = i2p::util::config::GetArg("-daemon", 0); + isLogging = i2p::util::config::GetArg("-log", 1); + + int port = i2p::util::config::GetArg("-port", 0); + if (port) + i2p::context.UpdatePort (port); + const char * host = i2p::util::config::GetCharArg("-host", ""); + if (host && host[0]) + i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host)); + + if (i2p::util::config::GetArg("-unreachable", 0)) + i2p::context.SetUnreachable (); + + i2p::context.SetSupportsV6 (i2p::util::config::GetArg("-v6", 0)); + + LogPrint("CMD parameters:"); + for (int i = 0; i < argc; ++i) + LogPrint(i, " ", argv[i]); + + return true; + } + + bool Daemon_Singleton::start() + { + // initialize log + if (isLogging) + { + if (isDaemon) + { + std::string logfile_path = IsService () ? "/var/log" : i2p::util::filesystem::GetDataDir().string(); +#ifndef _WIN32 + logfile_path.append("/i2pd.log"); +#else + logfile_path.append("\\i2pd.log"); +#endif + StartLog (logfile_path); + } + else + StartLog (""); // write to stdout + } + + d.httpServer = new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070)); + d.httpServer->Start(); + LogPrint("HTTP Server started"); + i2p::data::netdb.Start(); + LogPrint("NetDB started"); + i2p::transport::transports.Start(); + LogPrint("Transports started"); + i2p::tunnel::tunnels.Start(); + LogPrint("Tunnels started"); + i2p::client::context.Start (); + LogPrint("Client started"); +#ifdef USE_UPNP + i2p::UPnP::upnpc.Start(); + LogPrint("UPnP module loaded"); +#endif + return true; + } + + bool Daemon_Singleton::stop() + { + LogPrint("Shutdown started."); + i2p::client::context.Stop(); + LogPrint("Client stoped"); + i2p::tunnel::tunnels.Stop(); + LogPrint("Tunnels stoped"); + i2p::transport::transports.Stop(); + LogPrint("Transports stoped"); + i2p::data::netdb.Stop(); + LogPrint("NetDB stoped"); + d.httpServer->Stop(); + LogPrint("HTTP Server stoped"); +#ifdef USE_UPNP + i2p::UPnP::upnpc.Stop(); +#endif + StopLog (); + + delete d.httpServer; d.httpServer = nullptr; + + return true; + } + } +} diff --git a/Daemon.h b/Daemon.h new file mode 100644 index 00000000..bfbdcaa0 --- /dev/null +++ b/Daemon.h @@ -0,0 +1,71 @@ +#pragma once +#include + +#ifdef _WIN32 +#define Daemon i2p::util::DaemonWin32::Instance() +#else +#define Daemon i2p::util::DaemonLinux::Instance() +#endif + +namespace i2p +{ + namespace util + { + class Daemon_Singleton_Private; + class Daemon_Singleton + { + public: + virtual bool init(int argc, char* argv[]); + virtual bool start(); + virtual bool stop(); + + int isLogging; + int isDaemon; + + int running; + + protected: + Daemon_Singleton(); + virtual ~Daemon_Singleton(); + + bool IsService () const; + + // d-pointer for httpServer, httpProxy, etc. + class Daemon_Singleton_Private; + Daemon_Singleton_Private &d; + }; + +#ifdef _WIN32 + class DaemonWin32 : public Daemon_Singleton + { + public: + static DaemonWin32& Instance() + { + static DaemonWin32 instance; + return instance; + } + + virtual bool init(int argc, char* argv[]); + virtual bool start(); + virtual bool stop(); + }; +#else + class DaemonLinux : public Daemon_Singleton + { + public: + static DaemonLinux& Instance() + { + static DaemonLinux instance; + return instance; + } + + virtual bool start(); + virtual bool stop(); + private: + std::string pidfile; + int pidFilehandle; + + }; +#endif + } +} diff --git a/DaemonLinux.cpp b/DaemonLinux.cpp new file mode 100644 index 00000000..48a05031 --- /dev/null +++ b/DaemonLinux.cpp @@ -0,0 +1,118 @@ +#include "Daemon.h" + +#ifndef _WIN32 + +#include +#include +#include +#include +#include + +#include "Log.h" +#include "util.h" + + +void handle_signal(int sig) +{ + switch (sig) + { + case SIGHUP: + if (i2p::util::config::GetArg("daemon", 0) == 1) + { + static bool first=true; + if (first) + { + first=false; + return; + } + } + LogPrint("Reloading config."); + i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs); + break; + case SIGABRT: + case SIGTERM: + case SIGINT: + Daemon.running = 0; // Exit loop + break; + } +} + + +namespace i2p +{ + namespace util + { + bool DaemonLinux::start() + { + if (isDaemon == 1) + { + pid_t pid; + pid = fork(); + if (pid > 0) // parent + ::exit (EXIT_SUCCESS); + + if (pid < 0) // error + return false; + + // child + umask(0); + int sid = setsid(); + if (sid < 0) + { + LogPrint("Error, could not create process group."); + return false; + } + chdir(i2p::util::filesystem::GetDataDir().string().c_str()); + + // close stdin/stdout/stderr descriptors + ::close (0); + ::open ("/dev/null", O_RDWR); + ::close (1); + ::open ("/dev/null", O_RDWR); + ::close (2); + ::open ("/dev/null", O_RDWR); + } + + // Pidfile + pidfile = IsService () ? "/var/run" : i2p::util::filesystem::GetDataDir().string(); + pidfile.append("/i2pd.pid"); + pidFilehandle = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600); + if (pidFilehandle == -1) + { + LogPrint("Error, could not create pid file (", pidfile, ")\nIs an instance already running?"); + return false; + } + if (lockf(pidFilehandle, F_TLOCK, 0) == -1) + { + LogPrint("Error, could not lock pid file (", pidfile, ")\nIs an instance already running?"); + return false; + } + char pid[10]; + sprintf(pid, "%d\n", getpid()); + write(pidFilehandle, pid, strlen(pid)); + + // Signal handler + struct sigaction sa; + sa.sa_handler = handle_signal; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGHUP, &sa, 0); + sigaction(SIGABRT, &sa, 0); + sigaction(SIGTERM, &sa, 0); + sigaction(SIGINT, &sa, 0); + + return Daemon_Singleton::start(); + } + + bool DaemonLinux::stop() + { + close(pidFilehandle); + unlink(pidfile.c_str()); + + return Daemon_Singleton::stop(); + } + + } +} + +#endif diff --git a/DaemonWin32.cpp b/DaemonWin32.cpp new file mode 100644 index 00000000..96b28076 --- /dev/null +++ b/DaemonWin32.cpp @@ -0,0 +1,83 @@ +#include "Daemon.h" +#include "util.h" +#include "Log.h" + +#ifdef _WIN32 + +#include "./Win32/Win32Service.h" + +namespace i2p +{ + namespace util + { + bool DaemonWin32::init(int argc, char* argv[]) + { + setlocale(LC_CTYPE, ""); + SetConsoleCP(1251); + SetConsoleOutputCP(1251); + setlocale(LC_ALL, "Russian"); + + if (!Daemon_Singleton::init(argc, argv)) return false; + if (I2PService::isService()) + isDaemon = 1; + else + isDaemon = 0; + + std::string serviceControl = i2p::util::config::GetArg("-service", "none"); + if (serviceControl == "install") + { + InstallService( + SERVICE_NAME, // Name of service + SERVICE_DISPLAY_NAME, // Name to display + SERVICE_START_TYPE, // Service start type + SERVICE_DEPENDENCIES, // Dependencies + SERVICE_ACCOUNT, // Service running account + SERVICE_PASSWORD // Password of the account + ); + exit(0); + } + else if (serviceControl == "remove") + { + UninstallService(SERVICE_NAME); + exit(0); + } + else if (serviceControl != "none") + { + printf(" --service=install to install the service.\n"); + printf(" --service=remove to remove the service.\n"); + } + + if (isDaemon == 1) + { + LogPrint("Service session"); + I2PService service(SERVICE_NAME); + if (!I2PService::Run(service)) + { + LogPrint("Service failed to run w/err 0x%08lx\n", GetLastError()); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + else + LogPrint("User session"); + + return true; + } + bool DaemonWin32::start() + { + setlocale(LC_CTYPE, ""); + SetConsoleCP(1251); + SetConsoleOutputCP(1251); + setlocale(LC_ALL, "Russian"); + + return Daemon_Singleton::start(); + } + + bool DaemonWin32::stop() + { + return Daemon_Singleton::stop(); + } + } +} + +#endif \ No newline at end of file diff --git a/Datagram.cpp b/Datagram.cpp new file mode 100644 index 00000000..410b9740 --- /dev/null +++ b/Datagram.cpp @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include "Log.h" +#include "TunnelBase.h" +#include "RouterContext.h" +#include "Destination.h" +#include "Datagram.h" + +namespace i2p +{ +namespace datagram +{ + DatagramDestination::DatagramDestination (i2p::client::ClientDestination& owner): + m_Owner (owner), m_Receiver (nullptr) + { + } + + void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::LeaseSet& remote) + { + uint8_t buf[MAX_DATAGRAM_SIZE]; + auto identityLen = m_Owner.GetIdentity ().ToBuffer (buf, MAX_DATAGRAM_SIZE); + uint8_t * signature = buf + identityLen; + auto signatureLen = m_Owner.GetIdentity ().GetSignatureLen (); + uint8_t * buf1 = signature + signatureLen; + size_t headerLen = identityLen + signatureLen; + + memcpy (buf1, payload, len); + if (m_Owner.GetIdentity ().GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) + { + uint8_t hash[32]; + CryptoPP::SHA256().CalculateDigest (hash, buf1, len); + m_Owner.Sign (hash, 32, signature); + } + else + m_Owner.Sign (buf1, len, signature); + + m_Owner.GetService ().post (std::bind (&DatagramDestination::SendMsg, this, + CreateDataMessage (buf, len + headerLen), remote)); + } + + void DatagramDestination::SendMsg (I2NPMessage * msg, const i2p::data::LeaseSet& remote) + { + auto outboundTunnel = m_Owner.GetTunnelPool ()->GetNextOutboundTunnel (); + auto leases = remote.GetNonExpiredLeases (); + if (!leases.empty () && outboundTunnel) + { + std::vector msgs; + uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1); + auto garlic = m_Owner.WrapMessage (remote, msg, true); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeTunnel, + leases[i].tunnelGateway, leases[i].tunnelID, + garlic + }); + outboundTunnel->SendTunnelDataMsg (msgs); + } + else + { + if (outboundTunnel) + LogPrint (eLogWarning, "Failed to send datagram. All leases expired"); + else + LogPrint (eLogWarning, "Failed to send datagram. No outbound tunnels"); + DeleteI2NPMessage (msg); + } + } + + void DatagramDestination::HandleDatagram (const uint8_t * buf, size_t len) + { + i2p::data::IdentityEx identity; + size_t identityLen = identity.FromBuffer (buf, len); + const uint8_t * signature = buf + identityLen; + size_t headerLen = identityLen + identity.GetSignatureLen (); + + bool verified = false; + if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) + verified = CryptoPP::SHA256().VerifyDigest (signature, buf + headerLen, len - headerLen); + else + verified = identity.Verify (buf + headerLen, len - headerLen, signature); + + if (verified) + { + if (m_Receiver != nullptr) + m_Receiver (identity, buf + headerLen, len -headerLen); + else + LogPrint (eLogWarning, "Receiver for datagram is not set"); + } + else + LogPrint (eLogWarning, "Datagram signature verification failed"); + } + + void DatagramDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len) + { + // unzip it + CryptoPP::Gunzip decompressor; + decompressor.Put (buf, len); + decompressor.MessageEnd(); + uint8_t uncompressed[MAX_DATAGRAM_SIZE]; + auto uncompressedLen = decompressor.MaxRetrievable (); + if (uncompressedLen <= MAX_DATAGRAM_SIZE) + { + decompressor.Get (uncompressed, uncompressedLen); + HandleDatagram (uncompressed, uncompressedLen); + } + else + LogPrint ("Received datagram size ", uncompressedLen, " exceeds max size"); + + } + + I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len) + { + I2NPMessage * msg = NewI2NPMessage (); + CryptoPP::Gzip compressor; // default level + compressor.Put (payload, len); + compressor.MessageEnd(); + int size = compressor.MaxRetrievable (); + uint8_t * buf = msg->GetPayload (); + htobe32buf (buf, size); // length + buf += 4; + compressor.Get (buf, size); + memset (buf + 4, 0, 4); // source and destination are zeroes + buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol + msg->len += size + 4; + FillI2NPMessageHeader (msg, eI2NPData); + return msg; + } +} +} + diff --git a/Datagram.h b/Datagram.h new file mode 100644 index 00000000..b3eaa9e5 --- /dev/null +++ b/Datagram.h @@ -0,0 +1,49 @@ +#ifndef DATAGRAM_H__ +#define DATAGRAM_H__ + +#include +#include +#include "Identity.h" +#include "LeaseSet.h" +#include "I2NPProtocol.h" + +namespace i2p +{ +namespace client +{ + class ClientDestination; +} +namespace datagram +{ + const size_t MAX_DATAGRAM_SIZE = 32768; + class DatagramDestination + { + typedef std::function Receiver; + + public: + + DatagramDestination (i2p::client::ClientDestination& owner); + ~DatagramDestination () {}; + + void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::LeaseSet& remote); + void HandleDataMessagePayload (const uint8_t * buf, size_t len); + + void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; }; + void ResetReceiver () { m_Receiver = nullptr; }; + + private: + + I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len); + void SendMsg (I2NPMessage * msg, const i2p::data::LeaseSet& remote); + void HandleDatagram (const uint8_t * buf, size_t len); + + private: + + i2p::client::ClientDestination& m_Owner; + Receiver m_Receiver; + }; +} +} + +#endif + diff --git a/Destination.cpp b/Destination.cpp new file mode 100644 index 00000000..154cfee0 --- /dev/null +++ b/Destination.cpp @@ -0,0 +1,566 @@ +#include +#include +#include +#include "Log.h" +#include "util.h" +#include "ElGamal.h" +#include "Timestamp.h" +#include "NetDb.h" +#include "ClientContext.h" +#include "Destination.h" + +namespace i2p +{ +namespace client +{ + ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, + const std::map * params): + m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), + m_Keys (keys), m_LeaseSet (nullptr), m_IsPublic (isPublic), m_PublishReplyToken (0), + m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service) + { + i2p::crypto::GenerateElGamalKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey); + int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH; + int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH; + if (params) + { + auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH); + if (it != params->end ()) + { + int len = boost::lexical_cast(it->second); + if (len > 0) + { + inboundTunnelLen = len; + LogPrint (eLogInfo, "Inbound tunnel length set to ", len); + } + } + it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH); + if (it != params->end ()) + { + int len = boost::lexical_cast(it->second); + if (len > 0) + { + outboundTunnelLen = len; + LogPrint (eLogInfo, "Outbound tunnel length set to ", len); + } + } + } + m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen); + if (m_IsPublic) + LogPrint (eLogInfo, "Local address ", i2p::client::context.GetAddressBook ().ToAddress(GetIdentHash()), " created"); + m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO: + } + + ClientDestination::~ClientDestination () + { + if (m_IsRunning) + Stop (); + for (auto it: m_LeaseSetRequests) + delete it.second; + for (auto it: m_RemoteLeaseSets) + delete it.second; + if (m_Pool) + i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool); + if (m_StreamingDestination) + delete m_StreamingDestination; + if (m_DatagramDestination) + delete m_DatagramDestination; + } + + void ClientDestination::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint ("Destination: ", ex.what ()); + } + } + } + + void ClientDestination::Start () + { + if (!m_IsRunning) + { + m_IsRunning = true; + m_Pool->SetLocalDestination (this); + m_Pool->SetActive (true); + m_Thread = new std::thread (std::bind (&ClientDestination::Run, this)); + m_StreamingDestination->Start (); + } + } + + void ClientDestination::Stop () + { + if (m_IsRunning) + { + m_IsRunning = false; + m_StreamingDestination->Stop (); + if (m_DatagramDestination) + { + auto d = m_DatagramDestination; + m_DatagramDestination = nullptr; + delete d; + } + if (m_Pool) + { + m_Pool->SetLocalDestination (nullptr); + i2p::tunnel::tunnels.StopTunnelPool (m_Pool); + } + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = 0; + } + } + } + + const i2p::data::LeaseSet * ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident) + { + auto it = m_RemoteLeaseSets.find (ident); + if (it != m_RemoteLeaseSets.end ()) + { + if (it->second->HasNonExpiredLeases ()) + return it->second; + else + { + LogPrint ("All leases of remote LeaseSet expired. Request it"); + RequestDestination (ident); + } + } + else + { + auto ls = i2p::data::netdb.FindLeaseSet (ident); + if (ls) + { + ls = new i2p::data::LeaseSet (*ls); + m_RemoteLeaseSets[ident] = ls; + return ls; + } + } + return nullptr; + } + + const i2p::data::LeaseSet * ClientDestination::GetLeaseSet () + { + if (!m_Pool) return nullptr; + if (!m_LeaseSet) + UpdateLeaseSet (); + return m_LeaseSet; + } + + void ClientDestination::UpdateLeaseSet () + { + auto newLeaseSet = new i2p::data::LeaseSet (*m_Pool); + if (!m_LeaseSet) + m_LeaseSet = newLeaseSet; + else + { + // TODO: implement it better + *m_LeaseSet = *newLeaseSet; + delete newLeaseSet; + } + } + + bool ClientDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) + { + struct + { + uint8_t k[32], t[32]; + } data; + memcpy (data.k, key, 32); + memcpy (data.t, tag, 32); + m_Service.post ([this,data](void) + { + this->AddSessionKey (data.k, data.t); + }); + return true; + } + + void ClientDestination::ProcessGarlicMessage (I2NPMessage * msg) + { + m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, this, msg)); + } + + void ClientDestination::ProcessDeliveryStatusMessage (I2NPMessage * msg) + { + m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, this, msg)); + } + + void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from) + { + uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET]; + switch (typeID) + { + case eI2NPData: + HandleDataMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); + break; + case eI2NPDatabaseStore: + HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); + break; + case eI2NPDatabaseSearchReply: + HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET)); + break; + default: + i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); + } + } + + void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len) + { + uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); + size_t offset = DATABASE_STORE_HEADER_SIZE; + if (replyToken) // TODO: + offset += 36; + if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet + { + LogPrint (eLogDebug, "Remote LeaseSet"); + auto it = m_RemoteLeaseSets.find (buf + DATABASE_STORE_KEY_OFFSET); + if (it != m_RemoteLeaseSets.end ()) + { + it->second->Update (buf + offset, len - offset); + LogPrint (eLogDebug, "Remote LeaseSet updated"); + } + else + { + LogPrint (eLogDebug, "New remote LeaseSet added"); + m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = new i2p::data::LeaseSet (buf + offset, len - offset); + } + } + else + LogPrint (eLogError, "Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ". Dropped"); + + auto it1 = m_LeaseSetRequests.find (buf + DATABASE_STORE_KEY_OFFSET); + if (it1 != m_LeaseSetRequests.end ()) + { + it1->second->requestTimeoutTimer.cancel (); + if (it1->second->requestComplete) it1->second->requestComplete (true); + delete it1->second; + m_LeaseSetRequests.erase (it1); + } + } + + void ClientDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len) + { + i2p::data::IdentHash key (buf); + int num = buf[32]; // num + LogPrint ("DatabaseSearchReply for ", key.ToBase64 (), " num=", num); + auto it = m_LeaseSetRequests.find (key); + if (it != m_LeaseSetRequests.end ()) + { + LeaseSetRequest * request = it->second; + bool found = false; + if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST) + { + for (int i = 0; i < num; i++) + { + i2p::data::IdentHash peerHash (buf + 33 + i*32); + auto floodfill = i2p::data::netdb.FindRouter (peerHash); + if (floodfill) + { + LogPrint (eLogInfo, "Requesting ", key.ToBase64 (), " at ", peerHash.ToBase64 ()); + if (SendLeaseSetRequest (key, floodfill, request)) + found = true; + } + else + { + LogPrint (eLogInfo, "Found new floodfill. Request it"); + i2p::data::netdb.RequestDestination (peerHash); + } + } + if (!found) + LogPrint (eLogError, "Suggested floodfills are not presented in netDb"); + } + else + LogPrint (eLogInfo, key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST," floodfills"); + if (!found) + { + if (request->requestComplete) request->requestComplete (false); + delete request; + m_LeaseSetRequests.erase (key); + } + } + else + LogPrint ("Request for ", key.ToBase64 (), " not found"); + } + + void ClientDestination::HandleDeliveryStatusMessage (I2NPMessage * msg) + { + uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET); + if (msgID == m_PublishReplyToken) + { + LogPrint (eLogDebug, "Publishing confirmed"); + m_ExcludedFloodfills.clear (); + m_PublishReplyToken = 0; + i2p::DeleteI2NPMessage (msg); + } + else + i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg); + } + + void ClientDestination::SetLeaseSetUpdated () + { + i2p::garlic::GarlicDestination::SetLeaseSetUpdated (); + UpdateLeaseSet (); + if (m_IsPublic) + Publish (); + } + + void ClientDestination::Publish () + { + if (!m_LeaseSet || !m_Pool) + { + LogPrint (eLogError, "Can't publish non-existing LeaseSet"); + return; + } + if (m_PublishReplyToken) + { + LogPrint (eLogInfo, "Publishing is pending"); + return; + } + auto outbound = m_Pool->GetNextOutboundTunnel (); + if (!outbound) + { + LogPrint ("Can't publish LeaseSet. No outbound tunnels"); + return; + } + std::set excluded; + auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills); + if (!floodfill) + { + LogPrint ("Can't publish LeaseSet. No more floodfills found"); + m_ExcludedFloodfills.clear (); + return; + } + m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); + LogPrint (eLogDebug, "Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); + m_PublishReplyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); + auto msg = WrapMessage (*floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken)); + m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); + m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer, + this, std::placeholders::_1)); + outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg); + } + + void ClientDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + if (m_PublishReplyToken) + { + LogPrint (eLogWarning, "Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, "seconds. Try again"); + m_PublishReplyToken = 0; + Publish (); + } + } + } + + void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len) + { + uint32_t length = bufbe32toh (buf); + buf += 4; + // we assume I2CP payload + switch (buf[9]) + { + case PROTOCOL_TYPE_STREAMING: + // streaming protocol + if (m_StreamingDestination) + m_StreamingDestination->HandleDataMessagePayload (buf, length); + else + LogPrint ("Missing streaming destination"); + break; + case PROTOCOL_TYPE_DATAGRAM: + // datagram protocol + if (m_DatagramDestination) + m_DatagramDestination->HandleDataMessagePayload (buf, length); + else + LogPrint ("Missing streaming destination"); + break; + default: + LogPrint ("Data: unexpected protocol ", buf[9]); + } + } + + void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) { + assert(streamRequestComplete); + i2p::data::IdentHash identHash; + if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash)) + CreateStream (streamRequestComplete, identHash, port); + else + { + LogPrint (eLogWarning, "Remote destination ", dest, " not found"); + streamRequestComplete (nullptr); + } + } + + void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port) { + assert(streamRequestComplete); + const i2p::data::LeaseSet * leaseSet = FindLeaseSet (dest); + if (leaseSet) + streamRequestComplete(CreateStream (*leaseSet, port)); + else + { + RequestDestination (dest, + [this, streamRequestComplete, dest, port](bool success) + { + if (!success) + streamRequestComplete (nullptr); + else + { + const i2p::data::LeaseSet * leaseSet = FindLeaseSet (dest); + if (leaseSet) + streamRequestComplete(CreateStream (*leaseSet, port)); + else + streamRequestComplete (nullptr); + } + }); + } + } + + std::shared_ptr ClientDestination::CreateStream (const i2p::data::LeaseSet& remote, int port) + { + if (m_StreamingDestination) + return m_StreamingDestination->CreateNewOutgoingStream (remote, port); + else + return nullptr; + } + + void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor) + { + if (m_StreamingDestination) + m_StreamingDestination->SetAcceptor (acceptor); + } + + void ClientDestination::StopAcceptingStreams () + { + if (m_StreamingDestination) + m_StreamingDestination->ResetAcceptor (); + } + + bool ClientDestination::IsAcceptingStreams () const + { + if (m_StreamingDestination) + return m_StreamingDestination->IsAcceptorSet (); + return false; + } + + i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination () + { + if (!m_DatagramDestination) + m_DatagramDestination = new i2p::datagram::DatagramDestination (*this); + return m_DatagramDestination; + } + + bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete) + { + if (!m_Pool || !IsReady ()) + { + if (requestComplete) requestComplete (false); + return false; + } + m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, this, dest, requestComplete)); + return true; + } + + void ClientDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete) + { + std::set excluded; + auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded); + if (floodfill) + { + LeaseSetRequest * request = new LeaseSetRequest (m_Service); + request->requestComplete = requestComplete; + m_LeaseSetRequests[dest] = request; + if (!SendLeaseSetRequest (dest, floodfill, request)) + { + // request failed + if (request->requestComplete) request->requestComplete (false); + delete request; + m_LeaseSetRequests.erase (dest); + } + } + else + LogPrint (eLogError, "No floodfills found"); + } + + bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest, + std::shared_ptr nextFloodfill, LeaseSetRequest * request) + { + auto replyTunnel = m_Pool->GetNextInboundTunnel (); + if (!replyTunnel) LogPrint (eLogError, "No inbound tunnels found"); + + auto outboundTunnel = m_Pool->GetNextOutboundTunnel (); + if (!outboundTunnel) LogPrint (eLogError, "No outbound tunnels found"); + + if (replyTunnel && outboundTunnel) + { + request->excluded.insert (nextFloodfill->GetIdentHash ()); + request->requestTime = i2p::util::GetSecondsSinceEpoch (); + request->requestTimeoutTimer.cancel (); + + CryptoPP::AutoSeededRandomPool rnd; + uint8_t replyKey[32], replyTag[32]; + rnd.GenerateBlock (replyKey, 32); // random session key + rnd.GenerateBlock (replyTag, 32); // random session tag + AddSessionKey (replyKey, replyTag); + + I2NPMessage * msg = WrapMessage (*nextFloodfill, + CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, + replyTunnel, replyKey, replyTag)); + outboundTunnel->SendTunnelDataMsg ( + { + i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + nextFloodfill->GetIdentHash (), 0, msg + } + }); + request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT)); + request->requestTimeoutTimer.async_wait (std::bind (&ClientDestination::HandleRequestTimoutTimer, + this, std::placeholders::_1, dest)); + } + else + return false; + return true; + } + + void ClientDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto it = m_LeaseSetRequests.find (dest); + if (it != m_LeaseSetRequests.end ()) + { + bool done = false; + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT) + { + auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded); + if (floodfill) + SendLeaseSetRequest (dest, floodfill, it->second); + else + done = true; + } + else + { + LogPrint (eLogInfo, dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds"); + done = true; + } + + if (done) + { + if (it->second->requestComplete) it->second->requestComplete (false); + delete it->second; + m_LeaseSetRequests.erase (it); + } + } + } + } +} +} diff --git a/Destination.h b/Destination.h new file mode 100644 index 00000000..b4d910ad --- /dev/null +++ b/Destination.h @@ -0,0 +1,143 @@ +#ifndef DESTINATION_H__ +#define DESTINATION_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "Identity.h" +#include "TunnelPool.h" +#include "CryptoConst.h" +#include "LeaseSet.h" +#include "Garlic.h" +#include "NetDb.h" +#include "Streaming.h" +#include "Datagram.h" + +namespace i2p +{ +namespace client +{ + const uint8_t PROTOCOL_TYPE_STREAMING = 6; + const uint8_t PROTOCOL_TYPE_DATAGRAM = 17; + const uint8_t PROTOCOL_TYPE_RAW = 18; + const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds + const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds + const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds + const int MAX_NUM_FLOODFILLS_PER_REQUEST = 7; + + // I2CP + const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length"; + const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3; + const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length"; + const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3; + const int STREAM_REQUEST_TIMEOUT = 60; //in seconds + + class ClientDestination: public i2p::garlic::GarlicDestination + { + typedef std::function RequestComplete; + struct LeaseSetRequest + { + LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {}; + std::set excluded; + uint64_t requestTime; + boost::asio::deadline_timer requestTimeoutTimer; + RequestComplete requestComplete; + }; + + typedef std::function stream)> StreamRequestComplete; + + public: + + ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params = nullptr); + ~ClientDestination (); + + virtual void Start (); + virtual void Stop (); + bool IsRunning () const { return m_IsRunning; }; + boost::asio::io_service& GetService () { return m_Service; }; + i2p::tunnel::TunnelPool * GetTunnelPool () { return m_Pool; }; + bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases (); }; + const i2p::data::LeaseSet * FindLeaseSet (const i2p::data::IdentHash& ident); + bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr); + + // streaming + i2p::stream::StreamingDestination * GetStreamingDestination () const { return m_StreamingDestination; }; + void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0); + void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0); + std::shared_ptr CreateStream (const i2p::data::LeaseSet& remote, int port = 0); + void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor); + void StopAcceptingStreams (); + bool IsAcceptingStreams () const; + + // datagram + i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; }; + i2p::datagram::DatagramDestination * CreateDatagramDestination (); + + // implements LocalDestination + const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; + const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; }; + const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; }; + + // implements GarlicDestination + const i2p::data::LeaseSet * GetLeaseSet (); + void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from); + + // override GarlicDestination + bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); + void ProcessGarlicMessage (I2NPMessage * msg); + void ProcessDeliveryStatusMessage (I2NPMessage * msg); + void SetLeaseSetUpdated (); + + // I2CP + void HandleDataMessage (const uint8_t * buf, size_t len); + + private: + + void Run (); + void UpdateLeaseSet (); + void Publish (); + void HandlePublishConfirmationTimer (const boost::system::error_code& ecode); + void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len); + void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len); + void HandleDeliveryStatusMessage (I2NPMessage * msg); + + void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete); + bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr nextFloodfill, LeaseSetRequest * request); + void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest); + + private: + + volatile bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::io_service::work m_Work; + i2p::data::PrivateKeys m_Keys; + uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256]; + std::map m_RemoteLeaseSets; + std::map m_LeaseSetRequests; + + i2p::tunnel::TunnelPool * m_Pool; + i2p::data::LeaseSet * m_LeaseSet; + bool m_IsPublic; + uint32_t m_PublishReplyToken; + std::set m_ExcludedFloodfills; // for publishing + + i2p::stream::StreamingDestination * m_StreamingDestination; + i2p::datagram::DatagramDestination * m_DatagramDestination; + + boost::asio::deadline_timer m_PublishConfirmationTimer; + + public: + + // for HTTP only + int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); }; + }; +} +} + +#endif diff --git a/ElGamal.h b/ElGamal.h new file mode 100644 index 00000000..e69c5993 --- /dev/null +++ b/ElGamal.h @@ -0,0 +1,88 @@ +#ifndef EL_GAMAL_H__ +#define EL_GAMAL_H__ + +#include +#include +#include +#include +#include +#include "CryptoConst.h" +#include "Log.h" + +namespace i2p +{ +namespace crypto +{ + + class ElGamalEncryption + { + public: + + ElGamalEncryption (const uint8_t * key): + y (key, 256), k (rnd, CryptoPP::Integer::One(), elgp-1), + a (a_exp_b_mod_c (elgg, k, elgp)), b1 (a_exp_b_mod_c (y, k, elgp)) + { + } + + void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false) + { + // calculate b = b1*m mod p + uint8_t m[255]; + m[0] = 0xFF; + memcpy (m+33, data, len); + CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222); + CryptoPP::Integer b (a_times_b_mod_c (b1, CryptoPP::Integer (m, 255), elgp)); + + // copy a and b + if (zeroPadding) + { + encrypted[0] = 0; + a.Encode (encrypted + 1, 256); + encrypted[257] = 0; + b.Encode (encrypted + 258, 256); + } + else + { + a.Encode (encrypted, 256); + b.Encode (encrypted + 256, 256); + } + } + + private: + + CryptoPP::AutoSeededRandomPool rnd; + CryptoPP::Integer y, k, a, b1; + }; + + inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, + uint8_t * data, bool zeroPadding = false) + { + CryptoPP::Integer x(key, 256), a(zeroPadding? encrypted +1 : encrypted, 256), + b(zeroPadding? encrypted + 258 :encrypted + 256, 256); + uint8_t m[255], hash[32]; + a_times_b_mod_c (b, a_exp_b_mod_c (a, elgp - x - 1, elgp), elgp).Encode (m, 255); + CryptoPP::SHA256().CalculateDigest(hash, m+33, 222); + for (int i = 0; i < 32; i++) + if (hash[i] != m[i+1]) + { + LogPrint ("ElGamal decrypt hash doesn't match"); + return false; + } + memcpy (data, m + 33, 222); + return true; + } + + inline void GenerateElGamalKeyPair (CryptoPP::RandomNumberGenerator& rnd, uint8_t * priv, uint8_t * pub) + { +#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER) + rnd.GenerateBlock (priv, 256); + a_exp_b_mod_c (elgg, CryptoPP::Integer (priv, 256), elgp).Encode (pub, 256); +#else + CryptoPP::DH dh (elgp, elgg); + dh.GenerateKeyPair(rnd, priv, pub); +#endif + } +} +} + +#endif diff --git a/Garlic.cpp b/Garlic.cpp new file mode 100644 index 00000000..ff773978 --- /dev/null +++ b/Garlic.cpp @@ -0,0 +1,560 @@ +#include +#include "I2PEndian.h" +#include +#include +#include "RouterContext.h" +#include "I2NPProtocol.h" +#include "Tunnel.h" +#include "TunnelPool.h" +#include "Timestamp.h" +#include "Destination.h" +#include "Garlic.h" + +namespace i2p +{ +namespace garlic +{ + GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner, + const i2p::data::RoutingDestination * destination, int numTags): + m_Owner (owner), m_Destination (destination), m_NumTags (numTags), + m_LeaseSetUpdated (numTags > 0) + { + // create new session tags and session key + m_Rnd.GenerateBlock (m_SessionKey, 32); + m_Encryption.SetKey (m_SessionKey); + } + + GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag): + m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdated (false) + { + memcpy (m_SessionKey, sessionKey, 32); + m_Encryption.SetKey (m_SessionKey); + m_SessionTags.push_back (sessionTag); + m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch (); + } + + GarlicRoutingSession::~GarlicRoutingSession () + { + for (auto it: m_UnconfirmedTagsMsgs) + delete it.second; + m_UnconfirmedTagsMsgs.clear (); + } + + GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags () + { + auto tags = new UnconfirmedTags (m_NumTags); + tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch (); + for (int i = 0; i < m_NumTags; i++) + { + m_Rnd.GenerateBlock (tags->sessionTags[i], 32); + tags->sessionTags[i].creationTime = tags->tagsCreationTime; + } + return tags; + } + + void GarlicRoutingSession::TagsConfirmed (uint32_t msgID) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + auto it = m_UnconfirmedTagsMsgs.find (msgID); + if (it != m_UnconfirmedTagsMsgs.end ()) + { + UnconfirmedTags * tags = it->second; + if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + { + for (int i = 0; i < tags->numTags; i++) + m_SessionTags.push_back (tags->sessionTags[i]); + } + m_UnconfirmedTagsMsgs.erase (it); + delete tags; + } + // delete expired unconfirmed tags + for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();) + { + if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + { + delete it->second; + it = m_UnconfirmedTagsMsgs.erase (it); + } + else + it++; + } + } + + I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg) + { + I2NPMessage * m = NewI2NPMessage (); + m->Align (12); // in order to get buf aligned to 16 (12 + 4) + size_t len = 0; + uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length + + // find non-expired tag + bool tagFound = false; + SessionTag tag; + if (m_NumTags > 0) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + while (!m_SessionTags.empty ()) + { + if (ts < m_SessionTags.front ().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT) + { + tag = m_SessionTags.front (); + m_SessionTags.pop_front (); // use same tag only once + tagFound = true; + break; + } + else + m_SessionTags.pop_front (); // remove expired tag + } + } + // create message + if (!tagFound) // new session + { + LogPrint ("No garlic tags available. Use ElGamal"); + if (!m_Destination) + { + LogPrint ("Can't use ElGamal for unknown destination"); + return nullptr; + } + // create ElGamal block + ElGamalBlock elGamal; + memcpy (elGamal.sessionKey, m_SessionKey, 32); + m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV + uint8_t iv[32]; // IV is first 16 bytes + CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); + m_Destination->GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true); + m_Encryption.SetIV (iv); + buf += 514; + len += 514; + } + else // existing session + { + // session tag + memcpy (buf, tag, 32); + uint8_t iv[32]; // IV is first 16 bytes + CryptoPP::SHA256().CalculateDigest(iv, tag, 32); + m_Encryption.SetIV (iv); + buf += 32; + len += 32; + } + // AES block + len += CreateAESBlock (buf, msg); + htobe32buf (m->GetPayload (), len); + m->len += len + 4; + FillI2NPMessageHeader (m, eI2NPGarlic); + if (msg) + DeleteI2NPMessage (msg); + return m; + } + + size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, const I2NPMessage * msg) + { + size_t blockSize = 0; + bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags/2); + UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr; + htobuf16 (buf, newTags ? htobe16 (newTags->numTags) : 0); // tag count + blockSize += 2; + if (newTags) // session tags recreated + { + for (int i = 0; i < newTags->numTags; i++) + { + memcpy (buf + blockSize, newTags->sessionTags[i], 32); // tags + blockSize += 32; + } + } + uint32_t * payloadSize = (uint32_t *)(buf + blockSize); + blockSize += 4; + uint8_t * payloadHash = buf + blockSize; + blockSize += 32; + buf[blockSize] = 0; // flag + blockSize++; + size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags); + htobe32buf (payloadSize, len); + CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len); + blockSize += len; + size_t rem = blockSize % 16; + if (rem) + blockSize += (16-rem); //padding + m_Encryption.Encrypt(buf, blockSize, buf); + return blockSize; + } + + size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, const I2NPMessage * msg, UnconfirmedTags * newTags) + { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec + uint32_t msgID = m_Rnd.GenerateWord32 (); + size_t size = 0; + uint8_t * numCloves = payload + size; + *numCloves = 0; + size++; + + if (m_Owner) + { + if (newTags) // new session + { + // clove is DeliveryStatus + size += CreateDeliveryStatusClove (payload + size, msgID); + if (size > 0) // successive? + { + (*numCloves)++; + m_UnconfirmedTagsMsgs[msgID] = newTags; + m_Owner->DeliveryStatusSent (this, msgID); + } + else + LogPrint ("DeliveryStatus clove was not created"); + } + if (m_LeaseSetUpdated) + { + m_LeaseSetUpdated = false; + // clove if our leaseSet must be attached + auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ()); + size += CreateGarlicClove (payload + size, leaseSet, false); + DeleteI2NPMessage (leaseSet); + (*numCloves)++; + } + } + if (msg) // clove message ifself if presented + { + size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false); + (*numCloves)++; + } + + memset (payload + size, 0, 3); // certificate of message + size += 3; + htobe32buf (payload + size, msgID); // MessageID + size += 4; + htobe64buf (payload + size, ts); // Expiration of message + size += 8; + return size; + } + + size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, const I2NPMessage * msg, bool isDestination) + { + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec + size_t size = 0; + if (isDestination && m_Destination) + { + buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination + size++; + memcpy (buf + size, m_Destination->GetIdentHash (), 32); + size += 32; + } + else + { + buf[size] = 0;// delivery instructions flag local + size++; + } + + memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); + size += msg->GetLength (); + htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID + size += 4; + htobe64buf (buf + size, ts); // Expiration of clove + size += 8; + memset (buf + size, 0, 3); // certificate of clove + size += 3; + return size; + } + + size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID) + { + size_t size = 0; + if (m_Owner) + { + auto leases = m_Owner->GetLeaseSet ()->GetNonExpiredLeases (); + if (!leases.empty ()) + { + buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel + size++; + uint32_t i = m_Rnd.GenerateWord32 (0, leases.size () - 1); + // hash and tunnelID sequence is reversed for Garlic + memcpy (buf + size, leases[i].tunnelGateway, 32); // To Hash + size += 32; + htobe32buf (buf + size, leases[i].tunnelID); // tunnelID + size += 4; + // create msg + I2NPMessage * msg = CreateDeliveryStatusMsg (msgID); + if (m_Owner) + { + //encrypt + uint8_t key[32], tag[32]; + m_Rnd.GenerateBlock (key, 32); // random session key + m_Rnd.GenerateBlock (tag, 32); // random session tag + m_Owner->SubmitSessionKey (key, tag); + GarlicRoutingSession garlic (key, tag); + msg = garlic.WrapSingleMessage (msg); + } + memcpy (buf + size, msg->GetBuffer (), msg->GetLength ()); + size += msg->GetLength (); + DeleteI2NPMessage (msg); + // fill clove + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec + htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID + size += 4; + htobe64buf (buf + size, ts); // Expiration of clove + size += 8; + memset (buf + size, 0, 3); // certificate of clove + size += 3; + } + else + LogPrint ("All tunnels of local LeaseSet expired"); + } + else + LogPrint ("Missing local LeaseSet"); + + return size; + } + + GarlicDestination::~GarlicDestination () + { + for (auto it: m_Sessions) + delete it.second; + m_Sessions.clear (); + } + + void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag) + { + if (key) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + auto decryption = std::make_shared(); + decryption->SetKey (key); + m_Tags[SessionTag(tag, ts)] = decryption; + } + } + + bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag) + { + AddSessionKey (key, tag); + return true; + } + + void GarlicDestination::HandleGarlicMessage (I2NPMessage * msg) + { + uint8_t * buf = msg->GetPayload (); + uint32_t length = bufbe32toh (buf); + buf += 4; // length + auto it = m_Tags.find (SessionTag(buf)); + if (it != m_Tags.end ()) + { + // tag found. Use AES + uint8_t iv[32]; // IV is first 16 bytes + CryptoPP::SHA256().CalculateDigest(iv, buf, 32); + it->second->SetIV (iv); + it->second->Decrypt (buf + 32, length - 32, buf + 32); + HandleAESBlock (buf + 32, length - 32, it->second, msg->from); + m_Tags.erase (it); // tag might be used only once + } + else + { + // tag not found. Use ElGamal + ElGamalBlock elGamal; + if (i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true)) + { + auto decryption = std::make_shared(); + decryption->SetKey (elGamal.sessionKey); + uint8_t iv[32]; // IV is first 16 bytes + CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); + decryption->SetIV (iv); + decryption->Decrypt(buf + 514, length - 514, buf + 514); + HandleAESBlock (buf + 514, length - 514, decryption, msg->from); + } + else + LogPrint ("Failed to decrypt garlic"); + } + DeleteI2NPMessage (msg); + + // cleanup expired tags + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + if (ts > m_LastTagsCleanupTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) + { + if (m_LastTagsCleanupTime) + { + int numExpiredTags = 0; + for (auto it = m_Tags.begin (); it != m_Tags.end ();) + { + if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) + { + numExpiredTags++; + it = m_Tags.erase (it); + } + else + it++; + } + LogPrint (numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ()); + } + m_LastTagsCleanupTime = ts; + } + } + + void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, + i2p::tunnel::InboundTunnel * from) + { + uint16_t tagCount = bufbe16toh (buf); + buf += 2; len -= 2; + if (tagCount > 0) + { + if (tagCount*32 > len) + { + LogPrint (eLogError, "Tag count ", tagCount, " exceeds length ", len); + return ; + } + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + for (int i = 0; i < tagCount; i++) + m_Tags[SessionTag(buf + i*32, ts)] = decryption; + } + buf += tagCount*32; + len -= tagCount*32; + uint32_t payloadSize = bufbe32toh (buf); + if (payloadSize > len) + { + LogPrint (eLogError, "Unexpected payload size ", payloadSize); + return; + } + buf += 4; + uint8_t * payloadHash = buf; + buf += 32;// payload hash. + if (*buf) // session key? + buf += 32; // new session key + buf++; // flag + + // payload + if (!CryptoPP::SHA256().VerifyDigest (payloadHash, buf, payloadSize)) // payload hash doesn't match + { + LogPrint ("Wrong payload hash"); + return; + } + HandleGarlicPayload (buf, payloadSize, from); + } + + void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from) + { + int numCloves = buf[0]; + LogPrint (numCloves," cloves"); + buf++; + for (int i = 0; i < numCloves; i++) + { + // delivery instructions + uint8_t flag = buf[0]; + buf++; // flag + if (flag & 0x80) // encrypted? + { + // TODO: implement + LogPrint ("Clove encrypted"); + buf += 32; + } + GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03); + switch (deliveryType) + { + case eGarlicDeliveryTypeLocal: + LogPrint ("Garlic type local"); + HandleI2NPMessage (buf, len, from); + break; + case eGarlicDeliveryTypeDestination: + LogPrint ("Garlic type destination"); + buf += 32; // destination. check it later or for multiple destinations + HandleI2NPMessage (buf, len, from); + break; + case eGarlicDeliveryTypeTunnel: + { + LogPrint ("Garlic type tunnel"); + // gwHash and gwTunnel sequence is reverted + uint8_t * gwHash = buf; + buf += 32; + uint32_t gwTunnel = bufbe32toh (buf); + buf += 4; + i2p::tunnel::OutboundTunnel * tunnel = nullptr; + if (from && from->GetTunnelPool ()) + tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel (); + if (tunnel) // we have send it through an outbound tunnel + { + I2NPMessage * msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from); + tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg); + } + else + LogPrint ("No outbound tunnels available for garlic clove"); + break; + } + case eGarlicDeliveryTypeRouter: + LogPrint ("Garlic type router not supported"); + buf += 32; + break; + default: + LogPrint ("Unknow garlic delivery type ", (int)deliveryType); + } + buf += GetI2NPMessageLength (buf); // I2NP + buf += 4; // CloveID + buf += 8; // Date + buf += 3; // Certificate + } + } + + I2NPMessage * GarlicDestination::WrapMessage (const i2p::data::RoutingDestination& destination, + I2NPMessage * msg, bool attachLeaseSet) + { + if (attachLeaseSet) // we should maintain this session + { + auto session = GetRoutingSession (destination, 32); // 32 tags by default + return session->WrapSingleMessage (msg); + } + else // one time session + { + GarlicRoutingSession session (this, &destination, 0); // don't use tag if no LeaseSet + return session.WrapSingleMessage (msg); + } + } + + GarlicRoutingSession * GarlicDestination::GetRoutingSession ( + const i2p::data::RoutingDestination& destination, int numTags) + { + auto it = m_Sessions.find (destination.GetIdentHash ()); + GarlicRoutingSession * session = nullptr; + if (it != m_Sessions.end ()) + session = it->second; + if (!session) + { + session = new GarlicRoutingSession (this, &destination, numTags); + std::unique_lock l(m_SessionsMutex); + m_Sessions[destination.GetIdentHash ()] = session; + } + return session; + } + + void GarlicDestination::DeliveryStatusSent (GarlicRoutingSession * session, uint32_t msgID) + { + m_CreatedSessions[msgID] = session; + } + + void GarlicDestination::HandleDeliveryStatusMessage (I2NPMessage * msg) + { + uint32_t msgID = bufbe32toh (msg->GetPayload ()); + { + auto it = m_CreatedSessions.find (msgID); + if (it != m_CreatedSessions.end ()) + { + it->second->TagsConfirmed (msgID); + m_CreatedSessions.erase (it); + LogPrint ("Garlic message ", msgID, " acknowledged"); + } + } + DeleteI2NPMessage (msg); + } + + void GarlicDestination::SetLeaseSetUpdated () + { + std::unique_lock l(m_SessionsMutex); + for (auto it: m_Sessions) + it.second->SetLeaseSetUpdated (); + } + + void GarlicDestination::ProcessGarlicMessage (I2NPMessage * msg) + { + HandleGarlicMessage (msg); + } + + void GarlicDestination::ProcessDeliveryStatusMessage (I2NPMessage * msg) + { + HandleDeliveryStatusMessage (msg); + } + +} +} diff --git a/Garlic.h b/Garlic.h new file mode 100644 index 00000000..88d47280 --- /dev/null +++ b/Garlic.h @@ -0,0 +1,148 @@ +#ifndef GARLIC_H__ +#define GARLIC_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "aes.h" +#include "I2NPProtocol.h" +#include "LeaseSet.h" +#include "Queue.h" +#include "Identity.h" + +namespace i2p +{ +namespace garlic +{ + + enum GarlicDeliveryType + { + eGarlicDeliveryTypeLocal = 0, + eGarlicDeliveryTypeDestination = 1, + eGarlicDeliveryTypeRouter = 2, + eGarlicDeliveryTypeTunnel = 3 + }; + +#pragma pack(1) + struct ElGamalBlock + { + uint8_t sessionKey[32]; + uint8_t preIV[32]; + uint8_t padding[158]; + }; +#pragma pack() + + const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes + const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes + + struct SessionTag: public i2p::data::Tag<32> + { + SessionTag (const uint8_t * buf, uint32_t ts = 0): Tag<32>(buf), creationTime (ts) {}; + SessionTag () = default; + SessionTag (const SessionTag& ) = default; + SessionTag& operator= (const SessionTag& ) = default; +#ifndef _WIN32 + SessionTag (SessionTag&& ) = default; + SessionTag& operator= (SessionTag&& ) = default; +#endif + uint32_t creationTime; // seconds since epoch + }; + + class GarlicDestination; + class GarlicRoutingSession + { + struct UnconfirmedTags + { + UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; }; + ~UnconfirmedTags () { delete[] sessionTags; }; + int numTags; + SessionTag * sessionTags; + uint32_t tagsCreationTime; + }; + + public: + + GarlicRoutingSession (GarlicDestination * owner, const i2p::data::RoutingDestination * destination, int numTags); + GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption + ~GarlicRoutingSession (); + I2NPMessage * WrapSingleMessage (I2NPMessage * msg); + void TagsConfirmed (uint32_t msgID); + + void SetLeaseSetUpdated () { m_LeaseSetUpdated = true; }; + + private: + + size_t CreateAESBlock (uint8_t * buf, const I2NPMessage * msg); + size_t CreateGarlicPayload (uint8_t * payload, const I2NPMessage * msg, UnconfirmedTags * newTags); + size_t CreateGarlicClove (uint8_t * buf, const I2NPMessage * msg, bool isDestination); + size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID); + + UnconfirmedTags * GenerateSessionTags (); + + private: + + GarlicDestination * m_Owner; + const i2p::data::RoutingDestination * m_Destination; + i2p::crypto::AESKey m_SessionKey; + std::list m_SessionTags; + int m_NumTags; + std::map m_UnconfirmedTagsMsgs; + bool m_LeaseSetUpdated; + + i2p::crypto::CBCEncryption m_Encryption; + CryptoPP::AutoSeededRandomPool m_Rnd; + }; + + class GarlicDestination: public i2p::data::LocalDestination + { + public: + + GarlicDestination (): m_LastTagsCleanupTime (0) {}; + ~GarlicDestination (); + + GarlicRoutingSession * GetRoutingSession (const i2p::data::RoutingDestination& destination, int numTags); + I2NPMessage * WrapMessage (const i2p::data::RoutingDestination& destination, + I2NPMessage * msg, bool attachLeaseSet = false); + + void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag + virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread + void DeliveryStatusSent (GarlicRoutingSession * session, uint32_t msgID); + + virtual void ProcessGarlicMessage (I2NPMessage * msg); + virtual void ProcessDeliveryStatusMessage (I2NPMessage * msg); + virtual void SetLeaseSetUpdated (); + + virtual const i2p::data::LeaseSet * GetLeaseSet () = 0; // TODO + virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from) = 0; + + protected: + + void HandleGarlicMessage (I2NPMessage * msg); + void HandleDeliveryStatusMessage (I2NPMessage * msg); + + private: + + void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr decryption, + i2p::tunnel::InboundTunnel * from); + void HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from); + + private: + + // outgoing sessions + std::mutex m_SessionsMutex; + std::map m_Sessions; + // incoming + std::map> m_Tags; + uint32_t m_LastTagsCleanupTime; + // DeliveryStatus + std::map m_CreatedSessions; // msgID -> session + }; +} +} + +#endif diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp new file mode 100644 index 00000000..ae644b6a --- /dev/null +++ b/HTTPProxy.cpp @@ -0,0 +1,87 @@ +#include +#include + +#include "ClientContext.h" +#include "HTTPProxy.h" + +namespace i2p +{ +namespace proxy +{ + void HTTPProxyConnection::parseHeaders(const std::string& h, std::vector
& hm) { + std::string str (h); + std::string::size_type idx; + std::string t; + int i = 0; + while( (idx=str.find ("\r\n")) != std::string::npos) { + t=str.substr (0,idx); + str.erase (0,idx+2); + if (t == "") + break; + idx=t.find(": "); + if (idx == std::string::npos) + { + std::cout << "Bad header line: " << t << std::endl; + break; + } + LogPrint ("Name: ", t.substr (0,idx), " Value: ", t.substr (idx+2)); + hm[i].name = t.substr (0,idx); + hm[i].value = t.substr (idx+2); + i++; + } + } + + void HTTPProxyConnection::ExtractRequest(request& r) + { + std::string requestString = m_Buffer; + int idx=requestString.find(" "); + std::string method = requestString.substr(0,idx); + requestString = requestString.substr(idx+1); + idx=requestString.find(" "); + std::string requestUrl = requestString.substr(0,idx); + LogPrint("method is: ", method, "\nRequest is: ", requestUrl); + std::string server=""; + std::string port="80"; + boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)"); + boost::smatch m; + std::string path; + if(boost::regex_search(requestUrl, m, rHTTP, boost::match_extra)) { + server=m[1].str(); + if(m[2].str() != "") { + port=m[3].str(); + } + path=m[4].str(); + } + LogPrint("server is: ",server, " port is: ", port, "\n path is: ",path); + r.uri = path; + r.method = method; + r.host = server; + r.port = boost::lexical_cast(port); + } + + + void HTTPProxyConnection::RunRequest() + { + request r; + ExtractRequest(r); + parseHeaders(m_Buffer, r.headers); + size_t addressHelperPos = r.uri.find ("i2paddresshelper"); + if (addressHelperPos != std::string::npos) + { + // jump service + size_t addressPos = r.uri.find ("=", addressHelperPos); + if (addressPos != std::string::npos) + { + LogPrint ("Jump service for ", r.host, " found. Inserting to address book"); + auto base64 = r.uri.substr (addressPos + 1); + i2p::client::context.GetAddressBook ().InsertAddress (r.host, base64); + } + } + + LogPrint("Requesting ", r.host, ":", r.port, " with path ", r.uri, " and method ", r.method); + SendToAddress (r.host, r.port, m_Buffer, m_BufferLen); + } + +} +} + diff --git a/HTTPProxy.h b/HTTPProxy.h new file mode 100644 index 00000000..c9ccc31c --- /dev/null +++ b/HTTPProxy.h @@ -0,0 +1,42 @@ +#ifndef HTTP_PROXY_H__ +#define HTTP_PROXY_H__ + +#include +#include +#include +#include + +#include "HTTPServer.h" + +namespace i2p +{ +namespace proxy +{ + class HTTPProxyConnection : public i2p::util::HTTPConnection + { + public: + HTTPProxyConnection (boost::asio::ip::tcp::socket * socket): HTTPConnection(socket) { }; + + protected: + void RunRequest(); + void parseHeaders(const std::string& h, std::vector
& hm); + void ExtractRequest(request& r); + }; + + class HTTPProxy : public i2p::util::HTTPServer + { + public: + HTTPProxy (int port): HTTPServer(port) {}; + + private: + void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket) + { + new HTTPProxyConnection(m_NewSocket); + } + }; +} +} + +#endif + + diff --git a/HTTPServer.cpp b/HTTPServer.cpp new file mode 100644 index 00000000..9da6f206 --- /dev/null +++ b/HTTPServer.cpp @@ -0,0 +1,1009 @@ +#include +#include +#include "base64.h" +#include "Log.h" +#include "Tunnel.h" +#include "TransitTunnel.h" +#include "Transports.h" +#include "NetDb.h" +#include "I2PEndian.h" +#include "Streaming.h" +#include "Destination.h" +#include "RouterContext.h" +#include "ClientContext.h" +#include "HTTPServer.h" + +// For image and info +#include "version.h" + +namespace i2p +{ +namespace util +{ + + const std::string HTTPConnection::itoopieImage = + "\"ICToopie"; + + const std::string HTTPConnection::itoopieFavicon = + "data:image/vnd.microsoft.icon;base64," + "AAABAAEAQEAAAAEAIAAoQgAAFgAAACgAAABAAAAAgAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAABsAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgQAAAIMAAACEAAAAg" + "wAAAIAAAACCAAAASQAAACUAAAAmAAAAOwAAAFIAAABgAAAAVAAAACcAAAAVAAAADQAAAAcAAAADAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjAAAA/wAAAP4AAAD+AAAA/gAAAP4AAAD+A" + "AAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/" + "AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAACbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAP8AA" + "AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4A" + "AAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/wAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAKQAAAAAAAAAAAAAAAADbAAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP8AAAD/AAAA/gAA" + "AP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAIA/gAAAPwAAAD/AAAA/gAAAP4AAAD+AAAA/gA" + "AAP4AAAD+AAAA/wAAAFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAAAAAHAAAAP8AAAD+AAAA/gAAAPwACwD9ABEA" + "/QASAPgAFgD4ABUA+AATAP0ADQD8AAIA/AAAAP8AAAD+AAAA/gAAAP8ADQD7ACMA/QAlAP8AJwD/ACI" + "A/QATAPwAAAD8AAAA/gAAAP4AAAD+AAAA/gAAAKIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe" + "AAAA/gAAAPwAJAD+AAwA+wAAAP8AAAD/AAAA/wAEAPwAEgD8ACQA/gAmAP8AHAD8AAEA/gAAAP4ACwD" + "8ACUA/wAkAP8AFwD8AAkA/AAQAP0AIwD+ACQA/wANAPsAAAD+AAAA/gAAAP8AAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAEAAABNAAAA/wAAAP8AGAD8AAAA/QAYAPMARQDyAFEA9wBOAPIAPAD0ABEA8QAAAP8" + "AGgD7ACMA/wAVAP0AAAD9ACMA/QAkAP8ADwD9AAgA+ABBAPUALADyAAAA/AAUAPwAJgD/ABgA/AAAAP" + "0AAAD/AAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAP4ADQD8AAgA/wAqAPAAcAD/AGoA/wB" + "qAP8AagD/AGoA/wBvAP8ATwDvAAAA/QAbAP0AEwD8AAYA/QAkAP8AGAD8AAsA9wBuAPgAagD/AGsA/w" + "BjAPkADAD3AAQA/QAiAP4AGgD9AAAA/gAAAP8AAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZgAAAP8AAAD+ABY" + "A+wAQAPQAcQD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBTAPEAAAD/AAcA/AAMAPwAIQD9AA" + "AA+gBoAPgAagD/AGoA/wBqAP8AagD/AHAA/gAuAO0AAAD/AB8A/QAPAPwAAAD+AAAA+AAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAANIAAAD+AAwA+QAAAP0AZQD1AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8Abg" + "D/ABcA8QAAAP4ACAD9AAAA+QBeAPkAagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AbQD/ADkA7wAAAP8AJ" + "AD9AAAA/gAAAP8AAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAALi8AAAAAAAAAAAAAAAD/AAAA/QAAAP8AKwD1AGsA/wBqAP8AagD/AGoA/wBqAP" + "8AagD/AGoA/wBqAP8AagD/AGoA/wA9APUAAAD/AAAA/wBKAPMAagD/AGoA/wBqAP8AagD/AGoA/wBqA" + "P8AagD/AGoA/wBxAP8ADgD1ABMA/AAHAPsAAAD/AAAARQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzPAIAAAAgAAAA/wAAAPwAAAD/AGQA9A" + "BqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8ASQD1AAAA/wASAPMAcAD/AGoA/" + "wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AawD/ACsA+QAOAP4ADQD8AAAA/wAAAEUAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7gAAAAAAAADgwAQAYLwAA//8AAA" + "ICIwAAAP8AAAD+AAYA8wBwAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/A" + "EIA9QAAAP8AMADvAGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGsA/wApAP0AEQD8" + "AAIA/QAAAP8AAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAVgEAAA" + "AAACJ5AQA2ygAAND8BAHV+AgAAAAAAAAH/AAAA/gAcAPQAbAD/AGoA/wBqAP8AagD/AGoA/wBqAP8Aa" + "gD/AGoA/wBqAP8AagD/AGsA/wApAPYAAAD+AB4A+gBsAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8A" + "agD/AGoA/wBrAP8ALADxAAAA/AAAAP4AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAABYAhQAfAE8ABgBWAQAAAAAAIoABAAAAAAA5UgEAAAAAAAAA+AAAAP4AJQD2AGsA/wBqA" + "P8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBsAPgAAAD6AAAA/gANAPAAbgD/AGoA/wBq" + "AP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/ADsA+QAAAP8AAAD+AAAA4AAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtQAAAAAAAABYABgAZgEGAIMBAAAAAAA+fgEAAAAAAAAAA" + "AACA8wAAAH+ACkA9wBgAPgARQDwADoA9gA5APYASgD0AHAA9wBrAP8AagD/AGoA/wBxAP0AFQD0AAAA" + "/gAAAP4AAAD5AHMA/QBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBvAP0ATgDxAEwA9AA1AOkAAAD/AAA" + "A/gAAAP8AAAB3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAvAAzAKcBAADXAAAAAAAcAFYBF" + "gB4ASAAZwEAXT8BADpdAgAAAAAAAQG+AAEB/gAAAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAPsAJAD1" + "AEQA9ABEAPMACQD3AAAA/wAAAP4AAAD+AAAA/wAiAPMASAD4AE8A9gBuAPgAbQD/AGoA/wBvAP8AFQD" + "yAAAA/wAAAP8AAAD/AAAA/gAAAP4AAAD+AAAA/wAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAABgCuAQAAAAAAAAAAAAAAACEAkQIWAJMBGQBXAQCWlAIAAAAAAAAA7gABAf4AAQD+AAAA/wAAAP8A" + "AAD0AAAA/gAAAP8AAAD/AAAA/gAAAP8AAAD/AAAA/gAAAP4IAAn9JwAs/TsARP5CAEz+PQBG/ioAMP4" + "EAAX6ABkA9gBAAPUAUwDrAAAA/wADEfUAFXztAA5U9wAAAPcAAAD/AAAA/gAAAP4AAACYAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAABcAugAkALIAAAAAAAAAAAAJABQAHwCaAR8AhwMNKUwCAAMDRgAB" + "Af8AAAD+AAAA+QAbn/MAK/zwACz//wAs//cAIMDkAAAA+QAAAP4AAAD+CAAK/V0Aav6TAKn+nQC0/5c" + "Arf+VAKv/lACq/5QAq/+WAK3/nwC3/4IAlv5FAE/+AAAA/wAAAP8AHKnzACnx/wAq9v8ALP/5AAo+9A" + "AAAP4AAAD+AAAA7QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtAQAAAAAAAKoACABt" + "AQwAYwAAAAAAAQAERQAAAP8AAAD+AAAA/wAfuPEAKfL/ACnz/wAq+v8AIsrwAAEH9gAAAP4AAAD+JAA" + "p/ZoAsf+TAKj/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/lACq/0sAVv4AAAD/AC" + "PQ8gAp8/8AKfP/ACny/wAYke4AAAD/AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAgAAAAK0AAAAAAGsA/wAAAAAAAAACSgAAAf8AAQH+AAAA/gAAAP8AGZHnACfm+AAdqvIACjz" + "tAAAA/wAAAP4AAAD+CgAL/ZwAtP+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAK" + "n/kwCp/5IAqP9OAFn+AAAA/wAetPMAKfP/ACny/wAq+PcAAQfyAAAA/gAAAP4AAAD/AAAAEgAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKkAAAAAAAUAFwAAAAAAAAABeQAAAP8AAAH+AAAA/wMBBf0" + "AAAH+AAAA/wAAAP8AAAD/AAEB/gAAAP4AAAD9AAAB/k4AWv6SAKj/kwCp/5MAqf+TAKn/kwCp/5MAqf" + "+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/ZwB2/gAAAP4ACC32ACHC9AAZk/AAAQX3AAAA/gAAA" + "P4AAAD+AAAA/wAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAYQEAAAAAAAAAwAA" + "AAP8BAAH+EwAV/YkAnfJ6AIzzBQAH/QAAAP4AAAD+AAEB/gARF/0Aa5P9AMb//gACAv4oAC7+lwCu/5" + "MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5oAsv8YABv9AAAA/" + "gAAAP8AAAD/AAAA/ksAVv0SABX9AAAA/gAAAP8AAABmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAEAAAATAAAA6AAAAP0AAAD+IgAo+5oAsfCSAKnxlQCr8RYAGvwAAQH+AAAA/gAAAP4AQFf9AL" + "r//wC3/P8AXn79AAAA/o8ApP6TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/k" + "wCp/5MAqf+TAKn/mgCx/0QATv4AAAD+AAAA/gAAAP2gALj/WQBn/gAAAP4AAAD+AAAAtwAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAA9gAAAP0AAAD+HwAk+5wAs/CSAKnxkwCp8VEAXfcAAA" + "D+AAAB/gAAAP4AAQH+AAAA/QDD//8At/v/ALDz/gAAAP5aAGf+kwCo/5MAqf+TAKn/kwCp/5MAqf+TA" + "Kn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf9sAHz+AAAA/gAAAP4AAAD9nwC3/5YArf8A" + "AAD+AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAP0AAAD+AAAA/pgAr/" + "CTAKnxkwCp8ZIAqPGHAJvyAAAA/wAAAP4AAAD+AAEB/gAAAP4Ae6X9ALb7/wC+//8AJjX9KgAw/ZcAr" + "f+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/mQCv/wAA" + "AP4AAAD+AAAA/Z8Atv+YAK//HwAk/QAAAP4AAAD/AAAAZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHA" + "AAAP8AAAD+AAAA/gAAAP+RAKbxkgCo8ZMAqfGTAKnxkgCo8XYAiPQEAAX+AAAA/yUAK/sBAAH+ACg3/" + "QC+//8Atvr/AG6W/QAAAP6YAK//kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp" + "/5MAqf+TAKn/kwCp/5MAqf9oAHf+AAAA/kAASf6UAKv/kwCp/0cAUv4AAAD+AAAA/gAAAMkAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAACRAAAA/wAAAP4AAAD+BQAG/nsAjfOUAKrxkwCp8ZMAqfGTAKjxn" + "QC08JcArvCYAK/xSQBU9wAAAP4Aqen+ALf7/wC3+v4AAAD+WwBo/pMAqf+TAKn/kwCp/5MAqf+TAKn/" + "kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5wAs/6ZAK//kwCp/5MAqf9bAGj" + "+AAAA/gAAAP4AAAD/AAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE0AAAD7AAAA/QAAAP4AA" + "AD/TABY+J0AtPCTAKnxkwCp8ZMAqfGTAKnxkwCp8Y4ApPEAAAD/AFRx/QC4/P8Avf//AC0+/RQAGP2c" + "ALP/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+" + "TAKn/kwCp/5MAqf+TAKn/YQBv/wAAAP4AAAD+AAAA/wAAAG0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAABAAAAAAAAALgAAAD/AAAA/gABAP4bAB78jwCk8pMAqfGTAKnxkwCp8ZMAqfGXAK3xKwAy+wAC" + "BP0Axv//ALb6/wBvlf0AAAD+dwCJ/p8Atv+dALT+lgCt/p4Atv6eALb/lwCu/5MAqf+TAKn/kwCp/5M" + "Aqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCo/1IAXv0AAAD+AAAA/gAAAP4AAACRAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAP8AAAD9AAAB/gAAAP5ZAGf2ngC1" + "8JMAqfGTAKnxkwCp8WAAb/UAAAD+AJLH/gC3+/8As/X+AAAA/gsADP0AAAD9AAAA/gAAAP4AAAD+AQA" + "C/SMAKP1NAFj+gQCU/pwAs/+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5sAsv8cACH9AA" + "AA/gAAAP4AAAD/AAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO" + "AAAA1QAAAP8AAAD+AAAA/hMAFfxzAIT0nQC08JMAqfF5AIrzAAAA/gA6T/0Au///ALn//wBkh/4AERj" + "9ACs7/QBFX/0AT2z9AElk/gAxQ/0AAgT9AAAA/gAAAP4UABf9bwCA/pcArv+TAKn/kwCp/5MAqf+TAK" + "n/kwCp/5MAqv9rAHr+AAAA/gAAAP4AAAD/AAAAhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAA/wAAAP4AAAD+AAAA/hQAF/xJAFP4GwAf/AAAAP4" + "ABgn9AMP//wC3+/8Auf7/AML//wC8//8Auf7/ALj9/wC5/v8AvP//AMb//wCj4P0AWnj9AAAA/QAAAP" + "42AD/9mgCx/5MAqP+TAKn/lACq/54Atv9YAGX+AAAA/gAAAP4AAAD/AAAArAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAh4AAADVAAAA/gA" + "AAP4AAAD+AAAA/gAAAP4AEhr9AJ3V/gC3+v8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/w" + "C3+/8At/v/ALf8/wDB//8AYYT9AAAA/ggACv1cAGn+bAB9/UEAS/4CAAL+AAAA/gAAAP4AAAD/AAAAl" + "gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAABAAEBFAAAAP8AAAD+AAAA/gAAAP4AUGz9AML//wC2+/8At/v/ALf7/wC3+/8At/v/AL" + "f7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf8/wCq5v4AERj+AAEA/gAAAP4AAAD+A" + "AAA/gAAAP4AAAD/AAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOgAAAD+AAAA/gAAAP4Aeqf9ALz//wC3+/8At/" + "v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At" + "/r/ALz//gAcJvwAAQH+AAAA/gAAAP4AAAD/AAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0AAAD/AAAA/gAAAP" + "4Ac579ALn+/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+" + "/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Avf/+ABcg/QAAAf4AAAD+AAAA/wAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAABIAAAD/AAAA/gAAAP4AP1X9AL7//wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/" + "wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wCy8v4AAAD+AAAA" + "/gAAAP8AAACKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAACoAAAA/QAAAP4ABAb9AL7//wC3+/8At/v/ALf7/wC3+/8At/v/A" + "Lf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/" + "ALf7/wC3+/8Atvr/AHmm/QAAAP4AAAD+AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAA/wAAAP4AAAD+AHWh/QC2+v8At" + "/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8A" + "t/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wDD//8AGSP9AAAA/gAAAP8AAABuAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbQAAA" + "P8AAAD+AAUH/QDE//8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3" + "+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Atvv/AH+s/gA" + "AAP4AAAD+AAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAALMAAAD+AAAA/gBZd/0At/z/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7" + "/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf" + "7/wC3+/8At/v/ALf7/wDF//8AAAD9AAAA/gAAAP8AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA/gAAAP4Al9D9ALf7/wC3+/8At/v/" + "ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v" + "/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Auf7/AERe/QAAAP4AAAD/AAAAUQAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP4A" + "AAD9AMb//gC3+/8At/v/ALf7/wC3+/8Auf7/AMb//gC2+f4AwP/+AMT//wC4/f8At/v/ALf7/wC3+/8" + "At/v/ALf7/wC3+/8At/v/ALf7/wC8//8Avf//ALn+/wC3+/8At/v/ALf7/wC3+/8At/v/ALb7/wBvlf" + "4AAAD+AAAA/gAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAACgAAAP8AAAD+ABgg/QDA//8At/v/ALf7/wC2+v8AuPz+AEVe/QAAAP0AAAD+AAAA/gA" + "EBv0AU2/9ALz//wC3+/8At/v/ALf7/wC3+/8Atvr/AL7//wBmi/0ALj/9ACQx/QBJZP0Ai739AMD//w" + "C3+/8At/v/ALf7/wC3+/8Aksj+AAAA/gAAAP4AAACZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcAAAD/AAAA/gAtPf4AvP//ALf7/wC3+/8As/T+AAo" + "O/QAAAP0SEhLtAAAA/gAAAP4AAAD+AAAA/gAWH/0Av///ALf7/wC3+/8At/v/ALHx/gANEv0AAAD+AA" + "AA/gAAAP4AAAD/AAAA/wAuP/0Avv//ALf7/wC3+/8At/v/AKjm/QAAAP4AAAD+AAAApwAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAAAA/wAAAP4AL0H" + "+ALz//wC3+/8AvP//ADJE/QAAAPfc3Nz1TExM6wAAAP4AAAD+AAAA/gAAAP8AAAD+AGeN/gC3+/8At/" + "v/AML//wAeKf0AAAH/AAAA/gAAAP4AAAD+cXFx3YSEhOoAAAD9AEVe/QC6//8At/v/ALf7/wCs7P8AA" + "AD+AAAA/gAAAKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAP8AAAD+ACAt/QC+//8At/v/ALP1/gAAAP95eXns/////2FhYeoBAQH/AAAA/gEBAf" + "9eXl7lDQ0N9gAgLP0Av///ALf7/wCNwf4AAAD/NDQ03AAAAP8AAAD+AAAA/6Ojo+b/////eXl55AAAA" + "P8AtPf+ALf7/wC3+/8AoNv9AAAA/gAAAP4AAACHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/gAAAP0Ax///ALf7/wCIu/0AAAD/ysrK6/" + "/////8/Pz6Hx8f6wAAAP8kJCTm/f39/WVlZe4ABAX8AMT//wC3/P8AV3X9ERER7/////9MTEzrAAAA6" + "ElJSeb///////////X19e8AAAD/AHuo/QC3+/8At/v/AIW2/gAAAP4AAAD/AAAAZAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0wAAAP4AAAD+AK" + "fl/gC3+/8AjcH+AAAA/83Nze/////////////////////w//////////9gYGDsAA8V/QDC//8Auv//A" + "EJa/UFBQeb////////////////////////////////////vAAAA/wBvlfwAt/v/ALf7/wBdff0AAAD+" + "AAAA/wAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAI4AAAD+AAAA/gBnjP0At/v/ALz//gAAAP+CgoLx/////////////////////////////" + "///CAkJ8QBCXP4Auf7/ALj8/wBScf0DAwPw////////////////////////////////tra26AAAAP8A" + "hrf+ALf7/wDA//8AHCf9AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3AAAA/wAAAP4AEBb9AMT//wC6//8AQVn+AAAA9+Pj4" + "/L/////////////////////cnJy1gAAAP8AlMr9ALf7/wC3+/8AhLT+AAAA/5mZmeL/////////////" + "/////////////R0dHesAAAD9AL///gC3+/8ApuX+AAAA/gAAAP4AAAC1AAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMAAAD+AAAA/" + "gCGtv0Atvr/ALn+/wARF/0AAAD3cHBw7bGxseqioqLuOjo67QAAAP8ARFz9ALz//wC3+/8At/v/AML/" + "/wAbJf0AAAD/jo6O5v////b//////f397zw8PPEAAAD/AGCC/QC4/P8Auv//AEJb/QAAAP4AAAD/AAA" + "AUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAABpAAAA/wAAAP4AERf9AMP//wC3+/8Auv/+AEVf/AAAAP8AAAD/AAAA/wAAAP0AWnn9" + "AMH//wC3+/8At/v/ALf7/wC3+/8AtPf+ABsm/QAAAP8AAADwDg4O+QAAAPwAAAD+AD1T/QDB//8At/v" + "/AKLd/gAAAP4AAAD+AAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPIAAAD+AAAA/gBfgP0Auf7/ALf7/wC5/v8A" + "wv//AJnS/gCWzv4Awf//ALj9/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wDC//8Ahbb+AFBt/QA3TP0" + "ATGn9AI/D/gC+//8At/v/AMD//wATGv0AAAD+AAAA/wAAAE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAA9AAAA/wAA" + "AP4AAAD+AJPJ/QC2+v8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC" + "3+/8At/v/ALf7/wC4/f8Au///ALj9/wC3+/8At/v/AML//wA0SP0AAAD+AAAA/QAAAMMAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAJYAAAD/AAAA/gAAAP4Andj9ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf" + "7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/AML//wA+Vf0AAAD+AA" + "AA/gAAAPYAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwwAAAP4AAAD+AAAA/gCGt/0AvP/" + "/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Atv" + "r/ALr//gAoNv0AAAD+AAAA/gAAAP8AAAAeAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAC9AAAA/wAAAP4AAAD+AEFY/QC6/f4At/z/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/" + "8At/v/ALf7/wC2+v8Aw///AICv/gAEBv0AAAH+AAAA/QAAAP8AAAAiAAAAAQAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIMAAAD/AAAA/gAAAP4AAAD9AFl3/QCt7/4AwP//ALj9/w" + "C2+/8At/v/ALf7/wC3+/8At/v/ALz//wDB//4AeKP+ABki/QAAAP4AAAD+AAAA/wAAAOEAAAASAAAAA" + "QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAOUAAAD/AA" + "AA/gAAAP4AAAD+ABcg/QBQbv0AbJD9AH2s/gCBsP0Ac5v+AFt6/QAtPv0AAAD9AAAA/gAAAP4AAAD+A" + "AAA/wAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAWgAAAOIAAAD/AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AA" + "AD+AAAA/gAAAP8AAAD/AAAAmQAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAIgAAADRAAAA/wAAA" + "P8AAAD/AAAA/wAAAP8AAAD/AAAA5QAAAJoAAABWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAIAAAACQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///gAf///////4AAAAD/////wAAAAP////" + "/AAAAB/////+AAAAH/////4AAAA//////gAAAD/////8AAAAH/////wAAAAP////+AAAAA/////4AAA" + "AD/////gAAAAP////+AAAAA/////4AAAAD/////gAAAAP////+AAAAA/////4AAAAB/////gAAAAD//" + "//+AAAAAP////wAAAAA////+AAAAAD////wAAAAAP///8AAAAAA////gAAAAAB///8AAAAAAH///gAA" + "AAAAf//+AAAAAAA///4AAAAAAD///4AAAAAAP///wAAAAAAf///wAAAAAD////gAAAAAP////gAAAAB" + "/////AAAAAP////+AAAAD/////wAAAAf////+AAAAB/////4AAAAD/////AAAAAP////8AAAAA/////" + "wAAAAB////+AAAAAH////4AAAAAf////gAAAAA////+AAAAAD////4AAAAAP////gAAAAA////+AAAA" + "AD////4AAAAAf////gAAAAB////+AAAAAH////8AAAAAf////wAAAAD/////gAAAAP////+AAAAB///" + "//8AAAAH/////wAAAA//////gAAAH//////AAAA//////+AAAH//////+AAA///////+AAP///////+" + "AH//////////////8="; + + const char HTTP_COMMAND_TUNNELS[] = "tunnels"; + const char HTTP_COMMAND_TRANSIT_TUNNELS[] = "transit_tunnels"; + const char HTTP_COMMAND_TRANSPORTS[] = "transports"; + const char HTTP_COMMAND_START_ACCEPTING_TUNNELS[] = "start_accepting_tunnels"; + const char HTTP_COMMAND_STOP_ACCEPTING_TUNNELS[] = "stop_accepting_tunnels"; + const char HTTP_COMMAND_LOCAL_DESTINATIONS[] = "local_destinations"; + const char HTTP_COMMAND_LOCAL_DESTINATION[] = "local_destination"; + const char HTTP_PARAM_BASE32_ADDRESS[] = "b32"; + + namespace misc_strings + { + + const char name_value_separator[] = { ':', ' ' }; + const char crlf[] = { '\r', '\n' }; + + } // namespace misc_strings + + std::vector HTTPConnection::reply::to_buffers(int status) + { + std::vector buffers; + if (headers.size () > 0) + { + switch (status) + { + case 105: buffers.push_back(boost::asio::buffer("HTTP/1.1 105 Name Not Resolved\r\n")); break; + case 200: buffers.push_back(boost::asio::buffer("HTTP/1.1 200 OK\r\n")); break; + case 400: buffers.push_back(boost::asio::buffer("HTTP/1.1 400 Bad Request\r\n")); break; + case 404: buffers.push_back(boost::asio::buffer("HTTP/1.1 404 Not Found\r\n")); break; + case 408: buffers.push_back(boost::asio::buffer("HTTP/1.1 408 Request Timeout\r\n")); break; + case 500: buffers.push_back(boost::asio::buffer("HTTP/1.1 500 Internal Server Error\r\n")); break; + case 502: buffers.push_back(boost::asio::buffer("HTTP/1.1 502 Bad Gateway\r\n")); break; + case 503: buffers.push_back(boost::asio::buffer("HTTP/1.1 503 Not Implemented\r\n")); break; + case 504: buffers.push_back(boost::asio::buffer("HTTP/1.1 504 Gateway Timeout\r\n")); break; + default: + buffers.push_back(boost::asio::buffer("HTTP/1.1 200 OK\r\n")); + } + + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(boost::asio::buffer(h.name)); + buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(boost::asio::buffer(h.value)); + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + } + buffers.push_back(boost::asio::buffer(misc_strings::crlf)); + } + buffers.push_back(boost::asio::buffer(content)); + return buffers; + } + + void HTTPConnection::Terminate () + { + if (!m_Stream) return; + m_Socket->close (); + m_Stream->Close (); + + m_Socket->get_io_service ().post ([=](void) + { + m_Stream.reset (); + m_Stream = nullptr; + // delete this + }); + } + + void HTTPConnection::Receive () + { + m_Socket->async_read_some (boost::asio::buffer (m_Buffer, HTTP_CONNECTION_BUFFER_SIZE), + boost::bind(&HTTPConnection::HandleReceive, this, + boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } + + void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (!ecode) + { + if (!m_Stream) // new request + { + m_Buffer[bytes_transferred] = 0; + m_BufferLen = bytes_transferred; + RunRequest(); + } + else // follow-on + m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred); + Receive (); + } + else if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + + void HTTPConnection::RunRequest () + { + auto address = ExtractAddress (); + if (address.length () > 1 && address[1] != '?') // not just '/' or '/?' + { + std::string uri ("/"), b32; + size_t pos = address.find ('/', 1); + if (pos == std::string::npos) + b32 = address.substr (1); // excluding leading '/' to end of line + else + { + b32 = address.substr (1, pos - 1); // excluding leading '/' to next '/' + uri = address.substr (pos); // rest of line + } + + HandleDestinationRequest (b32, uri); + } + else + HandleRequest (address); + } + + std::string HTTPConnection::ExtractAddress () + { + char * get = strstr (m_Buffer, "GET"); + if (get) + { + char * http = strstr (get, "HTTP"); + if (http) + return std::string (get + 4, http - get - 5); + } + return ""; + } + + void HTTPConnection::ExtractParams (const std::string& str, std::map& params) + { + if (str[0] != '&') return; + size_t pos = 1, end; + do + { + end = str.find ('&', pos); + std::string param = str.substr (pos, end - pos); + LogPrint (param); + size_t e = param.find ('='); + if (e != std::string::npos) + params[param.substr(0, e)] = param.substr(e+1); + pos = end + 1; + } + while (end != std::string::npos); + } + + void HTTPConnection::HandleWriteReply (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + m_Socket->close (); + Terminate (); + } + } + + void HTTPConnection::HandleWrite (const boost::system::error_code& ecode) + { + if (ecode || (m_Stream && !m_Stream->IsOpen ())) + { + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else // data keeps coming + AsyncStreamReceive (); + } + + void HTTPConnection::HandleRequest (const std::string& address) + { + std::stringstream s; + // Html5 head start + s << "\n"; // TODO: Add support for locale. + s << ""; // TODO: Find something to parse html/template system. This is horrible. + s << "Purple I2P " << VERSION " Webconsole"; + // Head end + if (address.length () > 1) + HandleCommand (address.substr (2), s); + else + FillContent (s); + s << ""; + SendReply (s.str ()); + } + + void HTTPConnection::FillContent (std::stringstream& s) + { + s << "

Welcome to the Webconsole!



"; + s << "Data path: " << i2p::util::filesystem::GetDataDir().string() << "
" << "
"; + s << "Our external address:" << "
"; + for (auto& address : i2p::context.GetRouterInfo().GetAddresses()) + { + switch (address.transportStyle) + { + case i2p::data::RouterInfo::eTransportNTCP: + if (address.host.is_v6 ()) + s << "NTCP6  "; + else + s << "NTCP  "; + break; + case i2p::data::RouterInfo::eTransportSSU: + if (address.host.is_v6 ()) + s << "SSU6     "; + else + s << "SSU     "; + break; + default: + s << "Unknown  "; + } + s << address.host.to_string() << ":" << address.port << "
"; + } + s << "
Routers: " << i2p::data::netdb.GetNumRouters () << " "; + s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " "; + s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "
"; + + s << "
Local destinations"; + s << "
Tunnels"; + s << "
Transit tunnels"; + s << "
Transports
"; + + if (i2p::context.AcceptsTunnels ()) + s << "
Stop accepting tunnels
"; + else + s << "
Start accepting tunnels
"; + + s << "

Flibusta

"; + } + + void HTTPConnection::HandleCommand (const std::string& command, std::stringstream& s) + { + size_t paramsPos = command.find('&'); + std::string cmd = command.substr (0, paramsPos); + if (cmd == HTTP_COMMAND_TRANSPORTS) + ShowTransports (s); + else if (cmd == HTTP_COMMAND_TUNNELS) + ShowTunnels (s); + else if (cmd == HTTP_COMMAND_TRANSIT_TUNNELS) + ShowTransitTunnels (s); + else if (cmd == HTTP_COMMAND_START_ACCEPTING_TUNNELS) + StartAcceptingTunnels (s); + else if (cmd == HTTP_COMMAND_STOP_ACCEPTING_TUNNELS) + StopAcceptingTunnels (s); + else if (cmd == HTTP_COMMAND_LOCAL_DESTINATIONS) + ShowLocalDestinations (s); + else if (cmd == HTTP_COMMAND_LOCAL_DESTINATION) + { + std::map params; + ExtractParams (command.substr (paramsPos), params); + auto b32 = params[HTTP_PARAM_BASE32_ADDRESS]; + ShowLocalDestination (b32, s); + } + } + + void HTTPConnection::ShowTransports (std::stringstream& s) + { + s << "NTCP
"; + for (auto it: i2p::transport::transports.GetNTCPSessions ()) + { + if (it.second && it.second->IsEstablished ()) + { + // incoming connection doesn't have remote RI + auto outgoing = it.second->GetRemoteRouter (); + if (outgoing) s << "-->"; + s << it.second->GetRemoteIdentity ().GetIdentHash ().ToBase64 ().substr (0, 4) << ": " + << it.second->GetSocket ().remote_endpoint().address ().to_string (); + if (!outgoing) s << "-->"; + s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + s << "
"; + } + s << std::endl; + } + auto ssuServer = i2p::transport::transports.GetSSUServer (); + if (ssuServer) + { + s << "
SSU
"; + for (auto it: ssuServer->GetSessions ()) + { + // incoming connections don't have remote router + auto outgoing = it.second->GetRemoteRouter (); + auto endpoint = it.second->GetRemoteEndpoint (); + if (outgoing) s << "-->"; + s << endpoint.address ().to_string () << ":" << endpoint.port (); + if (!outgoing) s << "-->"; + s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + s << "
"; + s << std::endl; + } + } + } + + void HTTPConnection::ShowTunnels (std::stringstream& s) + { + for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ()) + { + it->GetTunnelConfig ()->Print (s); + auto state = it->GetState (); + if (state == i2p::tunnel::eTunnelStateFailed) + s << " " << "Failed"; + else if (state == i2p::tunnel::eTunnelStateExpiring) + s << " " << "Exp"; + s << " " << (int)it->GetNumSentBytes () << "
"; + s << std::endl; + } + + for (auto it: i2p::tunnel::tunnels.GetInboundTunnels ()) + { + it.second->GetTunnelConfig ()->Print (s); + auto state = it.second->GetState (); + if (state == i2p::tunnel::eTunnelStateFailed) + s << " " << "Failed"; + else if (state == i2p::tunnel::eTunnelStateExpiring) + s << " " << "Exp"; + s << " " << (int)it.second->GetNumReceivedBytes () << "
"; + s << std::endl; + } + } + + void HTTPConnection::ShowTransitTunnels (std::stringstream& s) + { + for (auto it: i2p::tunnel::tunnels.GetTransitTunnels ()) + { + if (dynamic_cast(it.second)) + s << it.second->GetTunnelID () << "-->"; + else if (dynamic_cast(it.second)) + s << "-->" << it.second->GetTunnelID (); + else + s << "-->" << it.second->GetTunnelID () << "-->"; + s << " " << it.second->GetNumTransmittedBytes () << "
"; + } + } + + void HTTPConnection::ShowLocalDestinations (std::stringstream& s) + { + for (auto& it: i2p::client::context.GetDestinations ()) + { + std::string b32 = it.first.ToBase32 (); + s << ""; + s << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetIdentHash()) << "
" << std::endl; + } + } + + void HTTPConnection::ShowLocalDestination (const std::string& b32, std::stringstream& s) + { + i2p::data::IdentHash ident; + i2p::data::Base32ToByteStream (b32.c_str (), b32.length (), ident, 32); + auto dest = i2p::client::context.FindLocalDestination (ident); + if (dest) + { + s << "LeaseSets: " << dest->GetNumRemoteLeaseSets () << "
"; + auto pool = dest->GetTunnelPool (); + if (pool) + { + s << "Tunnels:
"; + for (auto it: pool->GetOutboundTunnels ()) + { + it->GetTunnelConfig ()->Print (s); + auto state = it->GetState (); + if (state == i2p::tunnel::eTunnelStateFailed) + s << " " << "Failed"; + else if (state == i2p::tunnel::eTunnelStateExpiring) + s << " " << "Exp"; + s << "
" << std::endl; + } + for (auto it: pool->GetInboundTunnels ()) + { + it->GetTunnelConfig ()->Print (s); + auto state = it->GetState (); + if (state == i2p::tunnel::eTunnelStateFailed) + s << " " << "Failed"; + else if (state == i2p::tunnel::eTunnelStateExpiring) + s << " " << "Exp"; + s << "
" << std::endl; + } + } + s << "
Streams:
"; + for (auto it: dest->GetStreamingDestination ()->GetStreams ()) + { + s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " "; + s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]"; + s << "
"<< std::endl; + } + } + } + + void HTTPConnection::StartAcceptingTunnels (std::stringstream& s) + { + i2p::context.SetAcceptsTunnels (true); + s << "Accepting tunnels started" << std::endl; + } + + void HTTPConnection::StopAcceptingTunnels (std::stringstream& s) + { + i2p::context.SetAcceptsTunnels (false); + s << "Accepting tunnels stopped" << std::endl; + } + + void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri) + { + std::string request = "GET " + uri + " HTTP/1.1\r\nHost:" + address + "\r\n"; + LogPrint("HTTP Client Request: ", request); + SendToAddress (address, 80, request.c_str (), request.size ()); + } + + void HTTPConnection::SendToAddress (const std::string& address, int port, const char * buf, size_t len) + { + i2p::data::IdentHash destination; + if (!i2p::client::context.GetAddressBook ().GetIdentHash (address, destination)) + { + LogPrint ("Unknown address ", address); + SendReply ("" + itoopieImage + "
Unknown address " + address + "", 404); + return; + } + + auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination); + if (leaseSet && leaseSet->HasNonExpiredLeases ()) + SendToDestination (leaseSet, port, buf, len); + else + { + memcpy (m_Buffer, buf, len); + m_BufferLen = len; + i2p::client::context.GetSharedLocalDestination ()->RequestDestination (destination); + m_Timer.expires_from_now (boost::posix_time::seconds(HTTP_DESTINATION_REQUEST_TIMEOUT)); + m_Timer.async_wait (boost::bind (&HTTPConnection::HandleDestinationRequestTimeout, + this, boost::asio::placeholders::error, destination, port, m_Buffer, m_BufferLen)); + } + } + + void HTTPConnection::HandleDestinationRequestTimeout (const boost::system::error_code& ecode, + i2p::data::IdentHash destination, int port, const char * buf, size_t len) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination); + if (leaseSet && leaseSet->HasNonExpiredLeases ()) + SendToDestination (leaseSet, port, buf, len); + else + // still no LeaseSet + SendReply (leaseSet ? "" + itoopieImage + "
Leases expired" : "" + itoopieImage + "LeaseSet not found", 504); + } + } + + void HTTPConnection::SendToDestination (const i2p::data::LeaseSet * remote, int port, const char * buf, size_t len) + { + if (!m_Stream) + m_Stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (*remote, port); + if (m_Stream) + { + m_Stream->Send ((uint8_t *)buf, len); + AsyncStreamReceive (); + } + } + + void HTTPConnection::AsyncStreamReceive () + { + if (m_Stream) + m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, 8192), + boost::bind (&HTTPConnection::HandleStreamReceive, this, + boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred), + 45); // 45 seconds timeout + } + + void HTTPConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (!ecode) + { + boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred), + boost::bind (&HTTPConnection::HandleWrite, this, boost::asio::placeholders::error)); + } + else + { + if (ecode == boost::asio::error::timed_out) + SendReply ("" + itoopieImage + "
Not responding", 504); + else if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + } + + void HTTPConnection::SendReply (const std::string& content, int status) + { + m_Reply.content = content; + m_Reply.headers.resize(2); + m_Reply.headers[0].name = "Content-Length"; + m_Reply.headers[0].value = boost::lexical_cast(m_Reply.content.size()); + m_Reply.headers[1].name = "Content-Type"; + m_Reply.headers[1].value = "text/html"; + + boost::asio::async_write (*m_Socket, m_Reply.to_buffers(status), + boost::bind (&HTTPConnection::HandleWriteReply, this, + boost::asio::placeholders::error)); + } + + HTTPServer::HTTPServer (int port): + m_Thread (nullptr), m_Work (m_Service), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), + m_NewSocket (nullptr) + { + + } + + HTTPServer::~HTTPServer () + { + Stop (); + } + + void HTTPServer::Start () + { + m_Thread = new std::thread (std::bind (&HTTPServer::Run, this)); + m_Acceptor.listen (); + Accept (); + } + + void HTTPServer::Stop () + { + m_Acceptor.close(); + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + + void HTTPServer::Run () + { + m_Service.run (); + } + + void HTTPServer::Accept () + { + m_NewSocket = new boost::asio::ip::tcp::socket (m_Service); + m_Acceptor.async_accept (*m_NewSocket, boost::bind (&HTTPServer::HandleAccept, this, + boost::asio::placeholders::error)); + } + + void HTTPServer::HandleAccept(const boost::system::error_code& ecode) + { + if (!ecode) + { + CreateConnection(m_NewSocket); // new HTTPConnection(m_NewSocket); + Accept (); + } + } + + void HTTPServer::CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket) + { + new HTTPConnection (m_NewSocket); + } +} +} + diff --git a/HTTPServer.h b/HTTPServer.h new file mode 100644 index 00000000..51bbc98b --- /dev/null +++ b/HTTPServer.h @@ -0,0 +1,136 @@ +#ifndef HTTP_SERVER_H__ +#define HTTP_SERVER_H__ + +#include +#include +#include +#include +#include +#include "LeaseSet.h" +#include "Streaming.h" + +namespace i2p +{ +namespace util +{ + const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; + const int HTTP_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds + class HTTPConnection + { + protected: + + struct header + { + std::string name; + std::string value; + }; + + struct request + { + std::string method; + std::string uri; + std::string host; + int port; + int http_version_major; + int http_version_minor; + std::vector
headers; + }; + + struct reply + { + std::vector
headers; + std::string content; + + std::vector to_buffers (int status); + }; + + public: + + HTTPConnection (boost::asio::ip::tcp::socket * socket): + m_Socket (socket), m_Timer (socket->get_io_service ()), + m_Stream (nullptr), m_BufferLen (0) { Receive (); }; + virtual ~HTTPConnection() { delete m_Socket; } + + private: + + void Terminate (); + void Receive (); + void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void AsyncStreamReceive (); + void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleWriteReply(const boost::system::error_code& ecode); + void HandleWrite (const boost::system::error_code& ecode); + void SendReply (const std::string& content, int status = 200); + + void HandleRequest (const std::string& address); + void HandleCommand (const std::string& command, std::stringstream& s); + void ShowTransports (std::stringstream& s); + void ShowTunnels (std::stringstream& s); + void ShowTransitTunnels (std::stringstream& s); + void ShowLocalDestinations (std::stringstream& s); + void ShowLocalDestination (const std::string& b32, std::stringstream& s); + void StartAcceptingTunnels (std::stringstream& s); + void StopAcceptingTunnels (std::stringstream& s); + void FillContent (std::stringstream& s); + std::string ExtractAddress (); + void ExtractParams (const std::string& str, std::map& params); + + + protected: + + boost::asio::ip::tcp::socket * m_Socket; + boost::asio::deadline_timer m_Timer; + std::shared_ptr m_Stream; + char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1]; + size_t m_BufferLen; + request m_Request; + reply m_Reply; + + protected: + + virtual void RunRequest (); + void HandleDestinationRequest(const std::string& address, const std::string& uri); + void SendToAddress (const std::string& address, int port, const char * buf, size_t len); + void HandleDestinationRequestTimeout (const boost::system::error_code& ecode, + i2p::data::IdentHash destination, int port, const char * buf, size_t len); + void SendToDestination (const i2p::data::LeaseSet * remote, int port, const char * buf, size_t len); + + public: + + static const std::string itoopieImage; + static const std::string itoopieFavicon; + }; + + class HTTPServer + { + public: + + HTTPServer (int port); + virtual ~HTTPServer (); + + void Start (); + void Stop (); + + private: + + void Run (); + void Accept (); + void HandleAccept(const boost::system::error_code& ecode); + + private: + + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::io_service::work m_Work; + boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::ip::tcp::socket * m_NewSocket; + + protected: + virtual void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket); + }; +} +} + +#endif + + diff --git a/I2NPProtocol.cpp b/I2NPProtocol.cpp new file mode 100644 index 00000000..51e566e7 --- /dev/null +++ b/I2NPProtocol.cpp @@ -0,0 +1,582 @@ +#include +#include +#include "I2PEndian.h" +#include +#include "ElGamal.h" +#include "Timestamp.h" +#include "RouterContext.h" +#include "NetDb.h" +#include "Tunnel.h" +#include "base64.h" +#include "Transports.h" +#include "Garlic.h" +#include "I2NPProtocol.h" + +using namespace i2p::transport; + +namespace i2p +{ + I2NPMessage * NewI2NPMessage () + { + return new I2NPMessageBuffer(); + } + + I2NPMessage * NewI2NPShortMessage () + { + return new I2NPMessageBuffer(); + } + + I2NPMessage * NewI2NPMessage (size_t len) + { + return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage (); + } + + void DeleteI2NPMessage (I2NPMessage * msg) + { + delete msg; + } + + static std::atomic I2NPmsgID(0); // TODO: create class + void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID) + { + msg->SetTypeID (msgType); + if (replyMsgID) // for tunnel creation + msg->SetMsgID (replyMsgID); + else + { + msg->SetMsgID (I2NPmsgID); + I2NPmsgID++; + } + msg->SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number + msg->UpdateSize (); + msg->UpdateChks (); + } + + void RenewI2NPMessageHeader (I2NPMessage * msg) + { + if (msg) + { + msg->SetMsgID (I2NPmsgID); + I2NPmsgID++; + msg->SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); + } + } + + I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID) + { + I2NPMessage * msg = NewI2NPMessage (len); + memcpy (msg->GetPayload (), buf, len); + msg->len += len; + FillI2NPMessageHeader (msg, msgType, replyMsgID); + return msg; + } + + I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from) + { + I2NPMessage * msg = NewI2NPMessage (); + memcpy (msg->GetBuffer (), buf, len); + msg->len = msg->offset + len; + msg->from = from; + return msg; + } + + I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID) + { + I2NPMessage * m = NewI2NPMessage (); + uint8_t * buf = m->GetPayload (); + if (msgID) + { + htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID); + htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ()); + } + else // for SSU establishment + { + htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, i2p::context.GetRandomNumberGenerator ().GenerateWord32 ()); + htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2 + } + m->len += DELIVERY_STATUS_SIZE; + FillI2NPMessageHeader (m, eI2NPDeliveryStatus); + return m; + } + + I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, + uint32_t replyTunnelID, bool exploratory, std::set * excludedPeers) + { + I2NPMessage * m = NewI2NPMessage (); + uint8_t * buf = m->GetPayload (); + memcpy (buf, key, 32); // key + buf += 32; + memcpy (buf, from, 32); // from + buf += 32; + uint8_t flag = exploratory ? 0x0C : 0x08; // 1000 - RI, 1100 -exporatory + if (replyTunnelID) + { + *buf = flag | 0x01; // set delivery flag + htobe32buf (buf+1, replyTunnelID); + buf += 5; + } + else + { + *buf = flag; // flag + buf++; + } + + if (excludedPeers) + { + int cnt = excludedPeers->size (); + htobe16buf (buf, cnt); + buf += 2; + for (auto& it: *excludedPeers) + { + memcpy (buf, it, 32); + buf += 32; + } + } + else + { + // nothing to exclude + htobuf16 (buf, 0); + buf += 2; + } + + m->len += (buf - m->GetPayload ()); + FillI2NPMessageHeader (m, eI2NPDatabaseLookup); + return m; + } + + I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, + const std::set& excludedFloodfills, + const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag) + { + I2NPMessage * m = NewI2NPMessage (); + uint8_t * buf = m->GetPayload (); + memcpy (buf, dest, 32); // key + buf += 32; + memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW + buf += 32; + *buf = 7; // flags (01 - tunnel, 10 - encrypted, 0100 - LS lookup + htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID + buf += 5; + + // excluded + int cnt = excludedFloodfills.size (); + htobe16buf (buf, cnt); + buf += 2; + if (cnt > 0) + { + for (auto& it: excludedFloodfills) + { + memcpy (buf, it, 32); + buf += 32; + } + } + // encryption + memcpy (buf, replyKey, 32); + buf[32] = 1; // 1 tag + memcpy (buf + 33, replyTag, 32); + buf += 65; + + m->len += (buf - m->GetPayload ()); + FillI2NPMessageHeader (m, eI2NPDatabaseLookup); + return m; + } + + + + I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, + const i2p::data::RouterInfo * floodfill) + { + I2NPMessage * m = NewI2NPShortMessage (); + uint8_t * buf = m->GetPayload (); + size_t len = 0; + memcpy (buf, ident, 32); + len += 32; + buf[len] = floodfill ? 1 : 0; // 1 router for now + len++; + if (floodfill) + { + memcpy (buf + len, floodfill->GetIdentHash (), 32); + len += 32; + } + memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32); + len += 32; + m->len += len; + FillI2NPMessageHeader (m, eI2NPDatabaseSearchReply); + return m; + } + + I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router) + { + if (!router) // we send own RouterInfo + router = &context.GetRouterInfo (); + + I2NPMessage * m = NewI2NPShortMessage (); + uint8_t * payload = m->GetPayload (); + + memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32); + payload[DATABASE_STORE_TYPE_OFFSET] = 0; + htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); + + CryptoPP::Gzip compressor; + compressor.Put (router->GetBuffer (), router->GetBufferLen ()); + compressor.MessageEnd(); + auto size = compressor.MaxRetrievable (); + uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE; + htobe16buf (buf, size); // size + buf += 2; + // TODO: check if size doesn't exceed buffer + compressor.Get (buf, size); + m->len += DATABASE_STORE_HEADER_SIZE + 2 + size; // payload size + FillI2NPMessageHeader (m, eI2NPDatabaseStore); + + return m; + } + + I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken) + { + if (!leaseSet) return nullptr; + I2NPMessage * m = NewI2NPShortMessage (); + uint8_t * payload = m->GetPayload (); + memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32); + payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet + htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken); + size_t size = DATABASE_STORE_HEADER_SIZE; + if (replyToken) + { + auto leases = leaseSet->GetNonExpiredLeases (); + if (leases.size () > 0) + { + htobe32buf (payload + size, leases[0].tunnelID); + size += 4; // reply tunnelID + memcpy (payload + size, leases[0].tunnelGateway, 32); + size += 32; // reply tunnel gateway + } + else + htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0); + } + memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ()); + size += leaseSet->GetBufferLen (); + m->len += size; + FillI2NPMessageHeader (m, eI2NPDatabaseStore); + return m; + } + + bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) + { + for (int i = 0; i < num; i++) + { + uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE; + if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) + { + LogPrint ("Record ",i," is ours"); + + i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText); + // replace record to reply + if (i2p::context.AcceptsTunnels ()) + { + i2p::tunnel::TransitTunnel * transitTunnel = + i2p::tunnel::CreateTransitTunnel ( + bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), + clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, + clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, + clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80, + clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40); + i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); + record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0; + } + else + record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30) + + //TODO: fill filler + CryptoPP::SHA256().CalculateDigest(record + BUILD_RESPONSE_RECORD_HASH_OFFSET, + record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1); // + 1 byte of ret + // encrypt reply + i2p::crypto::CBCEncryption encryption; + for (int j = 0; j < num; j++) + { + encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); + encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET); + uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; + encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply); + } + return true; + } + } + return false; + } + + void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) + { + int num = buf[0]; + LogPrint ("VariableTunnelBuild ", num, " records"); + + i2p::tunnel::Tunnel * tunnel = i2p::tunnel::tunnels.GetPendingTunnel (replyMsgID); + if (tunnel) + { + // endpoint of inbound tunnel + LogPrint ("VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); + if (tunnel->HandleTunnelBuildResponse (buf, len)) + { + LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); + tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); + i2p::tunnel::tunnels.AddInboundTunnel (static_cast(tunnel)); + } + else + { + LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); + tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); + } + } + else + { + uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + if (HandleBuildRequestRecords (num, buf + 1, clearText)) + { + if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel + { + // so we send it to reply tunnel + transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + eI2NPVariableTunnelBuildReply, buf, len, + bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + } + else + transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, + bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + } + } + } + + void HandleTunnelBuildMsg (uint8_t * buf, size_t len) + { + uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText)) + { + if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outbound tunnel + { + // so we send it to reply tunnel + transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + eI2NPTunnelBuildReply, buf, len, + bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + } + else + transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateI2NPMessage (eI2NPTunnelBuild, buf, len, + bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + } + } + + void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) + { + LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID); + i2p::tunnel::Tunnel * tunnel = i2p::tunnel::tunnels.GetPendingTunnel (replyMsgID); + if (tunnel) + { + // reply for outbound tunnel + if (tunnel->HandleTunnelBuildResponse (buf, len)) + { + LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been created"); + tunnel->SetState (i2p::tunnel::eTunnelStateEstablished); + i2p::tunnel::tunnels.AddOutboundTunnel (static_cast(tunnel)); + } + else + { + LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been declined"); + tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed); + } + } + else + LogPrint ("Pending tunnel for message ", replyMsgID, " not found"); + } + + + I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf) + { + I2NPMessage * msg = NewI2NPMessage (); + memcpy (msg->GetPayload (), buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE); + msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; + FillI2NPMessageHeader (msg, eI2NPTunnelData); + return msg; + } + + I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload) + { + I2NPMessage * msg = NewI2NPMessage (); + memcpy (msg->GetPayload () + 4, payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4); + htobe32buf (msg->GetPayload (), tunnelID); + msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; + FillI2NPMessageHeader (msg, eI2NPTunnelData); + return msg; + } + + I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len) + { + I2NPMessage * msg = NewI2NPMessage (len); + uint8_t * payload = msg->GetPayload (); + htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); + htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); + memcpy (payload + TUNNEL_GATEWAY_HEADER_SIZE, buf, len); + msg->len += TUNNEL_GATEWAY_HEADER_SIZE + len; + FillI2NPMessageHeader (msg, eI2NPTunnelGateway); + return msg; + } + + I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg) + { + if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE) + { + // message is capable to be used without copying + uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE; + htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); + int len = msg->GetLength (); + htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); + msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE); + msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len; + FillI2NPMessageHeader (msg, eI2NPTunnelGateway); + return msg; + } + else + { + I2NPMessage * msg1 = CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ()); + DeleteI2NPMessage (msg); + return msg1; + } + } + + I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, + const uint8_t * buf, size_t len, uint32_t replyMsgID) + { + I2NPMessage * msg = NewI2NPMessage (len); + size_t gatewayMsgOffset = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; + msg->offset += gatewayMsgOffset; + msg->len += gatewayMsgOffset; + memcpy (msg->GetPayload (), buf, len); + msg->len += len; + FillI2NPMessageHeader (msg, msgType, replyMsgID); // create content message + len = msg->GetLength (); + msg->offset -= gatewayMsgOffset; + uint8_t * payload = msg->GetPayload (); + htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID); + htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len); + FillI2NPMessageHeader (msg, eI2NPTunnelGateway); // gateway message + return msg; + } + + void HandleTunnelGatewayMsg (I2NPMessage * msg) + { + const uint8_t * payload = msg->GetPayload (); + uint32_t tunnelID = bufbe32toh(payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET); + uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET); + // we make payload as new I2NP message to send + msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; + msg->len = msg->offset + len; + auto typeID = msg->GetTypeID (); + LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)typeID); + + if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply) + { + // transit DatabaseStore my contain new/updated RI + // or DatabaseSearchReply with new routers + auto ds = NewI2NPMessage (); + *ds = *msg; + i2p::data::netdb.PostI2NPMsg (ds); + } + i2p::tunnel::TransitTunnel * tunnel = i2p::tunnel::tunnels.GetTransitTunnel (tunnelID); + if (tunnel) + tunnel->SendTunnelDataMsg (msg); + else + { + LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found"); + i2p::DeleteI2NPMessage (msg); + } + } + + size_t GetI2NPMessageLength (const uint8_t * msg) + { + return bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE; + } + + void HandleI2NPMessage (uint8_t * msg, size_t len) + { + uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET]; + uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET); + LogPrint ("I2NP msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID); + + uint8_t * buf = msg + I2NP_HEADER_SIZE; + int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET); + switch (typeID) + { + case eI2NPVariableTunnelBuild: + LogPrint ("VariableTunnelBuild"); + HandleVariableTunnelBuildMsg (msgID, buf, size); + break; + case eI2NPVariableTunnelBuildReply: + LogPrint ("VariableTunnelBuildReply"); + HandleVariableTunnelBuildReplyMsg (msgID, buf, size); + break; + case eI2NPTunnelBuild: + LogPrint ("TunnelBuild"); + HandleTunnelBuildMsg (buf, size); + break; + case eI2NPTunnelBuildReply: + LogPrint ("TunnelBuildReply"); + // TODO: + break; + default: + LogPrint ("Unexpected message ", (int)typeID); + } + } + + void HandleI2NPMessage (I2NPMessage * msg) + { + if (msg) + { + switch (msg->GetTypeID ()) + { + case eI2NPTunnelData: + LogPrint ("TunnelData"); + i2p::tunnel::tunnels.PostTunnelData (msg); + break; + case eI2NPTunnelGateway: + LogPrint ("TunnelGateway"); + HandleTunnelGatewayMsg (msg); + break; + case eI2NPGarlic: + LogPrint ("Garlic"); + if (msg->from) + { + if (msg->from->GetTunnelPool ()) + msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg); + else + { + LogPrint (eLogInfo, "Local destination for garlic doesn't exist anymore"); + DeleteI2NPMessage (msg); + } + } + else + i2p::context.ProcessGarlicMessage (msg); + break; + case eI2NPDatabaseStore: + case eI2NPDatabaseSearchReply: + case eI2NPDatabaseLookup: + // forward to netDb + i2p::data::netdb.PostI2NPMsg (msg); + break; + case eI2NPDeliveryStatus: + LogPrint ("DeliveryStatus"); + if (msg->from && msg->from->GetTunnelPool ()) + msg->from->GetTunnelPool ()->ProcessDeliveryStatus (msg); + else + i2p::context.ProcessDeliveryStatusMessage (msg); + break; + default: + HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); + DeleteI2NPMessage (msg); + } + } + } +} diff --git a/I2NPProtocol.h b/I2NPProtocol.h new file mode 100644 index 00000000..8f8dddd3 --- /dev/null +++ b/I2NPProtocol.h @@ -0,0 +1,221 @@ +#ifndef I2NP_PROTOCOL_H__ +#define I2NP_PROTOCOL_H__ + +#include +#include +#include +#include +#include "I2PEndian.h" +#include "Identity.h" +#include "RouterInfo.h" +#include "LeaseSet.h" + +namespace i2p +{ + // I2NP header + const size_t I2NP_HEADER_TYPEID_OFFSET = 0; + const size_t I2NP_HEADER_MSGID_OFFSET = I2NP_HEADER_TYPEID_OFFSET + 1; + const size_t I2NP_HEADER_EXPIRATION_OFFSET = I2NP_HEADER_MSGID_OFFSET + 4; + const size_t I2NP_HEADER_SIZE_OFFSET = I2NP_HEADER_EXPIRATION_OFFSET + 8; + const size_t I2NP_HEADER_CHKS_OFFSET = I2NP_HEADER_SIZE_OFFSET + 2; + const size_t I2NP_HEADER_SIZE = I2NP_HEADER_CHKS_OFFSET + 1; + + // I2NP short header + const size_t I2NP_SHORT_HEADER_TYPEID_OFFSET = 0; + const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1; + const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4; + + // Tunnel Gateway header + const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0; + const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4; + const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2; + + // DeliveryStatus + const size_t DELIVERY_STATUS_MSGID_OFFSET = 0; + const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4; + const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8; + + // DatabaseStore + const size_t DATABASE_STORE_KEY_OFFSET = 0; + const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32; + const size_t DATABASE_STORE_REPLY_TOKEN_OFFSET = DATABASE_STORE_TYPE_OFFSET + 1; + const size_t DATABASE_STORE_HEADER_SIZE = DATABASE_STORE_REPLY_TOKEN_OFFSET + 4; + + // TunnelBuild + const size_t TUNNEL_BUILD_RECORD_SIZE = 528; + + //BuildRequestRecordClearText + const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0; + const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4; + const size_t BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET + 32; + const size_t BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4; + const size_t BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32; + const size_t BUILD_REQUEST_RECORD_IV_KEY_OFFSET = BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32; + const size_t BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32; + const size_t BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32; + const size_t BUILD_REQUEST_RECORD_FLAG_OFFSET = BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16; + const size_t BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = BUILD_REQUEST_RECORD_FLAG_OFFSET + 1; + const size_t BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4; + const size_t BUILD_REQUEST_RECORD_PADDING_OFFSET = BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4; + const size_t BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 222; + + // BuildRequestRecordEncrypted + const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0; + const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16; + + // BuildResponseRecord + const size_t BUILD_RESPONSE_RECORD_HASH_OFFSET = 0; + const size_t BUILD_RESPONSE_RECORD_PADDING_OFFSET = 32; + const size_t BUILD_RESPONSE_RECORD_PADDING_SIZE = 495; + const size_t BUILD_RESPONSE_RECORD_RET_OFFSET = BUILD_RESPONSE_RECORD_PADDING_OFFSET + BUILD_RESPONSE_RECORD_PADDING_SIZE; + + enum I2NPMessageType + { + eI2NPDatabaseStore = 1, + eI2NPDatabaseLookup = 2, + eI2NPDatabaseSearchReply = 3, + eI2NPDeliveryStatus = 10, + eI2NPGarlic = 11, + eI2NPTunnelData = 18, + eI2NPTunnelGateway = 19, + eI2NPData = 20, + eI2NPTunnelBuild = 21, + eI2NPTunnelBuildReply = 22, + eI2NPVariableTunnelBuild = 23, + eI2NPVariableTunnelBuildReply = 24 + }; + + const int NUM_TUNNEL_BUILD_RECORDS = 8; + +namespace tunnel +{ + class InboundTunnel; + class TunnelPool; +} + + const size_t I2NP_MAX_MESSAGE_SIZE = 32768; + const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 2400; + struct I2NPMessage + { + uint8_t * buf; + size_t len, offset, maxLen; + i2p::tunnel::InboundTunnel * from; + + I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2), + offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header + + // header accessors + uint8_t * GetHeader () { return GetBuffer (); }; + const uint8_t * GetHeader () const { return GetBuffer (); }; + void SetTypeID (uint8_t typeID) { GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = typeID; }; + uint8_t GetTypeID () const { return GetHeader ()[I2NP_HEADER_TYPEID_OFFSET]; }; + void SetMsgID (uint32_t msgID) { htobe32buf (GetHeader () + I2NP_HEADER_MSGID_OFFSET, msgID); }; + uint32_t GetMsgID () const { return bufbe32toh (GetHeader () + I2NP_HEADER_MSGID_OFFSET); }; + void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); }; + uint64_t GetExpiration () const { return bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET); }; + void SetSize (uint16_t size) { htobe16buf (GetHeader () + I2NP_HEADER_SIZE_OFFSET, size); }; + uint16_t GetSize () const { return bufbe16toh (GetHeader () + I2NP_HEADER_SIZE_OFFSET); }; + void UpdateSize () { SetSize (GetPayloadLength ()); }; + void SetChks (uint8_t chks) { GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = chks; }; + void UpdateChks () + { + uint8_t hash[32]; + CryptoPP::SHA256().CalculateDigest(hash, GetPayload (), GetPayloadLength ()); + GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0]; + } + + // payload + uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; }; + uint8_t * GetBuffer () { return buf + offset; }; + const uint8_t * GetBuffer () const { return buf + offset; }; + size_t GetLength () const { return len - offset; }; + size_t GetPayloadLength () const { return GetLength () - I2NP_HEADER_SIZE; }; + + void Align (size_t alignment) + { + size_t rem = ((size_t)GetBuffer ()) % alignment; + if (rem) + { + offset += (alignment - rem); + len += (alignment - rem); + } + } + + I2NPMessage& operator=(const I2NPMessage& other) + { + memcpy (buf + offset, other.buf + other.offset, other.GetLength ()); + len = offset + other.GetLength (); + from = other.from; + return *this; + } + + // for SSU only + uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; }; + void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular + { + const uint8_t * ssu = GetSSUHeader (); + GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid + SetMsgID (msgID); + SetExpiration (bufbe32toh (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET)*1000LL); + SetSize (len - offset - I2NP_HEADER_SIZE); + SetChks (0); + } + uint32_t ToSSU () // return msgID + { + uint8_t header[I2NP_HEADER_SIZE]; + memcpy (header, GetHeader (), I2NP_HEADER_SIZE); + uint8_t * ssu = GetSSUHeader (); + ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid + htobe32buf (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET, bufbe64toh (header + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL); + len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET); + return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET); + } + }; + + template + struct I2NPMessageBuffer: public I2NPMessage + { + I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; }; + uint8_t m_Buffer[sz]; + }; + + I2NPMessage * NewI2NPMessage (); + I2NPMessage * NewI2NPShortMessage (); + I2NPMessage * NewI2NPMessage (size_t len); + void DeleteI2NPMessage (I2NPMessage * msg); + void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID = 0); + void RenewI2NPMessageHeader (I2NPMessage * msg); + I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0); + I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from = nullptr); + + I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID); + I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, + uint32_t replyTunnelID, bool exploratory = false, std::set * excludedPeers = nullptr); + I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest, + const std::set& excludedFloodfills, + const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag); + I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, const i2p::data::RouterInfo * floodfill); + + I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr); + I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken = 0); + + bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText); + void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); + void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); + void HandleTunnelBuildMsg (uint8_t * buf, size_t len); + + I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf); + I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload); + + void HandleTunnelGatewayMsg (I2NPMessage * msg); + I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len); + I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, + const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); + I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg); + + size_t GetI2NPMessageLength (const uint8_t * msg); + void HandleI2NPMessage (uint8_t * msg, size_t len); + void HandleI2NPMessage (I2NPMessage * msg); +} + +#endif diff --git a/libi2pd/I2PEndian.cpp b/I2PEndian.cpp similarity index 54% rename from libi2pd/I2PEndian.cpp rename to I2PEndian.cpp index 5496f144..b8a041d8 100644 --- a/libi2pd/I2PEndian.cpp +++ b/I2PEndian.cpp @@ -1,11 +1,3 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - #include "I2PEndian.h" // http://habrahabr.ru/post/121811/ @@ -50,3 +42,42 @@ uint64_t be64toh(uint64_t big64) return u64.raw_value; } #endif + +/* it can be used in Windows 8 +#include + +uint16_t htobe16(uint16_t int16) +{ + return htons(int16); +} + +uint32_t htobe32(uint32_t int32) +{ + return htonl(int32); +} + +uint64_t htobe64(uint64_t int64) +{ + // http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx + //return htonll(int64); + return 0; +} + + +uint16_t be16toh(uint16_t big16) +{ + return ntohs(big16); +} + +uint32_t be32toh(uint32_t big32) +{ + return ntohl(big32); +} + +uint64_t be64toh(uint64_t big64) +{ + // http://msdn.microsoft.com/en-us/library/windows/desktop/jj710199%28v=vs.85%29.aspx + //return ntohll(big64); + return 0; +} +*/ \ No newline at end of file diff --git a/libi2pd/I2PEndian.h b/I2PEndian.h similarity index 57% rename from libi2pd/I2PEndian.h rename to I2PEndian.h index 681a4999..687dbc63 100644 --- a/libi2pd/I2PEndian.h +++ b/I2PEndian.h @@ -1,23 +1,14 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - #ifndef I2PENDIAN_H__ #define I2PENDIAN_H__ #include #include -#if defined(__FreeBSD__) || defined(__NetBSD__) -#include - -#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__GLIBC__) || defined(__HAIKU__) +#if defined(__linux__) || defined(__FreeBSD_kernel__) #include - +#elif __FreeBSD__ +#include #elif defined(__APPLE__) && defined(__MACH__) + #include #define htobe16(x) OSSwapHostToBigInt16(x) @@ -35,40 +26,6 @@ #define be64toh(x) OSSwapBigToHostInt64(x) #define le64toh(x) OSSwapLittleToHostInt64(x) -#elif defined(_WIN32) -#if defined(_MSC_VER) -#include -#define htobe16(x) _byteswap_ushort(x) -#define htole16(x) (x) -#define be16toh(x) _byteswap_ushort(x) -#define le16toh(x) (x) - -#define htobe32(x) _byteswap_ulong(x) -#define htole32(x) (x) -#define be32toh(x) _byteswap_ulong(x) -#define le32toh(x) (x) - -#define htobe64(x) _byteswap_uint64(x) -#define htole64(x) (x) -#define be64toh(x) _byteswap_uint64(x) -#define le64toh(x) (x) -#else -#define htobe16(x) __builtin_bswap16(x) -#define htole16(x) (x) -#define be16toh(x) __builtin_bswap16(x) -#define le16toh(x) (x) - -#define htobe32(x) __builtin_bswap32(x) -#define htole32(x) (x) -#define be32toh(x) __builtin_bswap32(x) -#define le32toh(x) (x) - -#define htobe64(x) __builtin_bswap64(x) -#define htole64(x) (x) -#define be64toh(x) __builtin_bswap64(x) -#define le64toh(x) (x) -#endif - #else #define NEEDS_LOCAL_ENDIAN #include @@ -156,35 +113,7 @@ inline void htobe64buf(void *buf, uint64_t big64) htobuf64(buf, htobe64(big64)); } -inline void htole16buf(void *buf, uint16_t big16) -{ - htobuf16(buf, htole16(big16)); -} -inline void htole32buf(void *buf, uint32_t big32) -{ - htobuf32(buf, htole32(big32)); -} - -inline void htole64buf(void *buf, uint64_t big64) -{ - htobuf64(buf, htole64(big64)); -} - -inline uint16_t bufle16toh(const void *buf) -{ - return le16toh(buf16toh(buf)); -} - -inline uint32_t bufle32toh(const void *buf) -{ - return le32toh(buf32toh(buf)); -} - -inline uint64_t bufle64toh(const void *buf) -{ - return le64toh(buf64toh(buf)); -} #endif // I2PENDIAN_H__ diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp new file mode 100644 index 00000000..e003f7e9 --- /dev/null +++ b/I2PTunnel.cpp @@ -0,0 +1,293 @@ +#include "base64.h" +#include "Log.h" +#include "Destination.h" +#include "ClientContext.h" +#include "I2PTunnel.h" + +namespace i2p +{ +namespace client +{ + I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, + boost::asio::ip::tcp::socket * socket, const i2p::data::LeaseSet * leaseSet): + m_Socket (socket), m_Owner (owner), m_RemoteEndpoint (socket->remote_endpoint ()), + m_IsQuiet (true) + { + m_Stream = m_Owner->GetLocalDestination ()->CreateStream (*leaseSet); + } + + I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, + boost::asio::ip::tcp::socket * socket, std::shared_ptr stream): + m_Socket (socket), m_Stream (stream), m_Owner (owner), + m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true) + { + } + + I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, std::shared_ptr stream, + boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target, bool quiet): + m_Socket (socket), m_Stream (stream), m_Owner (owner), m_RemoteEndpoint (target), m_IsQuiet (quiet) + { + } + + I2PTunnelConnection::~I2PTunnelConnection () + { + delete m_Socket; + } + + void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len) + { + if (msg) + m_Stream->Send (msg, len); // connect and send + else + m_Stream->Send (m_Buffer, 0); // connect + StreamReceive (); + Receive (); + } + + void I2PTunnelConnection::Connect () + { + if (m_Socket) + m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect, + shared_from_this (), std::placeholders::_1)); + } + + void I2PTunnelConnection::Terminate () + { + if (m_Stream) + { + m_Stream->Close (); + m_Stream.reset (); + } + m_Socket->close (); + if (m_Owner) + m_Owner->RemoveConnection (shared_from_this ()); + } + + void I2PTunnelConnection::Receive () + { + m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), + std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("I2PTunnel read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + if (m_Stream) + m_Stream->Send (m_Buffer, bytes_transferred); + Receive (); + } + } + + void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode) + { + if (ecode) + { + LogPrint ("I2PTunnel write error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + StreamReceive (); + } + + void I2PTunnelConnection::StreamReceive () + { + if (m_Stream) + m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), + std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (), + std::placeholders::_1, std::placeholders::_2), + I2P_TUNNEL_CONNECTION_MAX_IDLE); + } + + void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("I2PTunnel stream read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred), + std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); + } + } + + void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode) + { + if (ecode) + { + LogPrint ("I2PTunnel connect error: ", ecode.message ()); + Terminate (); + } + else + { + LogPrint ("I2PTunnel connected"); + if (m_IsQuiet) + StreamReceive (); + else + { + // send destination first like received from I2P + std::string dest = m_Stream->GetRemoteIdentity ().ToBase64 (); + dest += "\n"; + memcpy (m_StreamBuffer, dest.c_str (), dest.size ()); + HandleStreamReceive (boost::system::error_code (), dest.size ()); + } + Receive (); + } + } + + I2PTunnel::I2PTunnel (ClientDestination * localDestination) : + m_LocalDestination (localDestination ? localDestination : + i2p::client::context.CreateNewLocalDestination (false, I2P_TUNNEL_DEFAULT_KEY_TYPE)) + { + } + void I2PTunnel::AddConnection (std::shared_ptr conn) + { + m_Connections.insert (conn); + } + + void I2PTunnel::RemoveConnection (std::shared_ptr conn) + { + m_Connections.erase (conn); + } + + void I2PTunnel::ClearConnections () + { + m_Connections.clear (); + } + + I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination): + I2PTunnel (localDestination), + m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), + m_Timer (GetService ()), m_Destination (destination), m_DestinationIdentHash (nullptr) + { + } + + I2PClientTunnel::~I2PClientTunnel () + { + Stop (); + } + + void I2PClientTunnel::Start () + { + GetIdentHash(); + m_Acceptor.listen (); + Accept (); + } + + void I2PClientTunnel::Stop () + { + m_Acceptor.close(); + m_Timer.cancel (); + ClearConnections (); + auto *originalIdentHash = m_DestinationIdentHash; + m_DestinationIdentHash = nullptr; + delete originalIdentHash; + } + + /* HACK: maybe we should create a caching IdentHash provider in AddressBook */ + const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash () + { + if (!m_DestinationIdentHash) + { + i2p::data::IdentHash identHash; + if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash)) + m_DestinationIdentHash = new i2p::data::IdentHash (identHash); + else + LogPrint (eLogWarning,"Remote destination ", m_Destination, " not found"); + } + return m_DestinationIdentHash; + } + + + void I2PClientTunnel::Accept () + { + auto newSocket = new boost::asio::ip::tcp::socket (GetService ()); + m_Acceptor.async_accept (*newSocket, std::bind (&I2PClientTunnel::HandleAccept, this, + std::placeholders::_1, newSocket)); + } + + void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket) + { + if (!ecode) + { + const i2p::data::IdentHash *identHash = GetIdentHash(); + if (identHash) + GetLocalDestination ()->CreateStream ( + std::bind (&I2PClientTunnel::HandleStreamRequestComplete, + this, std::placeholders::_1, socket), *identHash); + else + { + LogPrint (eLogError,"Closing socket"); + delete socket; + } + Accept (); + } + else + { + LogPrint (eLogError,"Closing socket on accept because: ", ecode.message ()); + delete socket; + } + } + + void I2PClientTunnel::HandleStreamRequestComplete (std::shared_ptr stream, boost::asio::ip::tcp::socket * socket) + { + if (stream) + { + LogPrint (eLogInfo,"New I2PTunnel connection"); + auto connection = std::make_shared(this, socket, stream); + AddConnection (connection); + connection->I2PConnect (); + } + else + { + LogPrint (eLogError,"Issue when creating the stream, check the previous warnings for more info."); + delete socket; + } + } + + I2PServerTunnel::I2PServerTunnel (const std::string& address, int port, ClientDestination * localDestination): + I2PTunnel (localDestination), m_Endpoint (boost::asio::ip::address::from_string (address), port) + { + } + + void I2PServerTunnel::Start () + { + Accept (); + } + + void I2PServerTunnel::Stop () + { + ClearConnections (); + } + + void I2PServerTunnel::Accept () + { + auto localDestination = GetLocalDestination (); + if (localDestination) + localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); + else + LogPrint ("Local destination not set for server tunnel"); + } + + void I2PServerTunnel::HandleAccept (std::shared_ptr stream) + { + if (stream) + { + auto conn = std::make_shared (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint); + AddConnection (conn); + conn->Connect (); + } + } +} +} diff --git a/I2PTunnel.h b/I2PTunnel.h new file mode 100644 index 00000000..2a941f35 --- /dev/null +++ b/I2PTunnel.h @@ -0,0 +1,127 @@ +#ifndef I2PTUNNEL_H__ +#define I2PTUNNEL_H__ + +#include +#include +#include +#include +#include +#include "Identity.h" +#include "Destination.h" +#include "Streaming.h" + +namespace i2p +{ +namespace client +{ + const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192; + const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds + const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds + const i2p::data::SigningKeyType I2P_TUNNEL_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256; + + class I2PTunnel; + class I2PTunnelConnection: public std::enable_shared_from_this + { + public: + + I2PTunnelConnection (I2PTunnel * owner, boost::asio::ip::tcp::socket * socket, + const i2p::data::LeaseSet * leaseSet); // to I2P + I2PTunnelConnection (I2PTunnel * owner, boost::asio::ip::tcp::socket * socket, + std::shared_ptr stream); // to I2P using simplified API :) + I2PTunnelConnection (I2PTunnel * owner, std::shared_ptr stream, boost::asio::ip::tcp::socket * socket, + const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P + ~I2PTunnelConnection (); + + void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); + void Connect (); + + private: + + void Terminate (); + + void Receive (); + void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleWrite (const boost::system::error_code& ecode); + + void StreamReceive (); + void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleConnect (const boost::system::error_code& ecode); + + private: + + uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE]; + boost::asio::ip::tcp::socket * m_Socket; + std::shared_ptr m_Stream; + I2PTunnel * m_Owner; + boost::asio::ip::tcp::endpoint m_RemoteEndpoint; + bool m_IsQuiet; // don't send destination + }; + + class I2PTunnel + { + public: + + I2PTunnel (ClientDestination * localDestination = nullptr); + virtual ~I2PTunnel () { ClearConnections (); }; + + void AddConnection (std::shared_ptr conn); + void RemoveConnection (std::shared_ptr conn); + void ClearConnections (); + ClientDestination * GetLocalDestination () { return m_LocalDestination; }; + void SetLocalDestination (ClientDestination * dest) { m_LocalDestination = dest; }; + + boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); }; + + private: + + ClientDestination * m_LocalDestination; + std::set > m_Connections; + }; + + class I2PClientTunnel: public I2PTunnel + { + public: + + I2PClientTunnel (const std::string& destination, int port, ClientDestination * localDestination = nullptr); + ~I2PClientTunnel (); + + void Start (); + void Stop (); + + private: + + const i2p::data::IdentHash * GetIdentHash (); + void Accept (); + void HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket); + void HandleStreamRequestComplete (std::shared_ptr stream, boost::asio::ip::tcp::socket * socket); + + private: + + boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::deadline_timer m_Timer; + std::string m_Destination; + const i2p::data::IdentHash * m_DestinationIdentHash; + }; + + class I2PServerTunnel: public I2PTunnel + { + public: + + I2PServerTunnel (const std::string& address, int port, ClientDestination * localDestination); + + void Start (); + void Stop (); + + private: + + void Accept (); + void HandleAccept (std::shared_ptr stream); + + private: + + boost::asio::ip::tcp::endpoint m_Endpoint; + }; +} +} + +#endif diff --git a/Identity.cpp b/Identity.cpp new file mode 100644 index 00000000..410ddeb7 --- /dev/null +++ b/Identity.cpp @@ -0,0 +1,548 @@ +#include +#include +#include +#include +#include +#include "base64.h" +#include "CryptoConst.h" +#include "ElGamal.h" +#include "RouterContext.h" +#include "Identity.h" +#include "I2PEndian.h" + +namespace i2p +{ +namespace data +{ + Identity& Identity::operator=(const Keys& keys) + { + // copy public and signing keys together + memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey)); + memset (&certificate, 0, sizeof (certificate)); + return *this; + } + + size_t Identity::FromBuffer (const uint8_t * buf, size_t len) + { + memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE); + return DEFAULT_IDENTITY_SIZE; + } + + IdentHash Identity::Hash () const + { + IdentHash hash; + CryptoPP::SHA256().CalculateDigest(hash, publicKey, DEFAULT_IDENTITY_SIZE); + return hash; + } + + IdentityEx::IdentityEx (): + m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + { + } + + IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type) + { + memcpy (m_StandardIdentity.publicKey, publicKey, sizeof (m_StandardIdentity.publicKey)); + if (type != SIGNING_KEY_TYPE_DSA_SHA1) + { + size_t excessLen = 0; + uint8_t * excessBuf = nullptr; + switch (type) + { + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + { + size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 + memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH); + break; + } + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + { + size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96 + memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH); + break; + } + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + { + memcpy (m_StandardIdentity.signingKey, signingKey, 128); + excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132 - 128 + excessBuf = new uint8_t[excessLen]; + memcpy (excessBuf, signingKey + 128, excessLen); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + { + memcpy (m_StandardIdentity.signingKey, signingKey, 128); + excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256 - 128 + excessBuf = new uint8_t[excessLen]; + memcpy (excessBuf, signingKey + 128, excessLen); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + { + memcpy (m_StandardIdentity.signingKey, signingKey, 128); + excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384 - 128 + excessBuf = new uint8_t[excessLen]; + memcpy (excessBuf, signingKey + 128, excessLen); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + { + memcpy (m_StandardIdentity.signingKey, signingKey, 128); + excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512 - 128 + excessBuf = new uint8_t[excessLen]; + memcpy (excessBuf, signingKey + 128, excessLen); + break; + } + default: + LogPrint ("Signing key type ", (int)type, " is not supported"); + } + m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length + // fill certificate + m_StandardIdentity.certificate.type = CERTIFICATE_TYPE_KEY; + m_StandardIdentity.certificate.length = htobe16 (m_ExtendedLen); + // fill extended buffer + m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; + htobe16buf (m_ExtendedBuffer, type); + htobe16buf (m_ExtendedBuffer + 2, CRYPTO_KEY_TYPE_ELGAMAL); + if (excessLen && excessBuf) + { + memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen); + delete[] excessBuf; + } + // calculate ident hash + uint8_t * buf = new uint8_t[GetFullLen ()]; + ToBuffer (buf, GetFullLen ()); + CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ()); + delete[] buf; + } + else // DSA-SHA1 + { + memcpy (m_StandardIdentity.signingKey, signingKey, sizeof (m_StandardIdentity.signingKey)); + memset (&m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate)); + m_IdentHash = m_StandardIdentity.Hash (); + m_ExtendedLen = 0; + m_ExtendedBuffer = nullptr; + } + CreateVerifier (); + } + + IdentityEx::IdentityEx (const uint8_t * buf, size_t len): + m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr) + { + FromBuffer (buf, len); + } + + IdentityEx::IdentityEx (const IdentityEx& other): + m_Verifier (nullptr), m_ExtendedBuffer (nullptr) + { + *this = other; + } + + IdentityEx::~IdentityEx () + { + delete m_Verifier; + delete[] m_ExtendedBuffer; + } + + IdentityEx& IdentityEx::operator=(const IdentityEx& other) + { + memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE); + m_IdentHash = other.m_IdentHash; + + delete[] m_ExtendedBuffer; + m_ExtendedLen = other.m_ExtendedLen; + if (m_ExtendedLen > 0) + { + m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; + memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen); + } + else + m_ExtendedBuffer = nullptr; + + delete m_Verifier; + m_Verifier = nullptr; + + return *this; + } + + IdentityEx& IdentityEx::operator=(const Identity& standard) + { + m_StandardIdentity = standard; + m_IdentHash = m_StandardIdentity.Hash (); + + delete[] m_ExtendedBuffer; + m_ExtendedBuffer = nullptr; + m_ExtendedLen = 0; + + delete m_Verifier; + m_Verifier = nullptr; + + return *this; + } + + size_t IdentityEx::FromBuffer (const uint8_t * buf, size_t len) + { + if (len < DEFAULT_IDENTITY_SIZE) + { + LogPrint (eLogError, "Identity buffer length ", len, " is too small"); + return 0; + } + memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE); + + delete[] m_ExtendedBuffer; + if (m_StandardIdentity.certificate.length) + { + m_ExtendedLen = be16toh (m_StandardIdentity.certificate.length); + if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len) + { + m_ExtendedBuffer = new uint8_t[m_ExtendedLen]; + memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen); + } + else + { + LogPrint (eLogError, "Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE); + return 0; + } + } + else + { + m_ExtendedLen = 0; + m_ExtendedBuffer = nullptr; + } + CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ()); + + delete m_Verifier; + m_Verifier = nullptr; + + return GetFullLen (); + } + + size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const + { + memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE); + if (m_ExtendedLen > 0 && m_ExtendedBuffer) + memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen); + return GetFullLen (); + } + + size_t IdentityEx::FromBase64(const std::string& s) + { + uint8_t buf[1024]; + auto len = Base64ToByteStream (s.c_str(), s.length(), buf, 1024); + return FromBuffer (buf, len); + } + + std::string IdentityEx::ToBase64 () const + { + uint8_t buf[1024]; + char str[1536]; + size_t l = ToBuffer (buf, 1024); + size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, 1536); + str[l1] = 0; + return std::string (str); + } + + size_t IdentityEx::GetSigningPublicKeyLen () const + { + if (!m_Verifier) CreateVerifier (); + if (m_Verifier) + return m_Verifier->GetPublicKeyLen (); + return 128; + } + + size_t IdentityEx::GetSigningPrivateKeyLen () const + { + if (!m_Verifier) CreateVerifier (); + if (m_Verifier) + return m_Verifier->GetPrivateKeyLen (); + return GetSignatureLen ()/2; + } + + size_t IdentityEx::GetSignatureLen () const + { + if (!m_Verifier) CreateVerifier (); + if (m_Verifier) + return m_Verifier->GetSignatureLen (); + return 40; + } + bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const + { + if (!m_Verifier) CreateVerifier (); + if (m_Verifier) + return m_Verifier->Verify (buf, len, signature); + return false; + } + + SigningKeyType IdentityEx::GetSigningKeyType () const + { + if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer) + return bufbe16toh (m_ExtendedBuffer); // signing key + return SIGNING_KEY_TYPE_DSA_SHA1; + } + + CryptoKeyType IdentityEx::GetCryptoKeyType () const + { + if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer) + return bufbe16toh (m_ExtendedBuffer + 2); // crypto key + return CRYPTO_KEY_TYPE_ELGAMAL; + } + + void IdentityEx::CreateVerifier () const + { + auto keyType = GetSigningKeyType (); + switch (keyType) + { + case SIGNING_KEY_TYPE_DSA_SHA1: + m_Verifier = new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + { + size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64 + m_Verifier = new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding); + break; + } + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + { + size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96 + m_Verifier = new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding); + break; + } + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + { + uint8_t signingKey[i2p::crypto::ECDSAP521_KEY_LENGTH]; + memcpy (signingKey, m_StandardIdentity.signingKey, 128); + size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128 + memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types + m_Verifier = new i2p::crypto::ECDSAP521Verifier (signingKey); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + { + uint8_t signingKey[i2p::crypto::RSASHA2562048_KEY_LENGTH]; + memcpy (signingKey, m_StandardIdentity.signingKey, 128); + size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128 + memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types + m_Verifier = new i2p::crypto:: RSASHA2562048Verifier (signingKey); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + { + uint8_t signingKey[i2p::crypto::RSASHA3843072_KEY_LENGTH]; + memcpy (signingKey, m_StandardIdentity.signingKey, 128); + size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128 + memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types + m_Verifier = new i2p::crypto:: RSASHA3843072Verifier (signingKey); + break; + } + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + { + uint8_t signingKey[i2p::crypto::RSASHA5124096_KEY_LENGTH]; + memcpy (signingKey, m_StandardIdentity.signingKey, 128); + size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128 + memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types + m_Verifier = new i2p::crypto:: RSASHA5124096Verifier (signingKey); + break; + } + default: + LogPrint ("Signing key type ", (int)keyType, " is not supported"); + } + } + + void IdentityEx::DropVerifier () + { + auto verifier = m_Verifier; + m_Verifier = nullptr; // TODO: make this atomic + delete verifier; + } + + PrivateKeys& PrivateKeys::operator=(const Keys& keys) + { + m_Public = Identity (keys); + memcpy (m_PrivateKey, keys.privateKey, 256); // 256 + memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public.GetSigningPrivateKeyLen ()); + delete m_Signer; + m_Signer = nullptr; + CreateSigner (); + return *this; + } + + PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other) + { + m_Public = other.m_Public; + memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256 + memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public.GetSigningPrivateKeyLen ()); + delete m_Signer; + m_Signer = nullptr; + CreateSigner (); + return *this; + } + + size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len) + { + size_t ret = m_Public.FromBuffer (buf, len); + memcpy (m_PrivateKey, buf + ret, 256); // private key always 256 + ret += 256; + size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen (); + memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize); + ret += signingPrivateKeySize; + delete m_Signer; + m_Signer = nullptr; + CreateSigner (); + return ret; + } + + size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const + { + size_t ret = m_Public.ToBuffer (buf, len); + memcpy (buf + ret, m_PrivateKey, 256); // private key always 256 + ret += 256; + size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen (); + memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize); + ret += signingPrivateKeySize; + return ret; + } + + size_t PrivateKeys::FromBase64(const std::string& s) + { + uint8_t * buf = new uint8_t[s.length ()]; + size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ()); + size_t ret = FromBuffer (buf, l); + delete[] buf; + return ret; + } + + std::string PrivateKeys::ToBase64 () const + { + uint8_t * buf = new uint8_t[GetFullLen ()]; + char * str = new char[GetFullLen ()*2]; + size_t l = ToBuffer (buf, GetFullLen ()); + size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2); + str[l1] = 0; + delete[] buf; + std::string ret(str); + delete[] str; + return ret; + } + + void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const + { + if (m_Signer) + m_Signer->Sign (i2p::context.GetRandomNumberGenerator (), buf, len, signature); + } + + void PrivateKeys::CreateSigner () + { + switch (m_Public.GetSigningKeyType ()) + { + case SIGNING_KEY_TYPE_DSA_SHA1: + m_Signer = new i2p::crypto::DSASigner (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + m_Signer = new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + m_Signer = new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + m_Signer = new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + m_Signer = new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + m_Signer = new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + m_Signer = new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey); + break; + default: + LogPrint ("Signing key type ", (int)m_Public.GetSigningKeyType (), " is not supported"); + } + } + + PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type) + { + if (type != SIGNING_KEY_TYPE_DSA_SHA1) + { + PrivateKeys keys; + auto& rnd = i2p::context.GetRandomNumberGenerator (); + // signature + uint8_t signingPublicKey[512]; // signing public key is 512 bytes max + switch (type) + { + case SIGNING_KEY_TYPE_ECDSA_SHA256_P256: + i2p::crypto::CreateECDSAP256RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA384_P384: + i2p::crypto::CreateECDSAP384RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); + break; + case SIGNING_KEY_TYPE_ECDSA_SHA512_P521: + i2p::crypto::CreateECDSAP521RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA256_2048: + i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA384_3072: + i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); + break; + case SIGNING_KEY_TYPE_RSA_SHA512_4096: + i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey); + break; + default: + LogPrint ("Signing key type ", (int)type, " is not supported. Create DSA-SHA1"); + return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1 + } + // encryption + uint8_t publicKey[256]; + CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); + dh.GenerateKeyPair(rnd, keys.m_PrivateKey, publicKey); + // identity + keys.m_Public = IdentityEx (publicKey, signingPublicKey, type); + + keys.CreateSigner (); + return keys; + } + return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1 + } + + Keys CreateRandomKeys () + { + Keys keys; + auto& rnd = i2p::context.GetRandomNumberGenerator (); + // encryption + i2p::crypto::GenerateElGamalKeyPair(rnd, keys.privateKey, keys.publicKey); + // signing + i2p::crypto::CreateDSARandomKeys (rnd, keys.signingPrivateKey, keys.signingKey); + return keys; + } + + IdentHash CreateRoutingKey (const IdentHash& ident) + { + uint8_t buf[41]; // ident + yyyymmdd + memcpy (buf, (const uint8_t *)ident, 32); + time_t t = time (nullptr); + struct tm tm; +#ifdef _WIN32 + gmtime_s(&tm, &t); + sprintf_s((char *)(buf + 32), 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); +#else + gmtime_r(&t, &tm); + sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); +#endif + IdentHash key; + CryptoPP::SHA256().CalculateDigest((uint8_t *)key, buf, 40); + return key; + } + + XORMetric operator^(const IdentHash& key1, const IdentHash& key2) + { + XORMetric m; + const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL (); + m.metric_ll[0] = hash1[0] ^ hash2[0]; + m.metric_ll[1] = hash1[1] ^ hash2[1]; + m.metric_ll[2] = hash1[2] ^ hash2[2]; + m.metric_ll[3] = hash1[3] ^ hash2[3]; + return m; + } +} +} diff --git a/Identity.h b/Identity.h new file mode 100644 index 00000000..9fed07a1 --- /dev/null +++ b/Identity.h @@ -0,0 +1,261 @@ +#ifndef IDENTITY_H__ +#define IDENTITY_H__ + +#include +#include +#include +#include "base64.h" +#include "ElGamal.h" +#include "Signature.h" + +namespace i2p +{ +namespace data +{ + template + class Tag + { + public: + + Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); }; + Tag (const Tag& ) = default; +#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it + Tag (Tag&& ) = default; +#endif + Tag () = default; + + Tag& operator= (const Tag& ) = default; +#ifndef _WIN32 + Tag& operator= (Tag&& ) = default; +#endif + + uint8_t * operator()() { return m_Buf; }; + const uint8_t * operator()() const { return m_Buf; }; + + operator uint8_t * () { return m_Buf; }; + operator const uint8_t * () const { return m_Buf; }; + + const uint64_t * GetLL () const { return ll; }; + + bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); }; + bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; }; + + std::string ToBase64 () const + { + char str[sz*2]; + int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2); + str[l] = 0; + return std::string (str); + } + + std::string ToBase32 () const + { + char str[sz*2]; + int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2); + str[l] = 0; + return std::string (str); + } + + void FromBase32 (const std::string& s) + { + i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz); + } + + private: + + union // 8 bytes alignment + { + uint8_t m_Buf[sz]; + uint64_t ll[sz/8]; + }; + }; + typedef Tag<32> IdentHash; + +#pragma pack(1) + struct Keys + { + uint8_t privateKey[256]; + uint8_t signingPrivateKey[20]; + uint8_t publicKey[256]; + uint8_t signingKey[128]; + }; + + const uint8_t CERTIFICATE_TYPE_NULL = 0; + const uint8_t CERTIFICATE_TYPE_HASHCASH = 1; + const uint8_t CERTIFICATE_TYPE_HIDDEN = 2; + const uint8_t CERTIFICATE_TYPE_SIGNED = 3; + const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4; + const uint8_t CERTIFICATE_TYPE_KEY = 5; + + struct Identity + { + uint8_t publicKey[256]; + uint8_t signingKey[128]; + struct + { + uint8_t type; + uint16_t length; + } certificate; + + Identity () = default; + Identity (const Keys& keys) { *this = keys; }; + Identity& operator=(const Keys& keys); + size_t FromBuffer (const uint8_t * buf, size_t len); + IdentHash Hash () const; + }; +#pragma pack() + Keys CreateRandomKeys (); + + const size_t DEFAULT_IDENTITY_SIZE = sizeof (Identity); // 387 bytes + + const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0; + const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0; + const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1; + const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA384_P384 = 2; + const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA512_P521 = 3; + const uint16_t SIGNING_KEY_TYPE_RSA_SHA256_2048 = 4; + const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5; + const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6; + typedef uint16_t SigningKeyType; + typedef uint16_t CryptoKeyType; + + class IdentityEx + { + public: + + IdentityEx (); + IdentityEx (const uint8_t * publicKey, const uint8_t * signingKey, + SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1); + IdentityEx (const uint8_t * buf, size_t len); + IdentityEx (const IdentityEx& other); + ~IdentityEx (); + IdentityEx& operator=(const IdentityEx& other); + IdentityEx& operator=(const Identity& standard); + + size_t FromBuffer (const uint8_t * buf, size_t len); + size_t ToBuffer (uint8_t * buf, size_t len) const; + size_t FromBase64(const std::string& s); + std::string ToBase64 () const; + const Identity& GetStandardIdentity () const { return m_StandardIdentity; }; + const IdentHash& GetIdentHash () const { return m_IdentHash; }; + size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; }; + size_t GetSigningPublicKeyLen () const; + size_t GetSigningPrivateKeyLen () const; + size_t GetSignatureLen () const; + bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; + SigningKeyType GetSigningKeyType () const; + CryptoKeyType GetCryptoKeyType () const; + void DropVerifier (); // to save memory + + private: + + void CreateVerifier () const; + + private: + + Identity m_StandardIdentity; + IdentHash m_IdentHash; + mutable i2p::crypto::Verifier * m_Verifier; + size_t m_ExtendedLen; + uint8_t * m_ExtendedBuffer; + }; + + class PrivateKeys // for eepsites + { + public: + + PrivateKeys (): m_Signer (nullptr) {}; + PrivateKeys (const PrivateKeys& other): m_Signer (nullptr) { *this = other; }; + PrivateKeys (const Keys& keys): m_Signer (nullptr) { *this = keys; }; + PrivateKeys& operator=(const Keys& keys); + PrivateKeys& operator=(const PrivateKeys& other); + ~PrivateKeys () { delete m_Signer; }; + + const IdentityEx& GetPublic () const { return m_Public; }; + const uint8_t * GetPrivateKey () const { return m_PrivateKey; }; + const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; }; + void Sign (const uint8_t * buf, int len, uint8_t * signature) const; + + size_t GetFullLen () const { return m_Public.GetFullLen () + 256 + m_Public.GetSigningPrivateKeyLen (); }; + size_t FromBuffer (const uint8_t * buf, size_t len); + size_t ToBuffer (uint8_t * buf, size_t len) const; + + size_t FromBase64(const std::string& s); + std::string ToBase64 () const; + + static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1); + + private: + + void CreateSigner (); + + private: + + IdentityEx m_Public; + uint8_t m_PrivateKey[256]; + uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes + i2p::crypto::Signer * m_Signer; + }; + + // kademlia + struct XORMetric + { + union + { + uint8_t metric[32]; + uint64_t metric_ll[4]; + }; + + void SetMin () { memset (metric, 0, 32); }; + void SetMax () { memset (metric, 0xFF, 32); }; + bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; }; + }; + + IdentHash CreateRoutingKey (const IdentHash& ident); + XORMetric operator^(const IdentHash& key1, const IdentHash& key2); + + // destination for delivery instuctions + class RoutingDestination + { + public: + + RoutingDestination (): m_ElGamalEncryption (nullptr) {}; + virtual ~RoutingDestination () { delete m_ElGamalEncryption; }; + + virtual const IdentHash& GetIdentHash () const = 0; + virtual const uint8_t * GetEncryptionPublicKey () const = 0; + virtual bool IsDestination () const = 0; // for garlic + + i2p::crypto::ElGamalEncryption * GetElGamalEncryption () const + { + if (!m_ElGamalEncryption) + m_ElGamalEncryption = new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ()); + return m_ElGamalEncryption; + } + + private: + + mutable i2p::crypto::ElGamalEncryption * m_ElGamalEncryption; // use lazy initialization + }; + + class LocalDestination + { + public: + + virtual ~LocalDestination() {}; + virtual const PrivateKeys& GetPrivateKeys () const = 0; + virtual const uint8_t * GetEncryptionPrivateKey () const = 0; + virtual const uint8_t * GetEncryptionPublicKey () const = 0; + + const IdentityEx& GetIdentity () const { return GetPrivateKeys ().GetPublic (); }; + const IdentHash& GetIdentHash () const { return GetIdentity ().GetIdentHash (); }; + void Sign (const uint8_t * buf, int len, uint8_t * signature) const + { + GetPrivateKeys ().Sign (buf, len, signature); + }; + }; +} +} + + +#endif diff --git a/LICENSE b/LICENSE index f59491f5..e26c6733 100644 --- a/LICENSE +++ b/LICENSE @@ -1,27 +1,339 @@ -Copyright (c) 2013-2025, The PurpleI2P Project +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 -All rights reserved. + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. -Redistribution and use in source and binary forms, with or without modification, are -permitted provided that the following conditions are met: + Preamble -1. Redistributions of source code must retain the above copyright notice, this list of -conditions and the following disclaimer. + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. -2. Redistributions in binary form must reproduce the above copyright notice, this list of -conditions and the following disclaimer in the documentation and/or other materials -provided with the distribution. + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. -3. Neither the name of the copyright holder nor the names of its contributors may be used -to endorse or promote products derived from this software without specific prior written -permission. + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + i2p router for Linux written on C++ + Copyright (C) 2013 orignal + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/LeaseSet.cpp b/LeaseSet.cpp new file mode 100644 index 00000000..1a2c7d39 --- /dev/null +++ b/LeaseSet.cpp @@ -0,0 +1,131 @@ +#include +#include "I2PEndian.h" +#include +#include "CryptoConst.h" +#include "Log.h" +#include "Timestamp.h" +#include "NetDb.h" +#include "TunnelPool.h" +#include "LeaseSet.h" + +namespace i2p +{ +namespace data +{ + + LeaseSet::LeaseSet (const uint8_t * buf, int len) + { + memcpy (m_Buffer, buf, len); + m_BufferLen = len; + ReadFromBuffer (); + } + + LeaseSet::LeaseSet (const i2p::tunnel::TunnelPool& pool) + { + // header + const i2p::data::LocalDestination * localDestination = pool.GetLocalDestination (); + if (!localDestination) + { + m_BufferLen = 0; + LogPrint (eLogError, "Destination for local LeaseSet doesn't exist"); + return; + } + m_BufferLen = localDestination->GetIdentity ().ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE); + memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256); + m_BufferLen += 256; + auto signingKeyLen = localDestination->GetIdentity ().GetSigningPublicKeyLen (); + memset (m_Buffer + m_BufferLen, 0, signingKeyLen); + m_BufferLen += signingKeyLen; + auto tunnels = pool.GetInboundTunnels (5); // 5 tunnels maximum + m_Buffer[m_BufferLen] = tunnels.size (); // num leases + m_BufferLen++; + // leases + for (auto it: tunnels) + { + Lease lease; + memcpy (lease.tunnelGateway, it->GetNextIdentHash (), 32); + lease.tunnelID = htobe32 (it->GetNextTunnelID ()); + uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - 60; // 1 minute before expiration + ts *= 1000; // in milliseconds + lease.endDate = htobe64 (ts); + memcpy(m_Buffer + m_BufferLen, &lease, sizeof(Lease)); + m_BufferLen += sizeof (Lease); + } + // signature + localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen); + m_BufferLen += localDestination->GetIdentity ().GetSignatureLen (); + LogPrint ("Local LeaseSet of ", tunnels.size (), " leases created"); + + ReadFromBuffer (); + } + + void LeaseSet::Update (const uint8_t * buf, int len) + { + m_Leases.clear (); + memcpy (m_Buffer, buf, len); + m_BufferLen = len; + ReadFromBuffer (); + } + + void LeaseSet::ReadFromBuffer () + { + size_t size = m_Identity.FromBuffer (m_Buffer, m_BufferLen); + memcpy (m_EncryptionKey, m_Buffer + size, 256); + size += 256; // encryption key + size += m_Identity.GetSigningPublicKeyLen (); // unused signing key + uint8_t num = m_Buffer[size]; + size++; // num + LogPrint ("LeaseSet num=", (int)num); + + // process leases + const uint8_t * leases = m_Buffer + size; + for (int i = 0; i < num; i++) + { + Lease lease; + memcpy (&lease, leases, sizeof(Lease)); + lease.tunnelID = be32toh (lease.tunnelID); + lease.endDate = be64toh (lease.endDate); + m_Leases.push_back (lease); + leases += sizeof (Lease); + + // check if lease's gateway is in our netDb + if (!netdb.FindRouter (lease.tunnelGateway)) + { + // if not found request it + LogPrint ("Lease's tunnel gateway not found. Requested"); + netdb.RequestDestination (lease.tunnelGateway); + } + } + + // verify + if (!m_Identity.Verify (m_Buffer, leases - m_Buffer, leases)) + LogPrint ("LeaseSet verification failed"); + } + + const std::vector LeaseSet::GetNonExpiredLeases () const + { + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + std::vector leases; + for (auto& it: m_Leases) + if (ts < it.endDate) + leases.push_back (it); + return leases; + } + + bool LeaseSet::HasExpiredLeases () const + { + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + for (auto& it: m_Leases) + if (ts >= it.endDate) return true; + return false; + } + + bool LeaseSet::HasNonExpiredLeases () const + { + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + for (auto& it: m_Leases) + if (ts < it.endDate) return true; + return false; + } +} +} diff --git a/LeaseSet.h b/LeaseSet.h new file mode 100644 index 00000000..9b555daa --- /dev/null +++ b/LeaseSet.h @@ -0,0 +1,78 @@ +#ifndef LEASE_SET_H__ +#define LEASE_SET_H__ + +#include +#include +#include +#include "Identity.h" + +namespace i2p +{ + +namespace tunnel +{ + class TunnelPool; +} + +namespace data +{ + +#pragma pack(1) + + struct Lease + { + uint8_t tunnelGateway[32]; + uint32_t tunnelID; + uint64_t endDate; + + bool operator< (const Lease& other) const + { + if (endDate != other.endDate) + return endDate > other.endDate; + else + return tunnelID < other.tunnelID; + } + }; + +#pragma pack() + + const int MAX_LS_BUFFER_SIZE = 3072; + class LeaseSet: public RoutingDestination + { + public: + + LeaseSet (const uint8_t * buf, int len); + LeaseSet (const LeaseSet& ) = default; + LeaseSet (const i2p::tunnel::TunnelPool& pool); + LeaseSet& operator=(const LeaseSet& ) = default; + void Update (const uint8_t * buf, int len); + const IdentityEx& GetIdentity () const { return m_Identity; }; + + const uint8_t * GetBuffer () const { return m_Buffer; }; + size_t GetBufferLen () const { return m_BufferLen; }; + + // implements RoutingDestination + const IdentHash& GetIdentHash () const { return m_Identity.GetIdentHash (); }; + const std::vector& GetLeases () const { return m_Leases; }; + const std::vector GetNonExpiredLeases () const; + bool HasExpiredLeases () const; + bool HasNonExpiredLeases () const; + const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; }; + bool IsDestination () const { return true; }; + + private: + + void ReadFromBuffer (); + + private: + + std::vector m_Leases; + IdentityEx m_Identity; + uint8_t m_EncryptionKey[256]; + uint8_t m_Buffer[MAX_LS_BUFFER_SIZE]; + size_t m_BufferLen; + }; +} +} + +#endif diff --git a/LittleBigEndian.h b/LittleBigEndian.h new file mode 100644 index 00000000..69f10ee9 --- /dev/null +++ b/LittleBigEndian.h @@ -0,0 +1,242 @@ +// LittleBigEndian.h fixed for 64-bits added union +// + +#ifndef LITTLEBIGENDIAN_H +#define LITTLEBIGENDIAN_H + +// Determine Little-Endian or Big-Endian + +#define CURRENT_BYTE_ORDER (*(int *)"\x01\x02\x03\x04") +#define LITTLE_ENDIAN_BYTE_ORDER 0x04030201 +#define BIG_ENDIAN_BYTE_ORDER 0x01020304 +#define PDP_ENDIAN_BYTE_ORDER 0x02010403 + +#define IS_LITTLE_ENDIAN (CURRENT_BYTE_ORDER == LITTLE_ENDIAN_BYTE_ORDER) +#define IS_BIG_ENDIAN (CURRENT_BYTE_ORDER == BIG_ENDIAN_BYTE_ORDER) +#define IS_PDP_ENDIAN (CURRENT_BYTE_ORDER == PDP_ENDIAN_BYTE_ORDER) + +// Forward declaration + +template +struct LittleEndian; + +template +struct BigEndian; + +// Little-Endian template + +#pragma pack(push,1) +template +struct LittleEndian +{ + union + { + unsigned char bytes[sizeof(T)]; + T raw_value; + }; + + LittleEndian(T t = T()) + { + operator =(t); + } + + LittleEndian(const LittleEndian & t) + { + raw_value = t.raw_value; + } + + LittleEndian(const BigEndian & t) + { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[i] = t.bytes[sizeof(T)-1-i]; + } + + operator const T() const + { + T t = T(); + for (unsigned i = 0; i < sizeof(T); i++) + t |= T(bytes[i]) << (i << 3); + return t; + } + + const T operator = (const T t) + { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[sizeof(T)-1 - i] = static_cast(t >> (i << 3)); + return t; + } + + // operators + + const T operator += (const T t) + { + return (*this = *this + t); + } + + const T operator -= (const T t) + { + return (*this = *this - t); + } + + const T operator *= (const T t) + { + return (*this = *this * t); + } + + const T operator /= (const T t) + { + return (*this = *this / t); + } + + const T operator %= (const T t) + { + return (*this = *this % t); + } + + LittleEndian operator ++ (int) + { + LittleEndian tmp(*this); + operator ++ (); + return tmp; + } + + LittleEndian & operator ++ () + { + for (unsigned i = 0; i < sizeof(T); i++) + { + ++bytes[i]; + if (bytes[i] != 0) + break; + } + return (*this); + } + + LittleEndian operator -- (int) + { + LittleEndian tmp(*this); + operator -- (); + return tmp; + } + + LittleEndian & operator -- () + { + for (unsigned i = 0; i < sizeof(T); i++) + { + --bytes[i]; + if (bytes[i] != (T)(-1)) + break; + } + return (*this); + } +}; +#pragma pack(pop) + +// Big-Endian template + +#pragma pack(push,1) +template +struct BigEndian +{ + union + { + unsigned char bytes[sizeof(T)]; + T raw_value; + }; + + BigEndian(T t = T()) + { + operator =(t); + } + + BigEndian(const BigEndian & t) + { + raw_value = t.raw_value; + } + + BigEndian(const LittleEndian & t) + { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[i] = t.bytes[sizeof(T)-1-i]; + } + + operator const T() const + { + T t = T(); + for (unsigned i = 0; i < sizeof(T); i++) + t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3); + return t; + } + + const T operator = (const T t) + { + for (unsigned i = 0; i < sizeof(T); i++) + bytes[sizeof(T) - 1 - i] = t >> (i << 3); + return t; + } + + // operators + + const T operator += (const T t) + { + return (*this = *this + t); + } + + const T operator -= (const T t) + { + return (*this = *this - t); + } + + const T operator *= (const T t) + { + return (*this = *this * t); + } + + const T operator /= (const T t) + { + return (*this = *this / t); + } + + const T operator %= (const T t) + { + return (*this = *this % t); + } + + BigEndian operator ++ (int) + { + BigEndian tmp(*this); + operator ++ (); + return tmp; + } + + BigEndian & operator ++ () + { + for (unsigned i = 0; i < sizeof(T); i++) + { + ++bytes[sizeof(T) - 1 - i]; + if (bytes[sizeof(T) - 1 - i] != 0) + break; + } + return (*this); + } + + BigEndian operator -- (int) + { + BigEndian tmp(*this); + operator -- (); + return tmp; + } + + BigEndian & operator -- () + { + for (unsigned i = 0; i < sizeof(T); i++) + { + --bytes[sizeof(T) - 1 - i]; + if (bytes[sizeof(T) - 1 - i] != (T)(-1)) + break; + } + return (*this); + } +}; +#pragma pack(pop) + +#endif // LITTLEBIGENDIAN_H \ No newline at end of file diff --git a/Log.cpp b/Log.cpp new file mode 100644 index 00000000..ac2ba756 --- /dev/null +++ b/Log.cpp @@ -0,0 +1,43 @@ +#include "Log.h" +#include + +Log * g_Log = nullptr; + +static const char * g_LogLevelStr[eNumLogLevels] = +{ + "error", // eLogError + "warn", // eLogWarning + "info", // eLogInfo + "debug" // eLogDebug +}; + +void LogMsg::Process() +{ + output << boost::posix_time::second_clock::local_time().time_of_day () << + "/" << g_LogLevelStr[level] << " - "; + output << s.str(); +} + +void Log::Flush () +{ + if (m_LogStream) + m_LogStream->flush(); +} + +void Log::SetLogFile (const std::string& fullFilePath) +{ + auto logFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); + if (logFile->is_open ()) + { + SetLogStream (logFile); + LogPrint("Logging to file ", fullFilePath, " enabled."); + } + else + delete logFile; +} + +void Log::SetLogStream (std::ostream * logStream) +{ + if (m_LogStream) delete m_LogStream; + m_LogStream = logStream; +} diff --git a/Log.h b/Log.h new file mode 100644 index 00000000..b77528c5 --- /dev/null +++ b/Log.h @@ -0,0 +1,117 @@ +#ifndef LOG_H__ +#define LOG_H__ + +#include +#include +#include +#include +#include +#include "Queue.h" + +enum LogLevel +{ + eLogError = 0, + eLogWarning, + eLogInfo, + eLogDebug, + eNumLogLevels +}; + +struct LogMsg +{ + std::stringstream s; + std::ostream& output; + LogLevel level; + + LogMsg (std::ostream& o = std::cout, LogLevel l = eLogInfo): output (o), level (l) {}; + + void Process(); +}; + +class Log: public i2p::util::MsgQueue +{ + public: + + Log (): m_LogStream (nullptr) { SetOnEmpty (std::bind (&Log::Flush, this)); }; + ~Log () { delete m_LogStream; }; + + void SetLogFile (const std::string& fullFilePath); + void SetLogStream (std::ostream * logStream); + std::ostream * GetLogStream () const { return m_LogStream; }; + + private: + + void Flush (); + + private: + + std::ostream * m_LogStream; +}; + +extern Log * g_Log; + +inline void StartLog (const std::string& fullFilePath) +{ + if (!g_Log) + { + g_Log = new Log (); + if (fullFilePath.length () > 0) + g_Log->SetLogFile (fullFilePath); + } +} + +inline void StartLog (std::ostream * s) +{ + if (!g_Log) + { + g_Log = new Log (); + if (s) + g_Log->SetLogStream (s); + } +} + +inline void StopLog () +{ + if (g_Log) + { + delete g_Log; + g_Log = nullptr; + } +} + +template +void LogPrint (std::stringstream& s, TValue arg) +{ + s << arg; +} + +template +void LogPrint (std::stringstream& s, TValue arg, TArgs... args) +{ + LogPrint (s, arg); + LogPrint (s, args...); +} + +template +void LogPrint (LogLevel level, TArgs... args) +{ + LogMsg * msg = (g_Log && g_Log->GetLogStream ()) ? new LogMsg (*g_Log->GetLogStream (), level) : + new LogMsg (std::cout, level); + LogPrint (msg->s, args...); + msg->s << std::endl; + if (g_Log) + g_Log->Put (msg); + else + { + msg->Process (); + delete msg; + } +} + +template +void LogPrint (TArgs... args) +{ + LogPrint (eLogInfo, args...); +} + +#endif diff --git a/Makefile b/Makefile index 0d4ca48c..87a55fdb 100644 --- a/Makefile +++ b/Makefile @@ -1,113 +1,33 @@ -.DEFAULT_GOAL := all +UNAME := $(shell uname -s) +SHLIB := libi2pd.so +I2PD := i2p +GREP := fgrep +DEPS := obj/make.dep -SYS := $(shell $(CXX) -dumpmachine) - -ifneq (, $(findstring darwin, $(SYS))) - SHARED_SUFFIX = dylib -else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS))) - SHARED_SUFFIX = dll -else - SHARED_SUFFIX = so -endif - -SHLIB := libi2pd.$(SHARED_SUFFIX) -ARLIB := libi2pd.a -SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX) -ARLIB_LANG := libi2pdlang.a -SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX) -ARLIB_CLIENT := libi2pdclient.a -SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX) -ARLIB_WRAP := libi2pdwrapper.a -I2PD := i2pd - -LIB_SRC_DIR := libi2pd -LIB_CLIENT_SRC_DIR := libi2pd_client -WRAP_SRC_DIR := libi2pd_wrapper -LANG_SRC_DIR := i18n -DAEMON_SRC_DIR := daemon - -# import source files lists include filelist.mk -USE_STATIC := $(or $(USE_STATIC),no) -USE_UPNP := $(or $(USE_UPNP),no) -DEBUG := $(or $(DEBUG),yes) +USE_AESNI := yes +USE_STATIC := no -# for debugging purposes only, when commit hash needed in trunk builds in i2pd version string -USE_GIT_VERSION := $(or $(USE_GIT_VERSION),no) - -# for MacOS only, waiting for "1", not "yes" -HOMEBREW := $(or $(HOMEBREW),0) - -ifeq ($(DEBUG),yes) - CXX_DEBUG = -g -else - CXX_DEBUG = -Os - LD_DEBUG = -s -endif - -ifneq (, $(DESTDIR)) - PREFIX = $(DESTDIR) -endif - -ifneq (, $(findstring darwin, $(SYS))) - DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp - ifeq ($(HOMEBREW),1) - include Makefile.homebrew - else - include Makefile.osx - endif -else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS))) - DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp - include Makefile.mingw -else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS))) - DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp - include Makefile.linux -else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS))) - DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp +ifeq ($(UNAME),Darwin) + DAEMON_SRC += DaemonLinux.cpp + include Makefile.osx +else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1) + DAEMON_SRC += DaemonLinux.cpp include Makefile.bsd -else ifneq (, $(findstring haiku, $(SYS))) - DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp - include Makefile.haiku -else ifneq (, $(findstring solaris, $(SYS))) - DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp - include Makefile.solaris -else # not supported - $(error Not supported platform) +else ifeq ($(UNAME),Linux) + DAEMON_SRC += DaemonLinux.cpp + include Makefile.linux +else # win32 + DAEMON_SRC += DaemonWin32.cpp endif -INCFLAGS += -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR) -DEFINES += -DOPENSSL_SUPPRESS_DEPRECATED -NEEDED_CXXFLAGS += -MMD -MP +all: mk_build_dir $(SHLIB) $(I2PD) -ifeq ($(USE_GIT_VERSION),yes) - GIT_VERSION := $(shell git describe --tags) - DEFINES += -DGITVER=$(GIT_VERSION) -endif +mk_build_dir: + test -d obj || mkdir obj -LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC)) -LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC)) -LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC)) -DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) -WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC)) -DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d) - -## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary -all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD) - -mk_obj_dir: - @mkdir -p obj/$(LIB_SRC_DIR) - @mkdir -p obj/$(LIB_CLIENT_SRC_DIR) - @mkdir -p obj/$(LANG_SRC_DIR) - @mkdir -p obj/$(DAEMON_SRC_DIR) - @mkdir -p obj/$(WRAP_SRC_DIR) - @mkdir -p obj/Win32 - -api: $(SHLIB) $(ARLIB) -client: $(SHLIB_CLIENT) $(ARLIB_CLIENT) -lang: $(SHLIB_LANG) $(ARLIB_LANG) -api_client: api client lang -wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP) +api: $(SHLIB) ## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time ## **without** overwriting the CXXFLAGS which we need in order to build. @@ -116,77 +36,38 @@ wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP) ## -std=c++11. If you want to remove this variable please do so in a way that allows setting ## custom FLAGS to work at build-time. -obj/%.o: %.cpp | mk_obj_dir - $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(DEFINES) $(INCFLAGS) -c -o $@ $< +deps: + @test -d obj || mkdir obj + $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS) + @sed -i -e '/\.o:/ s/^/obj\//' $(DEPS) + +obj/%.o : %.cpp + @test -d obj || mkdir obj + $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $< # '-' is 'ignore if missing' on first run -include $(DEPS) -$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) - $(CXX) $(DEFINES) $(LDFLAGS) -o $@ $^ $(LDLIBS) +$(I2PD): $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) + $(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS) -$(SHLIB): $(LIB_OBJS) +$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC)) ifneq ($(USE_STATIC),yes) - $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) + $(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^ endif -$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS) $(SHLIB) $(SHLIB_LANG) -ifneq ($(USE_STATIC),yes) - $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG) -endif - -$(SHLIB_WRAP): $(WRAP_LIB_OBJS) -ifneq ($(USE_STATIC),yes) - $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) -endif - -$(SHLIB_LANG): $(LANG_OBJS) -ifneq ($(USE_STATIC),yes) - $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) -endif - -$(ARLIB): $(LIB_OBJS) - $(AR) -r $@ $^ - -$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS) - $(AR) -r $@ $^ - -$(ARLIB_WRAP): $(WRAP_LIB_OBJS) - $(AR) -r $@ $^ - -$(ARLIB_LANG): $(LANG_OBJS) - $(AR) -r $@ $^ - clean: - $(RM) -r obj - $(RM) -r docs/generated - $(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WRAP) $(ARLIB_WRAP) + rm -rf obj + $(RM) $(I2PD) $(SHLIB) -strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG) - strip $^ - -LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl) -BRANCH=$(shell git rev-parse --abbrev-ref HEAD) +LATEST_TAG=$(shell git describe --tags --abbrev=0 master) dist: git archive --format=tar.gz -9 --worktree-attributes \ --prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz -last-dist: - git archive --format=tar.gz -9 --worktree-attributes \ - --prefix=i2pd_$(LATEST_TAG)/ $(BRANCH) -o ../i2pd_$(LATEST_TAG).orig.tar.gz - -doxygen: - doxygen -s docs/Doxyfile - .PHONY: all .PHONY: clean -.PHONY: doxygen +.PHONY: deps .PHONY: dist -.PHONY: last-dist .PHONY: api -.PHONY: api_client -.PHONY: client -.PHONY: lang -.PHONY: mk_obj_dir -.PHONY: install -.PHONY: strip +.PHONY: mk_build_dir diff --git a/Makefile.bsd b/Makefile.bsd index 1c911802..c6c3ce65 100644 --- a/Makefile.bsd +++ b/Makefile.bsd @@ -1,22 +1,12 @@ -CXX = clang++ -CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation -DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1 -INCFLAGS = -I/usr/include/ -I/usr/local/include/ -LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib -LDLIBS = -lssl -lcrypto -lz -lpthread -lboost_system -lboost_program_options - +CXX = g++ +CXXFLAGS = -O2 ## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time ## **without** overwriting the CXXFLAGS which we need in order to build. ## For example, when adding 'hardening flags' to the build ## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove ## -std=c++11. If you want to remove this variable please do so in a way that allows setting ## custom FLAGS to work at build-time. -CXXVER := $(shell $(CXX) -dumpversion|cut -c 1-2) -ifeq (${CXXVER}, "4.") # older clang always returned 4.2.1 - $(error Compiler too old) -else ifeq (${CXXVER}, ${filter ${CXXVER},16 17 18 19}) # clang 16 - 19 - NEEDED_CXXFLAGS = -std=c++20 -else - NEEDED_CXXFLAGS = -std=c++17 -endif - +NEEDED_CXXFLAGS = -std=c++11 +INCFLAGS = -I/usr/include/ -I/usr/local/include/ +LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib +LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread diff --git a/Makefile.haiku b/Makefile.haiku deleted file mode 100644 index eb56a207..00000000 --- a/Makefile.haiku +++ /dev/null @@ -1,14 +0,0 @@ -ifeq ($(shell $(CXX) -dumpmachine | cut -c 1-4), i586) -CXX = g++-x86 -else -CXX = g++ -endif -CXXFLAGS := -Wall -std=c++20 -INCFLAGS = -I/system/develop/headers -DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE -LDLIBS = -lbe -lbsd -lnetwork -lz -lssl -lcrypto -lboost_program_options -lpthread - -ifeq ($(USE_UPNP),yes) - DEFINES += -DUSE_UPNP - LDLIBS += -lminiupnpc -endif diff --git a/Makefile.homebrew b/Makefile.homebrew deleted file mode 100644 index 706f9811..00000000 --- a/Makefile.homebrew +++ /dev/null @@ -1,49 +0,0 @@ -# root directory holding homebrew -BREWROOT = /opt/homebrew -BOOSTROOT = ${BREWROOT}/opt/boost -SSLROOT = ${BREWROOT}/opt/openssl@1.1 -UPNPROOT = ${BREWROOT}/opt/miniupnpc - -CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wno-overloaded-virtual -NEEDED_CXXFLAGS ?= -std=c++17 -INCFLAGS ?= -I${SSLROOT}/include -I${BOOSTROOT}/include -LDFLAGS ?= ${LD_DEBUG} -DEFINES += -DMAC_OSX - -ifeq ($(USE_STATIC),yes) - LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a -ifeq ($(USE_UPNP),yes) - LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a -endif - LDLIBS += -lpthread -ldl -else - LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib - LDLIBS = -lz -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lpthread -ifeq ($(USE_UPNP),yes) - LDFLAGS += -L${UPNPROOT}/lib - LDLIBS += -lminiupnpc -endif -endif - -ifeq ($(USE_UPNP),yes) - DEFINES += -DUSE_UPNP - INCFLAGS += -I${UPNPROOT}/include -endif - -install: all - install -d ${PREFIX}/bin - install -m 755 ${I2PD} ${PREFIX}/bin - install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d - install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd - install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd - install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd - install -d ${PREFIX}/share/i2pd - @cp -R contrib/certificates ${PREFIX}/share/i2pd/ - install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1 - @gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1 - install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd - @ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates - @ln -sf ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d - @ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf - @ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt - @ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf diff --git a/Makefile.linux b/Makefile.linux index 4ea39e22..100d6b6a 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -1,70 +1,53 @@ -# set defaults instead redefine -CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-psabi -LDFLAGS ?= ${LD_DEBUG} +CXXFLAGS = -g -Wall -fPIC +INCFLAGS = ## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time ## **without** overwriting the CXXFLAGS which we need in order to build. ## For example, when adding 'hardening flags' to the build ## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove ## -std=c++11. If you want to remove this variable please do so in a way that allows setting -## custom FDLAGS to work at build-time. +## custom FLAGS to work at build-time. -# detect proper flag for c++17 support by compilers +# detect proper flag for c++11 support by gcc CXXVER := $(shell $(CXX) -dumpversion) -ifeq ($(shell expr match $(CXX) 'clang'),5) - NEEDED_CXXFLAGS += -std=c++17 -else ifeq ($(shell expr match ${CXXVER} "[8-9]"),1) # gcc 8 - 9 - NEEDED_CXXFLAGS += -std=c++17 - LDLIBS = -lboost_system -lstdc++fs -else ifeq ($(shell expr match ${CXXVER} "1[0-2]"),2) # gcc 10 - 12 - NEEDED_CXXFLAGS += -std=c++17 -else ifeq ($(shell expr match ${CXXVER} "1[3-9]"),2) # gcc 13+ - NEEDED_CXXFLAGS += -std=c++20 +ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # >= 4.10 + NEEDED_CXXFLAGS += -std=c++11 +else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7 + NEEDED_CXXFLAGS += -std=c++11 +else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6 + NEEDED_CXXFLAGS += -std=c++0x +else ifeq ($(shell expr match $(CXX) 'clang'),5) + NEEDED_CXXFLAGS += -std=c++11 else # not supported -$(error Compiler too old) + $(error Compiler too old) endif -NEEDED_CXXFLAGS += -fPIC - ifeq ($(USE_STATIC),yes) -# NOTE: on glibc you will get this warning: -# Using 'getaddrinfo' in statically linked applications requires at runtime -# the shared libraries from the glibc version used for linking - LIBDIR := /usr/lib/$(SYS) - LDLIBS += $(LIBDIR)/libboost_program_options.a - LDLIBS += $(LIBDIR)/libssl.a - LDLIBS += $(LIBDIR)/libcrypto.a - LDLIBS += $(LIBDIR)/libz.a -ifeq ($(USE_UPNP),yes) - LDLIBS += $(LIBDIR)/libminiupnpc.a -endif - LDLIBS += -lpthread -ldl + LIBDIR := /usr/lib + LDLIBS = $(LIBDIR)/libboost_system.a + LDLIBS += $(LIBDIR)/libboost_date_time.a + LDLIBS += $(LIBDIR)/libboost_filesystem.a + LDLIBS += $(LIBDIR)/libboost_regex.a + LDLIBS += $(LIBDIR)/libboost_program_options.a + LDLIBS += $(LIBDIR)/libcryptopp.a + LDLIBS += -lpthread -static-libstdc++ -static-libgcc + USE_AESNI := no else - LDLIBS += -lssl -lcrypto -lz -lboost_program_options -lpthread -latomic -ifeq ($(USE_UPNP),yes) - LDLIBS += -lminiupnpc -endif + LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread endif -# UPNP Support (miniupnpc 1.5 and higher) -ifeq ($(USE_UPNP),yes) - DEFINES += -DUSE_UPNP +# UPNP Support (miniupnpc 1.5 or 1.6) +ifeq ($(USE_UPNP),1) + LDFLAGS += -ldl + CXXFLAGS += -DUSE_UPNP endif -install: all - install -d ${PREFIX}/bin - install -m 755 ${I2PD} ${PREFIX}/bin - install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d - install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd - install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd - install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd - install -d ${PREFIX}/share/i2pd - @cp -R contrib/certificates ${PREFIX}/share/i2pd/ - install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1 - @gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1 - install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd - @ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates - @ln -sf ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d - @ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf - @ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt - @ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf +IS_64 := $(shell $(CXX) -dumpmachine 2>&1 | $(GREP) -c "64") +ifeq ($(USE_AESNI),yes) +ifeq ($(IS_64),1) +#check if AES-NI is supported by CPU +ifneq ($(shell grep -c aes /proc/cpuinfo),0) + CPU_FLAGS = -maes -DAESNI +endif +endif +endif diff --git a/Makefile.mingw b/Makefile.mingw deleted file mode 100644 index 32d60764..00000000 --- a/Makefile.mingw +++ /dev/null @@ -1,50 +0,0 @@ -# Build application with GUI (tray, main window) -USE_WIN32_APP := yes - -WINDRES = windres - -CXXFLAGS := $(CXX_DEBUG) -fPIC -msse -INCFLAGS := -I$(DAEMON_SRC_DIR) -IWin32 -LDFLAGS := ${LD_DEBUG} -static -fPIC -msse - -NEEDED_CXXFLAGS += -std=c++20 -DEFINES += -DWIN32_LEAN_AND_MEAN - -# UPNP Support -ifeq ($(USE_UPNP),yes) - DEFINES += -DUSE_UPNP -DMINIUPNP_STATICLIB - LDLIBS = -lminiupnpc -endif - -ifeq ($(USE_WINXP_FLAGS), yes) - DEFINES += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501 -endif - -LDLIBS += \ - $(MINGW_PREFIX)/lib/libboost_filesystem-mt.a \ - $(MINGW_PREFIX)/lib/libboost_program_options-mt.a \ - $(MINGW_PREFIX)/lib/libssl.a \ - $(MINGW_PREFIX)/lib/libcrypto.a \ - $(MINGW_PREFIX)/lib/libz.a \ - -lwsock32 \ - -lws2_32 \ - -liphlpapi \ - -lcrypt32 \ - -lgdi32 \ - -lole32 \ - -luuid \ - -lpthread - -ifeq ($(USE_WIN32_APP), yes) - DEFINES += -DWIN32_APP - LDFLAGS += -mwindows - DAEMON_RC += Win32/Resource.rc - DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC)) -endif - -ifeq ($(USE_ASLR),yes) - LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols -endif - -obj/%.o : %.rc | mk_obj_dir - $(WINDRES) $(DEFINES) $(INCFLAGS) --preprocessor-arg=-MMD --preprocessor-arg=-MP --preprocessor-arg=-MF$@.d -i $< -o $@ diff --git a/Makefile.osx b/Makefile.osx index 52282307..7af2247b 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -1,29 +1,25 @@ CXX = clang++ -CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++17 +CXXFLAGS = -g -Wall -std=c++11 -DCRYPTOPP_DISABLE_ASM -DMAC_OSX +#CXXFLAGS = -g -O2 -Wall -std=c++11 -DCRYPTOPP_DISABLE_ASM INCFLAGS = -I/usr/local/include -DEFINES := -DMAC_OSX -LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib -LDFLAGS += -Wl,-dead_strip -LDFLAGS += -Wl,-dead_strip_dylibs +LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib +LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread -ifeq ($(USE_STATIC),yes) - LDLIBS = -lz /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread -else - LDLIBS = -lz -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lpthread +ifeq ($(USE_UPNP),1) + LDFLAGS += -ldl + CXXFLAGS += -DUSE_UPNP endif -ifeq ($(USE_UPNP),yes) - LDFLAGS += -ldl - DEFINES += -DUSE_UPNP - ifeq ($(USE_STATIC),yes) - LDLIBS += /usr/local/lib/libminiupnpc.a - else - LDLIBS += -lminiupnpc - endif +# OSX Notes +# http://www.hutsby.net/2011/08/macs-with-aes-ni.html +# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2 +# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic +ifeq ($(USE_AESNI),yes) + CXXFLAGS += -maes -DAESNI endif -OSARCH = $(shell uname -p) - -ifneq ($(OSARCH),powerpc) - CXXFLAGS += -msse -endif +# Disabled, since it will be the default make rule. I think its better +# to define the default rule in Makefile and not Makefile. - torkel +#install: all +# test -d ${PREFIX} || mkdir -p ${PREFIX}/ +# cp -r i2p ${PREFIX}/ diff --git a/Makefile.solaris b/Makefile.solaris deleted file mode 100644 index 77d34114..00000000 --- a/Makefile.solaris +++ /dev/null @@ -1,9 +0,0 @@ -CXX = g++ -INCFLAGS = -I/usr/openssl/3/include -CXXFLAGS := -Wall -std=c++20 -LDLIBS = -L/usr/openssl/3/lib/64 -lssl -lcrypto -lboost_program_options -lz -lpthread -lsocket - -ifeq ($(USE_UPNP),yes) - DEFINES += -DUSE_UPNP - LDLIBS += -lminiupnpc -endif \ No newline at end of file diff --git a/NTCPSession.cpp b/NTCPSession.cpp new file mode 100644 index 00000000..3f326098 --- /dev/null +++ b/NTCPSession.cpp @@ -0,0 +1,658 @@ +#include +#include +#include "I2PEndian.h" +#include +#include "base64.h" +#include "Log.h" +#include "Timestamp.h" +#include "CryptoConst.h" +#include "I2NPProtocol.h" +#include "RouterContext.h" +#include "Transports.h" +#include "NetDb.h" +#include "NTCPSession.h" + +using namespace i2p::crypto; + +namespace i2p +{ +namespace transport +{ + NTCPSession::NTCPSession (boost::asio::io_service& service, std::shared_ptr in_RemoteRouter): + TransportSession (in_RemoteRouter), m_Socket (service), + m_TerminationTimer (service), m_IsEstablished (false), m_ReceiveBufferOffset (0), + m_NextMessage (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0) + { + m_DHKeysPair = transports.GetNextDHKeysPair (); + m_Establisher = new Establisher; + } + + NTCPSession::~NTCPSession () + { + delete m_Establisher; + if (m_NextMessage) + i2p::DeleteI2NPMessage (m_NextMessage); + for (auto it :m_DelayedMessages) + i2p::DeleteI2NPMessage (it); + m_DelayedMessages.clear (); + } + + void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key) + { + CryptoPP::DH dh (elgp, elgg); + uint8_t sharedKey[256]; + if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey)) + { + LogPrint (eLogError, "Couldn't create shared key"); + Terminate (); + return; + }; + + uint8_t * aesKey = key; + if (sharedKey[0] & 0x80) + { + aesKey[0] = 0; + memcpy (aesKey + 1, sharedKey, 31); + } + else if (sharedKey[0]) + memcpy (aesKey, sharedKey, 32); + else + { + // find first non-zero byte + uint8_t * nonZero = sharedKey + 1; + while (!*nonZero) + { + nonZero++; + if (nonZero - sharedKey > 32) + { + LogPrint (eLogWarning, "First 32 bytes of shared key is all zeros. Ignored"); + return; + } + } + memcpy (aesKey, nonZero, 32); + } + } + + void NTCPSession::Terminate () + { + m_IsEstablished = false; + m_Socket.close (); + int numDelayed = 0; + for (auto it :m_DelayedMessages) + { + // try to send them again + if (m_RemoteRouter) + transports.SendMessage (m_RemoteRouter->GetIdentHash (), it); + numDelayed++; + } + m_DelayedMessages.clear (); + if (numDelayed > 0) + LogPrint (eLogWarning, "NTCP session ", numDelayed, " not sent"); + // TODO: notify tunnels + transports.RemoveNTCPSession (shared_from_this ()); + LogPrint ("NTCP session terminated"); + } + + void NTCPSession::Connected () + { + m_IsEstablished = true; + + delete m_Establisher; + m_Establisher = nullptr; + + delete m_DHKeysPair; + m_DHKeysPair = nullptr; + + SendTimeSyncMessage (); + SendI2NPMessage (CreateDatabaseStoreMsg ()); // we tell immediately who we are + + if (!m_DelayedMessages.empty ()) + { + for (auto it :m_DelayedMessages) + SendI2NPMessage (it); + m_DelayedMessages.clear (); + } + } + + void NTCPSession::ClientLogin () + { + if (!m_DHKeysPair) + m_DHKeysPair = transports.GetNextDHKeysPair (); + // send Phase1 + const uint8_t * x = m_DHKeysPair->publicKey; + memcpy (m_Establisher->phase1.pubKey, x, 256); + CryptoPP::SHA256().CalculateDigest(m_Establisher->phase1.HXxorHI, x, 256); + const uint8_t * ident = m_RemoteIdentity.GetIdentHash (); + for (int i = 0; i < 32; i++) + m_Establisher->phase1.HXxorHI[i] ^= ident[i]; + + boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + + void NTCPSession::ServerLogin () + { + // receive Phase1 + boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogWarning, "Couldn't send Phase 1 message: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + LogPrint (eLogDebug, "Phase 1 sent: ", bytes_transferred); + boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase2Received, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + } + + void NTCPSession::HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogError, "Phase 1 read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + LogPrint (eLogDebug, "Phase 1 received: ", bytes_transferred); + // verify ident + uint8_t digest[32]; + CryptoPP::SHA256().CalculateDigest(digest, m_Establisher->phase1.pubKey, 256); + const uint8_t * ident = i2p::context.GetRouterInfo ().GetIdentHash (); + for (int i = 0; i < 32; i++) + { + if ((m_Establisher->phase1.HXxorHI[i] ^ ident[i]) != digest[i]) + { + LogPrint (eLogError, "Wrong ident"); + Terminate (); + return; + } + } + + SendPhase2 (); + } + } + + void NTCPSession::SendPhase2 () + { + if (!m_DHKeysPair) + m_DHKeysPair = transports.GetNextDHKeysPair (); + const uint8_t * y = m_DHKeysPair->publicKey; + memcpy (m_Establisher->phase2.pubKey, y, 256); + uint8_t xy[512]; + memcpy (xy, m_Establisher->phase1.pubKey, 256); + memcpy (xy + 256, y, 256); + CryptoPP::SHA256().CalculateDigest(m_Establisher->phase2.encrypted.hxy, xy, 512); + uint32_t tsB = htobe32 (i2p::util::GetSecondsSinceEpoch ()); + m_Establisher->phase2.encrypted.timestamp = tsB; + // TODO: fill filler + + i2p::crypto::AESKey aesKey; + CreateAESKey (m_Establisher->phase1.pubKey, aesKey); + m_Encryption.SetKey (aesKey); + m_Encryption.SetIV (y + 240); + m_Decryption.SetKey (aesKey); + m_Decryption.SetIV (m_Establisher->phase1.HXxorHI + 16); + + m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); + boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase2Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsB)); + + } + + void NTCPSession::HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB) + { + if (ecode) + { + LogPrint (eLogWarning, "Couldn't send Phase 2 message: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + LogPrint (eLogDebug, "Phase 2 sent: ", bytes_transferred); + boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase3Received, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, tsB)); + } + } + + void NTCPSession::HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("Phase 2 read error: ", ecode.message (), ". Wrong ident assumed"); + if (ecode != boost::asio::error::operation_aborted) + { + // this RI is not valid + i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true); + transports.ReuseDHKeysPair (m_DHKeysPair); + m_DHKeysPair = nullptr; + Terminate (); + } + } + else + { + LogPrint (eLogDebug, "Phase 2 received: ", bytes_transferred); + + i2p::crypto::AESKey aesKey; + CreateAESKey (m_Establisher->phase2.pubKey, aesKey); + m_Decryption.SetKey (aesKey); + m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240); + m_Encryption.SetKey (aesKey); + m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16); + + m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); + // verify + uint8_t xy[512]; + memcpy (xy, m_DHKeysPair->publicKey, 256); + memcpy (xy + 256, m_Establisher->phase2.pubKey, 256); + if (!CryptoPP::SHA256().VerifyDigest(m_Establisher->phase2.encrypted.hxy, xy, 512)) + { + LogPrint (eLogError, "Incorrect hash"); + transports.ReuseDHKeysPair (m_DHKeysPair); + m_DHKeysPair = nullptr; + Terminate (); + return ; + } + SendPhase3 (); + } + } + + void NTCPSession::SendPhase3 () + { + auto keys = i2p::context.GetPrivateKeys (); + uint8_t * buf = m_ReceiveBuffer; + htobe16buf (buf, keys.GetPublic ().GetFullLen ()); + buf += 2; + buf += i2p::context.GetIdentity ().ToBuffer (buf, NTCP_BUFFER_SIZE); + uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ()); + htobuf32(buf,tsA); + buf += 4; + size_t signatureLen = keys.GetPublic ().GetSignatureLen (); + size_t len = (buf - m_ReceiveBuffer) + signatureLen; + size_t paddingSize = len & 0x0F; // %16 + if (paddingSize > 0) + { + paddingSize = 16 - paddingSize; + // TODO: fill padding with random data + buf += paddingSize; + len += paddingSize; + } + + SignedData s; + s.Insert (m_Establisher->phase1.pubKey, 256); // x + s.Insert (m_Establisher->phase2.pubKey, 256); // y + s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident + s.Insert (tsA); // tsA + s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB + s.Sign (keys, buf); + + m_Encryption.Encrypt(m_ReceiveBuffer, len, m_ReceiveBuffer); + boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, len), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase3Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsA)); + } + + void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA) + { + if (ecode) + { + LogPrint (eLogWarning, "Couldn't send Phase 3 message: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + LogPrint (eLogDebug, "Phase 3 sent: ", bytes_transferred); + // wait for phase4 + auto signatureLen = m_RemoteIdentity.GetSignatureLen (); + size_t paddingSize = signatureLen & 0x0F; // %16 + if (paddingSize > 0) signatureLen += (16 - paddingSize); + boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase4Received, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, tsA)); + } + } + + void NTCPSession::HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB) + { + if (ecode) + { + LogPrint (eLogError, "Phase 3 read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + LogPrint (eLogDebug, "Phase 3 received: ", bytes_transferred); + m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer); + uint8_t * buf = m_ReceiveBuffer; + uint16_t size = bufbe16toh (buf); + m_RemoteIdentity.FromBuffer (buf + 2, size); + size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen (); + size_t paddingLen = expectedSize & 0x0F; + if (paddingLen) paddingLen = (16 - paddingLen); + if (expectedSize > NTCP_DEFAULT_PHASE3_SIZE) + { + // we need more bytes for Phase3 + expectedSize += paddingLen; + LogPrint (eLogDebug, "Wait for ", expectedSize, " more bytes for Phase3"); + boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, tsB, paddingLen)); + } + else + HandlePhase3 (tsB, paddingLen); + } + } + + void NTCPSession::HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen) + { + if (ecode) + { + LogPrint (eLogError, "Phase 3 extra read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + LogPrint (eLogDebug, "Phase 3 extra received: ", bytes_transferred); + m_Decryption.Decrypt (m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, bytes_transferred, m_ReceiveBuffer+ NTCP_DEFAULT_PHASE3_SIZE); + HandlePhase3 (tsB, paddingLen); + } + } + + void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen) + { + uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity.GetFullLen () + 2 /*size*/; + uint32_t tsA = buf32toh(buf); + buf += 4; + buf += paddingLen; + + SignedData s; + s.Insert (m_Establisher->phase1.pubKey, 256); // x + s.Insert (m_Establisher->phase2.pubKey, 256); // y + s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident + s.Insert (tsA); // tsA + s.Insert (tsB); // tsB + if (!s.Verify (m_RemoteIdentity, buf)) + { + LogPrint (eLogError, "signature verification failed"); + Terminate (); + return; + } + + SendPhase4 (tsA, tsB); + } + + void NTCPSession::SendPhase4 (uint32_t tsA, uint32_t tsB) + { + SignedData s; + s.Insert (m_Establisher->phase1.pubKey, 256); // x + s.Insert (m_Establisher->phase2.pubKey, 256); // y + s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident + s.Insert (tsA); // tsA + s.Insert (tsB); // tsB + auto keys = i2p::context.GetPrivateKeys (); + auto signatureLen = keys.GetPublic ().GetSignatureLen (); + s.Sign (keys, m_ReceiveBuffer); + size_t paddingSize = signatureLen & 0x0F; // %16 + if (paddingSize > 0) signatureLen += (16 - paddingSize); + m_Encryption.Encrypt (m_ReceiveBuffer, signatureLen, m_ReceiveBuffer); + + boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandlePhase4Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + + void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogWarning, "Couldn't send Phase 4 message: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + LogPrint (eLogDebug, "Phase 4 sent: ", bytes_transferred); + LogPrint ("NTCP server session connected"); + transports.AddNTCPSession (shared_from_this ()); + + Connected (); + m_ReceiveBufferOffset = 0; + m_NextMessage = nullptr; + Receive (); + } + } + + void NTCPSession::HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA) + { + if (ecode) + { + LogPrint (eLogError, "Phase 4 read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + { + // this router doesn't like us + i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true); + Terminate (); + } + } + else + { + LogPrint (eLogDebug, "Phase 4 received: ", bytes_transferred); + m_Decryption.Decrypt(m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer); + + // verify signature + SignedData s; + s.Insert (m_Establisher->phase1.pubKey, 256); // x + s.Insert (m_Establisher->phase2.pubKey, 256); // y + s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident + s.Insert (tsA); // tsA + s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB + + if (!s.Verify (m_RemoteIdentity, m_ReceiveBuffer)) + { + LogPrint (eLogError, "signature verification failed"); + Terminate (); + return; + } + LogPrint ("NTCP session connected"); + Connected (); + + m_ReceiveBufferOffset = 0; + m_NextMessage = nullptr; + Receive (); + } + } + + void NTCPSession::Receive () + { + m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, NTCP_BUFFER_SIZE - m_ReceiveBufferOffset), + std::bind(&NTCPSession::HandleReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint (eLogError, "Read error: ", ecode.message ()); + //if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + m_NumReceivedBytes += bytes_transferred; + m_ReceiveBufferOffset += bytes_transferred; + + if (m_ReceiveBufferOffset >= 16) + { + uint8_t * nextBlock = m_ReceiveBuffer; + while (m_ReceiveBufferOffset >= 16) + { + if (!DecryptNextBlock (nextBlock)) // 16 bytes + { + Terminate (); + return; + } + nextBlock += 16; + m_ReceiveBufferOffset -= 16; + } + if (m_ReceiveBufferOffset > 0) + memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); + } + + ScheduleTermination (); // reset termination timer + Receive (); + } + } + + bool NTCPSession::DecryptNextBlock (const uint8_t * encrypted) // 16 bytes + { + if (!m_NextMessage) // new message, header expected + { + m_NextMessage = i2p::NewI2NPMessage (); + m_NextMessageOffset = 0; + + m_Decryption.Decrypt (encrypted, m_NextMessage->buf); + uint16_t dataSize = bufbe16toh (m_NextMessage->buf); + if (dataSize) + { + // new message + if (dataSize > NTCP_MAX_MESSAGE_SIZE) + { + LogPrint (eLogError, "NTCP data size ", dataSize, " exceeds max size"); + i2p::DeleteI2NPMessage (m_NextMessage); + m_NextMessage = nullptr; + return false; + } + m_NextMessageOffset += 16; + m_NextMessage->offset = 2; // size field + m_NextMessage->len = dataSize + 2; + } + else + { + // timestamp + LogPrint ("Timestamp"); + i2p::DeleteI2NPMessage (m_NextMessage); + m_NextMessage = nullptr; + return true; + } + } + else // message continues + { + m_Decryption.Decrypt (encrypted, m_NextMessage->buf + m_NextMessageOffset); + m_NextMessageOffset += 16; + } + + if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum + { + // we have a complete I2NP message + i2p::HandleI2NPMessage (m_NextMessage); + m_NextMessage = nullptr; + } + return true; + } + + void NTCPSession::Send (i2p::I2NPMessage * msg) + { + uint8_t * sendBuffer; + int len; + + if (msg) + { + // regular I2NP + if (msg->offset < 2) + { + LogPrint (eLogError, "Malformed I2NP message"); + i2p::DeleteI2NPMessage (msg); + } + sendBuffer = msg->GetBuffer () - 2; + len = msg->GetLength (); + htobe16buf (sendBuffer, len); + } + else + { + // prepare timestamp + sendBuffer = m_TimeSyncBuffer; + len = 4; + htobuf16(sendBuffer, 0); + htobe32buf (sendBuffer + 2, time (0)); + } + int rem = (len + 6) & 0x0F; // %16 + int padding = 0; + if (rem > 0) padding = 16 - rem; + // TODO: fill padding + m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding); + + int l = len + padding + 6; + m_Encryption.Encrypt(sendBuffer, l, sendBuffer); + + boost::asio::async_write (m_Socket, boost::asio::buffer (sendBuffer, l), boost::asio::transfer_all (), + std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msg)); + } + + void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg) + { + if (msg) + i2p::DeleteI2NPMessage (msg); + if (ecode) + { + LogPrint (eLogWarning, "Couldn't send msg: ", ecode.message ()); + // we shouldn't call Terminate () here, because HandleReceive takes care + // TODO: 'delete this' statement in Terminate () must be eliminated later + // Terminate (); + } + else + { + m_NumSentBytes += bytes_transferred; + ScheduleTermination (); // reset termination timer + } + } + + void NTCPSession::SendTimeSyncMessage () + { + Send (nullptr); + } + + void NTCPSession::SendI2NPMessage (I2NPMessage * msg) + { + if (msg) + { + if (m_IsEstablished) + Send (msg); + else + m_DelayedMessages.push_back (msg); + } + } + + void NTCPSession::ScheduleTermination () + { + m_TerminationTimer.cancel (); + m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT)); + m_TerminationTimer.async_wait (std::bind (&NTCPSession::HandleTerminationTimer, + shared_from_this (), std::placeholders::_1)); + } + + void NTCPSession::HandleTerminationTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + LogPrint ("No activity fo ", NTCP_TERMINATION_TIMEOUT, " seconds"); + //Terminate (); + m_Socket.close ();// invoke Terminate () from HandleReceive + } + } +} +} diff --git a/NTCPSession.h b/NTCPSession.h new file mode 100644 index 00000000..1d94475d --- /dev/null +++ b/NTCPSession.h @@ -0,0 +1,133 @@ +#ifndef NTCP_SESSION_H__ +#define NTCP_SESSION_H__ + +#include +#include +#include +#include +#include +#include +#include "aes.h" +#include "Identity.h" +#include "RouterInfo.h" +#include "I2NPProtocol.h" +#include "TransportSession.h" + +namespace i2p +{ +namespace transport +{ + +#pragma pack(1) + struct NTCPPhase1 + { + uint8_t pubKey[256]; + uint8_t HXxorHI[32]; + }; + + struct NTCPPhase2 + { + uint8_t pubKey[256]; + struct + { + uint8_t hxy[32]; + uint32_t timestamp; + uint8_t filler[12]; + } encrypted; + }; + +#pragma pack() + + const size_t NTCP_MAX_MESSAGE_SIZE = 16384; + const size_t NTCP_BUFFER_SIZE = 1040; // fits one tunnel message (1028) + const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes + const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448 + + class NTCPSession: public TransportSession, public std::enable_shared_from_this + { + public: + + NTCPSession (boost::asio::io_service& service, std::shared_ptr in_RemoteRouter = nullptr); + ~NTCPSession (); + void Terminate (); + + boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; + bool IsEstablished () const { return m_IsEstablished; }; + + void ClientLogin (); + void ServerLogin (); + void SendI2NPMessage (I2NPMessage * msg); + + size_t GetNumSentBytes () const { return m_NumSentBytes; }; + size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; + + protected: + + void Connected (); + void SendTimeSyncMessage (); + void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; } + + private: + + void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key); + + // 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 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 (); + 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 (i2p::I2NPMessage * msg); + void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg); + + + // timer + void ScheduleTermination (); + void HandleTerminationTimer (const boost::system::error_code& ecode); + + private: + + boost::asio::ip::tcp::socket m_Socket; + boost::asio::deadline_timer m_TerminationTimer; + bool m_IsEstablished; + + i2p::crypto::CBCDecryption m_Decryption; + i2p::crypto::CBCEncryption m_Encryption; + CryptoPP::Adler32 m_Adler; + + struct Establisher + { + NTCPPhase1 phase1; + NTCPPhase2 phase2; + } * m_Establisher; + + i2p::crypto::AESAlignedBuffer m_ReceiveBuffer; + i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer; + int m_ReceiveBufferOffset; + + i2p::I2NPMessage * m_NextMessage; + std::list m_DelayedMessages; + size_t m_NextMessageOffset; + + size_t m_NumSentBytes, m_NumReceivedBytes; + }; +} +} + +#endif diff --git a/NetDb.cpp b/NetDb.cpp new file mode 100644 index 00000000..c959edb8 --- /dev/null +++ b/NetDb.cpp @@ -0,0 +1,921 @@ +#include +#include "I2PEndian.h" +#include +#include +#include +#include +#include "base64.h" +#include "Log.h" +#include "Timestamp.h" +#include "I2NPProtocol.h" +#include "Tunnel.h" +#include "Transports.h" +#include "RouterContext.h" +#include "Garlic.h" +#include "NetDb.h" +#include "Reseed.h" +#include "util.h" + +using namespace i2p::transport; + +namespace i2p +{ +namespace data +{ + I2NPMessage * RequestedDestination::CreateRequestMessage (std::shared_ptr router, + const i2p::tunnel::InboundTunnel * replyTunnel) + { + I2NPMessage * msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, + replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory, + &m_ExcludedPeers); + m_ExcludedPeers.insert (router->GetIdentHash ()); + m_LastRouter = router; + m_CreationTime = i2p::util::GetSecondsSinceEpoch (); + return msg; + } + + I2NPMessage * RequestedDestination::CreateRequestMessage (const IdentHash& floodfill) + { + I2NPMessage * msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination, + i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers); + m_ExcludedPeers.insert (floodfill); + m_LastRouter = nullptr; + m_CreationTime = i2p::util::GetSecondsSinceEpoch (); + return msg; + } + + void RequestedDestination::ClearExcludedPeers () + { + m_ExcludedPeers.clear (); + } + +#ifndef _WIN32 + const char NetDb::m_NetDbPath[] = "/netDb"; +#else + const char NetDb::m_NetDbPath[] = "\\netDb"; +#endif + NetDb netdb; + + NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr) + { + } + + NetDb::~NetDb () + { + Stop (); + for (auto l:m_LeaseSets) + delete l.second; + for (auto r:m_RequestedDestinations) + delete r.second; + } + + void NetDb::Start () + { + Load (m_NetDbPath); + if (m_RouterInfos.size () < 50) // reseed if # of router less than 50 + { + Reseeder reseeder; + reseeder.LoadCertificates (); // we need certificates for SU3 verification + + // try SU3 first + int reseedRetries = 0; + while (m_RouterInfos.size () < 50 && reseedRetries < 10) + { + reseeder.ReseedNowSU3(); + reseedRetries++; + } + + // if still not enough download .dat files + reseedRetries = 0; + while (m_RouterInfos.size () < 50 && reseedRetries < 10) + { + reseeder.reseedNow(); + reseedRetries++; + Load (m_NetDbPath); + } + } + m_Thread = new std::thread (std::bind (&NetDb::Run, this)); + } + + void NetDb::Stop () + { + if (m_Thread) + { + m_IsRunning = false; + m_Queue.WakeUp (); + m_Thread->join (); + delete m_Thread; + m_Thread = 0; + } + } + + void NetDb::Run () + { + uint32_t lastSave = 0, lastPublish = 0; + m_IsRunning = true; + while (m_IsRunning) + { + try + { + I2NPMessage * msg = m_Queue.GetNextWithTimeout (15000); // 15 sec + if (msg) + { + while (msg) + { + switch (msg->GetTypeID ()) + { + case eI2NPDatabaseStore: + LogPrint ("DatabaseStore"); + HandleDatabaseStoreMsg (msg); + break; + case eI2NPDatabaseSearchReply: + LogPrint ("DatabaseSearchReply"); + HandleDatabaseSearchReplyMsg (msg); + break; + case eI2NPDatabaseLookup: + LogPrint ("DatabaseLookup"); + HandleDatabaseLookupMsg (msg); + break; + default: // WTF? + LogPrint ("NetDb: unexpected message type ", msg->GetTypeID ()); + i2p::HandleI2NPMessage (msg); + } + msg = m_Queue.Get (); + } + } + else + { + if (!m_IsRunning) break; + // if no new DatabaseStore coming, explore it + ManageRequests (); + auto numRouters = m_RouterInfos.size (); + Explore (numRouters < 1500 ? 5 : 1); + } + + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute + { + if (lastSave) + { + SaveUpdated (m_NetDbPath); + ManageLeaseSets (); + } + lastSave = ts; + } + if (ts - lastPublish >= 600) // publish every 10 minutes + { + Publish (); + lastPublish = ts; + } + } + catch (std::exception& ex) + { + LogPrint ("NetDb: ", ex.what ()); + } + } + } + + void NetDb::AddRouterInfo (const uint8_t * buf, int len) + { + IdentityEx identity; + if (identity.FromBuffer (buf, len)) + AddRouterInfo (identity.GetIdentHash (), buf, len); + } + + void NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len) + { + DeleteRequestedDestination (ident); + auto r = FindRouter (ident); + if (r) + { + auto ts = r->GetTimestamp (); + r->Update (buf, len); + if (r->GetTimestamp () > ts) + LogPrint ("RouterInfo updated"); + } + else + { + LogPrint ("New RouterInfo added"); + auto newRouter = std::make_shared (buf, len); + { + std::unique_lock l(m_RouterInfosMutex); + m_RouterInfos[newRouter->GetIdentHash ()] = newRouter; + } + if (newRouter->IsFloodfill ()) + { + std::unique_lock l(m_FloodfillsMutex); + m_Floodfills.push_back (newRouter); + } + } + } + + void NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, + i2p::tunnel::InboundTunnel * from) + { + DeleteRequestedDestination (ident); + if (!from) // unsolicited LS must be received directly + { + auto it = m_LeaseSets.find(ident); + if (it != m_LeaseSets.end ()) + { + it->second->Update (buf, len); + LogPrint ("LeaseSet updated"); + } + else + { + LogPrint ("New LeaseSet added"); + m_LeaseSets[ident] = new LeaseSet (buf, len); + } + } + } + + std::shared_ptr NetDb::FindRouter (const IdentHash& ident) const + { + std::unique_lock l(m_RouterInfosMutex); + auto it = m_RouterInfos.find (ident); + if (it != m_RouterInfos.end ()) + return it->second; + else + return nullptr; + } + + LeaseSet * NetDb::FindLeaseSet (const IdentHash& destination) const + { + auto it = m_LeaseSets.find (destination); + if (it != m_LeaseSets.end ()) + return it->second; + else + return nullptr; + } + + void NetDb::SetUnreachable (const IdentHash& ident, bool unreachable) + { + auto it = m_RouterInfos.find (ident); + if (it != m_RouterInfos.end ()) + return it->second->SetUnreachable (unreachable); + } + + // TODO: Move to reseed and/or scheduled tasks. (In java version, scheduler fix this as well as sort RIs.) + bool NetDb::CreateNetDb(boost::filesystem::path directory) + { + LogPrint (directory.string(), " doesn't exist, trying to create it."); + if (!boost::filesystem::create_directory (directory)) + { + LogPrint("Failed to create directory ", directory.string()); + return false; + } + + // list of chars might appear in base64 string + const char * chars = GetBase64SubstitutionTable (); // 64 bytes + boost::filesystem::path suffix; + for (int i = 0; i < 64; i++) + { +#ifndef _WIN32 + suffix = std::string ("/r") + chars[i]; +#else + suffix = std::string ("\\r") + chars[i]; +#endif + if (!boost::filesystem::create_directory( boost::filesystem::path (directory / suffix) )) return false; + } + return true; + } + + void NetDb::Load (const char * directory) + { + boost::filesystem::path p (i2p::util::filesystem::GetDataDir()); + p /= (directory); + if (!boost::filesystem::exists (p)) + { + // seems netDb doesn't exist yet + if (!CreateNetDb(p)) return; + } + // make sure we cleanup netDb from previous attempts + m_RouterInfos.clear (); + m_Floodfills.clear (); + + // load routers now + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + int numRouters = 0; + boost::filesystem::directory_iterator end; + for (boost::filesystem::directory_iterator it (p); it != end; ++it) + { + if (boost::filesystem::is_directory (it->status())) + { + for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1) + { +#if BOOST_VERSION > 10500 + const std::string& fullPath = it1->path().string(); +#else + const std::string& fullPath = it1->path(); +#endif + auto r = std::make_shared(fullPath); + if (!r->IsUnreachable () && (!r->UsesIntroducer () || ts < r->GetTimestamp () + 3600*1000LL)) // 1 hour + { + r->DeleteBuffer (); + m_RouterInfos[r->GetIdentHash ()] = r; + if (r->IsFloodfill ()) + m_Floodfills.push_back (r); + numRouters++; + } + else + { + if (boost::filesystem::exists (fullPath)) + boost::filesystem::remove (fullPath); + } + } + } + } + LogPrint (numRouters, " routers loaded"); + LogPrint (m_Floodfills.size (), " floodfills loaded"); + } + + void NetDb::SaveUpdated (const char * directory) + { + auto GetFilePath = [](const char * directory, const RouterInfo * routerInfo) + { +#ifndef _WIN32 + return std::string (directory) + "/r" + + routerInfo->GetIdentHashBase64 ()[0] + "/routerInfo-" + +#else + return std::string (directory) + "\\r" + + routerInfo->GetIdentHashBase64 ()[0] + "\\routerInfo-" + +#endif + routerInfo->GetIdentHashBase64 () + ".dat"; + }; + + boost::filesystem::path p (i2p::util::filesystem::GetDataDir()); + p /= (directory); +#if BOOST_VERSION > 10500 + const char * fullDirectory = p.string().c_str (); +#else + const char * fullDirectory = p.c_str (); +#endif + int count = 0, deletedCount = 0; + auto total = m_RouterInfos.size (); + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + for (auto it: m_RouterInfos) + { + if (it.second->IsUpdated ()) + { + it.second->SaveToFile (GetFilePath(fullDirectory, it.second.get ())); + it.second->SetUpdated (false); + it.second->DeleteBuffer (); + count++; + } + else + { + // RouterInfo expires after 1 hour if uses introducer + if ((it.second->UsesIntroducer () && ts > it.second->GetTimestamp () + 3600*1000LL) // 1 hour + // RouterInfo expires in 72 hours if more than 300 + || (total > 300 && ts > it.second->GetTimestamp () + 3*24*3600*1000LL)) // 3 days + { + total--; + it.second->SetUnreachable (true); + } + + if (it.second->IsUnreachable ()) + { + // delete RI file + if (boost::filesystem::exists (GetFilePath (fullDirectory, it.second.get ()))) + { + boost::filesystem::remove (GetFilePath (fullDirectory, it.second.get ())); + deletedCount++; + } + // delete from floodfills list + if (it.second->IsFloodfill ()) + { + std::unique_lock l(m_FloodfillsMutex); + m_Floodfills.remove (it.second); + } + } + } + } + if (count > 0) + LogPrint (count," new/updated routers saved"); + if (deletedCount > 0) + { + LogPrint (deletedCount," routers deleted"); + // clean up RouterInfos table + std::unique_lock l(m_RouterInfosMutex); + for (auto it = m_RouterInfos.begin (); it != m_RouterInfos.end ();) + { + if (it->second->IsUnreachable ()) + it = m_RouterInfos.erase (it); + else + it++; + } + } + } + + void NetDb::RequestDestination (const IdentHash& destination) + { + // request RouterInfo directly + RequestedDestination * dest = CreateRequestedDestination (destination, false); + auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ()); + if (floodfill) + transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); + else + { + LogPrint (eLogError, "No floodfills found"); + DeleteRequestedDestination (dest); + } + } + + void NetDb::HandleDatabaseStoreMsg (I2NPMessage * m) + { + const uint8_t * buf = m->GetPayload (); + size_t len = m->GetSize (); + uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); + size_t offset = DATABASE_STORE_HEADER_SIZE; + if (replyToken) + offset += 36; + if (buf[DATABASE_STORE_TYPE_OFFSET]) // type + { + LogPrint ("LeaseSet"); + AddLeaseSet (buf + DATABASE_STORE_KEY_OFFSET, buf + offset, len - offset, m->from); + } + else + { + LogPrint ("RouterInfo"); + size_t size = bufbe16toh (buf + offset); + if (size > 2048) + { + LogPrint ("Invalid RouterInfo length ", (int)size); + return; + } + offset += 2; + CryptoPP::Gunzip decompressor; + decompressor.Put (buf + offset, size); + decompressor.MessageEnd(); + uint8_t uncompressed[2048]; + size_t uncomressedSize = decompressor.MaxRetrievable (); + decompressor.Get (uncompressed, uncomressedSize); + AddRouterInfo (buf + DATABASE_STORE_KEY_OFFSET, uncompressed, uncomressedSize); + } + i2p::DeleteI2NPMessage (m); + } + + void NetDb::HandleDatabaseSearchReplyMsg (I2NPMessage * msg) + { + uint8_t * buf = msg->GetPayload (); + char key[48]; + int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); + key[l] = 0; + int num = buf[32]; // num + LogPrint ("DatabaseSearchReply for ", key, " num=", num); + auto it = m_RequestedDestinations.find (IdentHash (buf)); + if (it != m_RequestedDestinations.end ()) + { + RequestedDestination * dest = it->second; + bool deleteDest = true; + if (num > 0) + { + auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = pool->GetNextOutboundTunnel (); + auto inbound = pool->GetNextInboundTunnel (); + std::vector msgs; + if (!dest->IsExploratory ()) + { + // reply to our destination. Try other floodfills + if (outbound && inbound ) + { + auto count = dest->GetExcludedPeers ().size (); + if (count < 7) + { + auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); + if (nextFloodfill) + { + // tell floodfill about us + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + nextFloodfill->GetIdentHash (), 0, + CreateDatabaseStoreMsg () + }); + + // request destination + LogPrint ("Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ()); + auto msg = dest->CreateRequestMessage (nextFloodfill, inbound); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + nextFloodfill->GetIdentHash (), 0, msg + }); + deleteDest = false; + } + } + else + LogPrint (key, " was not found on 7 floodfills"); + } + } + + for (int i = 0; i < num; i++) + { + uint8_t * router = buf + 33 + i*32; + char peerHash[48]; + int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48); + peerHash[l1] = 0; + LogPrint (i,": ", peerHash); + + if (dest->IsExploratory ()) + { + auto r = FindRouter (router); + if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL) + { + // router with ident not found or too old (1 hour) + LogPrint ("Found new/outdated router. Requesting RouterInfo ..."); + if (outbound && inbound && dest->GetLastRouter ()) + { + RequestedDestination * d1 = CreateRequestedDestination (router, false); + auto msg = d1->CreateRequestMessage (dest->GetLastRouter (), inbound); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + dest->GetLastRouter ()->GetIdentHash (), 0, msg + }); + } + else + RequestDestination (router); + } + else + LogPrint ("Bayan"); + } + else + { + auto r = FindRouter (router); + // do we have that floodfill router in our database? + if (!r) + { + // request router + LogPrint ("Found new floodfill. Request it"); + RequestDestination (router); + } + } + } + + if (outbound && msgs.size () > 0) + outbound->SendTunnelDataMsg (msgs); + if (deleteDest) + { + // no more requests for the destinationation. delete it + delete it->second; + m_RequestedDestinations.erase (it); + } + } + else + { + // no more requests for detination possible. delete it + delete it->second; + m_RequestedDestinations.erase (it); + } + } + else + { + LogPrint ("Requested destination for ", key, " not found"); + // it might contain new routers + for (int i = 0; i < num; i++) + { + IdentHash router (buf + 33 + i*32); + if (!FindRouter (router)) + { + LogPrint ("New router ", router.ToBase64 (), " found. Request it"); + RequestDestination (router); + } + } + } + i2p::DeleteI2NPMessage (msg); + } + + void NetDb::HandleDatabaseLookupMsg (I2NPMessage * msg) + { + uint8_t * buf = msg->GetPayload (); + char key[48]; + int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48); + key[l] = 0; + LogPrint ("DatabaseLookup for ", key, " recieved"); + uint8_t flag = buf[64]; + uint8_t * excluded = buf + 65; + uint32_t replyTunnelID = 0; + if (flag & 0x01) //reply to tunnel + { + replyTunnelID = bufbe32toh (buf + 64); + excluded += 4; + } + uint16_t numExcluded = bufbe16toh (excluded); + excluded += 2; + if (numExcluded > 512) + { + LogPrint ("Number of excluded peers", numExcluded, " exceeds 512"); + numExcluded = 0; // TODO: + } + + I2NPMessage * replyMsg = nullptr; + + { + auto router = FindRouter (buf); + if (router) + { + LogPrint ("Requested RouterInfo ", key, " found"); + router->LoadBuffer (); + if (router->GetBuffer ()) + replyMsg = CreateDatabaseStoreMsg (router.get ()); + } + } + if (!replyMsg) + { + auto leaseSet = FindLeaseSet (buf); + if (leaseSet) // we don't send back our LeaseSets + { + LogPrint ("Requested LeaseSet ", key, " found"); + replyMsg = CreateDatabaseStoreMsg (leaseSet); + } + } + if (!replyMsg) + { + LogPrint ("Requested ", key, " not found. ", numExcluded, " excluded"); + std::set excludedRouters; + for (int i = 0; i < numExcluded; i++) + { + // TODO: check for all zeroes (exploratory) + excludedRouters.insert (excluded); + excluded += 32; + } + replyMsg = CreateDatabaseSearchReply (buf, GetClosestFloodfill (buf, excludedRouters).get ()); + } + else + excluded += numExcluded*32; // we don't care about exluded + + if (replyMsg) + { + if (replyTunnelID) + { + // encryption might be used though tunnel only + if (flag & 0x02) // encrypted reply requested + { + uint8_t * sessionKey = excluded; + uint8_t numTags = sessionKey[32]; + if (numTags > 0) + { + uint8_t * sessionTag = sessionKey + 33; // take first tag + i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag); + replyMsg = garlic.WrapSingleMessage (replyMsg); + } + } + auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; + if (outbound) + outbound->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg); + else + transports.SendMessage (buf+32, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg)); + } + else + transports.SendMessage (buf+32, replyMsg); + } + i2p::DeleteI2NPMessage (msg); + } + + void NetDb::Explore (int numDestinations) + { + // new requests + auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel (); + auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : i2p::tunnel::tunnels.GetNextInboundTunnel (); + bool throughTunnels = outbound && inbound; + + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint8_t randomHash[32]; + std::vector msgs; + std::set floodfills; + LogPrint ("Exploring new ", numDestinations, " routers ..."); + for (int i = 0; i < numDestinations; i++) + { + rnd.GenerateBlock (randomHash, 32); + RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), true); + auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ()); + if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once + { + floodfills.insert (floodfill.get ()); + if (throughTunnels) + { + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + floodfill->GetIdentHash (), 0, + CreateDatabaseStoreMsg () // tell floodfill about us + }); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeRouter, + floodfill->GetIdentHash (), 0, + dest->CreateRequestMessage (floodfill, inbound) // explore + }); + } + else + i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ())); + } + else + DeleteRequestedDestination (dest); + } + if (throughTunnels && msgs.size () > 0) + outbound->SendTunnelDataMsg (msgs); + } + + void NetDb::Publish () + { + std::set excluded; // TODO: fill up later + for (int i = 0; i < 3; i++) + { + auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded); + if (floodfill) + { + LogPrint ("Publishing our RouterInfo to ", floodfill->GetIdentHashAbbreviation ()); + transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg ()); + excluded.insert (floodfill->GetIdentHash ()); + } + } + } + + RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest, bool isExploratory) + { + std::unique_lock l(m_RequestedDestinationsMutex); + auto it = m_RequestedDestinations.find (dest); + if (it == m_RequestedDestinations.end ()) // not exist yet + { + RequestedDestination * d = new RequestedDestination (dest, isExploratory); + m_RequestedDestinations[dest] = d; + return d; + } + else + return it->second; + } + + bool NetDb::DeleteRequestedDestination (const IdentHash& dest) + { + auto it = m_RequestedDestinations.find (dest); + if (it != m_RequestedDestinations.end ()) + { + std::unique_lock l(m_RequestedDestinationsMutex); + delete it->second; + m_RequestedDestinations.erase (it); + return true; + } + return false; + } + + void NetDb::DeleteRequestedDestination (RequestedDestination * dest) + { + if (dest) + { + std::unique_lock l(m_RequestedDestinationsMutex); + m_RequestedDestinations.erase (dest->GetDestination ()); + delete dest; + } + } + + std::shared_ptr NetDb::GetRandomRouter () const + { + return GetRandomRouter ( + [](std::shared_ptr router)->bool + { + return !router->IsHidden (); + }); + } + + std::shared_ptr NetDb::GetRandomRouter (std::shared_ptr compatibleWith) const + { + return GetRandomRouter ( + [compatibleWith](std::shared_ptr router)->bool + { + return !router->IsHidden () && router != compatibleWith && + router->IsCompatible (*compatibleWith); + }); + } + + std::shared_ptr NetDb::GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith) const + { + return GetRandomRouter ( + [compatibleWith](std::shared_ptr router)->bool + { + return !router->IsHidden () && router != compatibleWith && + router->IsCompatible (*compatibleWith) && (router->GetCaps () & RouterInfo::eHighBandwidth); + }); + } + + template + std::shared_ptr NetDb::GetRandomRouter (Filter filter) const + { + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1); + for (int j = 0; j < 2; j++) + { + uint32_t i = 0; + std::unique_lock l(m_RouterInfosMutex); + for (auto it: m_RouterInfos) + { + if (i >= ind) + { + if (!it.second->IsUnreachable () && filter (it.second)) + return it.second; + } + else + i++; + } + // we couldn't find anything, try second pass + ind = 0; + } + return nullptr; // seems we have too few routers + } + + void NetDb::PostI2NPMsg (I2NPMessage * msg) + { + if (msg) m_Queue.Put (msg); + } + + std::shared_ptr NetDb::GetClosestFloodfill (const IdentHash& destination, + const std::set& excluded) const + { + std::shared_ptr r; + XORMetric minMetric; + IdentHash destKey = CreateRoutingKey (destination); + minMetric.SetMax (); + std::unique_lock l(m_FloodfillsMutex); + for (auto it: m_Floodfills) + { + if (!it->IsUnreachable () && !excluded.count (it->GetIdentHash ())) + { + XORMetric m = destKey ^ it->GetIdentHash (); + if (m < minMetric) + { + minMetric = m; + r = it; + } + } + } + return r; + } + + void NetDb::ManageLeaseSets () + { + for (auto it = m_LeaseSets.begin (); it != m_LeaseSets.end ();) + { + if (it->second->HasNonExpiredLeases ()) // all leases expired + { + LogPrint ("LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired"); + delete it->second; + it = m_LeaseSets.erase (it); + } + else + it++; + } + } + + void NetDb::ManageRequests () + { + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();) + { + auto dest = it->second; + bool done = false; + if (!dest->IsExploratory () && ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute + { + if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds + { + auto count = dest->GetExcludedPeers ().size (); + if (count < 7) + { + auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = pool->GetNextOutboundTunnel (); + auto inbound = pool->GetNextInboundTunnel (); + auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ()); + if (nextFloodfill && outbound && inbound) + outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0, + dest->CreateRequestMessage (nextFloodfill, inbound)); + else + { + done = true; + if (!inbound) LogPrint (eLogWarning, "No inbound tunnels"); + if (!outbound) LogPrint (eLogWarning, "No outbound tunnels"); + if (!nextFloodfill) LogPrint (eLogWarning, "No more floodfills"); + } + } + else + { + LogPrint (eLogWarning, dest->GetDestination ().ToBase64 (), " not found after 7 attempts"); + done = true; + } + } + } + else // delete previous exploratory + done = true; + + if (done) + { + delete it->second; + it = m_RequestedDestinations.erase (it); + } + else + it++; + } + } +} +} diff --git a/NetDb.h b/NetDb.h new file mode 100644 index 00000000..5aa6acec --- /dev/null +++ b/NetDb.h @@ -0,0 +1,124 @@ +#ifndef NETDB_H__ +#define NETDB_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "Queue.h" +#include "I2NPProtocol.h" +#include "RouterInfo.h" +#include "LeaseSet.h" +#include "Tunnel.h" +#include "TunnelPool.h" + +namespace i2p +{ +namespace data +{ + class RequestedDestination + { + public: + + RequestedDestination (const IdentHash& destination, bool isExploratory = false): + m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {}; + + const IdentHash& GetDestination () const { return m_Destination; }; + int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); }; + const std::set& GetExcludedPeers () { return m_ExcludedPeers; }; + void ClearExcludedPeers (); + std::shared_ptr GetLastRouter () const { return m_LastRouter; }; + bool IsExploratory () const { return m_IsExploratory; }; + bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); }; + uint64_t GetCreationTime () const { return m_CreationTime; }; + I2NPMessage * CreateRequestMessage (std::shared_ptr, const i2p::tunnel::InboundTunnel * replyTunnel); + I2NPMessage * CreateRequestMessage (const IdentHash& floodfill); + + private: + + IdentHash m_Destination; + bool m_IsExploratory; + std::set m_ExcludedPeers; + std::shared_ptr m_LastRouter; + uint64_t m_CreationTime; + }; + + class NetDb + { + public: + + NetDb (); + ~NetDb (); + + void Start (); + void Stop (); + + void AddRouterInfo (const uint8_t * buf, int len); + void AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len); + void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from); + std::shared_ptr FindRouter (const IdentHash& ident) const; + LeaseSet * FindLeaseSet (const IdentHash& destination) const; + + void RequestDestination (const IdentHash& destination); + + void HandleDatabaseStoreMsg (I2NPMessage * msg); + void HandleDatabaseSearchReplyMsg (I2NPMessage * msg); + void HandleDatabaseLookupMsg (I2NPMessage * msg); + + std::shared_ptr GetRandomRouter () const; + std::shared_ptr GetRandomRouter (std::shared_ptr compatibleWith) const; + std::shared_ptr GetHighBandwidthRandomRouter (std::shared_ptr compatibleWith) const; + std::shared_ptr GetClosestFloodfill (const IdentHash& destination, const std::set& excluded) const; + void SetUnreachable (const IdentHash& ident, bool unreachable); + + void PostI2NPMsg (I2NPMessage * msg); + + // for web interface + int GetNumRouters () const { return m_RouterInfos.size (); }; + int GetNumFloodfills () const { return m_Floodfills.size (); }; + int GetNumLeaseSets () const { return m_LeaseSets.size (); }; + + private: + + bool CreateNetDb(boost::filesystem::path directory); + void Load (const char * directory); + void SaveUpdated (const char * directory); + void Run (); // exploratory thread + void Explore (int numDestinations); + void Publish (); + void ManageLeaseSets (); + void ManageRequests (); + + RequestedDestination * CreateRequestedDestination (const IdentHash& dest, bool isExploratory = false); + bool DeleteRequestedDestination (const IdentHash& dest); // returns true if found + void DeleteRequestedDestination (RequestedDestination * dest); + + template + std::shared_ptr GetRandomRouter (Filter filter) const; + + private: + + std::map m_LeaseSets; + mutable std::mutex m_RouterInfosMutex; + std::map > m_RouterInfos; + mutable std::mutex m_FloodfillsMutex; + std::list > m_Floodfills; + std::mutex m_RequestedDestinationsMutex; + std::map m_RequestedDestinations; + + bool m_IsRunning; + std::thread * m_Thread; + i2p::util::Queue m_Queue; // of I2NPDatabaseStoreMsg + + static const char m_NetDbPath[]; + }; + + extern NetDb netdb; +} +} + +#endif diff --git a/libi2pd/Queue.h b/Queue.h similarity index 52% rename from libi2pd/Queue.h rename to Queue.h index 0e3e4fde..f98d7178 100644 --- a/libi2pd/Queue.h +++ b/Queue.h @@ -1,20 +1,11 @@ -/* -* Copyright (c) 2013-2024, 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 QUEUE_H__ #define QUEUE_H__ -#include +#include #include #include #include #include -#include namespace i2p { @@ -22,47 +13,37 @@ namespace util { template class Queue - { + { public: - void Put (Element e) + void Put (Element * e) { - std::unique_lock l(m_QueueMutex); - m_Queue.push_back (std::move(e)); + std::unique_lock l(m_QueueMutex); + m_Queue.push (e); m_NonEmpty.notify_one (); } - void Put (std::list& list) - { - if (!list.empty ()) - { - std::unique_lock l(m_QueueMutex); - m_Queue.splice (m_Queue.end (), list); - m_NonEmpty.notify_one (); - } - } - - Element GetNext () + Element * GetNext () { std::unique_lock l(m_QueueMutex); - auto el = GetNonThreadSafe (); + Element * el = GetNonThreadSafe (); if (!el) { m_NonEmpty.wait (l); el = GetNonThreadSafe (); - } + } return el; } - Element GetNextWithTimeout (int usec) + Element * GetNextWithTimeout (int usec) { std::unique_lock l(m_QueueMutex); - auto el = GetNonThreadSafe (); + Element * el = GetNonThreadSafe (); if (!el) { m_NonEmpty.wait_for (l, std::chrono::milliseconds (usec)); el = GetNonThreadSafe (); - } + } return el; } @@ -78,66 +59,93 @@ namespace util return m_NonEmpty.wait_for (l, std::chrono::seconds (sec) + std::chrono::milliseconds (usec)) != std::cv_status::timeout; } - bool IsEmpty () - { + bool IsEmpty () + { std::unique_lock l(m_QueueMutex); return m_Queue.empty (); } - - int GetSize () const - { - std::unique_lock l(m_QueueMutex); - return m_Queue.size (); - } - + void WakeUp () { m_NonEmpty.notify_all (); }; - Element Get () + Element * Get () { std::unique_lock l(m_QueueMutex); return GetNonThreadSafe (); - } + } - Element Peek () + Element * Peek () { std::unique_lock l(m_QueueMutex); return GetNonThreadSafe (true); - } - - void GetWholeQueue (std::list& queue) - { - if (!queue.empty ()) - { - std::list newQueue; - queue.swap (newQueue); - } - { - std::unique_lock l(m_QueueMutex); - m_Queue.swap (queue); - } - } - + } + private: - - Element GetNonThreadSafe (bool peek = false) + + Element * GetNonThreadSafe (bool peek = false) { if (!m_Queue.empty ()) { - auto el = m_Queue.front (); + Element * el = m_Queue.front (); if (!peek) - m_Queue.pop_front (); + m_Queue.pop (); return el; - } + } return nullptr; + } + + private: + + std::queue m_Queue; + std::mutex m_QueueMutex; + std::condition_variable m_NonEmpty; + }; + + template + class MsgQueue: public Queue + { + public: + + typedef std::function OnEmpty; + + MsgQueue (): m_IsRunning (true), m_Thread (std::bind (&MsgQueue::Run, this)) {}; + ~MsgQueue () { Stop (); }; + void Stop() + { + if (m_IsRunning) + { + m_IsRunning = false; + Queue::WakeUp (); + m_Thread.join(); + } } + void SetOnEmpty (OnEmpty const & e) { m_OnEmpty = e; }; + private: - std::list m_Queue; - mutable std::mutex m_QueueMutex; - std::condition_variable m_NonEmpty; - }; -} -} + void Run () + { + while (m_IsRunning) + { + while (Msg * msg = Queue::Get ()) + { + msg->Process (); + delete msg; + } + if (m_OnEmpty != nullptr) + m_OnEmpty (); + if (m_IsRunning) + Queue::Wait (); + } + } + + private: + + volatile bool m_IsRunning; + OnEmpty m_OnEmpty; + std::thread m_Thread; + }; +} +} #endif diff --git a/README.md b/README.md index ce25c8f2..2f55ca5f 100644 --- a/README.md +++ b/README.md @@ -1,124 +1,85 @@ -[![GitHub release](https://img.shields.io/github/release/PurpleI2P/i2pd.svg?label=latest%20release)](https://github.com/PurpleI2P/i2pd/releases/latest) -[![Snapcraft release](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) -[![License](https://img.shields.io/github/license/PurpleI2P/i2pd.svg)](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE) -[![Packaging status](https://repology.org/badge/tiny-repos/i2pd.svg)](https://repology.org/project/i2pd/versions) -[![Docker Pulls](https://img.shields.io/docker/pulls/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd) -[![Crowdin](https://badges.crowdin.net/i2pd/localized.svg)](https://crowdin.com/project/i2pd) - -*note: i2pd for Android can be found in [i2pd-android](https://github.com/PurpleI2P/i2pd-android) repository and with Qt GUI in [i2pd-qt](https://github.com/PurpleI2P/i2pd-qt) repository* - i2pd ==== -[Русская версия](https://github.com/PurpleI2P/i2pd_docs_ru/blob/master/README.md) +I2P router written in C++ -i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client. +Requirements for Linux/FreeBSD/OSX +---------------------------------- -I2P (Invisible Internet Protocol) is a universal anonymous network layer. -All communications over I2P are anonymous and end-to-end encrypted, participants -don't reveal their real IP addresses. +GCC 4.6 or newer, Boost 1.46 or newer, crypto++. Clang can be used instead of +GCC. -I2P client is a software used for building and using anonymous I2P -networks. Such networks are commonly used for anonymous peer-to-peer -applications (filesharing, cryptocurrencies) and anonymous client-server -applications (websites, instant messengers, chat-servers). +Requirements for Windows +------------------------ -I2P allows people from all around the world to communicate and share information -without restrictions. +VS2013 (known to work with 12.0.21005.1 or newer), Boost 1.46 or newer, +crypto++ 5.62. See Win32/README-Build.txt for instructions on how to build i2pd +and its dependencies. -Features --------- +Build Statuses +--------------- -* Distributed anonymous networking framework -* End-to-end encrypted communications -* Small footprint, simple dependencies, fast performance -* Rich set of APIs for developers of secure applications - -Resources ---------- - -* [Website](http://i2pd.website) -* [Documentation](https://i2pd.readthedocs.io/en/latest/) -* [Wiki](https://github.com/PurpleI2P/i2pd/wiki) -* [Tickets/Issues](https://github.com/PurpleI2P/i2pd/issues) -* [Specifications](https://geti2p.net/spec) -* [Twitter](https://twitter.com/hashtag/i2pd) - -Installing ----------- - -The easiest way to install i2pd is by using precompiled packages and binaries. -You can fetch most of them on [release](https://github.com/PurpleI2P/i2pd/releases/latest) page. -Please see [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/install/) for more info. - -Building --------- -See [documentation](https://i2pd.readthedocs.io/en/latest/) for how to build -i2pd from source on your OS. - -note: i2pd with Qt GUI can be found in [i2pd-qt](https://github.com/PurpleI2P/i2pd-qt) repository and for android in [i2pd-android](https://github.com/PurpleI2P/i2pd-android) repository. +- Linux x64 - [![Build Status](https://jenkins.nordcloud.no/buildStatus/icon?job=i2pd-linux)](https://jenkins.nordcloud.no/job/i2pd-linux/) +- Linux ARM - To be added +- Mac OS X - Got it working, but not well tested. (Only works with clang, not GCC.) +- Microsoft VC13 - To be added -Build instructions: - -* [unix](https://i2pd.readthedocs.io/en/latest/devs/building/unix/) -* [windows](https://i2pd.readthedocs.io/en/latest/devs/building/windows/) -* [iOS](https://i2pd.readthedocs.io/en/latest/devs/building/ios/) -* [android](https://i2pd.readthedocs.io/en/latest/devs/building/android/) - - -**Supported systems:** - -* GNU/Linux (Debian, Ubuntu, etc) - [![Build on Ubuntu](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml) -* CentOS, Fedora, Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/) -* Alpine, ArchLinux, openSUSE, Gentoo, etc. -* Windows - [![Build on Windows](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml) -* Mac OS - [![Build on OSX](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml) -* Docker image - [![Build containers](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml) -* Snap - [![i2pd](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) [![i2pd](https://snapcraft.io/i2pd/trending.svg?name=0)](https://snapcraft.io/i2pd) -* FreeBSD - [![Build on FreeBSD](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml) -* Android - [![Android CI](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml/badge.svg)](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml) -* iOS - -Using i2pd ----------- - -See [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/run/) and -[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/contrib/i2pd.conf). - -Localization ------------- - -You can help us with translation i2pd to your language using Crowdin platform! -Translation project can be found [here](https://crowdin.com/project/i2pd). - -New languages can be requested on project's [discussion page](https://crowdin.com/project/i2pd/discussions). - -Current status: [![Crowdin](https://badges.crowdin.net/i2pd/localized.svg)](https://crowdin.com/project/i2pd) - -Donations ---------- - -**E-Mail**: ```i2porignal at yandex.com``` - -**BTC**: ```3MDoGJW9TLMTCDGrR9bLgWXfm6sjmgy86f``` - -**LTC**: ```LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59``` - -**ETH**: ```0x9e5bac70d20d1079ceaa111127f4fb3bccce379d``` - -**GST**: ```GbD2JSQHBHCKLa9WTHmigJRpyFgmBj4woG``` - -**DASH**: ```Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF``` - -**ZEC**: ```t1cTckLuXsr1dwVrK4NDzfhehss4NvMadAJ``` - -**ANC**: ```AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z``` - -**XMR**: ```497pJc7X4xqKvcLBLpSUtRgWqMMyo24u4btCos3cak6gbMkpobgSU6492ztUcUBghyeHpYeczB55s38NpuHoH5WGNSPDRMH``` - -License +Testing ------- -This project is licensed under the BSD 3-clause license, which can be found in the file -LICENSE in the root of the project source code. +First, build it. + +* $ cd i2pd +* $ make + +Next, find out your public ip. (find it for example at http://www.whatismyip.com/) + +Then, run it with: + +$ ./i2p --host=YOUR_PUBLIC_IP + +The client should now reseed by itself. + +To visit an I2P page, you need to find the b32 address of your destination. +After that, go to the webconsole and add it behind the url. (Remove http:// from the address) + +This should resulting in for example: +http://localhost:7070/4oes3rlgrpbkmzv4lqcfili23h3cvpwslqcfjlk6vvguxyggspwa.b32.i2p + + +Cmdline options +--------------- + +* --host= - The external IP +* --port= - The port to listen on +* --httpport= - The http port to listen on +* --log= - Enable or disable logging to file. 1 for yes, 0 for no. +* --daemon= - Enable or disable daemon mode. 1 for yes, 0 for no. +* --service= - 1 if uses system folders (/var/run/i2pd.pid, /var/log/i2pd.log, /var/lib/i2pd). +* --unreachable= - 1 if router is declared as unreachable and works through introducers. +* --v6= - 1 if supports communication through ipv6, off by default +* --httpproxyport= - The port to listen on (HTTP Proxy) +* --socksproxyport= - The port to listen on (SOCKS Proxy) +* --ircport= - The local port of IRC tunnel to listen on. 6668 by default +* --ircdest= - I2P destination address of IRC server. For example irc.postman.i2p +* --irckeys= - optional keys file for local destination +* --eepkeys= - File name containing destination keys, for example privKeys.dat. + The file will be created if it does not already exist (issue #110). +* --eephost= - Address incoming trafic forward to. 127.0.0.1 by default +* --eepport= - Port incoming trafic forward to. 80 by default +* --samport= - Port of SAM bridge. Usually 7656. SAM is off if not specified +* --bobport= - Port of BOB command channel. Usually 2827. BOB is off if not specified +* --conf= - Config file (default: ~/.i2pd/i2p.conf or /var/lib/i2pd/i2p.conf) + This parameter will be silently ignored if the specified config file does not exist. + Options specified on the command line take precedence over those in the config file. + +Config file +----------- + +INI-like, syntax is the following : = . +All command-line parameters are allowed as keys, for example: + + log = 1 + v6 = 0 + ircdest = irc.postman.i2p diff --git a/Reseed.cpp b/Reseed.cpp new file mode 100644 index 00000000..9c72dc84 --- /dev/null +++ b/Reseed.cpp @@ -0,0 +1,502 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "I2PEndian.h" +#include "Reseed.h" +#include "Log.h" +#include "Identity.h" +#include "CryptoConst.h" +#include "NetDb.h" +#include "util.h" + + +namespace i2p +{ +namespace data +{ + + static std::vector httpReseedHostList = { + "http://193.150.121.66/netDb/", + "http://netdb.i2p2.no/", + "http://reseed.i2p-projekt.de/", + "http://cowpuncher.drollette.com/netdb/", + "http://i2p.mooo.com/netDb/", + "http://reseed.info/", + "http://uk.reseed.i2p2.no/", + "http://us.reseed.i2p2.no/", + "http://jp.reseed.i2p2.no/", + "http://i2p-netdb.innovatio.no/", + "http://ieb9oopo.mooo.com" + }; + + //TODO: Remember to add custom port support. Not all serves on 443 + static std::vector httpsReseedHostList = { + "https://193.150.121.66/netDb/", + "https://netdb.i2p2.no/", + "https://reseed.i2p-projekt.de/", + "https://cowpuncher.drollette.com/netdb/", + "https://i2p.mooo.com/netDb/", + "https://reseed.info/", + "https://i2p-netdb.innovatio.no/", + "https://ieb9oopo.mooo.com/", + "https://ssl.webpack.de/ivae2he9.sg4.e-plaza.de/" // Only HTTPS and SU3 (v2) support + }; + + //TODO: Implement v2 reseeding. Lightweight zip library is needed. + //TODO: Implement SU3, utils. + Reseeder::Reseeder() + { + } + + Reseeder::~Reseeder() + { + } + + bool Reseeder::reseedNow() + { + try + { + // Seems like the best place to try to intercept with SSL + /*ssl_server = true; + try { + // SSL + } + catch (std::exception& e) + { + LogPrint("Exception in SSL: ", e.what()); + }*/ + std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())]; + LogPrint("Reseeding from ", reseedHost); + std::string content = i2p::util::http::httpRequest(reseedHost); + if (content == "") + { + LogPrint("Reseed failed"); + return false; + } + boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*\"([^\"]*)\"", boost::regex::normal | boost::regbase::icase); + boost::sregex_token_iterator i(content.begin(), content.end(), e, 1); + boost::sregex_token_iterator j; + //TODO: Ugly code, try to clean up. + //TODO: Try to reduce N number of variables + std::string name; + std::string routerInfo; + std::string tmpUrl; + std::string filename; + std::string ignoreFileSuffix = ".su3"; + boost::filesystem::path root = i2p::util::filesystem::GetDataDir(); + while (i != j) + { + name = *i++; + if (name.find(ignoreFileSuffix)!=std::string::npos) + continue; + LogPrint("Downloading ", name); + tmpUrl = reseedHost; + tmpUrl.append(name); + routerInfo = i2p::util::http::httpRequest(tmpUrl); + if (routerInfo.size()==0) + continue; + filename = root.string(); +#ifndef _WIN32 + filename += "/netDb/r"; +#else + filename += "\\netDb\\r"; +#endif + filename += name.at(11); // first char in id +#ifndef _WIN32 + filename.append("/"); +#else + filename.append("\\"); +#endif + filename.append(name.c_str()); + std::ofstream outfile (filename, std::ios::binary); + outfile << routerInfo; + outfile.close(); + } + return true; + } + catch (std::exception& ex) + { + //TODO: error reporting + return false; + } + return false; + } + + int Reseeder::ReseedNowSU3 () + { + CryptoPP::AutoSeededRandomPool rnd; + auto ind = rnd.GenerateWord32 (0, httpReseedHostList.size() - 1); + std::string reseedHost = httpReseedHostList[ind]; + return ReseedFromSU3 (reseedHost); + } + + int Reseeder::ReseedFromSU3 (const std::string& host) + { + std::string url = host + "i2pseeds.su3"; + LogPrint (eLogInfo, "Dowloading SU3 from ", host); + std::string su3 = i2p::util::http::httpRequest (url); + if (su3.length () > 0) + { + std::stringstream s(su3); + return ProcessSU3Stream (s); + } + else + { + LogPrint (eLogWarning, "SU3 download failed"); + return 0; + } + } + + int Reseeder::ProcessSU3File (const char * filename) + { + std::ifstream s(filename, std::ifstream::binary); + if (s.is_open ()) + return ProcessSU3Stream (s); + else + { + LogPrint (eLogError, "Can't open file ", filename); + return 0; + } + } + + const char SU3_MAGIC_NUMBER[]="I2Psu3"; + const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50; + const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50; + const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008; + int Reseeder::ProcessSU3Stream (std::istream& s) + { + char magicNumber[7]; + s.read (magicNumber, 7); // magic number and zero byte 6 + if (strcmp (magicNumber, SU3_MAGIC_NUMBER)) + { + LogPrint (eLogError, "Unexpected SU3 magic number"); + return 0; + } + s.seekg (1, std::ios::cur); // su3 file format version + SigningKeyType signatureType; + s.read ((char *)&signatureType, 2); // signature type + signatureType = be16toh (signatureType); + uint16_t signatureLength; + s.read ((char *)&signatureLength, 2); // signature length + signatureLength = be16toh (signatureLength); + s.seekg (1, std::ios::cur); // unused + uint8_t versionLength; + s.read ((char *)&versionLength, 1); // version length + s.seekg (1, std::ios::cur); // unused + uint8_t signerIDLength; + s.read ((char *)&signerIDLength, 1); // signer ID length + uint64_t contentLength; + s.read ((char *)&contentLength, 8); // content length + contentLength = be64toh (contentLength); + s.seekg (1, std::ios::cur); // unused + uint8_t fileType; + s.read ((char *)&fileType, 1); // file type + if (fileType != 0x00) // zip file + { + LogPrint (eLogError, "Can't handle file type ", (int)fileType); + return 0; + } + s.seekg (1, std::ios::cur); // unused + uint8_t contentType; + s.read ((char *)&contentType, 1); // content type + if (contentType != 0x03) // reseed data + { + LogPrint (eLogError, "Unexpected content type ", (int)contentType); + return 0; + } + s.seekg (12, std::ios::cur); // unused + + s.seekg (versionLength, std::ios::cur); // skip version + char signerID[256]; + s.read (signerID, signerIDLength); // signerID + signerID[signerIDLength] = 0; + + //try to verify signature + auto it = m_SigningKeys.find (signerID); + if (it != m_SigningKeys.end ()) + { + // TODO: implement all signature types + if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096) + { + size_t pos = s.tellg (); + size_t tbsLen = pos + contentLength; + uint8_t * tbs = new uint8_t[tbsLen]; + s.seekg (0, std::ios::beg); + s.read ((char *)tbs, tbsLen); + uint8_t * signature = new uint8_t[signatureLength]; + s.read ((char *)signature, signatureLength); + // RSA-raw + i2p::crypto::RSASHA5124096RawVerifier verifier(it->second); + verifier.Update (tbs, tbsLen); + if (!verifier.Verify (signature)) + LogPrint (eLogWarning, "SU3 signature verification failed"); + delete[] signature; + delete[] tbs; + s.seekg (pos, std::ios::beg); + } + else + LogPrint (eLogWarning, "Signature type ", signatureType, " is not supported"); + } + else + LogPrint (eLogWarning, "Certificate for ", signerID, " not loaded"); + + // handle content + int numFiles = 0; + size_t contentPos = s.tellg (); + while (!s.eof ()) + { + uint32_t signature; + s.read ((char *)&signature, 4); + signature = le32toh (signature); + if (signature == ZIP_HEADER_SIGNATURE) + { + // next local file + s.seekg (2, std::ios::cur); // version + uint16_t bitFlag; + s.read ((char *)&bitFlag, 2); + bitFlag = le16toh (bitFlag); + uint16_t compressionMethod; + s.read ((char *)&compressionMethod, 2); + compressionMethod = le16toh (compressionMethod); + s.seekg (4, std::ios::cur); // skip fields we don't care about + uint32_t crc32, compressedSize, uncompressedSize; + s.read ((char *)&crc32, 4); + crc32 = le32toh (crc32); + s.read ((char *)&compressedSize, 4); + compressedSize = le32toh (compressedSize); + s.read ((char *)&uncompressedSize, 4); + uncompressedSize = le32toh (uncompressedSize); + uint16_t fileNameLength, extraFieldLength; + s.read ((char *)&fileNameLength, 2); + fileNameLength = le16toh (fileNameLength); + s.read ((char *)&extraFieldLength, 2); + extraFieldLength = le16toh (extraFieldLength); + char localFileName[255]; + s.read (localFileName, fileNameLength); + localFileName[fileNameLength] = 0; + s.seekg (extraFieldLength, std::ios::cur); + // take care about data desriptor if presented + if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) + { + size_t pos = s.tellg (); + if (!FindZipDataDescriptor (s)) + { + LogPrint (eLogError, "SU3 archive data descriptor not found"); + return numFiles; + } + + s.read ((char *)&crc32, 4); + crc32 = le32toh (crc32); + s.read ((char *)&compressedSize, 4); + compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data + s.read ((char *)&uncompressedSize, 4); + uncompressedSize = le32toh (uncompressedSize); + + // now we know compressed and uncompressed size + s.seekg (pos, std::ios::beg); // back to compressed data + } + + LogPrint (eLogDebug, "Proccessing file ", localFileName, " ", compressedSize, " bytes"); + if (!compressedSize) + { + LogPrint (eLogWarning, "Unexpected size 0. Skipped"); + continue; + } + + uint8_t * compressed = new uint8_t[compressedSize]; + s.read ((char *)compressed, compressedSize); + if (compressionMethod) // we assume Deflate + { + CryptoPP::Inflator decompressor; + decompressor.Put (compressed, compressedSize); + decompressor.MessageEnd(); + if (decompressor.MaxRetrievable () <= uncompressedSize) + { + uint8_t * uncompressed = new uint8_t[uncompressedSize]; + decompressor.Get (uncompressed, uncompressedSize); + if (CryptoPP::CRC32().VerifyDigest ((uint8_t *)&crc32, uncompressed, uncompressedSize)) + { + i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize); + numFiles++; + } + else + LogPrint (eLogError, "CRC32 verification failed"); + delete[] uncompressed; + } + else + LogPrint (eLogError, "Actual uncompressed size ", decompressor.MaxRetrievable (), " exceed ", uncompressedSize, " from header"); + } + else // no compression + { + i2p::data::netdb.AddRouterInfo (compressed, compressedSize); + numFiles++; + } + delete[] compressed; + if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) + s.seekg (12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4) + } + else + { + if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE) + LogPrint (eLogWarning, "Missing zip central directory header"); + break; // no more files + } + size_t end = s.tellg (); + if (end - contentPos >= contentLength) + break; // we are beyond contentLength + } + return numFiles; + } + + const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 }; + bool Reseeder::FindZipDataDescriptor (std::istream& s) + { + size_t nextInd = 0; + while (!s.eof ()) + { + uint8_t nextByte; + s.read ((char *)&nextByte, 1); + if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd]) + { + nextInd++; + if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE)) + return true; + } + else + nextInd = 0; + } + return false; + } + + const char CERTIFICATE_HEADER[] = "-----BEGIN CERTIFICATE-----"; + const char CERTIFICATE_FOOTER[] = "-----END CERTIFICATE-----"; + void Reseeder::LoadCertificate (const std::string& filename) + { + std::ifstream s(filename, std::ifstream::binary); + if (s.is_open ()) + { + s.seekg (0, std::ios::end); + size_t len = s.tellg (); + s.seekg (0, std::ios::beg); + char buf[2048]; + s.read (buf, len); + std::string cert (buf, len); + // assume file in pem format + auto pos1 = cert.find (CERTIFICATE_HEADER); + auto pos2 = cert.find (CERTIFICATE_FOOTER); + if (pos1 == std::string::npos || pos2 == std::string::npos) + { + LogPrint (eLogError, "Malformed certificate file"); + return; + } + pos1 += strlen (CERTIFICATE_HEADER); + pos2 -= pos1; + std::string base64 = cert.substr (pos1, pos2); + + CryptoPP::ByteQueue queue; + CryptoPP::Base64Decoder decoder; // regular base64 rather than I2P + decoder.Attach (new CryptoPP::Redirector (queue)); + decoder.Put ((const uint8_t *)base64.data(), base64.length()); + decoder.MessageEnd (); + + // extract X.509 + CryptoPP::BERSequenceDecoder x509Cert (queue); + CryptoPP::BERSequenceDecoder tbsCert (x509Cert); + // version + uint32_t ver; + CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED); + CryptoPP::BERDecodeUnsigned(context, ver, CryptoPP::INTEGER); + // serial + CryptoPP::Integer serial; + serial.BERDecode(tbsCert); + // signature + CryptoPP::BERSequenceDecoder signature (tbsCert); + signature.SkipAll(); + + // issuer + std::string name; + CryptoPP::BERSequenceDecoder issuer (tbsCert); + { + CryptoPP::BERSetDecoder c (issuer); c.SkipAll(); + CryptoPP::BERSetDecoder st (issuer); st.SkipAll(); + CryptoPP::BERSetDecoder l (issuer); l.SkipAll(); + CryptoPP::BERSetDecoder o (issuer); o.SkipAll(); + CryptoPP::BERSetDecoder ou (issuer); ou.SkipAll(); + CryptoPP::BERSetDecoder cn (issuer); + { + CryptoPP::BERSequenceDecoder attributes (cn); + { + CryptoPP::BERGeneralDecoder ident(attributes, CryptoPP::OBJECT_IDENTIFIER); + ident.SkipAll (); + CryptoPP::BERDecodeTextString (attributes, name, CryptoPP::UTF8_STRING); + } + } + } + issuer.SkipAll(); + // validity + CryptoPP::BERSequenceDecoder validity (tbsCert); + validity.SkipAll(); + // subject + CryptoPP::BERSequenceDecoder subject (tbsCert); + subject.SkipAll(); + // public key + CryptoPP::BERSequenceDecoder publicKey (tbsCert); + { + CryptoPP::BERSequenceDecoder ident (publicKey); + ident.SkipAll (); + CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING); + key.Skip (1); // FIXME: probably bug in crypto++ + CryptoPP::BERSequenceDecoder keyPair (key); + CryptoPP::Integer n; + n.BERDecode (keyPair); + if (name.length () > 0) + { + PublicKey value; + n.Encode (value, 512); + m_SigningKeys[name] = value; + } + else + LogPrint (eLogWarning, "Unknown issuer. Skipped"); + } + publicKey.SkipAll(); + + tbsCert.SkipAll(); + x509Cert.SkipAll(); + } + else + LogPrint (eLogError, "Can't open certificate file ", filename); + } + + void Reseeder::LoadCertificates () + { + boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed"; + + if (!boost::filesystem::exists (reseedDir)) + { + LogPrint (eLogWarning, "Reseed certificates not loaded. ", reseedDir, " doesn't exist"); + return; + } + + int numCertificates = 0; + boost::filesystem::directory_iterator end; // empty + for (boost::filesystem::directory_iterator it (reseedDir); it != end; ++it) + { + if (boost::filesystem::is_regular_file (it->status()) && it->path ().extension () == ".crt") + { + LoadCertificate (it->path ().string ()); + numCertificates++; + } + } + LogPrint (eLogInfo, numCertificates, " certificates loaded"); + } + +} +} + diff --git a/Reseed.h b/Reseed.h new file mode 100644 index 00000000..3857d057 --- /dev/null +++ b/Reseed.h @@ -0,0 +1,45 @@ +#ifndef RESEED_H +#define RESEED_H + +#include +#include +#include +#include +#include "Identity.h" + +namespace i2p +{ +namespace data +{ + + class Reseeder + { + typedef Tag<512> PublicKey; + + public: + + Reseeder(); + ~Reseeder(); + bool reseedNow(); // depreacted + int ReseedNowSU3 (); + + void LoadCertificates (); + + private: + + void LoadCertificate (const std::string& filename); + + int ReseedFromSU3 (const std::string& host); + int ProcessSU3File (const char * filename); + int ProcessSU3Stream (std::istream& s); + + bool FindZipDataDescriptor (std::istream& s); + + private: + + std::map m_SigningKeys; + }; +} +} + +#endif diff --git a/RouterContext.cpp b/RouterContext.cpp new file mode 100644 index 00000000..90f804df --- /dev/null +++ b/RouterContext.cpp @@ -0,0 +1,211 @@ +#include +#include +#include +#include "CryptoConst.h" +#include "RouterContext.h" +#include "Timestamp.h" +#include "I2NPProtocol.h" +#include "util.h" +#include "version.h" + +namespace i2p +{ + RouterContext context; + + RouterContext::RouterContext (): + m_LastUpdateTime (0), m_IsUnreachable (false), m_AcceptsTunnels (true) + { + } + + void RouterContext::Init () + { + if (!Load ()) + CreateNewRouter (); + UpdateRouterInfo (); + } + + void RouterContext::CreateNewRouter () + { + m_Keys = i2p::data::CreateRandomKeys (); + SaveKeys (); + NewRouterInfo (); + } + + void RouterContext::NewRouterInfo () + { + i2p::data::RouterInfo routerInfo; + routerInfo.SetRouterIdentity (GetIdentity ()); + int port = i2p::util::config::GetArg("-port", 0); + if (!port) + port = m_Rnd.GenerateWord32 (9111, 30777); // I2P network ports range + routerInfo.AddSSUAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port, routerInfo.GetIdentHash ()); + routerInfo.AddNTCPAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port); + routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | + i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC + routerInfo.SetProperty ("coreVersion", I2P_VERSION); + routerInfo.SetProperty ("netId", "2"); + routerInfo.SetProperty ("router.version", I2P_VERSION); + routerInfo.SetProperty ("stat_uptime", "90m"); + routerInfo.CreateBuffer (m_Keys); + m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); + } + + void RouterContext::UpdateRouterInfo () + { + m_RouterInfo.CreateBuffer (m_Keys); + m_RouterInfo.SaveToFile (i2p::util::filesystem::GetFullPath (ROUTER_INFO)); + m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch (); + } + + void RouterContext::UpdatePort (int port) + { + bool updated = false; + for (auto& address : m_RouterInfo.GetAddresses ()) + { + if (address.port != port) + { + address.port = port; + updated = true; + } + } + if (updated) + UpdateRouterInfo (); + } + + void RouterContext::UpdateAddress (const boost::asio::ip::address& host) + { + bool updated = false; + for (auto& address : m_RouterInfo.GetAddresses ()) + { + if (address.host != host && address.IsCompatible (host)) + { + address.host = host; + updated = true; + } + } + auto ts = i2p::util::GetSecondsSinceEpoch (); + if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL) + UpdateRouterInfo (); + } + + bool RouterContext::AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag) + { + bool ret = false; + auto address = routerInfo.GetSSUAddress (); + if (address) + { + ret = m_RouterInfo.AddIntroducer (address, tag); + if (ret) + UpdateRouterInfo (); + } + return ret; + } + + void RouterContext::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) + { + if (m_RouterInfo.RemoveIntroducer (e)) + UpdateRouterInfo (); + } + + void RouterContext::SetUnreachable () + { + m_IsUnreachable = true; + // set caps + m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B + // remove NTCP address + auto& addresses = m_RouterInfo.GetAddresses (); + for (size_t i = 0; i < addresses.size (); i++) + { + if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP) + { + addresses.erase (addresses.begin () + i); + break; + } + } + // delete previous introducers + for (auto& addr : addresses) + addr.introducers.clear (); + + // update + UpdateRouterInfo (); + } + + void RouterContext::SetSupportsV6 (bool supportsV6) + { + if (supportsV6) + m_RouterInfo.EnableV6 (); + else + m_RouterInfo.DisableV6 (); + UpdateRouterInfo (); + } + + void RouterContext::UpdateNTCPV6Address (const boost::asio::ip::address& host) + { + bool updated = false, found = false; + int port = 0; + auto& addresses = m_RouterInfo.GetAddresses (); + for (auto& addr : addresses) + { + if (addr.host.is_v6 () && addr.transportStyle == i2p::data::RouterInfo::eTransportNTCP) + { + if (addr.host != host) + { + addr.host = host; + updated = true; + } + found = true; + } + else + port = addr.port; + } + if (!found) + { + // create new address + m_RouterInfo.AddNTCPAddress (host.to_string ().c_str (), port); + auto mtu = i2p::util::net::GetMTU (host); + if (mtu) + { + LogPrint ("Our v6 MTU=", mtu); + if (mtu > 1472) mtu = 1472; + } + m_RouterInfo.AddSSUAddress (host.to_string ().c_str (), port, GetIdentHash (), mtu ? mtu : 1472); // TODO + updated = true; + } + if (updated) + UpdateRouterInfo (); + } + + bool RouterContext::Load () + { + std::ifstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ifstream::binary | std::ofstream::in); + if (!fk.is_open ()) return false; + + i2p::data::Keys keys; + fk.read ((char *)&keys, sizeof (keys)); + m_Keys = keys; + + i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (ROUTER_INFO)); // TODO + m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); + m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION); + m_RouterInfo.SetProperty ("router.version", I2P_VERSION); + + return true; + } + + void RouterContext::SaveKeys () + { + std::ofstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ofstream::binary | std::ofstream::out); + i2p::data::Keys keys; + memcpy (keys.privateKey, m_Keys.GetPrivateKey (), sizeof (keys.privateKey)); + memcpy (keys.signingPrivateKey, m_Keys.GetSigningPrivateKey (), sizeof (keys.signingPrivateKey)); + auto& ident = GetIdentity ().GetStandardIdentity (); + memcpy (keys.publicKey, ident.publicKey, sizeof (keys.publicKey)); + memcpy (keys.signingKey, ident.signingKey, sizeof (keys.signingKey)); + fk.write ((char *)&keys, sizeof (keys)); + } + + void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from) + { + i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); + } +} diff --git a/RouterContext.h b/RouterContext.h new file mode 100644 index 00000000..80928901 --- /dev/null +++ b/RouterContext.h @@ -0,0 +1,77 @@ +#ifndef ROUTER_CONTEXT_H__ +#define ROUTER_CONTEXT_H__ + +#include +#include +#include +#include +#include +#include +#include "Identity.h" +#include "RouterInfo.h" +#include "Garlic.h" + +namespace i2p +{ + const char ROUTER_INFO[] = "router.info"; + const char ROUTER_KEYS[] = "router.keys"; + const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes + + class RouterContext: public i2p::garlic::GarlicDestination + { + public: + + RouterContext (); + void Init (); + + i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; }; + std::shared_ptr GetSharedRouterInfo () const + { + return std::shared_ptr (&m_RouterInfo, + [](const i2p::data::RouterInfo *) {}); + } + CryptoPP::RandomNumberGenerator& GetRandomNumberGenerator () { return m_Rnd; }; + + void UpdatePort (int port); // called from Daemon + void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon + bool AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag); + void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); + bool IsUnreachable () const { return m_IsUnreachable; }; + void SetUnreachable (); + bool AcceptsTunnels () const { return m_AcceptsTunnels; }; + void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; }; + bool SupportsV6 () const { return m_RouterInfo.IsV6 (); }; + void SetSupportsV6 (bool supportsV6); + void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session + + // implements LocalDestination + const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; }; + const uint8_t * GetEncryptionPrivateKey () const { return m_Keys.GetPrivateKey (); }; + const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ().GetStandardIdentity ().publicKey; }; + void SetLeaseSetUpdated () {}; + + // implements GarlicDestination + const i2p::data::LeaseSet * GetLeaseSet () { return nullptr; }; + void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from); + + private: + + void CreateNewRouter (); + void NewRouterInfo (); + void UpdateRouterInfo (); + bool Load (); + void SaveKeys (); + + private: + + i2p::data::RouterInfo m_RouterInfo; + i2p::data::PrivateKeys m_Keys; + CryptoPP::AutoSeededRandomPool m_Rnd; + uint64_t m_LastUpdateTime; + bool m_IsUnreachable, m_AcceptsTunnels; + }; + + extern RouterContext context; +} + +#endif diff --git a/RouterInfo.cpp b/RouterInfo.cpp new file mode 100644 index 00000000..14d338e9 --- /dev/null +++ b/RouterInfo.cpp @@ -0,0 +1,646 @@ +#include +#include +#include "I2PEndian.h" +#include +#include +#include +#include +#include "CryptoConst.h" +#include "base64.h" +#include "Timestamp.h" +#include "Log.h" +#include "RouterInfo.h" +#include "RouterContext.h" + + +namespace i2p +{ +namespace data +{ + RouterInfo::RouterInfo (const std::string& fullPath): + m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false), + m_SupportedTransports (0), m_Caps (0) + { + m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; + ReadFromFile (); + } + + RouterInfo::RouterInfo (const uint8_t * buf, int len): + m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0) + { + m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; + memcpy (m_Buffer, buf, len); + m_BufferLen = len; + ReadFromBuffer (true); + } + + RouterInfo::~RouterInfo () + { + delete m_Buffer; + } + + void RouterInfo::Update (const uint8_t * buf, int len) + { + if (!m_Buffer) + m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; + m_IsUpdated = true; + m_IsUnreachable = false; + m_SupportedTransports = 0; + m_Caps = 0; + m_Addresses.clear (); + m_Properties.clear (); + memcpy (m_Buffer, buf, len); + m_BufferLen = len; + ReadFromBuffer (true); + // don't delete buffer until save to file + } + + void RouterInfo::SetRouterIdentity (const IdentityEx& identity) + { + m_RouterIdentity = identity; + m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); + } + + bool RouterInfo::LoadFile () + { + std::ifstream s(m_FullPath.c_str (), std::ifstream::binary); + if (s.is_open ()) + { + s.seekg (0,std::ios::end); + m_BufferLen = s.tellg (); + if (m_BufferLen < 40) + { + LogPrint(eLogError, "File", m_FullPath, " is malformed"); + return false; + } + s.seekg(0, std::ios::beg); + if (!m_Buffer) + m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; + s.read((char *)m_Buffer, m_BufferLen); + } + else + { + LogPrint (eLogError, "Can't open file ", m_FullPath); + return false; + } + return true; + } + + void RouterInfo::ReadFromFile () + { + if (LoadFile ()) + ReadFromBuffer (false); + } + + void RouterInfo::ReadFromBuffer (bool verifySignature) + { + size_t identityLen = m_RouterIdentity.FromBuffer (m_Buffer, m_BufferLen); + std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen)); + ReadFromStream (str); + if (verifySignature) + { + // verify signature + int l = m_BufferLen - m_RouterIdentity.GetSignatureLen (); + if (!m_RouterIdentity.Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l)) + { + LogPrint (eLogError, "signature verification failed"); + m_IsUnreachable = true; + } + m_RouterIdentity.DropVerifier (); + } + } + + void RouterInfo::ReadFromStream (std::istream& s) + { + s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); + m_Timestamp = be64toh (m_Timestamp); + // read addresses + uint8_t numAddresses; + s.read ((char *)&numAddresses, sizeof (numAddresses)); + bool introducers = false; + for (int i = 0; i < numAddresses; i++) + { + bool isValidAddress = true; + Address address; + s.read ((char *)&address.cost, sizeof (address.cost)); + s.read ((char *)&address.date, sizeof (address.date)); + char transportStyle[5]; + ReadString (transportStyle, s); + if (!strcmp (transportStyle, "NTCP")) + address.transportStyle = eTransportNTCP; + else if (!strcmp (transportStyle, "SSU")) + address.transportStyle = eTransportSSU; + else + address.transportStyle = eTransportUnknown; + address.port = 0; + address.mtu = 0; + uint16_t size, r = 0; + s.read ((char *)&size, sizeof (size)); + size = be16toh (size); + while (r < size) + { + char key[500], value[500]; + r += ReadString (key, s); + s.seekg (1, std::ios_base::cur); r++; // = + r += ReadString (value, s); + s.seekg (1, std::ios_base::cur); r++; // ; + if (!strcmp (key, "host")) + { + boost::system::error_code ecode; + address.host = boost::asio::ip::address::from_string (value, ecode); + if (ecode) + { + // TODO: we should try to resolve address here + LogPrint (eLogWarning, "Unexpected address ", value); + isValidAddress = false; + } + else + { + // add supported protocol + if (address.host.is_v4 ()) + m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4; + else + m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6; + } + } + else if (!strcmp (key, "port")) + address.port = boost::lexical_cast(value); + else if (!strcmp (key, "mtu")) + address.mtu = boost::lexical_cast(value); + else if (!strcmp (key, "key")) + Base64ToByteStream (value, strlen (value), address.key, 32); + else if (!strcmp (key, "caps")) + ExtractCaps (value); + else if (key[0] == 'i') + { + // introducers + introducers = true; + size_t l = strlen(key); + unsigned char index = key[l-1] - '0'; // TODO: + key[l-1] = 0; + if (index >= address.introducers.size ()) + address.introducers.resize (index + 1); + Introducer& introducer = address.introducers.at (index); + if (!strcmp (key, "ihost")) + { + boost::system::error_code ecode; + introducer.iHost = boost::asio::ip::address::from_string (value, ecode); + } + else if (!strcmp (key, "iport")) + introducer.iPort = boost::lexical_cast(value); + else if (!strcmp (key, "itag")) + introducer.iTag = boost::lexical_cast(value); + else if (!strcmp (key, "ikey")) + Base64ToByteStream (value, strlen (value), introducer.iKey, 32); + } + } + if (isValidAddress) + m_Addresses.push_back(address); + } + // read peers + uint8_t numPeers; + s.read ((char *)&numPeers, sizeof (numPeers)); + s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers + // read properties + uint16_t size, r = 0; + s.read ((char *)&size, sizeof (size)); + size = be16toh (size); + while (r < size) + { +#ifdef _WIN32 + char key[500], value[500]; + // TODO: investigate why properties get read as one long string under Windows + // length should not be more than 44 +#else + char key[50], value[50]; +#endif + r += ReadString (key, s); + s.seekg (1, std::ios_base::cur); r++; // = + r += ReadString (value, s); + s.seekg (1, std::ios_base::cur); r++; // ; + m_Properties[key] = value; + + // extract caps + if (!strcmp (key, "caps")) + ExtractCaps (value); + } + + if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers)) + SetUnreachable (true); + } + + void RouterInfo::ExtractCaps (const char * value) + { + const char * cap = value; + while (*cap) + { + switch (*cap) + { + case CAPS_FLAG_FLOODFILL: + m_Caps |= Caps::eFloodfill; + break; + case CAPS_FLAG_HIGH_BANDWIDTH1: + case CAPS_FLAG_HIGH_BANDWIDTH2: + case CAPS_FLAG_HIGH_BANDWIDTH3: + m_Caps |= Caps::eHighBandwidth; + break; + case CAPS_FLAG_HIDDEN: + m_Caps |= Caps::eHidden; + break; + case CAPS_FLAG_REACHABLE: + m_Caps |= Caps::eReachable; + break; + case CAPS_FLAG_UNREACHABLE: + m_Caps |= Caps::eUnreachable; + break; + case CAPS_FLAG_SSU_TESTING: + m_Caps |= Caps::eSSUTesting; + break; + case CAPS_FLAG_SSU_INTRODUCER: + m_Caps |= Caps::eSSUIntroducer; + break; + default: ; + } + cap++; + } + } + + void RouterInfo::UpdateCapsProperty () + { + std::string caps; + caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH1 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth + if (m_Caps & eFloodfill) caps += CAPS_FLAG_FLOODFILL; // floodfill + if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden + if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable + if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable + + SetProperty ("caps", caps.c_str ()); + } + + void RouterInfo::WriteToStream (std::ostream& s) + { + uint64_t ts = htobe64 (m_Timestamp); + s.write ((char *)&ts, sizeof (ts)); + + // addresses + uint8_t numAddresses = m_Addresses.size (); + s.write ((char *)&numAddresses, sizeof (numAddresses)); + for (auto& address : m_Addresses) + { + s.write ((char *)&address.cost, sizeof (address.cost)); + s.write ((char *)&address.date, sizeof (address.date)); + std::stringstream properties; + if (address.transportStyle == eTransportNTCP) + WriteString ("NTCP", s); + else if (address.transportStyle == eTransportSSU) + { + WriteString ("SSU", s); + // caps + WriteString ("caps", properties); + properties << '='; + std::string caps; + if (IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING; + if (IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER; + WriteString (caps, properties); + properties << ';'; + } + else + WriteString ("", s); + + WriteString ("host", properties); + properties << '='; + WriteString (address.host.to_string (), properties); + properties << ';'; + if (address.transportStyle == eTransportSSU) + { + // write introducers if any + if (address.introducers.size () > 0) + { + int i = 0; + for (auto introducer: address.introducers) + { + WriteString ("ihost" + boost::lexical_cast(i), properties); + properties << '='; + WriteString (introducer.iHost.to_string (), properties); + properties << ';'; + i++; + } + i = 0; + for (auto introducer: address.introducers) + { + WriteString ("ikey" + boost::lexical_cast(i), properties); + properties << '='; + char value[64]; + size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64); + value[l] = 0; + WriteString (value, properties); + properties << ';'; + i++; + } + i = 0; + for (auto introducer: address.introducers) + { + WriteString ("iport" + boost::lexical_cast(i), properties); + properties << '='; + WriteString (boost::lexical_cast(introducer.iPort), properties); + properties << ';'; + i++; + } + i = 0; + for (auto introducer: address.introducers) + { + WriteString ("itag" + boost::lexical_cast(i), properties); + properties << '='; + WriteString (boost::lexical_cast(introducer.iTag), properties); + properties << ';'; + i++; + } + } + // write intro key + WriteString ("key", properties); + properties << '='; + char value[64]; + size_t l = ByteStreamToBase64 (address.key, 32, value, 64); + value[l] = 0; + WriteString (value, properties); + properties << ';'; + // write mtu + if (address.mtu) + { + WriteString ("mtu", properties); + properties << '='; + WriteString (boost::lexical_cast(address.mtu), properties); + properties << ';'; + } + } + WriteString ("port", properties); + properties << '='; + WriteString (boost::lexical_cast(address.port), properties); + properties << ';'; + + uint16_t size = htobe16 (properties.str ().size ()); + s.write ((char *)&size, sizeof (size)); + s.write (properties.str ().c_str (), properties.str ().size ()); + } + + // peers + uint8_t numPeers = 0; + s.write ((char *)&numPeers, sizeof (numPeers)); + + // properties + std::stringstream properties; + for (auto& p : m_Properties) + { + WriteString (p.first, properties); + properties << '='; + WriteString (p.second, properties); + properties << ';'; + } + uint16_t size = htobe16 (properties.str ().size ()); + s.write ((char *)&size, sizeof (size)); + s.write (properties.str ().c_str (), properties.str ().size ()); + } + + const uint8_t * RouterInfo::LoadBuffer () + { + if (!m_Buffer) + { + if (LoadFile ()) + LogPrint ("Buffer for ", GetIdentHashAbbreviation (), " loaded from file"); + } + return m_Buffer; + } + + void RouterInfo::CreateBuffer (const PrivateKeys& privateKeys) + { + m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp + std::stringstream s; + uint8_t ident[1024]; + auto identLen = privateKeys.GetPublic ().ToBuffer (ident, 1024); + s.write ((char *)ident, identLen); + WriteToStream (s); + m_BufferLen = s.str ().size (); + if (!m_Buffer) + m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; + memcpy (m_Buffer, s.str ().c_str (), m_BufferLen); + // signature + privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen); + m_BufferLen += privateKeys.GetPublic ().GetSignatureLen (); + } + + void RouterInfo::SaveToFile (const std::string& fullPath) + { + m_FullPath = fullPath; + if (m_Buffer) + { + std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); + f.write ((char *)m_Buffer, m_BufferLen); + } + else + LogPrint (eLogError, "Can't save to file"); + } + + size_t RouterInfo::ReadString (char * str, std::istream& s) + { + uint8_t len; + s.read ((char *)&len, 1); + s.read (str, len); + str[len] = 0; + return len+1; + } + + void RouterInfo::WriteString (const std::string& str, std::ostream& s) + { + uint8_t len = str.size (); + s.write ((char *)&len, 1); + s.write (str.c_str (), len); + } + + void RouterInfo::AddNTCPAddress (const char * host, int port) + { + Address addr; + addr.host = boost::asio::ip::address::from_string (host); + addr.port = port; + addr.transportStyle = eTransportNTCP; + addr.cost = 2; + addr.date = 0; + addr.mtu = 0; + m_Addresses.push_back(addr); + m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eNTCPV4; + } + + void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu) + { + Address addr; + addr.host = boost::asio::ip::address::from_string (host); + addr.port = port; + addr.transportStyle = eTransportSSU; + addr.cost = 10; // NTCP should have priority over SSU + addr.date = 0; + addr.mtu = mtu; + memcpy (addr.key, key, 32); + m_Addresses.push_back(addr); + m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eSSUV4; + m_Caps |= eSSUTesting; + m_Caps |= eSSUIntroducer; + } + + bool RouterInfo::AddIntroducer (const Address * address, uint32_t tag) + { + for (auto& addr : m_Addresses) + { + if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ()) + { + for (auto intro: addr.introducers) + if (intro.iTag == tag) return false; // already presented + Introducer x; + x.iHost = address->host; + x.iPort = address->port; + x.iTag = tag; + memcpy (x.iKey, address->key, 32); // TODO: replace to Tag<32> + addr.introducers.push_back (x); + return true; + } + } + return false; + } + + bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e) + { + for (auto& addr : m_Addresses) + { + if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ()) + { + for (std::vector::iterator it = addr.introducers.begin (); it != addr.introducers.end (); it++) + if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) + { + addr.introducers.erase (it); + return true; + } + } + } + return false; + } + + void RouterInfo::SetCaps (uint8_t caps) + { + m_Caps = caps; + UpdateCapsProperty (); + } + + void RouterInfo::SetCaps (const char * caps) + { + SetProperty ("caps", caps); + m_Caps = 0; + ExtractCaps (caps); + } + + void RouterInfo::SetProperty (const char * key, const char * value) + { + m_Properties[key] = value; + } + + const char * RouterInfo::GetProperty (const char * key) const + { + auto it = m_Properties.find (key); + if (it != m_Properties.end ()) + return it->second.c_str (); + return 0; + } + + bool RouterInfo::IsFloodfill () const + { + return m_Caps & Caps::eFloodfill; + } + + bool RouterInfo::IsNTCP (bool v4only) const + { + if (v4only) + return m_SupportedTransports & eNTCPV4; + else + return m_SupportedTransports & (eNTCPV4 | eNTCPV6); + } + + bool RouterInfo::IsSSU (bool v4only) const + { + if (v4only) + return m_SupportedTransports & eSSUV4; + else + return m_SupportedTransports & (eSSUV4 | eSSUV6); + } + + bool RouterInfo::IsV6 () const + { + return m_SupportedTransports & (eNTCPV6 | eSSUV6); + } + + void RouterInfo::EnableV6 () + { + if (!IsV6 ()) + m_SupportedTransports |= eNTCPV6 | eSSUV6; + } + + void RouterInfo::DisableV6 () + { + if (IsV6 ()) + { + // NTCP + m_SupportedTransports &= ~eNTCPV6; + for (size_t i = 0; i < m_Addresses.size (); i++) + { + if (m_Addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP && + m_Addresses[i].host.is_v6 ()) + { + m_Addresses.erase (m_Addresses.begin () + i); + break; + } + } + + // SSU + m_SupportedTransports &= ~eSSUV6; + for (size_t i = 0; i < m_Addresses.size (); i++) + { + if (m_Addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU && + m_Addresses[i].host.is_v6 ()) + { + m_Addresses.erase (m_Addresses.begin () + i); + break; + } + } + } + } + + bool RouterInfo::UsesIntroducer () const + { + return m_Caps & Caps::eUnreachable; // non-reachable + } + + const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const + { + return GetAddress (eTransportNTCP, v4only); + } + + const RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const + { + return GetAddress (eTransportSSU, v4only); + } + + const RouterInfo::Address * RouterInfo::GetSSUV6Address () const + { + return GetAddress (eTransportSSU, false, true); + } + + const RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const + { + for (auto& address : m_Addresses) + { + if (address.transportStyle == s) + { + if ((!v4only || address.host.is_v4 ()) && (!v6only || address.host.is_v6 ())) + return &address; + } + } + return nullptr; + } +} +} diff --git a/RouterInfo.h b/RouterInfo.h new file mode 100644 index 00000000..d6492455 --- /dev/null +++ b/RouterInfo.h @@ -0,0 +1,174 @@ +#ifndef ROUTER_INFO_H__ +#define ROUTER_INFO_H__ + +#include +#include +#include +#include +#include +#include +#include "Identity.h" + +namespace i2p +{ +namespace data +{ + const char CAPS_FLAG_FLOODFILL = 'f'; + const char CAPS_FLAG_HIDDEN = 'H'; + const char CAPS_FLAG_REACHABLE = 'R'; + const char CAPS_FLAG_UNREACHABLE = 'U'; + const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; + const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; + const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M'; + const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N'; + const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O'; + + const char CAPS_FLAG_SSU_TESTING = 'B'; + const char CAPS_FLAG_SSU_INTRODUCER = 'C'; + + const int MAX_RI_BUFFER_SIZE = 2048; + class RouterInfo: public RoutingDestination + { + public: + + enum SupportedTranports + { + eNTCPV4 = 0x01, + eNTCPV6 = 0x02, + eSSUV4 = 0x04, + eSSUV6 = 0x08 + }; + + enum Caps + { + eFloodfill = 0x01, + eHighBandwidth = 0x02, + eReachable = 0x04, + eSSUTesting = 0x08, + eSSUIntroducer = 0x10, + eHidden = 0x20, + eUnreachable = 0x40 + }; + + enum TransportStyle + { + eTransportUnknown = 0, + eTransportNTCP, + eTransportSSU + }; + + struct Introducer + { + boost::asio::ip::address iHost; + int iPort; + Tag<32> iKey; + uint32_t iTag; + }; + + struct Address + { + TransportStyle transportStyle; + boost::asio::ip::address host; + int port, mtu; + uint64_t date; + uint8_t cost; + // SSU only + Tag<32> key; // intro key for SSU + std::vector introducers; + + bool IsCompatible (const boost::asio::ip::address& other) const + { + return (host.is_v4 () && other.is_v4 ()) || + (host.is_v6 () && other.is_v6 ()); + } + }; + + RouterInfo (const std::string& fullPath); + RouterInfo (): m_Buffer (nullptr) { }; + RouterInfo (const RouterInfo& ) = default; + RouterInfo& operator=(const RouterInfo& ) = default; + RouterInfo (const uint8_t * buf, int len); + ~RouterInfo (); + + const IdentityEx& GetRouterIdentity () const { return m_RouterIdentity; }; + void SetRouterIdentity (const IdentityEx& identity); + std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; + std::string GetIdentHashAbbreviation () const { return GetIdentHash ().ToBase64 ().substr (0, 4); }; + uint64_t GetTimestamp () const { return m_Timestamp; }; + std::vector
& GetAddresses () { return m_Addresses; }; + const Address * GetNTCPAddress (bool v4only = true) const; + const Address * GetSSUAddress (bool v4only = true) const; + 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); + bool AddIntroducer (const Address * address, uint32_t tag); + bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); + void SetProperty (const char * key, const char * value); + const char * GetProperty (const char * key) const; + bool IsFloodfill () const; + bool IsNTCP (bool v4only = true) const; + bool IsSSU (bool v4only = true) const; + bool IsV6 () const; + void EnableV6 (); + void DisableV6 (); + bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; + bool UsesIntroducer () const; + bool IsIntroducer () const { return m_Caps & eSSUIntroducer; }; + bool IsPeerTesting () const { return m_Caps & eSSUTesting; }; + bool IsHidden () const { return m_Caps & eHidden; }; + + uint8_t GetCaps () const { return m_Caps; }; + void SetCaps (uint8_t caps); + void SetCaps (const char * caps); + + void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; + bool IsUnreachable () const { return m_IsUnreachable; }; + + const uint8_t * GetBuffer () const { return m_Buffer; }; + const uint8_t * LoadBuffer (); // load if necessary + int GetBufferLen () const { return m_BufferLen; }; + void CreateBuffer (const PrivateKeys& privateKeys); + + bool IsUpdated () const { return m_IsUpdated; }; + void SetUpdated (bool updated) { m_IsUpdated = updated; }; + void SaveToFile (const std::string& fullPath); + + void Update (const uint8_t * buf, int len); + void DeleteBuffer () { delete m_Buffer; m_Buffer = nullptr; }; + + // implements RoutingDestination + const IdentHash& GetIdentHash () const { return m_RouterIdentity.GetIdentHash (); }; + const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity.GetStandardIdentity ().publicKey; }; + bool IsDestination () const { return false; }; + + + private: + + bool LoadFile (); + void ReadFromFile (); + void ReadFromStream (std::istream& s); + void ReadFromBuffer (bool verifySignature); + void WriteToStream (std::ostream& s); + size_t ReadString (char * str, std::istream& s); + void WriteString (const std::string& str, std::ostream& s); + void ExtractCaps (const char * value); + const Address * GetAddress (TransportStyle s, bool v4only, bool v6only = false) const; + void UpdateCapsProperty (); + + private: + + std::string m_FullPath; + IdentityEx m_RouterIdentity; + uint8_t * m_Buffer; + int m_BufferLen; + uint64_t m_Timestamp; + std::vector
m_Addresses; + std::map m_Properties; + bool m_IsUpdated, m_IsUnreachable; + uint8_t m_SupportedTransports, m_Caps; + }; +} +} + +#endif diff --git a/SAM.cpp b/SAM.cpp new file mode 100644 index 00000000..0432b245 --- /dev/null +++ b/SAM.cpp @@ -0,0 +1,773 @@ +#include +#include +#ifdef _MSC_VER +#include +#endif +#include +#include "base64.h" +#include "Identity.h" +#include "Log.h" +#include "Destination.h" +#include "ClientContext.h" +#include "SAM.h" + +namespace i2p +{ +namespace client +{ + SAMSocket::SAMSocket (SAMBridge& owner): + m_Owner (owner), m_Socket (m_Owner.GetService ()), m_Timer (m_Owner.GetService ()), + m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), m_Stream (nullptr), + m_Session (nullptr) + { + } + + SAMSocket::~SAMSocket () + { + Terminate (); + } + + void SAMSocket::CloseStream () + { + if (m_Stream) + { + m_Stream->Close (); + m_Stream.reset (); + } + } + + void SAMSocket::Terminate () + { + CloseStream (); + + switch (m_SocketType) + { + case eSAMSocketTypeSession: + m_Owner.CloseSession (m_ID); + break; + case eSAMSocketTypeStream: + { + if (m_Session) + m_Session->sockets.remove (shared_from_this ()); + break; + } + case eSAMSocketTypeAcceptor: + { + if (m_Session) + { + m_Session->sockets.remove (shared_from_this ()); + m_Session->localDestination->StopAcceptingStreams (); + } + break; + } + default: + ; + } + m_SocketType = eSAMSocketTypeTerminated; + m_Socket.close (); + } + + void SAMSocket::ReceiveHandshake () + { + m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + + void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("SAM handshake read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + m_Buffer[bytes_transferred] = 0; + LogPrint ("SAM handshake ", m_Buffer); + char * separator = strchr (m_Buffer, ' '); + if (separator) + { + separator = strchr (separator + 1, ' '); + if (separator) + *separator = 0; + } + + if (!strcmp (m_Buffer, SAM_HANDSHAKE)) + { + std::string version("3.0"); + // try to find MIN and MAX, 3.0 if not found + if (separator) + { + separator++; + std::map params; + ExtractParams (separator, bytes_transferred - (separator - m_Buffer), params); + auto it = params.find (SAM_PARAM_MAX); + // TODO: check MIN as well + if (it != params.end ()) + version = it->second; + } + if (version[0] == '3') // we support v3 (3.0 and 3.1) only + { +#ifdef _MSC_VER + size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); +#else + size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); +#endif + boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), + std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + else + SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true); + } + else + { + LogPrint ("SAM handshake mismatch"); + Terminate (); + } + } + } + + void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("SAM handshake reply send error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + std::bind(&SAMSocket::HandleMessage, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } + } + + void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close) + { + if (!m_IsSilent || m_SocketType == eSAMSocketTypeAcceptor) + boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), + std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, close)); + else + { + if (close) + Terminate (); + else + Receive (); + } + } + + void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close) + { + if (ecode) + { + LogPrint ("SAM reply send error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + if (close) + Terminate (); + else + Receive (); + } + } + + void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("SAM read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + m_Buffer[bytes_transferred] = 0; + char * eol = strchr (m_Buffer, '\n'); + if (eol) + { + *eol = 0; + char * separator = strchr (m_Buffer, ' '); + if (separator) + { + separator = strchr (separator + 1, ' '); + if (separator) + *separator = 0; + else + separator = eol; + + if (!strcmp (m_Buffer, SAM_SESSION_CREATE)) + ProcessSessionCreate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp (m_Buffer, SAM_STREAM_CONNECT)) + ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT)) + ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else if (!strcmp (m_Buffer, SAM_DEST_GENERATE)) + ProcessDestGenerate (); + else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP)) + ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); + else + { + LogPrint ("SAM unexpected message ", m_Buffer); + Terminate (); + } + } + else + { + LogPrint ("SAM malformed message ", m_Buffer); + Terminate (); + } + } + else + { + LogPrint ("SAM malformed message ", m_Buffer); + Terminate (); + } + } + } + + void SAMSocket::ProcessSessionCreate (char * buf, size_t len) + { + LogPrint ("SAM session create: ", buf); + std::map params; + ExtractParams (buf, len, params); + std::string& style = params[SAM_PARAM_STYLE]; + std::string& id = params[SAM_PARAM_ID]; + std::string& destination = params[SAM_PARAM_DESTINATION]; + m_ID = id; + if (m_Owner.FindSession (id)) + { + // session exists + SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true); + return; + } + + // create destination + m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms); + if (m_Session) + { + m_SocketType = eSAMSocketTypeSession; + if (m_Session->localDestination->IsReady ()) + { + if (style == SAM_VALUE_DATAGRAM) + { + auto dest = m_Session->localDestination->CreateDatagramDestination (); + dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + } + SendSessionCreateReplyOk (); + } + else + { + m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); + m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, + shared_from_this (), std::placeholders::_1)); + } + } + else + SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true); + } + + void SAMSocket::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + if (m_Session->localDestination->IsReady ()) + SendSessionCreateReplyOk (); + else + { + m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); + m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, + shared_from_this (), std::placeholders::_1)); + } + } + } + + void SAMSocket::SendSessionCreateReplyOk () + { + uint8_t buf[1024]; + char priv[1024]; + size_t l = m_Session->localDestination->GetPrivateKeys ().ToBuffer (buf, 1024); + size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024); + priv[l1] = 0; +#ifdef _MSC_VER + size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); +#else + size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); +#endif + SendMessageReply (m_Buffer, l2, false); + } + + void SAMSocket::ProcessStreamConnect (char * buf, size_t len) + { + LogPrint ("SAM stream connect: ", buf); + std::map params; + ExtractParams (buf, len, params); + std::string& id = params[SAM_PARAM_ID]; + std::string& destination = params[SAM_PARAM_DESTINATION]; + std::string& silent = params[SAM_PARAM_SILENT]; + if (silent == SAM_VALUE_TRUE) m_IsSilent = true; + m_ID = id; + m_Session = m_Owner.FindSession (id); + if (m_Session) + { + i2p::data::IdentityEx dest; + dest.FromBase64 (destination); + context.GetAddressBook ().InsertAddress (dest); + auto leaseSet = i2p::data::netdb.FindLeaseSet (dest.GetIdentHash ()); + if (leaseSet) + Connect (*leaseSet); + else + { + m_Session->localDestination->RequestDestination (dest.GetIdentHash (), + std::bind (&SAMSocket::HandleLeaseSetRequestComplete, + shared_from_this (), std::placeholders::_1, dest.GetIdentHash ())); + } + } + else + SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + } + + void SAMSocket::Connect (const i2p::data::LeaseSet& remote) + { + m_SocketType = eSAMSocketTypeStream; + m_Session->sockets.push_back (shared_from_this ()); + m_Stream = m_Session->localDestination->CreateStream (remote); + m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect + I2PReceive (); + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + } + + void SAMSocket::HandleLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident) + { + const i2p::data::LeaseSet * leaseSet = nullptr; + if (success) // timeout expired + leaseSet = m_Session->localDestination->FindLeaseSet (ident); + if (leaseSet) + Connect (*leaseSet); + else + { + LogPrint ("SAM destination to connect not found"); + SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true); + } + } + + void SAMSocket::ProcessStreamAccept (char * buf, size_t len) + { + LogPrint ("SAM stream accept: ", buf); + std::map params; + ExtractParams (buf, len, params); + std::string& id = params[SAM_PARAM_ID]; + std::string& silent = params[SAM_PARAM_SILENT]; + if (silent == SAM_VALUE_TRUE) m_IsSilent = true; + m_ID = id; + m_Session = m_Owner.FindSession (id); + if (m_Session) + { + if (!m_Session->localDestination->IsAcceptingStreams ()) + { + m_SocketType = eSAMSocketTypeAcceptor; + m_Session->sockets.push_back (shared_from_this ()); + m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); + SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); + } + else + SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true); + } + else + SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + } + + void SAMSocket::ProcessDestGenerate () + { + LogPrint ("SAM dest generate"); + auto localDestination = i2p::client::context.CreateNewLocalDestination (); + if (localDestination) + { + auto priv = localDestination->GetPrivateKeys ().ToBase64 (); + auto pub = localDestination->GetIdentity ().ToBase64 (); +#ifdef _MSC_VER + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, pub.c_str (), priv.c_str ()); +#else + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, pub.c_str (), priv.c_str ()); +#endif + SendMessageReply (m_Buffer, len, true); + } + else + SendMessageReply (SAM_DEST_REPLY_I2P_ERROR, strlen(SAM_DEST_REPLY_I2P_ERROR), true); + } + + void SAMSocket::ProcessNamingLookup (char * buf, size_t len) + { + LogPrint ("SAM naming lookup: ", buf); + std::map params; + ExtractParams (buf, len, params); + std::string& name = params[SAM_PARAM_NAME]; + i2p::data::IdentHash ident; + i2p::data::IdentityEx identity; + if (name == "ME") + SendNamingLookupReply (nullptr); + else if (context.GetAddressBook ().GetAddress (name, identity)) + SendNamingLookupReply (identity); + else + { +#ifdef _MSC_VER + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); +#else + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); +#endif + SendMessageReply (m_Buffer, len, false); + } + } + + void SAMSocket::SendNamingLookupReply (const i2p::data::LeaseSet * leaseSet) + { + const i2p::data::IdentityEx& identity = leaseSet ? leaseSet->GetIdentity () : m_Session->localDestination->GetIdentity (); + if (leaseSet) + // we found LeaseSet for our address, store it to addressbook + context.GetAddressBook ().InsertAddress (identity); + SendNamingLookupReply (identity); + } + + void SAMSocket::SendNamingLookupReply (const i2p::data::IdentityEx& identity) + { + auto base64 = identity.ToBase64 (); +#ifdef _MSC_VER + size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); +#else + size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); +#endif + SendMessageReply (m_Buffer, l, false); + } + + void SAMSocket::ExtractParams (char * buf, size_t len, std::map& params) + { + char * separator; + do + { + separator = strchr (buf, ' '); + if (separator) *separator = 0; + char * value = strchr (buf, '='); + if (value) + { + *value = 0; + value++; + params[buf] = value; + } + buf = separator + 1; + } + while (separator); + } + + void SAMSocket::Receive () + { + m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + std::bind((m_SocketType == eSAMSocketTypeSession) ? &SAMSocket::HandleMessage : &SAMSocket::HandleReceived, + shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + + void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("SAM read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + if (m_Stream) + m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred); + Receive (); + } + } + + void SAMSocket::I2PReceive () + { + if (m_Stream) + m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE), + std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (), + std::placeholders::_1, std::placeholders::_2), + SAM_SOCKET_CONNECTION_MAX_IDLE); + } + + void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + { + LogPrint ("SAM stream read error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + { + boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred), + std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); + } + } + + void SAMSocket::HandleWriteI2PData (const boost::system::error_code& ecode) + { + if (ecode) + { + LogPrint ("SAM socket write error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + Terminate (); + } + else + I2PReceive (); + } + + void SAMSocket::HandleI2PAccept (std::shared_ptr stream) + { + if (stream) + { + LogPrint ("SAM incoming I2P connection for session ", m_ID); + m_Stream = stream; + context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ()); + auto session = m_Owner.FindSession (m_ID); + if (session) + session->localDestination->StopAcceptingStreams (); + if (!m_IsSilent) + { + // send remote peer address + uint8_t ident[1024]; + size_t l = stream->GetRemoteIdentity ().ToBuffer (ident, 1024); + size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, m_Buffer, SAM_SOCKET_BUFFER_SIZE); + m_Buffer[l1] = '\n'; + SendMessageReply (m_Buffer, l1 + 1, false); + } + I2PReceive (); + } + } + + void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& ident, const uint8_t * buf, size_t len) + { + auto base64 = ident.ToBase64 (); +#ifdef _MSC_VER + size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); +#else + size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); +#endif + if (len < SAM_SOCKET_BUFFER_SIZE - l) + { + memcpy (m_StreamBuffer + l, buf, len); + boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l), + std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); + } + else + LogPrint (eLogWarning, "Datagram size ", len," exceeds buffer"); + } + + SAMSession::SAMSession (ClientDestination * dest): + localDestination (dest) + { + } + + SAMSession::~SAMSession () + { + for (auto it: sockets) + it->SetSocketType (eSAMSocketTypeTerminated); + i2p::client::context.DeleteLocalDestination (localDestination); + } + + void SAMSession::CloseStreams () + { + for (auto it: sockets) + { + it->CloseStream (); + it->SetSocketType (eSAMSocketTypeTerminated); + } + sockets.clear (); + } + + SAMBridge::SAMBridge (int port): + m_IsRunning (false), m_Thread (nullptr), + m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), + m_DatagramEndpoint (boost::asio::ip::udp::v4 (), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint) + { + } + + SAMBridge::~SAMBridge () + { + if (m_IsRunning) + Stop (); + } + + void SAMBridge::Start () + { + Accept (); + ReceiveDatagram (); + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&SAMBridge::Run, this)); + } + + void SAMBridge::Stop () + { + m_IsRunning = false; + m_Acceptor.cancel (); + for (auto it: m_Sessions) + delete it.second; + m_Sessions.clear (); + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + + void SAMBridge::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint ("SAM: ", ex.what ()); + } + } + } + + void SAMBridge::Accept () + { + auto newSocket = std::make_shared (*this); + m_Acceptor.async_accept (newSocket->GetSocket (), std::bind (&SAMBridge::HandleAccept, this, + std::placeholders::_1, newSocket)); + } + + void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) + { + if (!ecode) + { + LogPrint ("New SAM connection from ", socket->GetSocket ().remote_endpoint ()); + socket->ReceiveHandshake (); + } + else + LogPrint ("SAM accept error: ", ecode.message ()); + + if (ecode != boost::asio::error::operation_aborted) + Accept (); + } + + SAMSession * SAMBridge::CreateSession (const std::string& id, const std::string& destination, + const std::map * params) + { + ClientDestination * localDestination = nullptr; + if (destination != "") + { + i2p::data::PrivateKeys keys; + keys.FromBase64 (destination); + localDestination = i2p::client::context.CreateNewLocalDestination (keys, true, params); + } + else // transient + { + // extract signature type + i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; + if (params) + { + auto it = params->find (SAM_PARAM_SIGNATURE_TYPE); + if (it != params->end ()) + // TODO: extract string values + signatureType = boost::lexical_cast (it->second); + } + localDestination = i2p::client::context.CreateNewLocalDestination (false, signatureType, params); + } + if (localDestination) + { + std::unique_lock l(m_SessionsMutex); + auto ret = m_Sessions.insert (std::pair(id, new SAMSession (localDestination))); + if (!ret.second) + LogPrint ("Session ", id, " already exists"); + return ret.first->second; + } + return nullptr; + } + + void SAMBridge::CloseSession (const std::string& id) + { + std::unique_lock l(m_SessionsMutex); + auto it = m_Sessions.find (id); + if (it != m_Sessions.end ()) + { + auto session = it->second; + session->CloseStreams (); + m_Sessions.erase (it); + delete session; + } + } + + SAMSession * SAMBridge::FindSession (const std::string& id) + { + std::unique_lock l(m_SessionsMutex); + auto it = m_Sessions.find (id); + if (it != m_Sessions.end ()) + return it->second; + return nullptr; + } + + void SAMBridge::ReceiveDatagram () + { + m_DatagramSocket.async_receive_from ( + boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE), + m_SenderEndpoint, + std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2)); + } + + void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (!ecode) + { + m_DatagramReceiveBuffer[bytes_transferred] = 0; + char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n'); + *eol = 0; eol++; + size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer); + LogPrint ("SAM datagram received ", m_DatagramReceiveBuffer," size=", payloadLen); + char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' '); + if (sessionID) + { + sessionID++; + char * destination = strchr (sessionID, ' '); + if (destination) + { + *destination = 0; destination++; + auto session = FindSession (sessionID); + if (session) + { + i2p::data::IdentityEx dest; + dest.FromBase64 (destination); + auto leaseSet = i2p::data::netdb.FindLeaseSet (dest.GetIdentHash ()); + if (leaseSet) + session->localDestination->GetDatagramDestination ()-> + SendDatagramTo ((uint8_t *)eol, payloadLen, *leaseSet); + else + { + LogPrint ("SAM datagram destination not found"); + session->localDestination->RequestDestination (dest.GetIdentHash ()); + } + } + else + LogPrint ("Session ", sessionID, " not found"); + } + else + LogPrint ("Missing destination key"); + } + else + LogPrint ("Missing sessionID"); + ReceiveDatagram (); + } + else + LogPrint ("SAM datagram receive error: ", ecode.message ()); + } +} +} diff --git a/SAM.h b/SAM.h new file mode 100644 index 00000000..348070e6 --- /dev/null +++ b/SAM.h @@ -0,0 +1,181 @@ +#ifndef SAM_H__ +#define SAM_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "Identity.h" +#include "LeaseSet.h" +#include "Streaming.h" +#include "Destination.h" + +namespace i2p +{ +namespace client +{ + const size_t SAM_SOCKET_BUFFER_SIZE = 4096; + const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds + const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds + const char SAM_HANDSHAKE[] = "HELLO VERSION"; + const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; + const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n"; + const char SAM_SESSION_CREATE[] = "SESSION CREATE"; + const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n"; + const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n"; + const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; + const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; + const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; + const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; + const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n"; + const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n"; + const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; + const char SAM_DEST_GENERATE[] = "DEST GENERATE"; + const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n"; + const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n"; + const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP"; + const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n"; + const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM_RECEIVED DESTINATION=%s SIZE=%lu\n"; + const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n"; + const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=INVALID_KEY_NOT_FOUND NAME=%s\n"; + const char SAM_PARAM_MIN[] = "MIN"; + const char SAM_PARAM_MAX[] = "MAX"; + const char SAM_PARAM_STYLE[] = "STYLE"; + const char SAM_PARAM_ID[] = "ID"; + const char SAM_PARAM_SILENT[] = "SILENT"; + const char SAM_PARAM_DESTINATION[] = "DESTINATION"; + const char SAM_PARAM_NAME[] = "NAME"; + const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE"; + const char SAM_VALUE_TRANSIENT[] = "TRANSIENT"; + const char SAM_VALUE_STREAM[] = "STREAM"; + const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; + const char SAM_VALUE_RAW[] = "RAW"; + const char SAM_VALUE_TRUE[] = "true"; + const char SAM_VALUE_FALSE[] = "false"; + + enum SAMSocketType + { + eSAMSocketTypeUnknown, + eSAMSocketTypeSession, + eSAMSocketTypeStream, + eSAMSocketTypeAcceptor, + eSAMSocketTypeTerminated + }; + + class SAMBridge; + struct SAMSession; + class SAMSocket: public std::enable_shared_from_this + { + public: + + SAMSocket (SAMBridge& owner); + ~SAMSocket (); + void CloseStream (); // TODO: implement it better + + boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; + void ReceiveHandshake (); + void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; }; + + private: + + void Terminate (); + void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void SendMessageReply (const char * msg, size_t len, bool close); + void HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close); + void Receive (); + void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + + void I2PReceive (); + void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleI2PAccept (std::shared_ptr stream); + void HandleWriteI2PData (const boost::system::error_code& ecode); + void HandleI2PDatagramReceive (const i2p::data::IdentityEx& ident, const uint8_t * buf, size_t len); + + void ProcessSessionCreate (char * buf, size_t len); + void ProcessStreamConnect (char * buf, size_t len); + void ProcessStreamAccept (char * buf, size_t len); + void ProcessDestGenerate (); + void ProcessNamingLookup (char * buf, size_t len); + void ExtractParams (char * buf, size_t len, std::map& params); + + void Connect (const i2p::data::LeaseSet& remote); + void HandleLeaseSetRequestComplete (bool success, i2p::data::IdentHash ident); + void SendNamingLookupReply (const i2p::data::LeaseSet * leaseSet); + void SendNamingLookupReply (const i2p::data::IdentityEx& identity); + void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode); + void SendSessionCreateReplyOk (); + + private: + + SAMBridge& m_Owner; + boost::asio::ip::tcp::socket m_Socket; + boost::asio::deadline_timer m_Timer; + char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1]; + uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE]; + SAMSocketType m_SocketType; + std::string m_ID; // nickname + bool m_IsSilent; + std::shared_ptr m_Stream; + SAMSession * m_Session; + }; + + struct SAMSession + { + ClientDestination * localDestination; + std::list > sockets; + + SAMSession (ClientDestination * localDestination); + ~SAMSession (); + + void CloseStreams (); + }; + + class SAMBridge + { + public: + + SAMBridge (int port); + ~SAMBridge (); + + void Start (); + void Stop (); + + boost::asio::io_service& GetService () { return m_Service; }; + SAMSession * CreateSession (const std::string& id, const std::string& destination, // empty string means transient + const std::map * params); + void CloseSession (const std::string& id); + SAMSession * FindSession (const std::string& id); + + private: + + void Run (); + + void Accept (); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + + void ReceiveDatagram (); + void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred); + + private: + + bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint; + boost::asio::ip::udp::socket m_DatagramSocket; + std::mutex m_SessionsMutex; + std::map m_Sessions; + uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1]; + }; +} +} + +#endif + diff --git a/SOCKS.cpp b/SOCKS.cpp new file mode 100644 index 00000000..29ef3955 --- /dev/null +++ b/SOCKS.cpp @@ -0,0 +1,449 @@ +#include "SOCKS.h" +#include "Identity.h" +#include "NetDb.h" +#include "Destination.h" +#include "ClientContext.h" +#include "I2PEndian.h" +#include +#include + +namespace i2p +{ +namespace proxy +{ + void SOCKSHandler::AsyncSockRead() + { + LogPrint(eLogDebug,"--- SOCKS async sock read"); + if(m_sock) { + m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size), + std::bind(&SOCKSHandler::HandleSockRecv, this, + std::placeholders::_1, std::placeholders::_2)); + } else { + LogPrint(eLogError,"--- SOCKS no socket for read"); + } + } + + void SOCKSHandler::Done() { + if (m_parent) m_parent->RemoveHandler (shared_from_this ()); + } + + void SOCKSHandler::Terminate() { + if (dead.exchange(true)) return; + if (m_sock) { + LogPrint(eLogDebug,"--- SOCKS close sock"); + m_sock->close(); + delete m_sock; + m_sock = nullptr; + } + if (m_stream) { + LogPrint(eLogDebug,"--- SOCKS close stream"); + m_stream.reset (); + } + Done(); + } + + boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port) + { + assert(error >= SOCKS4_OK); + m_response[0] = '\x00'; //Version + m_response[1] = error; //Response code + htobe16buf(m_response+2,port); //Port + htobe32buf(m_response+4,ip); //IP + return boost::asio::const_buffers_1(m_response,8); + } + + boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, + const SOCKSHandler::address &addr, uint16_t port) + { + size_t size; + assert(error <= SOCKS5_ADDR_UNSUP); + m_response[0] = '\x05'; //Version + m_response[1] = error; //Response code + m_response[2] = '\x00'; //RSV + m_response[3] = type; //Address type + switch (type) { + case ADDR_IPV4: + size = 10; + htobe32buf(m_response+4,addr.ip); + break; + case ADDR_IPV6: + size = 22; + memcpy(m_response+4,addr.ipv6, 16); + break; + case ADDR_DNS: + size = 7+addr.dns.size; + m_response[4] = addr.dns.size; + memcpy(m_response+5,addr.dns.value, addr.dns.size); + break; + } + htobe16buf(m_response+size-2,port); //Port + return boost::asio::const_buffers_1(m_response,size); + } + + bool SOCKSHandler::Socks5ChooseAuth() + { + m_response[0] = '\x05'; //Version + m_response[1] = m_authchosen; //Response code + boost::asio::const_buffers_1 response(m_response,2); + if (m_authchosen == AUTH_UNACCEPTABLE) { + LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed"); + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, this, std::placeholders::_1)); + return false; + } else { + LogPrint(eLogDebug,"--- SOCKS5 choosing authentication method: ", m_authchosen); + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse, this, std::placeholders::_1)); + return true; + } + } + + /* All hope is lost beyond this point */ + void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error) + { + boost::asio::const_buffers_1 response(nullptr,0); + assert(error != SOCKS4_OK && error != SOCKS5_OK); + switch (m_socksv) { + case SOCKS4: + LogPrint(eLogWarning,"--- SOCKS4 failed: ", error); + if (error < SOCKS4_OK) error = SOCKS4_FAIL; //Transparently map SOCKS5 errors + response = GenerateSOCKS4Response(error, m_4aip, m_port); + break; + case SOCKS5: + LogPrint(eLogWarning,"--- SOCKS5 failed: ", error); + response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port); + break; + } + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, this, std::placeholders::_1)); + } + + void SOCKSHandler::SocksRequestSuccess() + { + boost::asio::const_buffers_1 response(nullptr,0); + //TODO: this should depend on things like the command type and callbacks may change + switch (m_socksv) { + case SOCKS4: + LogPrint(eLogInfo,"--- SOCKS4 connection success"); + response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port); + break; + case SOCKS5: + LogPrint(eLogInfo,"--- SOCKS5 connection success"); + auto s = i2p::client::context.GetAddressBook().ToAddress(m_parent->GetLocalDestination()->GetIdentHash()); + address ad; ad.dns.FromString(s); + //HACK only 16 bits passed in port as SOCKS5 doesn't allow for more + response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID()); + break; + } + boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, this, std::placeholders::_1)); + } + + void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) { + switch (nstate) { + case GET_PORT: parseleft = 2; break; + case GET_IPV4: m_addrtype = ADDR_IPV4; m_address.ip = 0; parseleft = 4; break; + case GET4_IDENT: m_4aip = m_address.ip; break; + case GET4A_HOST: + case GET5_HOST: m_addrtype = ADDR_DNS; m_address.dns.size = 0; break; + case GET5_IPV6: m_addrtype = ADDR_IPV6; parseleft = 16; break; + default:; + } + m_parseleft = parseleft; + m_state = nstate; + } + + void SOCKSHandler::ValidateSOCKSRequest() { + if ( m_cmd != CMD_CONNECT ) { + //TODO: we need to support binds and other shit! + LogPrint(eLogError,"--- SOCKS unsupported command: ", m_cmd); + SocksRequestFailed(SOCKS5_CMD_UNSUP); + return; + } + //TODO: we may want to support other address types! + if ( m_addrtype != ADDR_DNS ) { + switch (m_socksv) { + case SOCKS5: + LogPrint(eLogError,"--- SOCKS5 unsupported address type: ", m_addrtype); + break; + case SOCKS4: + LogPrint(eLogError,"--- SOCKS4a rejected because it's actually SOCKS4"); + break; + } + SocksRequestFailed(SOCKS5_ADDR_UNSUP); + return; + } + //TODO: we may want to support other domains + if(m_addrtype == ADDR_DNS && m_address.dns.ToString().find(".i2p") == std::string::npos) { + LogPrint(eLogError,"--- SOCKS invalid hostname: ", m_address.dns.ToString()); + SocksRequestFailed(SOCKS5_ADDR_UNSUP); + return; + } + } + + bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len) + { + assert(len); // This should always be called with a least a byte left to parse + while (len > 0) { + switch (m_state) { + case GET_SOCKSV: + m_socksv = (SOCKSHandler::socksVersions) *sock_buff; + switch (*sock_buff) { + case SOCKS4: + EnterState(GET_COMMAND); //Initialize the parser at the right position + break; + case SOCKS5: + EnterState(GET5_AUTHNUM); //Initialize the parser at the right position + break; + default: + LogPrint(eLogError,"--- SOCKS rejected invalid version: ", ((int)*sock_buff)); + Terminate(); + return false; + } + break; + case GET5_AUTHNUM: + EnterState(GET5_AUTH, *sock_buff); + break; + case GET5_AUTH: + m_parseleft --; + if (*sock_buff == AUTH_NONE) + m_authchosen = AUTH_NONE; + if ( m_parseleft == 0 ) { + if (!Socks5ChooseAuth()) return false; + EnterState(GET5_REQUESTV); + } + break; + case GET_COMMAND: + switch (*sock_buff) { + case CMD_CONNECT: + case CMD_BIND: + break; + case CMD_UDP: + if (m_socksv == SOCKS5) break; + default: + LogPrint(eLogError,"--- SOCKS invalid command: ", ((int)*sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + m_cmd = (SOCKSHandler::cmdTypes)*sock_buff; + switch (m_socksv) { + case SOCKS5: EnterState(GET5_GETRSV); break; + case SOCKS4: EnterState(GET_PORT); break; + } + break; + case GET_PORT: + m_port = (m_port << 8)|((uint16_t)*sock_buff); + m_parseleft--; + if (m_parseleft == 0) { + switch (m_socksv) { + case SOCKS5: EnterState(DONE); break; + case SOCKS4: EnterState(GET_IPV4); break; + } + } + break; + case GET_IPV4: + m_address.ip = (m_address.ip << 8)|((uint32_t)*sock_buff); + m_parseleft--; + if (m_parseleft == 0) { + switch (m_socksv) { + case SOCKS5: EnterState(GET_PORT); break; + case SOCKS4: EnterState(GET4_IDENT); m_4aip = m_address.ip; break; + } + } + break; + case GET4_IDENT: + if (!*sock_buff) { + if( m_4aip == 0 || m_4aip > 255 ) { + EnterState(DONE); + } else { + EnterState(GET4A_HOST); + } + } + break; + case GET4A_HOST: + if (!*sock_buff) { + EnterState(DONE); + break; + } + if (m_address.dns.size >= max_socks_hostname_size) { + LogPrint(eLogError,"--- SOCKS4a destination is too large"); + SocksRequestFailed(SOCKS4_FAIL); + return false; + } + m_address.dns.push_back(*sock_buff); + break; + case GET5_REQUESTV: + if (*sock_buff != SOCKS5) { + LogPrint(eLogError,"--- SOCKS5 rejected unknown request version: ", ((int)*sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + EnterState(GET_COMMAND); + break; + case GET5_GETRSV: + if ( *sock_buff != 0 ) { + LogPrint(eLogError,"--- SOCKS5 unknown reserved field: ", ((int)*sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + EnterState(GET5_GETADDRTYPE); + break; + case GET5_GETADDRTYPE: + switch (*sock_buff) { + case ADDR_IPV4: EnterState(GET_IPV4); break; + case ADDR_IPV6: EnterState(GET5_IPV6); break; + case ADDR_DNS : EnterState(GET5_HOST_SIZE); break; + default: + LogPrint(eLogError,"--- SOCKS5 unknown address type: ", ((int)*sock_buff)); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + break; + case GET5_IPV6: + m_address.ipv6[16-m_parseleft] = *sock_buff; + m_parseleft--; + if (m_parseleft == 0) EnterState(GET_PORT); + break; + case GET5_HOST_SIZE: + EnterState(GET5_HOST, *sock_buff); + break; + case GET5_HOST: + m_address.dns.push_back(*sock_buff); + m_parseleft--; + if (m_parseleft == 0) EnterState(GET_PORT); + break; + default: + LogPrint(eLogError,"--- SOCKS parse state?? ", m_state); + Terminate(); + return false; + } + sock_buff++; + len--; + if (len && m_state == DONE) { + LogPrint(eLogError,"--- SOCKS rejected because we can't handle extra data"); + SocksRequestFailed(SOCKS5_GEN_FAIL); + return false; + } + } + return true; + } + + void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) + { + LogPrint(eLogDebug,"--- SOCKS sock recv: ", len); + if(ecode) { + LogPrint(eLogWarning," --- SOCKS sock recv got error: ", ecode); + Terminate(); + return; + } + + if (HandleData(m_sock_buff, len)) { + if (m_state == DONE) { + LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port); + m_parent->GetLocalDestination ()->CreateStream ( + std::bind (&SOCKSHandler::HandleStreamRequestComplete, + this, std::placeholders::_1), m_address.dns.ToString(), m_port); + } else { + AsyncSockRead(); + } + } + + } + + void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode) + { + if (!ecode) { + Terminate(); + } else { + LogPrint (eLogError,"--- SOCKS Closing socket after sending failure because: ", ecode.message ()); + Terminate(); + } + } + + void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode) + { + if (!ecode) { + if (dead.exchange(true)) return; + LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection"); + auto connection = std::make_shared((i2p::client::I2PTunnel *)m_parent, m_sock, m_stream); + m_parent->AddConnection (connection); + connection->I2PConnect (); + Done(); + } + else + { + LogPrint (eLogError,"--- SOCKS Closing socket after completion reply because: ", ecode.message ()); + Terminate(); + } + } + + void SOCKSHandler::SentSocksResponse(const boost::system::error_code & ecode) + { + if (ecode) { + LogPrint (eLogError,"--- SOCKS Closing socket after sending reply because: ", ecode.message ()); + Terminate(); + } + } + + void SOCKSHandler::HandleStreamRequestComplete (std::shared_ptr stream) + { + if (stream) { + m_stream = stream; + SocksRequestSuccess(); + } else { + LogPrint (eLogError,"--- SOCKS Issue when creating the stream, check the previous warnings for more info."); + SocksRequestFailed(SOCKS5_HOST_UNREACH); + } + } + + void SOCKSServer::Start () + { + m_Acceptor.listen (); + Accept (); + } + + void SOCKSServer::Stop () + { + m_Acceptor.close(); + m_Timer.cancel (); + ClearConnections (); + ClearHandlers(); + } + + void SOCKSServer::Accept () + { + auto newSocket = new boost::asio::ip::tcp::socket (GetService ()); + m_Acceptor.async_accept (*newSocket, std::bind (&SOCKSServer::HandleAccept, this, + std::placeholders::_1, newSocket)); + } + + void SOCKSServer::AddHandler (std::shared_ptr handler) { + std::unique_lock l(m_HandlersMutex); + m_Handlers.insert (handler); + } + + void SOCKSServer::RemoveHandler (std::shared_ptr handler) + { + std::unique_lock l(m_HandlersMutex); + m_Handlers.erase (handler); + } + + void SOCKSServer::ClearHandlers () + { + std::unique_lock l(m_HandlersMutex); + m_Handlers.clear (); + } + + void SOCKSServer::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket) + { + if (!ecode) + { + LogPrint(eLogDebug,"--- SOCKS accepted"); + AddHandler(std::make_shared (this, socket)); + Accept(); + } + else + { + LogPrint (eLogError,"--- SOCKS Closing socket on accept because: ", ecode.message ()); + delete socket; + } + } + +} +} diff --git a/SOCKS.h b/SOCKS.h new file mode 100644 index 00000000..3fd0f52f --- /dev/null +++ b/SOCKS.h @@ -0,0 +1,169 @@ +#ifndef SOCKS_H__ +#define SOCKS_H__ + +#include +#include +#include +#include +#include +#include +#include "Identity.h" +#include "Streaming.h" +#include "I2PTunnel.h" + +namespace i2p +{ +namespace proxy +{ + + const size_t socks_buffer_size = 8192; + const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse + + struct SOCKSDnsAddress { + uint8_t size; + char value[max_socks_hostname_size]; + void FromString (std::string str) { + size = str.length(); + if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size; + memcpy(value,str.c_str(),size); + } + std::string ToString() { return std::string(value, size); } + void push_back (char c) { value[size++] = c; } + }; + + class SOCKSServer; + class SOCKSHandler: public std::enable_shared_from_this { + private: + enum state { + GET_SOCKSV, + GET_COMMAND, + GET_PORT, + GET_IPV4, + GET4_IDENT, + GET4A_HOST, + GET5_AUTHNUM, + GET5_AUTH, + GET5_REQUESTV, + GET5_GETRSV, + GET5_GETADDRTYPE, + GET5_IPV6, + GET5_HOST_SIZE, + GET5_HOST, + DONE + }; + enum authMethods { + AUTH_NONE = 0, //No authentication, skip to next step + AUTH_GSSAPI = 1, //GSSAPI authentication + AUTH_USERPASSWD = 2, //Username and password + AUTH_UNACCEPTABLE = 0xff //No acceptable method found + }; + enum addrTypes { + ADDR_IPV4 = 1, //IPv4 address (4 octets) + ADDR_DNS = 3, // DNS name (up to 255 octets) + ADDR_IPV6 = 4 //IPV6 address (16 octets) + }; + enum errTypes { + SOCKS5_OK = 0, // No error for SOCKS5 + SOCKS5_GEN_FAIL = 1, // General server failure + SOCKS5_RULE_DENIED = 2, // Connection disallowed by ruleset + SOCKS5_NET_UNREACH = 3, // Network unreachable + SOCKS5_HOST_UNREACH = 4, // Host unreachable + SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer + SOCKS5_TTL_EXPIRED = 6, // TTL Expired + SOCKS5_CMD_UNSUP = 7, // Command unsuported + SOCKS5_ADDR_UNSUP = 8, // Address type unsuported + SOCKS4_OK = 90, // No error for SOCKS4 + SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed + SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server + SOCKS4_IDENTD_DIFFER = 93 // The ID reported by the application and by identd differ + }; + enum cmdTypes { + CMD_CONNECT = 1, // TCP Connect + CMD_BIND = 2, // TCP Bind + CMD_UDP = 3 // UDP associate + }; + enum socksVersions { + SOCKS4 = 4, // SOCKS4 + SOCKS5 = 5 // SOCKS5 + }; + union address { + uint32_t ip; + SOCKSDnsAddress dns; + uint8_t ipv6[16]; + }; + + void EnterState(state nstate, uint8_t parseleft = 1); + bool HandleData(uint8_t *sock_buff, std::size_t len); + void ValidateSOCKSRequest(); + void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); + void Done(); + void Terminate(); + void AsyncSockRead(); + boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method); + boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port); + boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port); + bool Socks5ChooseAuth(); + void SocksRequestFailed(errTypes error); + void SocksRequestSuccess(); + void SentSocksFailed(const boost::system::error_code & ecode); + void SentSocksDone(const boost::system::error_code & ecode); + void SentSocksResponse(const boost::system::error_code & ecode); + void HandleStreamRequestComplete (std::shared_ptr stream); + + uint8_t m_sock_buff[socks_buffer_size]; + SOCKSServer * m_parent; + boost::asio::ip::tcp::socket * m_sock; + std::shared_ptr m_stream; + uint8_t m_response[7+max_socks_hostname_size]; + address m_address; //Address + uint32_t m_4aip; //Used in 4a requests + uint16_t m_port; + uint8_t m_command; + uint8_t m_parseleft; //Octets left to parse + authMethods m_authchosen; //Authentication chosen + addrTypes m_addrtype; //Address type chosen + socksVersions m_socksv; //Socks version + cmdTypes m_cmd; // Command requested + state m_state; + std::atomic dead; //To avoid cleaning up multiple times + + public: + SOCKSHandler(SOCKSServer * parent, boost::asio::ip::tcp::socket * sock) : + m_parent(parent), m_sock(sock), m_stream(nullptr), + m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4), dead(false) + { m_address.ip = 0; AsyncSockRead(); EnterState(GET_SOCKSV); } + ~SOCKSHandler() { Terminate(); } + }; + + class SOCKSServer: public i2p::client::I2PTunnel + { + private: + std::set > m_Handlers; + boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::deadline_timer m_Timer; + std::mutex m_HandlersMutex; + + private: + + void Accept(); + void HandleAccept(const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket); + + public: + SOCKSServer(int port) : I2PTunnel(nullptr), + m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)), + m_Timer (GetService ()) {}; + ~SOCKSServer() { Stop(); } + + void Start (); + void Stop (); + void AddHandler (std::shared_ptr handler); + void RemoveHandler (std::shared_ptr handler); + void ClearHandlers (); + }; + + typedef SOCKSServer SOCKSProxy; +} +} + + +#endif diff --git a/SSU.cpp b/SSU.cpp new file mode 100644 index 00000000..6f9a80a6 --- /dev/null +++ b/SSU.cpp @@ -0,0 +1,386 @@ +#include +#include +#include "Log.h" +#include "Timestamp.h" +#include "RouterContext.h" +#include "SSU.h" + +namespace i2p +{ +namespace transport +{ + SSUServer::SSUServer (int port): m_Thread (nullptr), m_ThreadV6 (nullptr), m_Work (m_Service), + m_WorkV6 (m_ServiceV6),m_Endpoint (boost::asio::ip::udp::v4 (), port), + m_EndpointV6 (boost::asio::ip::udp::v6 (), port), m_Socket (m_Service, m_Endpoint), + m_SocketV6 (m_ServiceV6), m_IntroducersUpdateTimer (m_Service) + { + m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535)); + m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535)); + if (context.SupportsV6 ()) + { + m_SocketV6.open (boost::asio::ip::udp::v6()); + m_SocketV6.set_option (boost::asio::ip::v6_only (true)); + m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535)); + m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535)); + m_SocketV6.bind (m_EndpointV6); + } + } + + SSUServer::~SSUServer () + { + } + + void SSUServer::Start () + { + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&SSUServer::Run, this)); + m_Service.post (std::bind (&SSUServer::Receive, this)); + if (context.SupportsV6 ()) + { + m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this)); + m_ServiceV6.post (std::bind (&SSUServer::ReceiveV6, this)); + } + if (i2p::context.IsUnreachable ()) + ScheduleIntroducersUpdateTimer (); + } + + void SSUServer::Stop () + { + DeleteAllSessions (); + m_IsRunning = false; + m_Service.stop (); + m_Socket.close (); + m_ServiceV6.stop (); + m_SocketV6.close (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + if (m_ThreadV6) + { + m_ThreadV6->join (); + delete m_ThreadV6; + m_ThreadV6 = nullptr; + } + } + + void SSUServer::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "SSU server: ", ex.what ()); + } + } + } + + void SSUServer::RunV6 () + { + while (m_IsRunning) + { + try + { + m_ServiceV6.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "SSU V6 server: ", ex.what ()); + } + } + } + + void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay) + { + m_Relays[tag] = relay; + } + + std::shared_ptr SSUServer::FindRelaySession (uint32_t tag) + { + auto it = m_Relays.find (tag); + if (it != m_Relays.end ()) + return FindSession (it->second); + return nullptr; + } + + void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to) + { + if (to.protocol () == boost::asio::ip::udp::v4()) + m_Socket.send_to (boost::asio::buffer (buf, len), to); + else + m_SocketV6.send_to (boost::asio::buffer (buf, len), to); + } + + void SSUServer::Receive () + { + m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, SSU_MTU_V4), m_SenderEndpoint, + boost::bind (&SSUServer::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } + + void SSUServer::ReceiveV6 () + { + m_SocketV6.async_receive_from (boost::asio::buffer (m_ReceiveBufferV6, SSU_MTU_V6), m_SenderEndpointV6, + boost::bind (&SSUServer::HandleReceivedFromV6, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } + + void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (!ecode) + { + HandleReceivedBuffer (m_SenderEndpoint, m_ReceiveBuffer, bytes_transferred); + Receive (); + } + else + LogPrint ("SSU receive error: ", ecode.message ()); + } + + void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (!ecode) + { + HandleReceivedBuffer (m_SenderEndpointV6, m_ReceiveBufferV6, bytes_transferred); + ReceiveV6 (); + } + else + LogPrint ("SSU V6 receive error: ", ecode.message ()); + } + + void SSUServer::HandleReceivedBuffer (boost::asio::ip::udp::endpoint& from, uint8_t * buf, std::size_t bytes_transferred) + { + std::shared_ptr session; + auto it = m_Sessions.find (from); + if (it != m_Sessions.end ()) + session = it->second; + if (!session) + { + session = std::make_shared (*this, from); + session->WaitForConnect (); + m_Sessions[from] = session; + LogPrint ("New SSU session from ", from.address ().to_string (), ":", from.port (), " created"); + } + session->ProcessNextMessage (buf, bytes_transferred, from); + } + + std::shared_ptr SSUServer::FindSession (std::shared_ptr router) const + { + if (!router) return nullptr; + auto address = router->GetSSUAddress (true); // v4 only + if (!address) return nullptr; + auto session = FindSession (boost::asio::ip::udp::endpoint (address->host, address->port)); + if (session || !context.SupportsV6 ()) + return session; + // try v6 + address = router->GetSSUV6Address (); + if (!address) return nullptr; + return FindSession (boost::asio::ip::udp::endpoint (address->host, address->port)); + } + + std::shared_ptr SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const + { + auto it = m_Sessions.find (e); + if (it != m_Sessions.end ()) + return it->second; + else + return nullptr; + } + + std::shared_ptr SSUServer::GetSession (std::shared_ptr router, bool peerTest) + { + std::shared_ptr session; + if (router) + { + auto address = router->GetSSUAddress (!context.SupportsV6 ()); + if (address) + { + boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port); + auto it = m_Sessions.find (remoteEndpoint); + if (it != m_Sessions.end ()) + session = it->second; + else + { + // otherwise create new session + session = std::make_shared (*this, remoteEndpoint, router, peerTest); + m_Sessions[remoteEndpoint] = session; + + if (!router->UsesIntroducer ()) + { + // connect directly + LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ", + remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ()); + session->Connect (); + } + else + { + // connect through introducer + int numIntroducers = address->introducers.size (); + if (numIntroducers > 0) + { + std::shared_ptr introducerSession; + const i2p::data::RouterInfo::Introducer * introducer = nullptr; + // we might have a session to introducer already + for (int i = 0; i < numIntroducers; i++) + { + introducer = &(address->introducers[i]); + it = m_Sessions.find (boost::asio::ip::udp::endpoint (introducer->iHost, introducer->iPort)); + if (it != m_Sessions.end ()) + { + introducerSession = it->second; + break; + } + } + + if (introducerSession) // session found + LogPrint ("Session to introducer already exists"); + else // create new + { + LogPrint ("Creating new session to introducer"); + introducer = &(address->introducers[0]); // TODO: + boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort); + introducerSession = std::make_shared (*this, introducerEndpoint, router); + m_Sessions[introducerEndpoint] = introducerSession; + } + // introduce + LogPrint ("Introduce new SSU session to [", router->GetIdentHashAbbreviation (), + "] through introducer ", introducer->iHost, ":", introducer->iPort); + session->WaitForIntroduction (); + if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable + Send (m_ReceiveBuffer, 0, remoteEndpoint); // send HolePunch + introducerSession->Introduce (introducer->iTag, introducer->iKey); + } + else + { + LogPrint (eLogWarning, "Can't connect to unreachable router. No introducers presented"); + m_Sessions.erase (remoteEndpoint); + session.reset (); + } + } + } + } + else + LogPrint (eLogWarning, "Router ", router->GetIdentHashAbbreviation (), " doesn't have SSU address"); + } + return session; + } + + void SSUServer::DeleteSession (std::shared_ptr session) + { + if (session) + { + session->Close (); + m_Sessions.erase (session->GetRemoteEndpoint ()); + } + } + + void SSUServer::DeleteAllSessions () + { + for (auto it: m_Sessions) + it.second->Close (); + m_Sessions.clear (); + } + + template + std::shared_ptr SSUServer::GetRandomSession (Filter filter) + { + std::vector > filteredSessions; + for (auto s :m_Sessions) + if (filter (s.second)) filteredSessions.push_back (s.second); + if (filteredSessions.size () > 0) + { + auto ind = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, filteredSessions.size ()-1); + return filteredSessions[ind]; + } + return nullptr; + } + + std::shared_ptr SSUServer::GetRandomEstablishedSession (std::shared_ptr excluded) + { + return GetRandomSession ( + [excluded](std::shared_ptr session)->bool + { + return session->GetState () == eSessionStateEstablished && + session != excluded; + } + ); + } + + std::set SSUServer::FindIntroducers (int maxNumIntroducers) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + std::set ret; + for (int i = 0; i < maxNumIntroducers; i++) + { + auto session = GetRandomSession ( + [&ret, ts](std::shared_ptr session)->bool + { + return session->GetRelayTag () && !ret.count (session.get ()) && + session->GetState () == eSessionStateEstablished && + ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION; + } + ); + if (session) + { + ret.insert (session.get ()); + break; + } + } + return ret; + } + + void SSUServer::ScheduleIntroducersUpdateTimer () + { + m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL)); + m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer, + this, std::placeholders::_1)); + } + + void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode) + { + if (!ecode) + { + // timeout expired + std::list newList; + size_t numIntroducers = 0; + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it :m_Introducers) + { + auto session = FindSession (it); + if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION) + { + session->SendKeepAlive (); + newList.push_back (it); + numIntroducers++; + } + else + i2p::context.RemoveIntroducer (it); + } + + if (numIntroducers < SSU_MAX_NUM_INTRODUCERS) + { + // create new + auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS); + if (introducers.size () > 0) + { + for (auto it1: introducers) + { + auto router = it1->GetRemoteRouter (); + if (router && i2p::context.AddIntroducer (*router, it1->GetRelayTag ())) + { + newList.push_back (it1->GetRemoteEndpoint ()); + if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break; + } + } + } + } + m_Introducers = newList; + ScheduleIntroducersUpdateTimer (); + } + } +} +} + diff --git a/SSU.h b/SSU.h new file mode 100644 index 00000000..1e3624a4 --- /dev/null +++ b/SSU.h @@ -0,0 +1,88 @@ +#ifndef SSU_H__ +#define SSU_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "aes.h" +#include "I2PEndian.h" +#include "Identity.h" +#include "RouterInfo.h" +#include "I2NPProtocol.h" +#include "SSUSession.h" + +namespace i2p +{ +namespace transport +{ + const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds + const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour + const size_t SSU_MAX_NUM_INTRODUCERS = 3; + + class SSUServer + { + public: + + SSUServer (int port); + ~SSUServer (); + void Start (); + void Stop (); + std::shared_ptr GetSession (std::shared_ptr router, bool peerTest = false); + std::shared_ptr FindSession (std::shared_ptr router) const; + std::shared_ptr FindSession (const boost::asio::ip::udp::endpoint& e) const; + std::shared_ptr GetRandomEstablishedSession (std::shared_ptr excluded); + void DeleteSession (std::shared_ptr session); + void DeleteAllSessions (); + + boost::asio::io_service& GetService () { return m_Socket.get_io_service(); }; + const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; }; + void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to); + void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay); + std::shared_ptr FindRelaySession (uint32_t tag); + + private: + + void Run (); + void RunV6 (); + void Receive (); + void ReceiveV6 (); + void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleReceivedBuffer (boost::asio::ip::udp::endpoint& from, uint8_t * buf, std::size_t bytes_transferred); + + template + std::shared_ptr GetRandomSession (Filter filter); + + std::set FindIntroducers (int maxNumIntroducers); + void ScheduleIntroducersUpdateTimer (); + void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode); + + private: + + bool m_IsRunning; + std::thread * m_Thread, * m_ThreadV6; + boost::asio::io_service m_Service, m_ServiceV6; + boost::asio::io_service::work m_Work, m_WorkV6; + boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6; + boost::asio::ip::udp::socket m_Socket, m_SocketV6; + boost::asio::ip::udp::endpoint m_SenderEndpoint, m_SenderEndpointV6; + boost::asio::deadline_timer m_IntroducersUpdateTimer; + std::list m_Introducers; // introducers we are connected to + i2p::crypto::AESAlignedBuffer<2*SSU_MTU_V4> m_ReceiveBuffer; + i2p::crypto::AESAlignedBuffer<2*SSU_MTU_V6> m_ReceiveBufferV6; + std::map > m_Sessions; + std::map m_Relays; // we are introducer + + public: + // for HTTP only + const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; + }; +} +} + +#endif + diff --git a/SSUData.cpp b/SSUData.cpp new file mode 100644 index 00000000..a4c47559 --- /dev/null +++ b/SSUData.cpp @@ -0,0 +1,430 @@ +#include +#include +#include "Log.h" +#include "Timestamp.h" +#include "NetDb.h" +#include "SSU.h" +#include "SSUData.h" + +namespace i2p +{ +namespace transport +{ + SSUData::SSUData (SSUSession& session): + m_Session (session), m_ResendTimer (session.m_Server.GetService ()) + { + m_MaxPacketSize = session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE; + m_PacketSize = m_MaxPacketSize; + auto remoteRouter = session.GetRemoteRouter (); + if (remoteRouter) + AdjustPacketSize (*remoteRouter); + } + + SSUData::~SSUData () + { + for (auto it: m_IncomleteMessages) + if (it.second) + { + DeleteI2NPMessage (it.second->msg); + delete it.second; + } + for (auto it: m_SentMessages) + delete it.second; + } + + void SSUData::AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter) + { + auto ssuAddress = remoteRouter.GetSSUAddress (); + if (ssuAddress && ssuAddress->mtu) + { + if (m_Session.IsV6 ()) + m_PacketSize = ssuAddress->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; + else + m_PacketSize = ssuAddress->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; + if (m_PacketSize > 0) + { + // make sure packet size multiple of 16 + m_PacketSize >>= 4; + m_PacketSize <<= 4; + if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize; + LogPrint ("MTU=", ssuAddress->mtu, " packet size=", m_PacketSize); + } + else + { + LogPrint (eLogWarning, "Unexpected MTU ", ssuAddress->mtu); + m_PacketSize = m_MaxPacketSize; + } + } + } + + void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent) + { + auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent); + if (routerInfo) + AdjustPacketSize (*routerInfo); + } + + void SSUData::ProcessSentMessageAck (uint32_t msgID) + { + auto it = m_SentMessages.find (msgID); + if (it != m_SentMessages.end ()) + { + delete it->second; + m_SentMessages.erase (it); + if (m_SentMessages.empty ()) + m_ResendTimer.cancel (); + } + } + + void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag) + { + if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED) + { + // explicit ACKs + uint8_t numAcks =*buf; + buf++; + for (int i = 0; i < numAcks; i++) + ProcessSentMessageAck (bufbe32toh (buf+i*4)); + buf += numAcks*4; + } + if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED) + { + // explicit ACK bitfields + uint8_t numBitfields =*buf; + buf++; + for (int i = 0; i < numBitfields; i++) + { + uint32_t msgID = bufbe32toh (buf); + buf += 4; // msgID + auto it = m_SentMessages.find (msgID); + // process individual Ack bitfields + bool isNonLast = false; + int fragment = 0; + do + { + uint8_t bitfield = *buf; + isNonLast = bitfield & 0x80; + bitfield &= 0x7F; // clear MSB + if (bitfield && it != m_SentMessages.end ()) + { + int numSentFragments = it->second->fragments.size (); + // process bits + uint8_t mask = 0x01; + for (int j = 0; j < 7; j++) + { + if (bitfield & mask) + { + if (fragment < numSentFragments) + { + delete it->second->fragments[fragment]; + it->second->fragments[fragment] = nullptr; + } + } + fragment++; + mask <<= 1; + } + } + buf++; + } + while (isNonLast); + } + } + } + + void SSUData::ProcessFragments (uint8_t * buf) + { + uint8_t numFragments = *buf; // number of fragments + buf++; + for (int i = 0; i < numFragments; i++) + { + uint32_t msgID = bufbe32toh (buf); // message ID + buf += 4; + uint8_t frag[4]; + frag[0] = 0; + memcpy (frag + 1, buf, 3); + buf += 3; + uint32_t fragmentInfo = bufbe32toh (frag); // fragment info + uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13 + bool isLast = fragmentInfo & 0x010000; // bit 16 + uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17 + LogPrint (eLogDebug, "SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last"); + if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE) + { + LogPrint (eLogError, "Fragment size ", fragmentSize, "exceeds max SSU packet size"); + return; + } + + // find message with msgID + I2NPMessage * msg = nullptr; + IncompleteMessage * incompleteMessage = nullptr; + auto it = m_IncomleteMessages.find (msgID); + if (it != m_IncomleteMessages.end ()) + { + // message exists + incompleteMessage = it->second; + msg = incompleteMessage->msg; + } + else + { + // create new message + msg = NewI2NPMessage (); + msg->len -= I2NP_SHORT_HEADER_SIZE; + incompleteMessage = new IncompleteMessage (msg); + m_IncomleteMessages[msgID] = incompleteMessage; + } + + // handle current fragment + if (fragmentNum == incompleteMessage->nextFragmentNum) + { + // expected fragment + memcpy (msg->buf + msg->len, buf, fragmentSize); + msg->len += fragmentSize; + incompleteMessage->nextFragmentNum++; + if (!isLast && !incompleteMessage->savedFragments.empty ()) + { + // try saved fragments + for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();) + { + auto savedFragment = *it1; + if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum) + { + memcpy (msg->buf + msg->len, savedFragment->buf, savedFragment->len); + msg->len += savedFragment->len; + isLast = savedFragment->isLast; + incompleteMessage->nextFragmentNum++; + incompleteMessage->savedFragments.erase (it1++); + delete savedFragment; + } + else + break; + } + if (isLast) + LogPrint (eLogDebug, "Message ", msgID, " complete"); + } + } + else + { + if (fragmentNum < incompleteMessage->nextFragmentNum) + // duplicate fragment + LogPrint (eLogWarning, "Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ". Ignored"); + else + { + // missing fragment + LogPrint (eLogWarning, "Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID); + auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast); + if (!incompleteMessage->savedFragments.insert (savedFragment).second) + { + LogPrint (eLogWarning, "Fragment ", (int)fragmentNum, " of message ", msgID, " already saved"); + delete savedFragment; + } + } + isLast = false; + } + + if (isLast) + { + // delete incomplete message + delete incompleteMessage; + m_IncomleteMessages.erase (msgID); + // process message + SendMsgAck (msgID); + msg->FromSSU (msgID); + if (m_Session.GetState () == eSessionStateEstablished) + { + if (!m_ReceivedMessages.count (msgID)) + { + if (m_ReceivedMessages.size () > 100) m_ReceivedMessages.clear (); + m_ReceivedMessages.insert (msgID); + i2p::HandleI2NPMessage (msg); + } + else + { + LogPrint (eLogWarning, "SSU message ", msgID, " already received"); + i2p::DeleteI2NPMessage (msg); + } + } + else + { + // we expect DeliveryStatus + if (msg->GetTypeID () == eI2NPDeliveryStatus) + { + LogPrint ("SSU session established"); + m_Session.Established (); + } + else + LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetTypeID ()); + DeleteI2NPMessage (msg); + } + } + else + SendFragmentAck (msgID, fragmentNum); + buf += fragmentSize; + } + } + + void SSUData::ProcessMessage (uint8_t * buf, size_t len) + { + //uint8_t * start = buf; + uint8_t flag = *buf; + buf++; + LogPrint (eLogDebug, "Process SSU data flags=", (int)flag); + // process acks if presented + if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED)) + ProcessAcks (buf, flag); + // extended data if presented + if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED) + { + uint8_t extendedDataSize = *buf; + buf++; // size + LogPrint (eLogDebug, "SSU extended data of ", extendedDataSize, " bytes presented"); + buf += extendedDataSize; + } + // process data + ProcessFragments (buf); + } + + void SSUData::Send (i2p::I2NPMessage * msg) + { + uint32_t msgID = msg->ToSSU (); + if (m_SentMessages.count (msgID) > 0) + { + LogPrint (eLogWarning, "SSU message ", msgID, " already sent"); + DeleteI2NPMessage (msg); + return; + } + if (m_SentMessages.empty ()) // schedule resend at first message only + ScheduleResend (); + SentMessage * sentMessage = new SentMessage; + m_SentMessages[msgID] = sentMessage; + sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL; + sentMessage->numResends = 0; + auto& fragments = sentMessage->fragments; + msgID = htobe32 (msgID); + size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3) + size_t len = msg->GetLength (); + uint8_t * msgBuf = msg->GetSSUHeader (); + + uint32_t fragmentNum = 0; + while (len > 0) + { + Fragment * fragment = new Fragment; + fragment->fragmentNum = fragmentNum; + uint8_t * buf = fragment->buf; + fragments.push_back (fragment); + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = DATA_FLAG_WANT_REPLY; // for compatibility + payload++; + *payload = 1; // always 1 message fragment per message + payload++; + *(uint32_t *)payload = msgID; + payload += 4; + bool isLast = (len <= payloadSize); + size_t size = isLast ? len : payloadSize; + uint32_t fragmentInfo = (fragmentNum << 17); + if (isLast) + fragmentInfo |= 0x010000; + + fragmentInfo |= size; + fragmentInfo = htobe32 (fragmentInfo); + memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3); + payload += 3; + memcpy (payload, msgBuf, size); + + size += payload - buf; + if (size & 0x0F) // make sure 16 bytes boundary + size = ((size >> 4) + 1) << 4; // (/16 + 1)*16 + fragment->len = size; + + // encrypt message with session key + m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size); + m_Session.Send (buf, size); + + if (!isLast) + { + len -= payloadSize; + msgBuf += payloadSize; + } + else + len = 0; + fragmentNum++; + } + DeleteI2NPMessage (msg); + } + + void SSUData::SendMsgAck (uint32_t msgID) + { + uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16 + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag + payload++; + *payload = 1; // number of ACKs + payload++; + *(uint32_t *)(payload) = htobe32 (msgID); // msgID + payload += 4; + *payload = 0; // number of fragments + + // encrypt message with session key + m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); + m_Session.Send (buf, 48); + } + + void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum) + { + if (fragmentNum > 64) + { + LogPrint (eLogWarning, "Fragment number ", fragmentNum, " exceeds 64"); + return; + } + uint8_t buf[64 + 18]; + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag + payload++; + *payload = 1; // number of ACK bitfields + payload++; + // one ack + *(uint32_t *)(payload) = htobe32 (msgID); // msgID + payload += 4; + div_t d = div (fragmentNum, 7); + memset (payload, 0x80, d.quot); // 0x80 means non-last + payload += d.quot; + *payload = 0x01 << d.rem; // set corresponding bit + payload++; + *payload = 0; // number of fragments + + size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1) + // encrypt message with session key + m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len); + m_Session.Send (buf, len); + } + + void SSUData::ScheduleResend() + { + m_ResendTimer.cancel (); + m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL)); + auto s = m_Session.shared_from_this(); + m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode) + { s->m_Data.HandleResendTimer (ecode); }); + } + + void SSUData::HandleResendTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it : m_SentMessages) + { + if (ts >= it.second->nextResendTime && it.second->numResends < MAX_NUM_RESENDS) + { + for (auto f: it.second->fragments) + if (f) m_Session.Send (f->buf, f->len); // resend + + it.second->numResends++; + it.second->nextResendTime += it.second->numResends*RESEND_INTERVAL; + } + } + ScheduleResend (); + } + } +} +} + diff --git a/SSUData.h b/SSUData.h new file mode 100644 index 00000000..e362acd3 --- /dev/null +++ b/SSUData.h @@ -0,0 +1,114 @@ +#ifndef SSU_DATA_H__ +#define SSU_DATA_H__ + +#include +#include +#include +#include +#include +#include +#include "I2NPProtocol.h" +#include "Identity.h" +#include "RouterInfo.h" + +namespace i2p +{ +namespace transport +{ + + const size_t SSU_MTU_V4 = 1484; + const size_t SSU_MTU_V6 = 1472; + const size_t IPV4_HEADER_SIZE = 20; + const size_t IPV6_HEADER_SIZE = 40; + const size_t UDP_HEADER_SIZE = 8; + const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456 + const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1424 + const int RESEND_INTERVAL = 3; // in seconds + const int MAX_NUM_RESENDS = 5; + // data flags + const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02; + const uint8_t DATA_FLAG_WANT_REPLY = 0x04; + const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08; + const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10; + const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40; + const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80; + + struct Fragment + { + int fragmentNum; + size_t len; + bool isLast; + uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest + + Fragment () = default; + Fragment (int n, const uint8_t * b, int l, bool last): + fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); }; + }; + + struct FragmentCmp + { + bool operator() (const Fragment * f1, const Fragment * f2) const + { + return f1->fragmentNum < f2->fragmentNum; + }; + }; + + struct IncompleteMessage + { + I2NPMessage * msg; + int nextFragmentNum; + std::set savedFragments; + + IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (0) {}; + ~IncompleteMessage () { for (auto it: savedFragments) { delete it; }; }; + }; + + struct SentMessage + { + std::vector fragments; + uint32_t nextResendTime; // in seconds + int numResends; + + ~SentMessage () { for (auto it: fragments) { delete it; }; }; + }; + + class SSUSession; + class SSUData + { + public: + + SSUData (SSUSession& session); + ~SSUData (); + + void ProcessMessage (uint8_t * buf, size_t len); + void Send (i2p::I2NPMessage * msg); + + void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent); + + private: + + void SendMsgAck (uint32_t msgID); + void SendFragmentAck (uint32_t msgID, int fragmentNum); + void ProcessAcks (uint8_t *& buf, uint8_t flag); + void ProcessFragments (uint8_t * buf); + void ProcessSentMessageAck (uint32_t msgID); + + void ScheduleResend (); + void HandleResendTimer (const boost::system::error_code& ecode); + + void AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter); + + private: + + SSUSession& m_Session; + std::map m_IncomleteMessages; + std::map m_SentMessages; + std::set m_ReceivedMessages; + boost::asio::deadline_timer m_ResendTimer; + int m_MaxPacketSize, m_PacketSize; + }; +} +} + +#endif + diff --git a/SSUSession.cpp b/SSUSession.cpp new file mode 100644 index 00000000..41a24470 --- /dev/null +++ b/SSUSession.cpp @@ -0,0 +1,1026 @@ +#include +#include +#include +#include "CryptoConst.h" +#include "Log.h" +#include "Timestamp.h" +#include "RouterContext.h" +#include "Transports.h" +#include "SSU.h" +#include "SSUSession.h" + +namespace i2p +{ +namespace transport +{ + SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, + std::shared_ptr router, bool peerTest ): TransportSession (router), + m_Server (server), m_RemoteEndpoint (remoteEndpoint), + m_Timer (m_Server.GetService ()), m_PeerTest (peerTest), + m_State (eSessionStateUnknown), m_IsSessionKey (false), m_RelayTag (0), + m_Data (*this), m_NumSentBytes (0), m_NumReceivedBytes (0) + { + m_CreationTime = i2p::util::GetSecondsSinceEpoch (); + } + + SSUSession::~SSUSession () + { + } + + void SSUSession::CreateAESandMacKey (const uint8_t * pubKey) + { + CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); + uint8_t sharedKey[256]; + if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey)) + { + LogPrint (eLogError, "Couldn't create shared key"); + return; + }; + + uint8_t * sessionKey = m_SessionKey, * macKey = m_MacKey; + if (sharedKey[0] & 0x80) + { + sessionKey[0] = 0; + memcpy (sessionKey + 1, sharedKey, 31); + memcpy (macKey, sharedKey + 31, 32); + } + else if (sharedKey[0]) + { + memcpy (sessionKey, sharedKey, 32); + memcpy (macKey, sharedKey + 32, 32); + } + else + { + // find first non-zero byte + uint8_t * nonZero = sharedKey + 1; + while (!*nonZero) + { + nonZero++; + if (nonZero - sharedKey > 32) + { + LogPrint ("First 32 bytes of shared key is all zeros. Ignored"); + return; + } + } + + memcpy (sessionKey, nonZero, 32); + CryptoPP::SHA256().CalculateDigest(macKey, nonZero, 64 - (nonZero - sharedKey)); + } + m_IsSessionKey = true; + m_SessionKeyEncryption.SetKey (m_SessionKey); + m_SessionKeyDecryption.SetKey (m_SessionKey); + } + + void SSUSession::ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) + { + m_NumReceivedBytes += len; + if (m_State == eSessionStateIntroduced) + { + // HolePunch received + LogPrint ("SSU HolePunch of ", len, " bytes received"); + m_State = eSessionStateUnknown; + Connect (); + } + else + { + if (!len) return; // ignore zero-length packets + if (m_State == eSessionStateEstablished) + ScheduleTermination (); + + if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first + DecryptSessionKey (buf, len); + else + { + // try intro key depending on side + auto introKey = GetIntroKey (); + if (introKey && Validate (buf, len, introKey)) + Decrypt (buf, len, introKey); + else + { + // try own intro key + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + if (!address) + { + LogPrint (eLogError, "SSU is not supported"); + return; + } + if (Validate (buf, len, address->key)) + Decrypt (buf, len, address->key); + else + { + LogPrint (eLogError, "MAC verification failed ", len, " bytes from ", senderEndpoint); + m_Server.DeleteSession (shared_from_this ()); + return; + } + } + } + // successfully decrypted + ProcessMessage (buf, len, senderEndpoint); + } + } + + void SSUSession::ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) + { + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + SSUHeader * header = (SSUHeader *)buf; + switch (header->GetPayloadType ()) + { + case PAYLOAD_TYPE_DATA: + LogPrint (eLogDebug, "SSU data received"); + ProcessData (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); + break; + case PAYLOAD_TYPE_SESSION_REQUEST: + ProcessSessionRequest (buf, len, senderEndpoint); + break; + case PAYLOAD_TYPE_SESSION_CREATED: + ProcessSessionCreated (buf, len); + break; + case PAYLOAD_TYPE_SESSION_CONFIRMED: + ProcessSessionConfirmed (buf, len); + break; + case PAYLOAD_TYPE_PEER_TEST: + LogPrint (eLogDebug, "SSU peer test received"); + ProcessPeerTest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint); + break; + case PAYLOAD_TYPE_SESSION_DESTROYED: + { + LogPrint (eLogDebug, "SSU session destroy received"); + m_Server.DeleteSession (shared_from_this ()); + break; + } + case PAYLOAD_TYPE_RELAY_RESPONSE: + ProcessRelayResponse (buf, len); + if (m_State != eSessionStateEstablished) + m_Server.DeleteSession (shared_from_this ()); + break; + case PAYLOAD_TYPE_RELAY_REQUEST: + LogPrint (eLogDebug, "SSU relay request received"); + ProcessRelayRequest (buf + sizeof (SSUHeader), len - sizeof (SSUHeader), senderEndpoint); + break; + case PAYLOAD_TYPE_RELAY_INTRO: + LogPrint (eLogDebug, "SSU relay intro received"); + ProcessRelayIntro (buf + sizeof (SSUHeader), len - sizeof (SSUHeader)); + break; + default: + LogPrint (eLogWarning, "Unexpected SSU payload type ", (int)header->GetPayloadType ()); + } + } + + void SSUSession::ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) + { + LogPrint (eLogDebug, "Session request received"); + m_RemoteEndpoint = senderEndpoint; + if (!m_DHKeysPair) + m_DHKeysPair = transports.GetNextDHKeysPair (); + CreateAESandMacKey (buf + sizeof (SSUHeader)); + SendSessionCreated (buf + sizeof (SSUHeader)); + } + + void SSUSession::ProcessSessionCreated (uint8_t * buf, size_t len) + { + if (!m_RemoteRouter || !m_DHKeysPair) + { + LogPrint (eLogWarning, "Unsolicited session created message"); + return; + } + + LogPrint (eLogDebug, "Session created received"); + m_Timer.cancel (); // connect timer + SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time + uint8_t * payload = buf + sizeof (SSUHeader); + uint8_t * y = payload; + CreateAESandMacKey (y); + s.Insert (m_DHKeysPair->publicKey, 256); // x + s.Insert (y, 256); // y + payload += 256; + uint8_t addressSize = *payload; + payload += 1; // size + uint8_t * ourAddress = payload; + boost::asio::ip::address ourIP; + if (addressSize == 4) // v4 + { + boost::asio::ip::address_v4::bytes_type bytes; + memcpy (bytes.data (), ourAddress, 4); + ourIP = boost::asio::ip::address_v4 (bytes); + } + else // v6 + { + boost::asio::ip::address_v6::bytes_type bytes; + memcpy (bytes.data (), ourAddress, 16); + ourIP = boost::asio::ip::address_v6 (bytes); + } + s.Insert (ourAddress, addressSize); // our IP + payload += addressSize; // address + uint16_t ourPort = bufbe16toh (payload); + s.Insert (payload, 2); // our port + payload += 2; // port + LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); + i2p::context.UpdateAddress (ourIP); + if (m_RemoteEndpoint.address ().is_v4 ()) + s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP v4 + else + s.Insert (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP v6 + s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port + s.Insert (payload, 8); // relayTag and signed on time + m_RelayTag = bufbe32toh (payload); + payload += 4; // relayTag + payload += 4; // signed on time + // decrypt signature + size_t signatureLen = m_RemoteIdentity.GetSignatureLen (); + size_t paddingSize = signatureLen & 0x0F; // %16 + if (paddingSize > 0) signatureLen += (16 - paddingSize); + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + m_SessionKeyDecryption.SetIV (((SSUHeader *)buf)->iv); + m_SessionKeyDecryption.Decrypt (payload, signatureLen, payload); + // verify + if (!s.Verify (m_RemoteIdentity, payload)) + LogPrint (eLogError, "SSU signature verification failed"); + + SendSessionConfirmed (y, ourAddress, addressSize + 2); + } + + void SSUSession::ProcessSessionConfirmed (uint8_t * buf, size_t len) + { + LogPrint (eLogDebug, "Session confirmed received"); + uint8_t * payload = buf + sizeof (SSUHeader); + payload++; // identity fragment info + uint16_t identitySize = bufbe16toh (payload); + payload += 2; // size of identity fragment + m_RemoteIdentity.FromBuffer (payload, identitySize); + m_Data.UpdatePacketSize (m_RemoteIdentity.GetIdentHash ()); + payload += identitySize; // identity + payload += 4; // signed-on time + size_t paddingSize = (payload - buf) + m_RemoteIdentity.GetSignatureLen (); + paddingSize &= 0x0F; // %16 + if (paddingSize > 0) paddingSize = 16 - paddingSize; + payload += paddingSize; + // TODO: verify signature (need data from session request), payload points to signature + SendI2NPMessage (CreateDeliveryStatusMsg (0)); + Established (); + } + + void SSUSession::SendSessionRequest () + { + auto introKey = GetIntroKey (); + if (!introKey) + { + LogPrint (eLogError, "SSU is not supported"); + return; + } + + uint8_t buf[320 + 18]; // 304 bytes for ipv4, 320 for ipv6 + uint8_t * payload = buf + sizeof (SSUHeader); + memcpy (payload, m_DHKeysPair->publicKey, 256); // x + bool isV4 = m_RemoteEndpoint.address ().is_v4 (); + if (isV4) + { + payload[256] = 4; + memcpy (payload + 257, m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data(), 4); + } + else + { + payload[256] = 16; + memcpy (payload + 257, m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data(), 16); + } + + uint8_t iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_REQUEST, buf, isV4 ? 304 : 320, introKey, iv, introKey); + m_Server.Send (buf, isV4 ? 304 : 320, m_RemoteEndpoint); + } + + void SSUSession::SendRelayRequest (uint32_t iTag, const uint8_t * iKey) + { + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + if (!address) + { + LogPrint (eLogError, "SSU is not supported"); + return; + } + + uint8_t buf[96 + 18]; + uint8_t * payload = buf + sizeof (SSUHeader); + htobe32buf (payload, iTag); + payload += 4; + *payload = 0; // no address + payload++; + htobuf16(payload, 0); // port = 0 + payload += 2; + *payload = 0; // challenge + payload++; + memcpy (payload, (const uint8_t *)address->key, 32); + payload += 32; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + htobe32buf (payload, rnd.GenerateWord32 ()); // nonce + + uint8_t iv[16]; + rnd.GenerateBlock (iv, 16); // random iv + if (m_State == eSessionStateEstablished) + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, m_SessionKey, iv, m_MacKey); + else + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_REQUEST, buf, 96, iKey, iv, iKey); + m_Server.Send (buf, 96, m_RemoteEndpoint); + } + + void SSUSession::SendSessionCreated (const uint8_t * x) + { + auto introKey = GetIntroKey (); + auto address = IsV6 () ? i2p::context.GetRouterInfo ().GetSSUV6Address () : + i2p::context.GetRouterInfo ().GetSSUAddress (true); //v4 only + if (!introKey || !address) + { + LogPrint (eLogError, "SSU is not supported"); + return; + } + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time + s.Insert (x, 256); // x + + uint8_t buf[384 + 18]; + uint8_t * payload = buf + sizeof (SSUHeader); + memcpy (payload, m_DHKeysPair->publicKey, 256); + s.Insert (payload, 256); // y + payload += 256; + if (m_RemoteEndpoint.address ().is_v4 ()) + { + // ipv4 + *payload = 4; + payload++; + memcpy (payload, m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data(), 4); + s.Insert (payload, 4); // remote endpoint IP V4 + payload += 4; + } + else + { + // ipv6 + *payload = 16; + payload++; + memcpy (payload, m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data(), 16); + s.Insert (payload, 16); // remote endpoint IP V6 + payload += 16; + } + htobe16buf (payload, m_RemoteEndpoint.port ()); + s.Insert (payload, 2); // remote port + payload += 2; + if (address->host.is_v4 ()) + s.Insert (address->host.to_v4 ().to_bytes ().data (), 4); // our IP V4 + else + s.Insert (address->host.to_v6 ().to_bytes ().data (), 16); // our IP V6 + s.Insert (htobe16 (address->port)); // our port + uint32_t relayTag = 0; + if (i2p::context.GetRouterInfo ().IsIntroducer ()) + { + relayTag = rnd.GenerateWord32 (); + if (!relayTag) relayTag = 1; + m_Server.AddRelay (relayTag, m_RemoteEndpoint); + } + htobe32buf (payload, relayTag); + payload += 4; // relay tag + htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time + payload += 4; + s.Insert (payload - 8, 8); // relayTag and signed on time + s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature + // TODO: fill padding with random data + + uint8_t iv[16]; + rnd.GenerateBlock (iv, 16); // random iv + // encrypt signature and padding with newly created session key + size_t signatureLen = i2p::context.GetIdentity ().GetSignatureLen (); + size_t paddingSize = signatureLen & 0x0F; // %16 + if (paddingSize > 0) signatureLen += (16 - paddingSize); + m_SessionKeyEncryption.SetIV (iv); + m_SessionKeyEncryption.Encrypt (payload, signatureLen, payload); + payload += signatureLen; + size_t msgLen = payload - buf; + + // encrypt message with intro key + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CREATED, buf, msgLen, introKey, iv, introKey); + Send (buf, msgLen); + } + + void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen) + { + uint8_t buf[512 + 18]; + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = 1; // 1 fragment + payload++; // info + size_t identLen = i2p::context.GetIdentity ().GetFullLen (); // 387+ bytes + htobe16buf (payload, identLen); + payload += 2; // cursize + i2p::context.GetIdentity ().ToBuffer (payload, identLen); + payload += identLen; + uint32_t signedOnTime = i2p::util::GetSecondsSinceEpoch (); + htobe32buf (payload, signedOnTime); // signed on time + payload += 4; + auto signatureLen = i2p::context.GetIdentity ().GetSignatureLen (); + size_t paddingSize = ((payload - buf) + signatureLen)%16; + if (paddingSize > 0) paddingSize = 16 - paddingSize; + // TODO: fill padding + payload += paddingSize; // padding size + + // signature + SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time + s.Insert (m_DHKeysPair->publicKey, 256); // x + s.Insert (y, 256); // y + s.Insert (ourAddress, ourAddressLen); // our address/port as seem by party + if (m_RemoteEndpoint.address ().is_v4 ()) + s.Insert (m_RemoteEndpoint.address ().to_v4 ().to_bytes ().data (), 4); // remote IP V4 + else + s.Insert (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), 16); // remote IP V6 + s.Insert (htobe16 (m_RemoteEndpoint.port ())); // remote port + s.Insert (htobe32 (m_RelayTag)); // relay tag + s.Insert (htobe32 (signedOnTime)); // signed on time + s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature + payload += signatureLen; + + size_t msgLen = payload - buf; + uint8_t iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + // encrypt message with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_CONFIRMED, buf, msgLen, m_SessionKey, iv, m_MacKey); + Send (buf, msgLen); + } + + void SSUSession::ProcessRelayRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from) + { + uint32_t relayTag = bufbe32toh (buf); + auto session = m_Server.FindRelaySession (relayTag); + if (session) + { + buf += 4; // relay tag + uint8_t size = *buf; + buf++; // size + buf += size; // address + buf += 2; // port + uint8_t challengeSize = *buf; + buf++; // challenge size + buf += challengeSize; + uint8_t * introKey = buf; + buf += 32; // introkey + uint32_t nonce = bufbe32toh (buf); + SendRelayResponse (nonce, from, introKey, session->m_RemoteEndpoint); + SendRelayIntro (session.get (), from); + } + } + + void SSUSession::SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, + const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to) + { + uint8_t buf[80 + 18]; // 64 Alice's ipv4 and 80 Alice's ipv6 + uint8_t * payload = buf + sizeof (SSUHeader); + // Charlie's address always v4 + if (!to.address ().is_v4 ()) + { + LogPrint (eLogError, "Charlie's IP must be v4"); + return; + } + *payload = 4; + payload++; // size + htobe32buf (payload, to.address ().to_v4 ().to_ulong ()); // Charlie's IP + payload += 4; // address + htobe16buf (payload, to.port ()); // Charlie's port + payload += 2; // port + // Alice + bool isV4 = from.address ().is_v4 (); // Alice's + if (isV4) + { + *payload = 4; + payload++; // size + memcpy (payload, from.address ().to_v4 ().to_bytes ().data (), 4); // Alice's IP V4 + payload += 4; // address + } + else + { + *payload = 16; + payload++; // size + memcpy (payload, from.address ().to_v6 ().to_bytes ().data (), 16); // Alice's IP V6 + payload += 16; // address + } + htobe16buf (payload, from.port ()); // Alice's port + payload += 2; // port + htobe32buf (payload, nonce); + + if (m_State == eSessionStateEstablished) + { + // encrypt with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80); + Send (buf, isV4 ? 64 : 80); + } + else + { + // ecrypt with Alice's intro key + uint8_t iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_RESPONSE, buf, isV4 ? 64 : 80, introKey, iv, introKey); + m_Server.Send (buf, isV4 ? 64 : 80, from); + } + LogPrint (eLogDebug, "SSU relay response sent"); + } + + void SSUSession::SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from) + { + if (!session) return; + // Alice's address always v4 + if (!from.address ().is_v4 ()) + { + LogPrint (eLogError, "Alice's IP must be v4"); + return; + } + uint8_t buf[48 + 18]; + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = 4; + payload++; // size + htobe32buf (payload, from.address ().to_v4 ().to_ulong ()); // Alice's IP + payload += 4; // address + htobe16buf (payload, from.port ()); // Alice's port + payload += 2; // port + *payload = 0; // challenge size + uint8_t iv[16]; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + FillHeaderAndEncrypt (PAYLOAD_TYPE_RELAY_INTRO, buf, 48, session->m_SessionKey, iv, session->m_MacKey); + m_Server.Send (buf, 48, session->m_RemoteEndpoint); + LogPrint (eLogDebug, "SSU relay intro sent"); + } + + void SSUSession::ProcessRelayResponse (uint8_t * buf, size_t len) + { + LogPrint (eLogDebug, "Relay response received"); + uint8_t * payload = buf + sizeof (SSUHeader); + uint8_t remoteSize = *payload; + payload++; // remote size + //boost::asio::ip::address_v4 remoteIP (bufbe32toh (payload)); + payload += remoteSize; // remote address + //uint16_t remotePort = bufbe16toh (payload); + payload += 2; // remote port + uint8_t ourSize = *payload; + payload++; // our size + boost::asio::ip::address ourIP; + if (ourSize == 4) + { + boost::asio::ip::address_v4::bytes_type bytes; + memcpy (bytes.data (), payload, 4); + ourIP = boost::asio::ip::address_v4 (bytes); + } + else + { + boost::asio::ip::address_v6::bytes_type bytes; + memcpy (bytes.data (), payload, 16); + ourIP = boost::asio::ip::address_v6 (bytes); + } + payload += ourSize; // our address + uint16_t ourPort = bufbe16toh (payload); + payload += 2; // our port + LogPrint ("Our external address is ", ourIP.to_string (), ":", ourPort); + i2p::context.UpdateAddress (ourIP); + } + + void SSUSession::ProcessRelayIntro (uint8_t * buf, size_t len) + { + uint8_t size = *buf; + if (size == 4) + { + buf++; // size + boost::asio::ip::address_v4 address (bufbe32toh (buf)); + buf += 4; // address + uint16_t port = bufbe16toh (buf); + // send hole punch of 1 byte + m_Server.Send (buf, 0, boost::asio::ip::udp::endpoint (address, port)); + } + else + LogPrint (eLogWarning, "Address size ", size, " is not supported"); + } + + void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, + const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey) + { + if (len < sizeof (SSUHeader)) + { + LogPrint (eLogError, "Unexpected SSU packet length ", len); + return; + } + //TODO: we are using a dirty solution here but should work for now + SSUHeader * header = (SSUHeader *)buf; + memcpy (header->iv, iv, 16); + header->flag = payloadType << 4; // MSB is 0 + htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ()); + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + i2p::crypto::CBCEncryption encryption; + encryption.SetKey (aesKey); + encryption.SetIV (iv); + encryption.Encrypt (encrypted, encryptedLen, encrypted); + // assume actual buffer size is 18 (16 + 2) bytes more + memcpy (buf + len, iv, 16); + htobe16buf (buf + len + 16, encryptedLen); + i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, header->mac); + } + + void SSUSession::FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len) + { + if (len < sizeof (SSUHeader)) + { + LogPrint (eLogError, "Unexpected SSU packet length ", len); + return; + } + //TODO: we are using a dirty solution here but should work for now + SSUHeader * header = (SSUHeader *)buf; + i2p::context.GetRandomNumberGenerator ().GenerateBlock (header->iv, 16); // random iv + m_SessionKeyEncryption.SetIV (header->iv); + header->flag = payloadType << 4; // MSB is 0 + htobe32buf (&(header->time), i2p::util::GetSecondsSinceEpoch ()); + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + m_SessionKeyEncryption.Encrypt (encrypted, encryptedLen, encrypted); + // assume actual buffer size is 18 (16 + 2) bytes more + memcpy (buf + len, header->iv, 16); + htobe16buf (buf + len + 16, encryptedLen); + i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, m_MacKey, header->mac); + } + + void SSUSession::Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey) + { + if (len < sizeof (SSUHeader)) + { + LogPrint (eLogError, "Unexpected SSU packet length ", len); + return; + } + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + SSUHeader * header = (SSUHeader *)buf; + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + i2p::crypto::CBCDecryption decryption; + decryption.SetKey (aesKey); + decryption.SetIV (header->iv); + decryption.Decrypt (encrypted, encryptedLen, encrypted); + } + + void SSUSession::DecryptSessionKey (uint8_t * buf, size_t len) + { + if (len < sizeof (SSUHeader)) + { + LogPrint (eLogError, "Unexpected SSU packet length ", len); + return; + } + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + SSUHeader * header = (SSUHeader *)buf; + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + if (encryptedLen > 0) + { + m_SessionKeyDecryption.SetIV (header->iv); + m_SessionKeyDecryption.Decrypt (encrypted, encryptedLen, encrypted); + } + } + + bool SSUSession::Validate (uint8_t * buf, size_t len, const uint8_t * macKey) + { + if (len < sizeof (SSUHeader)) + { + LogPrint (eLogError, "Unexpected SSU packet length ", len); + return false; + } + //TODO: since we are accessing a uint8_t this is unlikely to crash due to alignment but should be improved + SSUHeader * header = (SSUHeader *)buf; + uint8_t * encrypted = &header->flag; + uint16_t encryptedLen = len - (encrypted - buf); + // assume actual buffer size is 18 (16 + 2) bytes more + memcpy (buf + len, header->iv, 16); + htobe16buf (buf + len + 16, encryptedLen); + uint8_t digest[16]; + i2p::crypto::HMACMD5Digest (encrypted, encryptedLen + 18, macKey, digest); + return !memcmp (header->mac, digest, 16); + } + + void SSUSession::Connect () + { + if (m_State == eSessionStateUnknown) + { + // set connect timer + ScheduleConnectTimer (); + m_DHKeysPair = transports.GetNextDHKeysPair (); + SendSessionRequest (); + } + } + + void SSUSession::WaitForConnect () + { + if (!m_RemoteRouter) // incoming session + ScheduleConnectTimer (); + else + LogPrint (eLogError, "SSU wait for connect for outgoing session"); + } + + void SSUSession::ScheduleConnectTimer () + { + m_Timer.cancel (); + m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); + m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer, + shared_from_this (), std::placeholders::_1)); +} + + void SSUSession::HandleConnectTimer (const boost::system::error_code& ecode) + { + if (!ecode) + { + // timeout expired + LogPrint ("SSU session was not established after ", SSU_CONNECT_TIMEOUT, " second"); + Failed (); + } + } + + void SSUSession::Introduce (uint32_t iTag, const uint8_t * iKey) + { + if (m_State == eSessionStateUnknown) + { + // set connect timer + m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); + m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer, + shared_from_this (), std::placeholders::_1)); + } + SendRelayRequest (iTag, iKey); + } + + void SSUSession::WaitForIntroduction () + { + m_State = eSessionStateIntroduced; + // set connect timer + m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT)); + m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer, + shared_from_this (), std::placeholders::_1)); + } + + void SSUSession::Close () + { + SendSesionDestroyed (); + if (!m_DelayedMessages.empty ()) + { + for (auto it :m_DelayedMessages) + DeleteI2NPMessage (it); + m_DelayedMessages.clear (); + } + } + + void SSUSession::Established () + { + m_State = eSessionStateEstablished; + if (m_DHKeysPair) + { + delete m_DHKeysPair; + m_DHKeysPair = nullptr; + } + SendI2NPMessage (CreateDatabaseStoreMsg ()); + if (!m_DelayedMessages.empty ()) + { + for (auto it :m_DelayedMessages) + m_Data.Send (it); + m_DelayedMessages.clear (); + } + if (m_PeerTest && (m_RemoteRouter && m_RemoteRouter->IsPeerTesting ())) + SendPeerTest (); + ScheduleTermination (); + } + + void SSUSession::Failed () + { + if (m_State != eSessionStateFailed) + { + m_State = eSessionStateFailed; + m_Server.DeleteSession (shared_from_this ()); + } + } + + void SSUSession::ScheduleTermination () + { + m_Timer.cancel (); + m_Timer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_TIMEOUT)); + m_Timer.async_wait (std::bind (&SSUSession::HandleTerminationTimer, + shared_from_this (), std::placeholders::_1)); + } + + void SSUSession::HandleTerminationTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + LogPrint ("SSU no activity fo ", SSU_TERMINATION_TIMEOUT, " seconds"); + Failed (); + } + } + + const uint8_t * SSUSession::GetIntroKey () const + { + if (m_RemoteRouter) + { + // we are client + auto address = m_RemoteRouter->GetSSUAddress (); + return address ? (const uint8_t *)address->key : nullptr; + } + else + { + // we are server + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + return address ? (const uint8_t *)address->key : nullptr; + } + } + + void SSUSession::SendI2NPMessage (I2NPMessage * msg) + { + m_Server.GetService ().post (std::bind (&SSUSession::PostI2NPMessage, shared_from_this (), msg)); + } + + void SSUSession::PostI2NPMessage (I2NPMessage * msg) + { + if (msg) + { + if (m_State == eSessionStateEstablished) + m_Data.Send (msg); + else + m_DelayedMessages.push_back (msg); + } + } + + void SSUSession::ProcessData (uint8_t * buf, size_t len) + { + m_Data.ProcessMessage (buf, len); + } + + + void SSUSession::ProcessPeerTest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) + { + uint8_t * buf1 = buf; + uint32_t nonce = bufbe32toh (buf); + buf += 4; // nonce + uint8_t size = *buf; + buf++; // size + + uint32_t address = (size == 4) ? buf32toh(buf) : 0; // use it as is + buf += size; // address + uint16_t port = buf16toh(buf); // use it as is + buf += 2; // port + uint8_t * introKey = buf; + if (port && !address) + { + LogPrint (eLogWarning, "Address of ", size, " bytes not supported"); + return; + } + if (m_PeerTestNonces.count (nonce) > 0) + { + // existing test + if (m_PeerTest) + { + LogPrint (eLogDebug, "SSU peer test from Bob. We are Alice"); + m_PeerTestNonces.erase (nonce); + m_PeerTest = false; + } + else if (port) + { + LogPrint (eLogDebug, "SSU peer test from Charlie. We are Bob"); + boost::asio::ip::udp::endpoint ep (boost::asio::ip::address_v4 (be32toh (address)), be16toh (port)); // Alice's address/port + auto session = m_Server.FindSession (ep); // find session with Alice + if (session) + session->Send (PAYLOAD_TYPE_PEER_TEST, buf1, len); // back to Alice + } + else + { + LogPrint (eLogDebug, "SSU peer test from Alice. We are Charlie"); + SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (), + senderEndpoint.port (), introKey); // to Alice + } + } + else + { + if (m_State == eSessionStateEstablished) + { + // new test + m_PeerTestNonces.insert (nonce); + if (port) + { + LogPrint (eLogDebug, "SSU peer test from Bob. We are Charlie"); + Send (PAYLOAD_TYPE_PEER_TEST, buf1, len); // back to Bob + SendPeerTest (nonce, be32toh (address), be16toh (port), introKey); // to Alice + } + else + { + LogPrint (eLogDebug, "SSU peer test from Alice. We are Bob"); + auto session = m_Server.GetRandomEstablishedSession (shared_from_this ()); // charlie + if (session) + session->SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (), + senderEndpoint.port (), introKey, false); + } + } + else + LogPrint (eLogDebug, "SSU peer test from Charlie. We are Alice"); + } + } + + void SSUSession::SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, + const uint8_t * introKey, bool toAddress) + { + uint8_t buf[80 + 18]; + uint8_t iv[16]; + uint8_t * payload = buf + sizeof (SSUHeader); + htobe32buf (payload, nonce); + payload += 4; // nonce + if (address) + { + *payload = 4; + payload++; // size + htobe32buf (payload, address); + payload += 4; // address + } + else + { + *payload = 0; + payload++; //size + } + htobe16buf (payload, port); + payload += 2; // port + memcpy (payload, introKey, 32); // intro key + + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (iv, 16); // random iv + if (toAddress) + { + // encrypt message with specified intro key + FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey); + boost::asio::ip::udp::endpoint e (boost::asio::ip::address_v4 (address), port); + m_Server.Send (buf, 80, e); + } + else + { + // encrypt message with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80); + Send (buf, 80); + } + } + + void SSUSession::SendPeerTest () + { + LogPrint (eLogDebug, "SSU sending peer test"); + auto address = i2p::context.GetRouterInfo ().GetSSUAddress (); + if (!address) + { + LogPrint (eLogError, "SSU is not supported. Can't send peer test"); + return; + } + uint32_t nonce = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); + if (!nonce) nonce = 1; + m_PeerTestNonces.insert (nonce); + SendPeerTest (nonce, 0, 0, address->key, false); // address and port always zero for Alice + } + + void SSUSession::SendKeepAlive () + { + if (m_State == eSessionStateEstablished) + { + uint8_t buf[48 + 18]; + uint8_t * payload = buf + sizeof (SSUHeader); + *payload = 0; // flags + payload++; + *payload = 0; // num fragments + // encrypt message with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48); + Send (buf, 48); + LogPrint (eLogDebug, "SSU keep-alive sent"); + ScheduleTermination (); + } + } + + void SSUSession::SendSesionDestroyed () + { + if (m_IsSessionKey) + { + uint8_t buf[48 + 18]; + // encrypt message with session key + FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48); + Send (buf, 48); + LogPrint (eLogDebug, "SSU session destroyed sent"); + } + } + + void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len) + { + uint8_t buf[SSU_MTU_V4 + 18]; + size_t msgSize = len + sizeof (SSUHeader); + if (msgSize > SSU_MTU_V4) + { + LogPrint (eLogWarning, "SSU payload size ", msgSize, " exceeds MTU"); + return; + } + memcpy (buf + sizeof (SSUHeader), payload, len); + // encrypt message with session key + FillHeaderAndEncrypt (type, buf, msgSize); + Send (buf, msgSize); + } + + void SSUSession::Send (const uint8_t * buf, size_t size) + { + m_NumSentBytes += size; + m_Server.Send (buf, size, m_RemoteEndpoint); + } +} +} + diff --git a/SSUSession.h b/SSUSession.h new file mode 100644 index 00000000..e9848840 --- /dev/null +++ b/SSUSession.h @@ -0,0 +1,146 @@ +#ifndef SSU_SESSION_H__ +#define SSU_SESSION_H__ + +#include +#include +#include +#include +#include "aes.h" +#include "hmac.h" +#include "I2NPProtocol.h" +#include "TransportSession.h" +#include "SSUData.h" + +namespace i2p +{ +namespace transport +{ +#pragma pack(1) + struct SSUHeader + { + uint8_t mac[16]; + uint8_t iv[16]; + uint8_t flag; + uint32_t time; + + uint8_t GetPayloadType () const { return flag >> 4; }; + }; +#pragma pack() + + const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds + const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes + + // payload types (4 bits) + const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0; + const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1; + const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2; + const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3; + const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4; + const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5; + const uint8_t PAYLOAD_TYPE_DATA = 6; + const uint8_t PAYLOAD_TYPE_PEER_TEST = 7; + const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8; + + enum SessionState + { + eSessionStateUnknown, + eSessionStateIntroduced, + eSessionStateEstablished, + eSessionStateFailed + }; + + class SSUServer; + class SSUSession: public TransportSession, public std::enable_shared_from_this + { + public: + + SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint, + std::shared_ptr router = nullptr, bool peerTest = false); + void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); + ~SSUSession (); + + void Connect (); + void WaitForConnect (); + void Introduce (uint32_t iTag, const uint8_t * iKey); + void WaitForIntroduction (); + void Close (); + boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; }; + bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); }; + void SendI2NPMessage (I2NPMessage * msg); + void SendPeerTest (); // Alice + + SessionState GetState () const { return m_State; }; + size_t GetNumSentBytes () const { return m_NumSentBytes; }; + size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; + + void SendKeepAlive (); + uint32_t GetRelayTag () const { return m_RelayTag; }; + uint32_t GetCreationTime () const { return m_CreationTime; }; + + private: + + void CreateAESandMacKey (const uint8_t * pubKey); + + void PostI2NPMessage (I2NPMessage * msg); + void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session + void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); + void SendSessionRequest (); + void SendRelayRequest (uint32_t iTag, const uint8_t * iKey); + void ProcessSessionCreated (uint8_t * buf, size_t len); + void SendSessionCreated (const uint8_t * x); + void ProcessSessionConfirmed (uint8_t * buf, size_t len); + void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen); + void ProcessRelayRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from); + void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from, + const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to); + void SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from); + void ProcessRelayResponse (uint8_t * buf, size_t len); + void ProcessRelayIntro (uint8_t * buf, size_t len); + void Established (); + void Failed (); + void ScheduleConnectTimer (); + void HandleConnectTimer (const boost::system::error_code& ecode); + void ProcessPeerTest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); + void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, const uint8_t * introKey, bool toAddress = true); + void ProcessData (uint8_t * buf, size_t len); + void SendSesionDestroyed (); + void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key + void Send (const uint8_t * buf, size_t size); + + void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey); + void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key + void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey); + void DecryptSessionKey (uint8_t * buf, size_t len); + bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey); + const uint8_t * GetIntroKey () const; + + void ScheduleTermination (); + void HandleTerminationTimer (const boost::system::error_code& ecode); + + private: + + friend class SSUData; // TODO: change in later + SSUServer& m_Server; + boost::asio::ip::udp::endpoint m_RemoteEndpoint; + boost::asio::deadline_timer m_Timer; + bool m_PeerTest; + SessionState m_State; + bool m_IsSessionKey; + uint32_t m_RelayTag; + std::set m_PeerTestNonces; + i2p::crypto::CBCEncryption m_SessionKeyEncryption; + i2p::crypto::CBCDecryption m_SessionKeyDecryption; + i2p::crypto::AESKey m_SessionKey; + i2p::crypto::MACKey m_MacKey; + std::list m_DelayedMessages; + SSUData m_Data; + size_t m_NumSentBytes, m_NumReceivedBytes; + uint32_t m_CreationTime; // seconds since epoch + }; + + +} +} + +#endif + diff --git a/Signature.h b/Signature.h new file mode 100644 index 00000000..23840562 --- /dev/null +++ b/Signature.h @@ -0,0 +1,417 @@ +#ifndef SIGNATURE_H__ +#define SIGNATURE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "CryptoConst.h" + +namespace i2p +{ +namespace crypto +{ + class Verifier + { + public: + + virtual ~Verifier () {}; + virtual bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const = 0; + virtual size_t GetPublicKeyLen () const = 0; + virtual size_t GetSignatureLen () const = 0; + virtual size_t GetPrivateKeyLen () const { return GetSignatureLen ()/2; }; + }; + + class Signer + { + public: + + virtual ~Signer () {}; + virtual void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const = 0; + }; + + const size_t DSA_PUBLIC_KEY_LENGTH = 128; + const size_t DSA_SIGNATURE_LENGTH = 40; + const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH/2; + class DSAVerifier: public Verifier + { + public: + + DSAVerifier (const uint8_t * signingKey) + { + m_PublicKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingKey, DSA_PUBLIC_KEY_LENGTH)); + } + + bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const + { + CryptoPP::DSA::Verifier verifier (m_PublicKey); + return verifier.VerifyMessage (buf, len, signature, DSA_SIGNATURE_LENGTH); + } + + size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; }; + size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; }; + + private: + + CryptoPP::DSA::PublicKey m_PublicKey; + }; + + class DSASigner: public Signer + { + public: + + DSASigner (const uint8_t * signingPrivateKey) + { + m_PrivateKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH)); + } + + void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const + { + CryptoPP::DSA::Signer signer (m_PrivateKey); + signer.SignMessage (rnd, buf, len, signature); + } + + private: + + CryptoPP::DSA::PrivateKey m_PrivateKey; + }; + + inline void CreateDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + CryptoPP::DSA::PrivateKey privateKey; + CryptoPP::DSA::PublicKey publicKey; + privateKey.Initialize (rnd, dsap, dsaq, dsag); + privateKey.MakePublicKey (publicKey); + privateKey.GetPrivateExponent ().Encode (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH); + publicKey.GetPublicElement ().Encode (signingPublicKey, DSA_PUBLIC_KEY_LENGTH); + } + + template + class ECDSAVerifier: public Verifier + { + public: + + template + ECDSAVerifier (Curve curve, const uint8_t * signingKey) + { + m_PublicKey.Initialize (curve, + CryptoPP::ECP::Point (CryptoPP::Integer (signingKey, keyLen/2), + CryptoPP::Integer (signingKey + keyLen/2, keyLen/2))); + } + + bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const + { + typename CryptoPP::ECDSA::Verifier verifier (m_PublicKey); + return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length + } + + size_t GetPublicKeyLen () const { return keyLen; }; + size_t GetSignatureLen () const { return keyLen; }; // signature length = key length + + private: + + typename CryptoPP::ECDSA::PublicKey m_PublicKey; + }; + + template + class ECDSASigner: public Signer + { + public: + + template + ECDSASigner (Curve curve, const uint8_t * signingPrivateKey, size_t keyLen) + { + m_PrivateKey.Initialize (curve, CryptoPP::Integer (signingPrivateKey, keyLen/2)); // private key length + } + + void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const + { + typename CryptoPP::ECDSA::Signer signer (m_PrivateKey); + signer.SignMessage (rnd, buf, len, signature); + } + + private: + + typename CryptoPP::ECDSA::PrivateKey m_PrivateKey; + }; + + template + inline void CreateECDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, Curve curve, + size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + typename CryptoPP::ECDSA::PrivateKey privateKey; + typename CryptoPP::ECDSA::PublicKey publicKey; + privateKey.Initialize (rnd, curve); + privateKey.MakePublicKey (publicKey); + privateKey.GetPrivateExponent ().Encode (signingPrivateKey, keyLen/2); + auto q = publicKey.GetPublicElement (); + q.x.Encode (signingPublicKey, keyLen/2); + q.y.Encode (signingPublicKey + keyLen/2, keyLen/2); + } + +// ECDSA_SHA256_P256 + const size_t ECDSAP256_KEY_LENGTH = 64; + class ECDSAP256Verifier: public ECDSAVerifier + { + public: + + ECDSAP256Verifier (const uint8_t * signingKey): + ECDSAVerifier (CryptoPP::ASN1::secp256r1(), signingKey) + { + } + }; + + class ECDSAP256Signer: public ECDSASigner + { + public: + + ECDSAP256Signer (const uint8_t * signingPrivateKey): + ECDSASigner (CryptoPP::ASN1::secp256r1(), signingPrivateKey, ECDSAP256_KEY_LENGTH) + { + } + }; + + inline void CreateECDSAP256RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + CreateECDSARandomKeys (rnd, CryptoPP::ASN1::secp256r1(), ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey); + } + +// ECDSA_SHA384_P384 + const size_t ECDSAP384_KEY_LENGTH = 96; + class ECDSAP384Verifier: public ECDSAVerifier + { + public: + + ECDSAP384Verifier (const uint8_t * signingKey): + ECDSAVerifier (CryptoPP::ASN1::secp384r1(), signingKey) + { + } + }; + + class ECDSAP384Signer: public ECDSASigner + { + public: + + ECDSAP384Signer (const uint8_t * signingPrivateKey): + ECDSASigner (CryptoPP::ASN1::secp384r1(), signingPrivateKey, ECDSAP384_KEY_LENGTH) + { + } + }; + + inline void CreateECDSAP384RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + CreateECDSARandomKeys (rnd, CryptoPP::ASN1::secp384r1(), ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey); + } + +// ECDSA_SHA512_P521 + const size_t ECDSAP521_KEY_LENGTH = 132; + class ECDSAP521Verifier: public ECDSAVerifier + { + public: + + ECDSAP521Verifier (const uint8_t * signingKey): + ECDSAVerifier (CryptoPP::ASN1::secp521r1(), signingKey) + { + } + }; + + class ECDSAP521Signer: public ECDSASigner + { + public: + + ECDSAP521Signer (const uint8_t * signingPrivateKey): + ECDSASigner (CryptoPP::ASN1::secp521r1(), signingPrivateKey, ECDSAP521_KEY_LENGTH) + { + } + }; + + inline void CreateECDSAP521RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + CreateECDSARandomKeys (rnd, CryptoPP::ASN1::secp521r1(), ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey); + } + +// RSA + template + class RSAVerifier: public Verifier + { + public: + + RSAVerifier (const uint8_t * signingKey) + { + m_PublicKey.Initialize (CryptoPP::Integer (signingKey, keyLen), CryptoPP::Integer (rsae)); + } + + bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const + { + typename CryptoPP::RSASS::Verifier verifier (m_PublicKey); + return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length + } + size_t GetPublicKeyLen () const { return keyLen; } + size_t GetSignatureLen () const { return keyLen; } + size_t GetPrivateKeyLen () const { return GetSignatureLen ()*2; }; + + private: + + CryptoPP::RSA::PublicKey m_PublicKey; + }; + + + template + class RSASigner: public Signer + { + public: + + RSASigner (const uint8_t * signingPrivateKey, size_t keyLen) + { + m_PrivateKey.Initialize (CryptoPP::Integer (signingPrivateKey, keyLen/2), + rsae, + CryptoPP::Integer (signingPrivateKey + keyLen/2, keyLen/2)); + } + + void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const + { + typename CryptoPP::RSASS::Signer signer (m_PrivateKey); + signer.SignMessage (rnd, buf, len, signature); + } + + private: + + CryptoPP::RSA::PrivateKey m_PrivateKey; + }; + + inline void CreateRSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, + size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) + { + CryptoPP::RSA::PrivateKey privateKey; + privateKey.Initialize (rnd, publicKeyLen*8, rsae); + privateKey.GetModulus ().Encode (signingPrivateKey, publicKeyLen); + privateKey.GetPrivateExponent ().Encode (signingPrivateKey + publicKeyLen, publicKeyLen); + privateKey.GetModulus ().Encode (signingPublicKey, publicKeyLen); + } + + +// RSA_SHA256_2048 + const size_t RSASHA2562048_KEY_LENGTH = 256; + class RSASHA2562048Verifier: public RSAVerifier + { + public: + + RSASHA2562048Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) + { + } + }; + + class RSASHA2562048Signer: public RSASigner + { + public: + + RSASHA2562048Signer (const uint8_t * signingPrivateKey): + RSASigner (signingPrivateKey, RSASHA2562048_KEY_LENGTH*2) + { + } + }; + +// RSA_SHA384_3072 + const size_t RSASHA3843072_KEY_LENGTH = 384; + class RSASHA3843072Verifier: public RSAVerifier + { + public: + + RSASHA3843072Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) + { + } + }; + + class RSASHA3843072Signer: public RSASigner + { + public: + + RSASHA3843072Signer (const uint8_t * signingPrivateKey): + RSASigner (signingPrivateKey, RSASHA3843072_KEY_LENGTH*2) + { + } + }; + +// RSA_SHA512_4096 + const size_t RSASHA5124096_KEY_LENGTH = 512; + class RSASHA5124096Verifier: public RSAVerifier + { + public: + + RSASHA5124096Verifier (const uint8_t * signingKey): RSAVerifier (signingKey) + { + } + }; + + class RSASHA5124096Signer: public RSASigner + { + public: + + RSASHA5124096Signer (const uint8_t * signingPrivateKey): + RSASigner (signingPrivateKey, RSASHA5124096_KEY_LENGTH*2) + { + } + }; + +// Raw verifiers + class RawVerifier + { + public: + + virtual ~RawVerifier () {}; + virtual void Update (const uint8_t * buf, size_t len) = 0; + virtual bool Verify (const uint8_t * signature) = 0; + }; + + template + class RSARawVerifier: public RawVerifier + { + public: + + RSARawVerifier (const uint8_t * signingKey): + n (signingKey, keyLen) + { + } + + void Update (const uint8_t * buf, size_t len) + { + m_Hash.Update (buf, len); + } + + bool Verify (const uint8_t * signature) + { + // RSA encryption first + CryptoPP::Integer enSig (a_exp_b_mod_c (CryptoPP::Integer (signature, keyLen), + CryptoPP::Integer (i2p::crypto::rsae), n)); // s^e mod n + uint8_t enSigBuf[keyLen]; + enSig.Encode (enSigBuf, keyLen); + + uint8_t digest[Hash::DIGESTSIZE]; + m_Hash.Final (digest); + if ((int)keyLen < Hash::DIGESTSIZE) return false; // can't verify digest longer than key + // we assume digest is right aligned, at least for PKCS#1 v1.5 padding + return !memcmp (enSigBuf + (keyLen - Hash::DIGESTSIZE), digest, Hash::DIGESTSIZE); + } + + private: + + CryptoPP::Integer n; // RSA modulus + Hash m_Hash; + }; + + class RSASHA5124096RawVerifier: public RSARawVerifier + { + public: + + RSASHA5124096RawVerifier (const uint8_t * signingKey): RSARawVerifier (signingKey) + { + } + }; +} +} + +#endif + diff --git a/Streaming.cpp b/Streaming.cpp new file mode 100644 index 00000000..79296fe4 --- /dev/null +++ b/Streaming.cpp @@ -0,0 +1,739 @@ +#include +#include "Log.h" +#include "RouterInfo.h" +#include "RouterContext.h" +#include "Tunnel.h" +#include "Timestamp.h" +#include "Destination.h" +#include "Streaming.h" + +namespace i2p +{ +namespace stream +{ + Stream::Stream (boost::asio::io_service& service, StreamingDestination& local, + const i2p::data::LeaseSet& remote, int port): m_Service (service), m_SendStreamID (0), + m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_IsOpen (false), + m_IsReset (false), m_IsAckSendScheduled (false), m_LocalDestination (local), + m_RemoteLeaseSet (&remote), m_RoutingSession (nullptr), m_CurrentOutboundTunnel (nullptr), + m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), + m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port) + { + m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); + UpdateCurrentRemoteLease (); + } + + Stream::Stream (boost::asio::io_service& service, StreamingDestination& local): + m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), + m_IsOpen (false), m_IsReset (false), m_IsAckSendScheduled (false), m_LocalDestination (local), + m_RemoteLeaseSet (nullptr), m_RoutingSession (nullptr), m_CurrentOutboundTunnel (nullptr), + m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), + m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0) + { + m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); + } + + Stream::~Stream () + { + m_AckSendTimer.cancel (); + while (!m_ReceiveQueue.empty ()) + { + auto packet = m_ReceiveQueue.front (); + m_ReceiveQueue.pop (); + delete packet; + } + m_ReceiveTimer.cancel (); + + for (auto it: m_SentPackets) + delete it; + m_SentPackets.clear (); + m_ResendTimer.cancel (); + + for (auto it: m_SavedPackets) + delete it; + m_SavedPackets.clear (); + LogPrint (eLogDebug, "Stream deleted"); + } + + void Stream::HandleNextPacket (Packet * packet) + { + m_NumReceivedBytes += packet->GetLength (); + if (!m_SendStreamID) + m_SendStreamID = packet->GetReceiveStreamID (); + + if (!packet->IsNoAck ()) // ack received + ProcessAck (packet); + + int32_t receivedSeqn = packet->GetSeqn (); + bool isSyn = packet->IsSYN (); + if (!receivedSeqn && !isSyn) + { + // plain ack + LogPrint (eLogDebug, "Plain ACK received"); + delete packet; + return; + } + + LogPrint (eLogDebug, "Received seqn=", receivedSeqn); + if (isSyn || receivedSeqn == m_LastReceivedSequenceNumber + 1) + { + // we have received next in sequence message + ProcessPacket (packet); + + // we should also try stored messages if any + for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();) + { + if ((*it)->GetSeqn () == (uint32_t)(m_LastReceivedSequenceNumber + 1)) + { + Packet * savedPacket = *it; + m_SavedPackets.erase (it++); + + ProcessPacket (savedPacket); + } + else + break; + } + + // schedule ack for last message + if (m_IsOpen) + { + if (!m_IsAckSendScheduled) + { + m_IsAckSendScheduled = true; + m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT)); + m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, + shared_from_this (), std::placeholders::_1)); + } + } + else if (isSyn) + // we have to send SYN back to incoming connection + Send (nullptr, 0); // also sets m_IsOpen + } + else + { + if (receivedSeqn <= m_LastReceivedSequenceNumber) + { + // we have received duplicate. Most likely our outbound tunnel is dead + LogPrint (eLogWarning, "Duplicate message ", receivedSeqn, " received"); + m_CurrentOutboundTunnel = nullptr; // pick another outbound tunnel + UpdateCurrentRemoteLease (); // pick another lease + SendQuickAck (); // resend ack for previous message again + delete packet; // packet dropped + } + else + { + LogPrint (eLogWarning, "Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1); + // save message and wait for missing message again + SavePacket (packet); + // send NACKs for missing messages ASAP + if (m_IsAckSendScheduled) + { + m_IsAckSendScheduled = false; + m_AckSendTimer.cancel (); + } + SendQuickAck (); + } + } + } + + void Stream::SavePacket (Packet * packet) + { + m_SavedPackets.insert (packet); + } + + void Stream::ProcessPacket (Packet * packet) + { + // process flags + uint32_t receivedSeqn = packet->GetSeqn (); + uint16_t flags = packet->GetFlags (); + LogPrint (eLogDebug, "Process seqn=", receivedSeqn, ", flags=", flags); + + const uint8_t * optionData = packet->GetOptionData (); + if (flags & PACKET_FLAG_SYNCHRONIZE) + LogPrint (eLogDebug, "Synchronize"); + + if (flags & PACKET_FLAG_DELAY_REQUESTED) + { + optionData += 2; + } + + if (flags & PACKET_FLAG_FROM_INCLUDED) + { + optionData += m_RemoteIdentity.FromBuffer (optionData, packet->GetOptionSize ()); + LogPrint (eLogInfo, "From identity ", m_RemoteIdentity.GetIdentHash ().ToBase64 ()); + if (!m_RemoteLeaseSet) + LogPrint (eLogDebug, "Incoming stream from ", m_RemoteIdentity.GetIdentHash ().ToBase64 ()); + } + + if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) + { + uint16_t maxPacketSize = bufbe16toh (optionData); + LogPrint (eLogDebug, "Max packet size ", maxPacketSize); + optionData += 2; + } + + if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) + { + LogPrint (eLogDebug, "Signature"); + uint8_t signature[256]; + auto signatureLen = m_RemoteIdentity.GetSignatureLen (); + memcpy (signature, optionData, signatureLen); + memset (const_cast(optionData), 0, signatureLen); + if (!m_RemoteIdentity.Verify (packet->GetBuffer (), packet->GetLength (), signature)) + { + LogPrint (eLogError, "Signature verification failed"); + Close (); + flags |= PACKET_FLAG_CLOSE; + } + memcpy (const_cast(optionData), signature, signatureLen); + optionData += signatureLen; + } + + packet->offset = packet->GetPayload () - packet->buf; + if (packet->GetLength () > 0) + { + m_ReceiveQueue.push (packet); + m_ReceiveTimer.cancel (); + } + else + delete packet; + + m_LastReceivedSequenceNumber = receivedSeqn; + + if (flags & PACKET_FLAG_CLOSE) + { + LogPrint (eLogInfo, "Closed"); + Close (); + m_IsOpen = false; + m_IsReset = true; + } + } + + void Stream::ProcessAck (Packet * packet) + { + uint32_t ackThrough = packet->GetAckThrough (); + int nackCount = packet->GetNACKCount (); + for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();) + { + auto seqn = (*it)->GetSeqn (); + if (seqn <= ackThrough) + { + if (nackCount > 0) + { + bool nacked = false; + for (int i = 0; i < nackCount; i++) + if (seqn == packet->GetNACK (i)) + { + nacked = true; + break; + } + if (nacked) + { + LogPrint (eLogDebug, "Packet ", seqn, " NACK"); + it++; + continue; + } + } + auto sentPacket = *it; + LogPrint (eLogDebug, "Packet ", seqn, " acknowledged"); + m_SentPackets.erase (it++); + delete sentPacket; + } + else + break; + } + if (m_SentPackets.empty ()) + m_ResendTimer.cancel (); + } + + size_t Stream::Send (const uint8_t * buf, size_t len) + { + bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet + std::vector packets; + while (!m_IsOpen || len > 0) + { + Packet * p = new Packet (); + uint8_t * packet = p->GetBuffer (); + // TODO: implement setters + size_t size = 0; + htobe32buf (packet + size, m_SendStreamID); + size += 4; // sendStreamID + htobe32buf (packet + size, m_RecvStreamID); + size += 4; // receiveStreamID + htobe32buf (packet + size, m_SequenceNumber++); + size += 4; // sequenceNum + if (isNoAck) + htobe32buf (packet + size, m_LastReceivedSequenceNumber); + else + htobuf32 (packet + size, 0); + size += 4; // ack Through + packet[size] = 0; + size++; // NACK count + packet[size] = RESEND_TIMEOUT; + size++; // resend delay + if (!m_IsOpen) + { + // initial packet + m_IsOpen = true; m_IsReset = false; + uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | + PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; + if (isNoAck) flags |= PACKET_FLAG_NO_ACK; + htobe16buf (packet + size, flags); + size += 2; // flags + size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen (); + size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen (); + htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size + size += 2; // options size + m_LocalDestination.GetOwner ().GetIdentity ().ToBuffer (packet + size, identityLen); + size += identityLen; // from + htobe16buf (packet + size, STREAMING_MTU); + size += 2; // max packet size + uint8_t * signature = packet + size; // set it later + memset (signature, 0, signatureLen); // zeroes for now + size += signatureLen; // signature + size_t sentLen = STREAMING_MTU - size; + if (len < sentLen) sentLen = len; + memcpy (packet + size, buf, sentLen); + buf += sentLen; + len -= sentLen; + size += sentLen; // payload + m_LocalDestination.GetOwner ().Sign (packet, size, signature); + } + else + { + // follow on packet + htobuf16 (packet + size, 0); + size += 2; // flags + htobuf16 (packet + size, 0); // no options + size += 2; // options size + size_t sentLen = STREAMING_MTU - size; + if (len < sentLen) sentLen = len; + memcpy (packet + size, buf, sentLen); + buf += sentLen; + len -= sentLen; + size += sentLen; // payload + } + p->len = size; + packets.push_back (p); + } + if (packets.size () > 0) + m_Service.post (std::bind (&Stream::PostPackets, shared_from_this (), packets)); + return len; + } + + + void Stream::SendQuickAck () + { + int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber; + if (!m_SavedPackets.empty ()) + { + int32_t seqn = (*m_SavedPackets.rbegin ())->GetSeqn (); + if (seqn > lastReceivedSeqn) lastReceivedSeqn = seqn; + } + if (lastReceivedSeqn < 0) + { + LogPrint (eLogError, "No packets have been received yet"); + return; + } + + Packet p; + uint8_t * packet = p.GetBuffer (); + size_t size = 0; + htobe32buf (packet + size, m_SendStreamID); + size += 4; // sendStreamID + htobe32buf (packet + size, m_RecvStreamID); + size += 4; // receiveStreamID + htobuf32 (packet + size, 0); // this is plain Ack message + size += 4; // sequenceNum + htobe32buf (packet + size, lastReceivedSeqn); + size += 4; // ack Through + uint8_t numNacks = 0; + if (lastReceivedSeqn > m_LastReceivedSequenceNumber) + { + // fill NACKs + uint8_t * nacks = packet + size + 1; + auto nextSeqn = m_LastReceivedSequenceNumber + 1; + for (auto it: m_SavedPackets) + { + auto seqn = it->GetSeqn (); + for (uint32_t i = nextSeqn; i < seqn; i++) + { + htobe32buf (nacks, i); + nacks += 4; + numNacks++; + } + nextSeqn = seqn + 1; + } + packet[size] = numNacks; + size++; // NACK count + size += numNacks*4; // NACKs + } + else + { + // No NACKs + packet[size] = 0; + size++; // NACK count + } + size++; // resend delay + htobuf16 (packet + size, 0); // nof flags set + size += 2; // flags + htobuf16 (packet + size, 0); // no options + size += 2; // options size + p.len = size; + + SendPackets (std::vector { &p }); + LogPrint ("Quick Ack sent. ", (int)numNacks, " NACKs"); + } + + void Stream::Close () + { + if (m_IsOpen) + { + m_IsOpen = false; + Packet * p = new Packet (); + uint8_t * packet = p->GetBuffer (); + size_t size = 0; + htobe32buf (packet + size, m_SendStreamID); + size += 4; // sendStreamID + htobe32buf (packet + size, m_RecvStreamID); + size += 4; // receiveStreamID + htobe32buf (packet + size, m_SequenceNumber++); + size += 4; // sequenceNum + htobe32buf (packet + size, m_LastReceivedSequenceNumber); + size += 4; // ack Through + packet[size] = 0; + size++; // NACK count + size++; // resend delay + htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); + size += 2; // flags + size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen (); + htobe16buf (packet + size, signatureLen); // signature only + size += 2; // options size + uint8_t * signature = packet + size; + memset (packet + size, 0, signatureLen); + size += signatureLen; // signature + m_LocalDestination.GetOwner ().Sign (packet, size, signature); + + p->len = size; + m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p)); + LogPrint ("FIN sent"); + m_ReceiveTimer.cancel (); + m_LocalDestination.DeleteStream (shared_from_this ()); + } + } + + size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len) + { + size_t pos = 0; + while (pos < len && !m_ReceiveQueue.empty ()) + { + Packet * packet = m_ReceiveQueue.front (); + size_t l = std::min (packet->GetLength (), len - pos); + memcpy (buf + pos, packet->GetBuffer (), l); + pos += l; + packet->offset += l; + if (!packet->GetLength ()) + { + m_ReceiveQueue.pop (); + delete packet; + } + } + return pos; + } + + bool Stream::SendPacket (Packet * packet) + { + if (packet) + { + if (m_IsAckSendScheduled) + { + m_IsAckSendScheduled = false; + m_AckSendTimer.cancel (); + } + SendPackets (std::vector { packet }); + if (m_IsOpen) + { + bool isEmpty = m_SentPackets.empty (); + m_SentPackets.insert (packet); + if (isEmpty) + ScheduleResend (); + } + else + delete packet; + return true; + } + else + return false; + } + + void Stream::PostPackets (const std::vector packets) + { + if (m_IsOpen) + { + if (packets.size () > 0) + { + m_IsAckSendScheduled = false; + m_AckSendTimer.cancel (); + } + bool isEmpty = m_SentPackets.empty (); + for (auto it: packets) + m_SentPackets.insert (it); + SendPackets (packets); + if (isEmpty) + ScheduleResend (); + } + else + { + // delete + for (auto it: packets) + delete it; + } + } + + void Stream::SendPackets (const std::vector& packets) + { + if (!m_RemoteLeaseSet) + { + UpdateCurrentRemoteLease (); + if (!m_RemoteLeaseSet) + { + LogPrint ("Can't send packets. Missing remote LeaseSet"); + return; + } + } + m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); + if (!m_CurrentOutboundTunnel) + { + LogPrint ("No outbound tunnels in the pool"); + return; + } + + auto ts = i2p::util::GetMillisecondsSinceEpoch (); + if (ts >= m_CurrentRemoteLease.endDate) + UpdateCurrentRemoteLease (); + if (ts < m_CurrentRemoteLease.endDate) + { + std::vector msgs; + for (auto it: packets) + { + auto msg = m_RoutingSession->WrapSingleMessage (CreateDataMessage (it->GetBuffer (), it->GetLength ())); + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { + i2p::tunnel::eDeliveryTypeTunnel, + m_CurrentRemoteLease.tunnelGateway, m_CurrentRemoteLease.tunnelID, + msg + }); + m_NumSentBytes += it->GetLength (); + } + m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs); + } + else + LogPrint ("All leases are expired"); + } + + void Stream::ScheduleResend () + { + m_ResendTimer.cancel (); + m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_TIMEOUT)); + m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, + shared_from_this (), std::placeholders::_1)); + } + + void Stream::HandleResendTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + std::vector packets; + for (auto it : m_SentPackets) + { + it->numResendAttempts++; + if (it->numResendAttempts <= MAX_NUM_RESEND_ATTEMPTS) + packets.push_back (it); + else + { + LogPrint (eLogWarning, "Packet ", it->GetSeqn (), "was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts. Terminate"); + m_IsOpen = false; + m_IsReset = true; + m_ReceiveTimer.cancel (); + return; + } + } + if (packets.size () > 0) + { + m_CurrentOutboundTunnel = nullptr; // pick another outbound tunnel + UpdateCurrentRemoteLease (); // pick another lease + SendPackets (packets); + } + ScheduleResend (); + } + } + + void Stream::HandleAckSendTimer (const boost::system::error_code& ecode) + { + if (m_IsAckSendScheduled) + { + if (m_IsOpen) + SendQuickAck (); + m_IsAckSendScheduled = false; + } + } + + void Stream::UpdateCurrentRemoteLease () + { + if (!m_RemoteLeaseSet) + { + m_RemoteLeaseSet = m_LocalDestination.GetOwner ().FindLeaseSet (m_RemoteIdentity.GetIdentHash ()); + if (!m_RemoteLeaseSet) + LogPrint ("LeaseSet ", m_RemoteIdentity.GetIdentHash ().ToBase64 (), " not found"); + } + if (m_RemoteLeaseSet) + { + if (!m_RoutingSession) + m_RoutingSession = m_LocalDestination.GetOwner ().GetRoutingSession (*m_RemoteLeaseSet, 32); + auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (); + if (!leases.empty ()) + { + uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1); + m_CurrentRemoteLease = leases[i]; + } + else + { + m_RemoteLeaseSet = m_LocalDestination.GetOwner ().FindLeaseSet (m_RemoteIdentity.GetIdentHash ()); // re-request expired + m_CurrentRemoteLease.endDate = 0; + } + } + else + m_CurrentRemoteLease.endDate = 0; + } + + I2NPMessage * Stream::CreateDataMessage (const uint8_t * payload, size_t len) + { + I2NPMessage * msg = NewI2NPShortMessage (); + CryptoPP::Gzip compressor; + if (len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE) + compressor.SetDeflateLevel (CryptoPP::Gzip::MIN_DEFLATE_LEVEL); + else + compressor.SetDeflateLevel (CryptoPP::Gzip::DEFAULT_DEFLATE_LEVEL); + compressor.Put (payload, len); + compressor.MessageEnd(); + int size = compressor.MaxRetrievable (); + uint8_t * buf = msg->GetPayload (); + htobe32buf (buf, size); // length + buf += 4; + compressor.Get (buf, size); + htobuf16(buf + 4, 0); // source port + htobe16buf (buf + 6, m_Port); // destination port + buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol + msg->len += size + 4; + FillI2NPMessageHeader (msg, eI2NPData); + + return msg; + } + + void StreamingDestination::Start () + { + } + + void StreamingDestination::Stop () + { + ResetAcceptor (); + { + std::unique_lock l(m_StreamsMutex); + m_Streams.clear (); + } + } + + void StreamingDestination::HandleNextPacket (Packet * packet) + { + uint32_t sendStreamID = packet->GetSendStreamID (); + if (sendStreamID) + { + auto it = m_Streams.find (sendStreamID); + if (it != m_Streams.end ()) + it->second->HandleNextPacket (packet); + else + { + LogPrint ("Unknown stream sendStreamID=", sendStreamID); + delete packet; + } + } + else + { + if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream + { + auto incomingStream = CreateNewIncomingStream (); + incomingStream->HandleNextPacket (packet); + if (m_Acceptor != nullptr) + m_Acceptor (incomingStream); + else + { + LogPrint ("Acceptor for incoming stream is not set"); + DeleteStream (incomingStream); + } + } + else // follow on packet without SYN + { + uint32_t receiveStreamID = packet->GetReceiveStreamID (); + for (auto it: m_Streams) + if (it.second->GetSendStreamID () == receiveStreamID) + { + // found + it.second->HandleNextPacket (packet); + return; + } + // TODO: should queue it up + LogPrint ("Unknown stream receiveStreamID=", receiveStreamID); + delete packet; + } + } + } + + std::shared_ptr StreamingDestination::CreateNewOutgoingStream (const i2p::data::LeaseSet& remote, int port) + { + auto s = std::make_shared (m_Owner.GetService (), *this, remote, port); + std::unique_lock l(m_StreamsMutex); + m_Streams[s->GetRecvStreamID ()] = s; + return s; + } + + std::shared_ptr StreamingDestination::CreateNewIncomingStream () + { + auto s = std::make_shared (m_Owner.GetService (), *this); + std::unique_lock l(m_StreamsMutex); + m_Streams[s->GetRecvStreamID ()] = s; + return s; + } + + void StreamingDestination::DeleteStream (std::shared_ptr stream) + { + if (stream) + { + std::unique_lock l(m_StreamsMutex); + auto it = m_Streams.find (stream->GetRecvStreamID ()); + if (it != m_Streams.end ()) + m_Streams.erase (it); + } + } + + void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len) + { + // unzip it + CryptoPP::Gunzip decompressor; + decompressor.Put (buf, len); + decompressor.MessageEnd(); + Packet * uncompressed = new Packet; + uncompressed->offset = 0; + uncompressed->len = decompressor.MaxRetrievable (); + if (uncompressed->len <= MAX_PACKET_SIZE) + { + decompressor.Get (uncompressed->buf, uncompressed->len); + HandleNextPacket (uncompressed); + } + else + { + LogPrint ("Received packet size ", uncompressed->len, " exceeds max packet size. Skipped"); + delete uncompressed; + } + } +} +} diff --git a/Streaming.h b/Streaming.h new file mode 100644 index 00000000..69ef124d --- /dev/null +++ b/Streaming.h @@ -0,0 +1,238 @@ +#ifndef STREAMING_H__ +#define STREAMING_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "I2PEndian.h" +#include "Identity.h" +#include "LeaseSet.h" +#include "I2NPProtocol.h" +#include "Garlic.h" +#include "Tunnel.h" + +namespace i2p +{ +namespace client +{ + class ClientDestination; +} +namespace stream +{ + const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001; + const uint16_t PACKET_FLAG_CLOSE = 0x0002; + const uint16_t PACKET_FLAG_RESET = 0x0004; + const uint16_t PACKET_FLAG_SIGNATURE_INCLUDED = 0x0008; + const uint16_t PACKET_FLAG_SIGNATURE_REQUESTED = 0x0010; + const uint16_t PACKET_FLAG_FROM_INCLUDED = 0x0020; + const uint16_t PACKET_FLAG_DELAY_REQUESTED = 0x0040; + const uint16_t PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED = 0x0080; + const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100; + const uint16_t PACKET_FLAG_ECHO = 0x0200; + const uint16_t PACKET_FLAG_NO_ACK = 0x0400; + + const size_t STREAMING_MTU = 1730; + const size_t MAX_PACKET_SIZE = 4096; + const size_t COMPRESSION_THRESHOLD_SIZE = 66; + const int RESEND_TIMEOUT = 10; // in seconds + const int ACK_SEND_TIMEOUT = 200; // in milliseconds + const int MAX_NUM_RESEND_ATTEMPTS = 5; + + struct Packet + { + size_t len, offset; + uint8_t buf[MAX_PACKET_SIZE]; + int numResendAttempts; + + Packet (): len (0), offset (0), numResendAttempts (0) {}; + uint8_t * GetBuffer () { return buf + offset; }; + size_t GetLength () const { return len - offset; }; + + uint32_t GetSendStreamID () const { return bufbe32toh (buf); }; + uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); }; + uint32_t GetSeqn () const { return bufbe32toh (buf + 8); }; + uint32_t GetAckThrough () const { return bufbe32toh (buf + 12); }; + uint8_t GetNACKCount () const { return buf[16]; }; + uint32_t GetNACK (int i) const { return bufbe32toh (buf + 17 + 4 * i); }; + const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags + uint16_t GetFlags () const { return bufbe16toh (GetOption () - 2); }; + uint16_t GetOptionSize () const { return bufbe16toh (GetOption ()); }; + const uint8_t * GetOptionData () const { return GetOption () + 2; }; + const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); }; + + bool IsSYN () const { return GetFlags () & PACKET_FLAG_SYNCHRONIZE; }; + bool IsNoAck () const { return GetFlags () & PACKET_FLAG_NO_ACK; }; + }; + + struct PacketCmp + { + bool operator() (const Packet * p1, const Packet * p2) const + { + return p1->GetSeqn () < p2->GetSeqn (); + }; + }; + + class StreamingDestination; + class Stream: public std::enable_shared_from_this + { + public: + + Stream (boost::asio::io_service& service, StreamingDestination& local, + const i2p::data::LeaseSet& remote, int port = 0); // outgoing + Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming + + ~Stream (); + uint32_t GetSendStreamID () const { return m_SendStreamID; }; + uint32_t GetRecvStreamID () const { return m_RecvStreamID; }; + const i2p::data::LeaseSet * GetRemoteLeaseSet () const { return m_RemoteLeaseSet; }; + const i2p::data::IdentityEx& GetRemoteIdentity () const { return m_RemoteIdentity; }; + bool IsOpen () const { return m_IsOpen; }; + bool IsEstablished () const { return m_SendStreamID; }; + StreamingDestination& GetLocalDestination () { return m_LocalDestination; }; + + void HandleNextPacket (Packet * packet); + size_t Send (const uint8_t * buf, size_t len); + + template + void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); + size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; + + void Close (); + void Cancel () { m_ReceiveTimer.cancel (); }; + + size_t GetNumSentBytes () const { return m_NumSentBytes; }; + size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; + size_t GetSendQueueSize () const { return m_SentPackets.size (); }; + size_t GetReceiveQueueSize () const { return m_ReceiveQueue.size (); }; + + private: + + void SendQuickAck (); + bool SendPacket (Packet * packet); + void PostPackets (const std::vector packets); + void SendPackets (const std::vector& packets); + + void SavePacket (Packet * packet); + void ProcessPacket (Packet * packet); + void ProcessAck (Packet * packet); + size_t ConcatenatePackets (uint8_t * buf, size_t len); + + void UpdateCurrentRemoteLease (); + + template + void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler); + + void ScheduleResend (); + void HandleResendTimer (const boost::system::error_code& ecode); + void HandleAckSendTimer (const boost::system::error_code& ecode); + + I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len); + + private: + + boost::asio::io_service& m_Service; + uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber; + int32_t m_LastReceivedSequenceNumber; + bool m_IsOpen, m_IsReset, m_IsAckSendScheduled; + StreamingDestination& m_LocalDestination; + i2p::data::IdentityEx m_RemoteIdentity; + const i2p::data::LeaseSet * m_RemoteLeaseSet; + i2p::garlic::GarlicRoutingSession * m_RoutingSession; + i2p::data::Lease m_CurrentRemoteLease; + i2p::tunnel::OutboundTunnel * m_CurrentOutboundTunnel; + std::queue m_ReceiveQueue; + std::set m_SavedPackets; + std::set m_SentPackets; + boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer; + size_t m_NumSentBytes, m_NumReceivedBytes; + uint16_t m_Port; + }; + + class StreamingDestination + { + public: + + typedef std::function)> Acceptor; + + StreamingDestination (i2p::client::ClientDestination& owner): m_Owner (owner) {}; + ~StreamingDestination () {}; + + void Start (); + void Stop (); + + std::shared_ptr CreateNewOutgoingStream (const i2p::data::LeaseSet& remote, int port = 0); + void DeleteStream (std::shared_ptr stream); + void SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; }; + void ResetAcceptor () { m_Acceptor = nullptr; }; + bool IsAcceptorSet () const { return m_Acceptor != nullptr; }; + i2p::client::ClientDestination& GetOwner () { return m_Owner; }; + + void HandleDataMessagePayload (const uint8_t * buf, size_t len); + + private: + + void HandleNextPacket (Packet * packet); + std::shared_ptr CreateNewIncomingStream (); + + private: + + i2p::client::ClientDestination& m_Owner; + std::mutex m_StreamsMutex; + std::map > m_Streams; + Acceptor m_Acceptor; + + public: + + // for HTTP only + const decltype(m_Streams)& GetStreams () const { return m_Streams; }; + }; + +//------------------------------------------------- + + template + void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout) + { + if (!m_ReceiveQueue.empty ()) + { + auto s = shared_from_this(); + m_Service.post ([=](void) { s->HandleReceiveTimer ( + boost::asio::error::make_error_code (boost::asio::error::operation_aborted), + buffer, handler); }); + } + else + { + m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout)); + auto s = shared_from_this(); + m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode) + { s->HandleReceiveTimer (ecode, buffer, handler); }); + } + } + + template + void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler) + { + size_t received = ConcatenatePackets (boost::asio::buffer_cast(buffer), boost::asio::buffer_size(buffer)); + if (ecode == boost::asio::error::operation_aborted) + { + // timeout not expired + if (m_IsOpen) + // no error + handler (boost::system::error_code (), received); + else + // socket closed + handler (m_IsReset ? boost::asio::error::make_error_code (boost::asio::error::connection_reset) : + boost::asio::error::make_error_code (boost::asio::error::operation_aborted), received); + } + else + // timeout expired + handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received); + } +} +} + +#endif diff --git a/Timestamp.h b/Timestamp.h new file mode 100644 index 00000000..d48cb164 --- /dev/null +++ b/Timestamp.h @@ -0,0 +1,32 @@ +#ifndef TIMESTAMP_H__ +#define TIMESTAMP_H__ + +#include +#include + +namespace i2p +{ +namespace util +{ + inline uint64_t GetMillisecondsSinceEpoch () + { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count (); + } + + inline uint32_t GetHoursSinceEpoch () + { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count (); + } + + inline uint64_t GetSecondsSinceEpoch () + { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count (); + } +} +} + +#endif + diff --git a/TransitTunnel.cpp b/TransitTunnel.cpp new file mode 100644 index 00000000..1f755be5 --- /dev/null +++ b/TransitTunnel.cpp @@ -0,0 +1,85 @@ +#include +#include "I2PEndian.h" +#include "Log.h" +#include "RouterContext.h" +#include "I2NPProtocol.h" +#include "Tunnel.h" +#include "Transports.h" +#include "TransitTunnel.h" + +namespace i2p +{ +namespace tunnel +{ + TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey): + m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID), + m_NextIdent (nextIdent), m_NumTransmittedBytes (0) + { + m_Encryption.SetKeys (layerKey, ivKey); + } + + void TransitTunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg) + { + m_Encryption.Encrypt (tunnelMsg->GetPayload () + 4); + } + + void TransitTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg) + { + EncryptTunnelMsg (tunnelMsg); + + LogPrint ("TransitTunnel: ",m_TunnelID,"->", m_NextTunnelID); + m_NumTransmittedBytes += tunnelMsg->GetLength (); + htobe32buf (tunnelMsg->GetPayload (), m_NextTunnelID); + FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData); + + i2p::transport::transports.SendMessage (m_NextIdent, tunnelMsg); + } + + void TransitTunnel::SendTunnelDataMsg (i2p::I2NPMessage * msg) + { + LogPrint ("We are not a gateway for transit tunnel ", m_TunnelID); + i2p::DeleteI2NPMessage (msg); + } + + void TransitTunnelGateway::SendTunnelDataMsg (i2p::I2NPMessage * msg) + { + TunnelMessageBlock block; + block.deliveryType = eDeliveryTypeLocal; + block.data = msg; + std::unique_lock l(m_SendMutex); + m_Gateway.SendTunnelDataMsg (block); + } + + void TransitTunnelEndpoint::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg) + { + EncryptTunnelMsg (tunnelMsg); + + LogPrint ("TransitTunnel endpoint for ", GetTunnelID ()); + m_Endpoint.HandleDecryptedTunnelDataMsg (tunnelMsg); + } + + TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey, + bool isGateway, bool isEndpoint) + { + if (isEndpoint) + { + LogPrint ("TransitTunnel endpoint: ", receiveTunnelID, " created"); + return new TransitTunnelEndpoint (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); + } + else if (isGateway) + { + LogPrint ("TransitTunnel gateway: ", receiveTunnelID, " created"); + return new TransitTunnelGateway (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); + } + else + { + LogPrint ("TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created"); + return new TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); + } + } +} +} diff --git a/TransitTunnel.h b/TransitTunnel.h new file mode 100644 index 00000000..e71c16e1 --- /dev/null +++ b/TransitTunnel.h @@ -0,0 +1,88 @@ +#ifndef TRANSIT_TUNNEL_H__ +#define TRANSIT_TUNNEL_H__ + +#include +#include +#include "aes.h" +#include "I2NPProtocol.h" +#include "TunnelEndpoint.h" +#include "TunnelGateway.h" +#include "TunnelBase.h" + +namespace i2p +{ +namespace tunnel +{ + class TransitTunnel: public TunnelBase // tunnel patricipant + { + public: + + TransitTunnel (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey); + + virtual void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg); + virtual void SendTunnelDataMsg (i2p::I2NPMessage * msg); + virtual size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; }; + + uint32_t GetTunnelID () const { return m_TunnelID; }; + + // implements TunnelBase + void EncryptTunnelMsg (I2NPMessage * tunnelMsg); + uint32_t GetNextTunnelID () const { return m_NextTunnelID; }; + const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; }; + + private: + + uint32_t m_TunnelID, m_NextTunnelID; + i2p::data::IdentHash m_NextIdent; + size_t m_NumTransmittedBytes; + + i2p::crypto::TunnelEncryption m_Encryption; + }; + + class TransitTunnelGateway: public TransitTunnel + { + public: + + TransitTunnelGateway (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey): + TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, + layerKey, ivKey), m_Gateway(this) {}; + + void SendTunnelDataMsg (i2p::I2NPMessage * msg); + size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); }; + + private: + + std::mutex m_SendMutex; + TunnelGateway m_Gateway; + }; + + class TransitTunnelEndpoint: public TransitTunnel + { + public: + + TransitTunnelEndpoint (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey): + TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey), + m_Endpoint (false) {}; // transit endpoint is always outbound + + void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg); + size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); } + + private: + + TunnelEndpoint m_Endpoint; + }; + + TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID, + const uint8_t * nextIdent, uint32_t nextTunnelID, + const uint8_t * layerKey,const uint8_t * ivKey, + bool isGateway, bool isEndpoint); +} +} + +#endif diff --git a/TransportSession.h b/TransportSession.h new file mode 100644 index 00000000..f4079590 --- /dev/null +++ b/TransportSession.h @@ -0,0 +1,76 @@ +#ifndef TRANSPORT_SESSION_H__ +#define TRANSPORT_SESSION_H__ + +#include +#include +#include +#include "Identity.h" +#include "RouterInfo.h" + +namespace i2p +{ +namespace transport +{ + struct DHKeysPair // transient keys for transport sessions + { + uint8_t publicKey[256]; + uint8_t privateKey[256]; + }; + + class SignedData + { + public: + + SignedData () {}; + void Insert (const uint8_t * buf, size_t len) + { + m_Stream.write ((char *)buf, len); + } + + template + void Insert (T t) + { + m_Stream.write ((char *)&t, sizeof (T)); + } + + bool Verify (const i2p::data::IdentityEx& ident, const uint8_t * signature) const + { + return ident.Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); + } + + void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const + { + keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); + } + + private: + + std::stringstream m_Stream; + }; + + class TransportSession + { + public: + + TransportSession (std::shared_ptr in_RemoteRouter): + m_RemoteRouter (in_RemoteRouter), m_DHKeysPair (nullptr) + { + if (m_RemoteRouter) + m_RemoteIdentity = m_RemoteRouter->GetRouterIdentity (); + } + + virtual ~TransportSession () { delete m_DHKeysPair; }; + + std::shared_ptr GetRemoteRouter () { return m_RemoteRouter; }; + const i2p::data::IdentityEx& GetRemoteIdentity () { return m_RemoteIdentity; }; + + protected: + + std::shared_ptr m_RemoteRouter; + i2p::data::IdentityEx m_RemoteIdentity; + DHKeysPair * m_DHKeysPair; // X - for client and Y - for server + }; +} +} + +#endif diff --git a/Transports.cpp b/Transports.cpp new file mode 100644 index 00000000..3992290a --- /dev/null +++ b/Transports.cpp @@ -0,0 +1,401 @@ +#include +#include +#include "Log.h" +#include "CryptoConst.h" +#include "RouterContext.h" +#include "I2NPProtocol.h" +#include "NetDb.h" +#include "Transports.h" + +using namespace i2p::data; + +namespace i2p +{ +namespace transport +{ + DHKeysPairSupplier::DHKeysPairSupplier (int size): + m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr) + { + } + + DHKeysPairSupplier::~DHKeysPairSupplier () + { + Stop (); + } + + void DHKeysPairSupplier::Start () + { + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&DHKeysPairSupplier::Run, this)); + } + + void DHKeysPairSupplier::Stop () + { + m_IsRunning = false; + m_Acquired.notify_one (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = 0; + } + } + + void DHKeysPairSupplier::Run () + { + while (m_IsRunning) + { + int num; + while ((num = m_QueueSize - m_Queue.size ()) > 0) + CreateDHKeysPairs (num); + std::unique_lock l(m_AcquiredMutex); + m_Acquired.wait (l); // wait for element gets aquired + } + } + + void DHKeysPairSupplier::CreateDHKeysPairs (int num) + { + if (num > 0) + { + CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); + for (int i = 0; i < num; i++) + { + i2p::transport::DHKeysPair * pair = new i2p::transport::DHKeysPair (); + dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey); + std::unique_lock l(m_AcquiredMutex); + m_Queue.push (pair); + } + } + } + + DHKeysPair * DHKeysPairSupplier::Acquire () + { + if (!m_Queue.empty ()) + { + std::unique_lock l(m_AcquiredMutex); + auto pair = m_Queue.front (); + m_Queue.pop (); + m_Acquired.notify_one (); + return pair; + } + else // queue is empty, create new + { + DHKeysPair * pair = new DHKeysPair (); + CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg); + dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey); + return pair; + } + } + + void DHKeysPairSupplier::Return (DHKeysPair * pair) + { + std::unique_lock l(m_AcquiredMutex); + m_Queue.push (pair); + } + + Transports transports; + + Transports::Transports (): + m_Thread (nullptr), m_Work (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr), + m_SSUServer (nullptr), m_DHKeysPairSupplier (5) // 5 pre-generated keys + { + } + + Transports::~Transports () + { + Stop (); + } + + void Transports::Start () + { + m_DHKeysPairSupplier.Start (); + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&Transports::Run, this)); + // create acceptors + auto addresses = context.GetRouterInfo ().GetAddresses (); + for (auto& address : addresses) + { + if (address.transportStyle == RouterInfo::eTransportNTCP && address.host.is_v4 ()) + { + m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service, + boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port)); + + LogPrint ("Start listening TCP port ", address.port); + auto conn = std::make_shared(m_Service); + m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this, + conn, boost::asio::placeholders::error)); + + if (context.SupportsV6 ()) + { + m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service); + m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6()); + m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true)); + m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address.port)); + m_NTCPV6Acceptor->listen (); + + LogPrint ("Start listening V6 TCP port ", address.port); + auto conn = std::make_shared (m_Service); + m_NTCPV6Acceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAcceptV6, + this, conn, boost::asio::placeholders::error)); + } + } + else if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ()) + { + if (!m_SSUServer) + { + m_SSUServer = new SSUServer (address.port); + LogPrint ("Start listening UDP port ", address.port); + m_SSUServer->Start (); + DetectExternalIP (); + } + else + LogPrint ("SSU server already exists"); + } + } + } + + void Transports::Stop () + { + if (m_SSUServer) + { + m_SSUServer->Stop (); + delete m_SSUServer; + m_SSUServer = nullptr; + } + m_NTCPSessions.clear (); + + delete m_NTCPAcceptor; + m_NTCPAcceptor = nullptr; + delete m_NTCPV6Acceptor; + m_NTCPV6Acceptor = nullptr; + + m_DHKeysPairSupplier.Stop (); + m_IsRunning = false; + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + + void Transports::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint ("Transports: ", ex.what ()); + } + } + } + + void Transports::AddNTCPSession (std::shared_ptr session) + { + if (session) + m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session; + } + + void Transports::RemoveNTCPSession (std::shared_ptr session) + { + if (session) + m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ()); + } + + void Transports::HandleAccept (std::shared_ptr conn, const boost::system::error_code& error) + { + if (!error) + { + LogPrint ("Connected from ", conn->GetSocket ().remote_endpoint().address ().to_string ()); + conn->ServerLogin (); + } + + + if (error != boost::asio::error::operation_aborted) + { + conn = std::make_shared (m_Service); + m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this, + conn, boost::asio::placeholders::error)); + } + } + + void Transports::HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error) + { + if (!error) + { + LogPrint ("Connected from ", conn->GetSocket ().remote_endpoint().address ().to_string ()); + conn->ServerLogin (); + } + + if (error != boost::asio::error::operation_aborted) + { + conn = std::make_shared (m_Service); + m_NTCPV6Acceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAcceptV6, this, + conn, boost::asio::placeholders::error)); + } + } + + void Transports::Connect (const boost::asio::ip::address& address, int port, std::shared_ptr conn) + { + LogPrint ("Connecting to ", address ,":", port); + conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), + boost::bind (&Transports::HandleConnect, this, boost::asio::placeholders::error, conn)); + } + + void Transports::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn) + { + if (ecode) + { + LogPrint ("Connect error: ", ecode.message ()); + if (ecode != boost::asio::error::operation_aborted) + { + i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ().GetIdentHash (), true); + conn->Terminate (); + } + } + else + { + LogPrint ("Connected"); + if (conn->GetSocket ().local_endpoint ().protocol () == boost::asio::ip::tcp::v6()) // ipv6 + context.UpdateNTCPV6Address (conn->GetSocket ().local_endpoint ().address ()); + conn->ClientLogin (); + } + } + + std::shared_ptr Transports::GetNextNTCPSession () + { + for (auto session: m_NTCPSessions) + if (session.second->IsEstablished ()) + return session.second; + return 0; + } + + std::shared_ptr Transports::FindNTCPSession (const i2p::data::IdentHash& ident) + { + auto it = m_NTCPSessions.find (ident); + if (it != m_NTCPSessions.end ()) + return it->second; + return 0; + } + + void Transports::SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg) + { + if (ident == i2p::context.GetRouterInfo ().GetIdentHash ()) + // we send it to ourself + i2p::HandleI2NPMessage (msg); + else + m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg)); + } + + void Transports::PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg) + { + auto session = FindNTCPSession (ident); + if (session) + session->SendI2NPMessage (msg); + else + { + auto r = netdb.FindRouter (ident); + if (r) + { + auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (r) : nullptr; + if (ssuSession) + ssuSession->SendI2NPMessage (msg); + else + { + // existing session not found. create new + // try NTCP first if message size < 16K + auto address = r->GetNTCPAddress (!context.SupportsV6 ()); + if (address && !r->UsesIntroducer () && !r->IsUnreachable () && msg->GetLength () < NTCP_MAX_MESSAGE_SIZE) + { + auto s = std::make_shared (m_Service, r); + AddNTCPSession (s); + s->SendI2NPMessage (msg); + Connect (address->host, address->port, s); + } + else + { + // then SSU + auto s = m_SSUServer ? m_SSUServer->GetSession (r) : nullptr; + if (s) + s->SendI2NPMessage (msg); + else + { + LogPrint ("No NTCP and SSU addresses available"); + DeleteI2NPMessage (msg); + } + } + } + } + else + { + LogPrint ("Router not found. Requested"); + i2p::data::netdb.RequestDestination (ident); + auto resendTimer = new boost::asio::deadline_timer (m_Service); + resendTimer->expires_from_now (boost::posix_time::seconds(5)); // 5 seconds + resendTimer->async_wait (boost::bind (&Transports::HandleResendTimer, + this, boost::asio::placeholders::error, resendTimer, ident, msg)); + } + } + } + + void Transports::HandleResendTimer (const boost::system::error_code& ecode, + boost::asio::deadline_timer * timer, const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg) + { + auto r = netdb.FindRouter (ident); + if (r) + { + LogPrint ("Router found. Sending message"); + PostMessage (ident, msg); + } + else + { + LogPrint ("Router not found. Failed to send message"); + DeleteI2NPMessage (msg); + } + delete timer; + } + + void Transports::CloseSession (std::shared_ptr router) + { + if (!router) return; + m_Service.post (boost::bind (&Transports::PostCloseSession, this, router)); + } + + void Transports::PostCloseSession (std::shared_ptr router) + { + auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr; + if (ssuSession) // try SSU first + { + m_SSUServer->DeleteSession (ssuSession); + LogPrint ("SSU session closed"); + } + // TODO: delete NTCP + } + + void Transports::DetectExternalIP () + { + for (int i = 0; i < 5; i++) + { + auto router = i2p::data::netdb.GetRandomRouter (); + if (router && router->IsSSU () && m_SSUServer) + m_SSUServer->GetSession (router, true); // peer test + } + } + + DHKeysPair * Transports::GetNextDHKeysPair () + { + return m_DHKeysPairSupplier.Acquire (); + } + + void Transports::ReuseDHKeysPair (DHKeysPair * pair) + { + m_DHKeysPairSupplier.Return (pair); + } +} +} + diff --git a/Transports.h b/Transports.h new file mode 100644 index 00000000..3b5b2d4e --- /dev/null +++ b/Transports.h @@ -0,0 +1,115 @@ +#ifndef TRANSPORTS_H__ +#define TRANSPORTS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "TransportSession.h" +#include "NTCPSession.h" +#include "SSU.h" +#include "RouterInfo.h" +#include "I2NPProtocol.h" +#include "Identity.h" + +namespace i2p +{ +namespace transport +{ + class DHKeysPairSupplier + { + public: + + DHKeysPairSupplier (int size); + ~DHKeysPairSupplier (); + void Start (); + void Stop (); + DHKeysPair * Acquire (); + void Return (DHKeysPair * pair); + + private: + + void Run (); + void CreateDHKeysPairs (int num); + + private: + + const int m_QueueSize; + std::queue m_Queue; + + bool m_IsRunning; + std::thread * m_Thread; + std::condition_variable m_Acquired; + std::mutex m_AcquiredMutex; + CryptoPP::AutoSeededRandomPool m_Rnd; + }; + + class Transports + { + public: + + Transports (); + ~Transports (); + + void Start (); + void Stop (); + + boost::asio::io_service& GetService () { return m_Service; }; + i2p::transport::DHKeysPair * GetNextDHKeysPair (); + void ReuseDHKeysPair (DHKeysPair * pair); + + void AddNTCPSession (std::shared_ptr session); + void RemoveNTCPSession (std::shared_ptr session); + + std::shared_ptr GetNextNTCPSession (); + std::shared_ptr FindNTCPSession (const i2p::data::IdentHash& ident); + + void SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg); + void CloseSession (std::shared_ptr router); + + private: + + void Run (); + void HandleAccept (std::shared_ptr conn, const boost::system::error_code& error); + void HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error); + void HandleResendTimer (const boost::system::error_code& ecode, boost::asio::deadline_timer * timer, + const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg); + void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg); + void PostCloseSession (std::shared_ptr router); + + void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr conn); + void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn); + + void DetectExternalIP (); + + private: + + bool m_IsRunning; + std::thread * m_Thread; + boost::asio::io_service m_Service; + boost::asio::io_service::work m_Work; + boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor; + + std::map > m_NTCPSessions; + SSUServer * m_SSUServer; + + DHKeysPairSupplier m_DHKeysPairSupplier; + + public: + + // for HTTP only + const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; }; + const SSUServer * GetSSUServer () const { return m_SSUServer; }; + }; + + extern Transports transports; +} +} + +#endif diff --git a/Tunnel.cpp b/Tunnel.cpp new file mode 100644 index 00000000..1e6aacd2 --- /dev/null +++ b/Tunnel.cpp @@ -0,0 +1,614 @@ +#include +#include "I2PEndian.h" +#include +#include +#include +#include +#include "RouterContext.h" +#include "Log.h" +#include "Timestamp.h" +#include "I2NPProtocol.h" +#include "Transports.h" +#include "NetDb.h" +#include "Tunnel.h" + +namespace i2p +{ +namespace tunnel +{ + + Tunnel::Tunnel (TunnelConfig * config): + m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending) + { + } + + Tunnel::~Tunnel () + { + delete m_Config; + } + + void Tunnel::Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel) + { + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + auto numHops = m_Config->GetNumHops (); + int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops; + I2NPMessage * msg = NewI2NPMessage (); + *msg->GetPayload () = numRecords; + msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 1; + + // shuffle records + std::vector recordIndicies; + for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); + std::random_shuffle (recordIndicies.begin(), recordIndicies.end()); + + // create real records + uint8_t * records = msg->GetPayload () + 1; + TunnelHopConfig * hop = m_Config->GetFirstHop (); + int i = 0; + while (hop) + { + int idx = recordIndicies[i]; + hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, + hop->next ? rnd.GenerateWord32 () : replyMsgID); // we set replyMsgID for last hop only + hop->recordIndex = idx; + i++; + hop = hop->next; + } + // fill up fake records with random data + for (int i = numHops; i < numRecords; i++) + { + int idx = recordIndicies[i]; + rnd.GenerateBlock (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE); + } + + // decrypt real records + i2p::crypto::CBCDecryption decryption; + hop = m_Config->GetLastHop ()->prev; + while (hop) + { + decryption.SetKey (hop->replyKey); + // decrypt records after current hop + TunnelHopConfig * hop1 = hop->next; + while (hop1) + { + decryption.SetIV (hop->replyIV); + uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE; + decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); + hop1 = hop1->next; + } + hop = hop->prev; + } + FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild); + + // send message + if (outboundTunnel) + outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg); + else + i2p::transport::transports.SendMessage (GetNextIdentHash (), msg); + } + + bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) + { + LogPrint ("TunnelBuildResponse ", (int)msg[0], " records."); + + i2p::crypto::CBCDecryption decryption; + TunnelHopConfig * hop = m_Config->GetLastHop (); + while (hop) + { + decryption.SetKey (hop->replyKey); + // decrypt records before and including current hop + TunnelHopConfig * hop1 = hop; + while (hop1) + { + auto idx = hop1->recordIndex; + if (idx >= 0 && idx < msg[0]) + { + uint8_t * record = msg + 1 + idx*TUNNEL_BUILD_RECORD_SIZE; + decryption.SetIV (hop->replyIV); + decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record); + } + else + LogPrint ("Tunnel hop index ", idx, " is out of range"); + hop1 = hop1->prev; + } + hop = hop->prev; + } + + bool established = true; + hop = m_Config->GetFirstHop (); + while (hop) + { + const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE; + uint8_t ret = record[BUILD_RESPONSE_RECORD_RET_OFFSET]; + LogPrint ("Ret code=", (int)ret); + if (ret) + // if any of participants declined the tunnel is not established + established = false; + hop = hop->next; + } + if (established) + { + // change reply keys to layer keys + hop = m_Config->GetFirstHop (); + while (hop) + { + hop->decryption.SetKeys (hop->layerKey, hop->ivKey); + hop = hop->next; + } + } + if (established) m_State = eTunnelStateEstablished; + return established; + } + + void Tunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg) + { + uint8_t * payload = tunnelMsg->GetPayload () + 4; + TunnelHopConfig * hop = m_Config->GetLastHop (); + while (hop) + { + hop->decryption.Decrypt (payload); + hop = hop->prev; + } + } + + void InboundTunnel::HandleTunnelDataMsg (I2NPMessage * msg) + { + if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive + msg->from = this; + EncryptTunnelMsg (msg); + m_Endpoint.HandleDecryptedTunnelDataMsg (msg); + } + + void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg) + { + TunnelMessageBlock block; + if (gwHash) + { + block.hash = gwHash; + if (gwTunnel) + { + block.deliveryType = eDeliveryTypeTunnel; + block.tunnelID = gwTunnel; + } + else + block.deliveryType = eDeliveryTypeRouter; + } + else + block.deliveryType = eDeliveryTypeLocal; + block.data = msg; + + std::unique_lock l(m_SendMutex); + m_Gateway.SendTunnelDataMsg (block); + } + + void OutboundTunnel::SendTunnelDataMsg (const std::vector& msgs) + { + std::unique_lock l(m_SendMutex); + for (auto& it : msgs) + m_Gateway.PutTunnelDataMsg (it); + m_Gateway.SendBuffer (); + } + + Tunnels tunnels; + + Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), m_ExploratoryPool (nullptr) + { + } + + Tunnels::~Tunnels () + { + for (auto& it : m_OutboundTunnels) + delete it; + m_OutboundTunnels.clear (); + + for (auto& it : m_InboundTunnels) + delete it.second; + m_InboundTunnels.clear (); + + for (auto& it : m_TransitTunnels) + delete it.second; + m_TransitTunnels.clear (); + + /*for (auto& it : m_PendingTunnels) + delete it.second; + m_PendingTunnels.clear ();*/ + + for (auto& it: m_Pools) + delete it; + m_Pools.clear (); + } + + InboundTunnel * Tunnels::GetInboundTunnel (uint32_t tunnelID) + { + auto it = m_InboundTunnels.find(tunnelID); + if (it != m_InboundTunnels.end ()) + return it->second; + return nullptr; + } + + TransitTunnel * Tunnels::GetTransitTunnel (uint32_t tunnelID) + { + auto it = m_TransitTunnels.find(tunnelID); + if (it != m_TransitTunnels.end ()) + return it->second; + return nullptr; + } + + Tunnel * Tunnels::GetPendingTunnel (uint32_t replyMsgID) + { + auto it = m_PendingTunnels.find(replyMsgID); + if (it != m_PendingTunnels.end () && it->second->GetState () == eTunnelStatePending) + { + it->second->SetState (eTunnelStateBuildReplyReceived); + return it->second; + } + return nullptr; + } + + InboundTunnel * Tunnels::GetNextInboundTunnel () + { + InboundTunnel * tunnel = nullptr; + size_t minReceived = 0; + std::unique_lock l(m_InboundTunnelsMutex); + for (auto it : m_InboundTunnels) + { + if (!it.second->IsEstablished ()) continue; + if (!tunnel || it.second->GetNumReceivedBytes () < minReceived) + { + tunnel = it.second; + minReceived = it.second->GetNumReceivedBytes (); + } + } + return tunnel; + } + + OutboundTunnel * Tunnels::GetNextOutboundTunnel () + { + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0; + OutboundTunnel * tunnel = nullptr; + std::unique_lock l(m_OutboundTunnelsMutex); + for (auto it: m_OutboundTunnels) + { + if (it->IsEstablished ()) + { + tunnel = it; + i++; + } + if (i > ind && tunnel) break; + } + return tunnel; + } + + TunnelPool * Tunnels::CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops) + { + auto pool = new TunnelPool (localDestination, numInboundHops, numOutboundHops); + std::unique_lock l(m_PoolsMutex); + m_Pools.push_back (pool); + return pool; + } + + void Tunnels::DeleteTunnelPool (TunnelPool * pool) + { + if (pool) + { + StopTunnelPool (pool); + { + std::unique_lock l(m_PoolsMutex); + m_Pools.remove (pool); + } + for (auto it: m_PendingTunnels) + if (it.second->GetTunnelPool () == pool) + it.second->SetTunnelPool (nullptr); + delete pool; + } + } + + void Tunnels::StopTunnelPool (TunnelPool * pool) + { + if (pool) + { + pool->SetActive (false); + pool->DetachTunnels (); + } + } + + void Tunnels::AddTransitTunnel (TransitTunnel * tunnel) + { + std::unique_lock l(m_TransitTunnelsMutex); + m_TransitTunnels[tunnel->GetTunnelID ()] = tunnel; + } + + void Tunnels::Start () + { + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&Tunnels::Run, this)); + } + + void Tunnels::Stop () + { + m_IsRunning = false; + m_Queue.WakeUp (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = 0; + } + } + + void Tunnels::Run () + { + std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready + + uint64_t lastTs = 0; + while (m_IsRunning) + { + try + { + I2NPMessage * msg = m_Queue.GetNextWithTimeout (1000); // 1 sec + while (msg) + { + uint32_t tunnelID = bufbe32toh (msg->GetPayload ()); + InboundTunnel * tunnel = GetInboundTunnel (tunnelID); + if (tunnel) + tunnel->HandleTunnelDataMsg (msg); + else + { + TransitTunnel * transitTunnel = GetTransitTunnel (tunnelID); + if (transitTunnel) + transitTunnel->HandleTunnelDataMsg (msg); + else + { + LogPrint ("Tunnel ", tunnelID, " not found"); + i2p::DeleteI2NPMessage (msg); + } + } + msg = m_Queue.Get (); + } + + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + if (ts - lastTs >= 15) // manage tunnels every 15 seconds + { + ManageTunnels (); + lastTs = ts; + } + } + catch (std::exception& ex) + { + LogPrint ("Tunnels: ", ex.what ()); + } + } + } + + void Tunnels::ManageTunnels () + { + ManagePendingTunnels (); + ManageInboundTunnels (); + ManageOutboundTunnels (); + ManageTransitTunnels (); + ManageTunnelPools (); + } + + void Tunnels::ManagePendingTunnels () + { + // check pending tunnel. delete failed or timeout + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_PendingTunnels.begin (); it != m_PendingTunnels.end ();) + { + auto tunnel = it->second; + switch (tunnel->GetState ()) + { + case eTunnelStatePending: + if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT) + { + LogPrint ("Pending tunnel build request ", it->first, " timeout. Deleted"); + delete tunnel; + it = m_PendingTunnels.erase (it); + } + else + it++; + break; + case eTunnelStateBuildFailed: + LogPrint ("Pending tunnel build request ", it->first, " failed. Deleted"); + delete tunnel; + it = m_PendingTunnels.erase (it); + break; + case eTunnelStateBuildReplyReceived: + // intermidiate state, will be either established of build failed + it++; + break; + default: + it = m_PendingTunnels.erase (it); + } + } + } + + void Tunnels::ManageOutboundTunnels () + { + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + { + for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();) + { + auto tunnel = *it; + if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + { + LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired"); + { + std::unique_lock l(m_PoolsMutex); + auto pool = tunnel->GetTunnelPool (); + if (pool) + pool->TunnelExpired (tunnel); + } + { + std::unique_lock l(m_OutboundTunnelsMutex); + it = m_OutboundTunnels.erase (it); + } + delete tunnel; + } + else + { + if (tunnel->IsEstablished () && ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + tunnel->SetState (eTunnelStateExpiring); + it++; + } + } + } + + if (m_OutboundTunnels.size () < 5) + { + // trying to create one more oubound tunnel + InboundTunnel * inboundTunnel = GetNextInboundTunnel (); + if (!inboundTunnel) return; + LogPrint ("Creating one hop outbound tunnel..."); + CreateTunnel ( + new TunnelConfig (std::vector > + { + i2p::data::netdb.GetRandomRouter () + }, + inboundTunnel->GetTunnelConfig ())); + } + } + + void Tunnels::ManageInboundTunnels () + { + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + { + for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();) + { + auto tunnel = it->second; + if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + { + LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired"); + { + std::unique_lock l(m_PoolsMutex); + auto pool = tunnel->GetTunnelPool (); + if (pool) + pool->TunnelExpired (tunnel); + } + { + std::unique_lock l(m_InboundTunnelsMutex); + it = m_InboundTunnels.erase (it); + } + delete tunnel; + } + else + { + if (tunnel->IsEstablished () && ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + tunnel->SetState (eTunnelStateExpiring); + it++; + } + } + } + + if (m_InboundTunnels.empty ()) + { + LogPrint ("Creating zero hops inbound tunnel..."); + CreateZeroHopsInboundTunnel (); + if (!m_ExploratoryPool) + m_ExploratoryPool = CreateTunnelPool (&i2p::context, 2, 2); // 2-hop exploratory + return; + } + + if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5) + { + // trying to create one more inbound tunnel + LogPrint ("Creating one hop inbound tunnel..."); + CreateTunnel ( + new TunnelConfig (std::vector > + { + i2p::data::netdb.GetRandomRouter () + })); + } + } + + void Tunnels::ManageTransitTunnels () + { + uint32_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();) + { + if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) + { + LogPrint ("Transit tunnel ", it->second->GetTunnelID (), " expired"); + auto tmp = it->second; + { + std::unique_lock l(m_TransitTunnelsMutex); + it = m_TransitTunnels.erase (it); + } + delete tmp; + } + else + it++; + } + } + + void Tunnels::ManageTunnelPools () + { + std::unique_lock l(m_PoolsMutex); + for (auto it: m_Pools) + { + TunnelPool * pool = it; + if (pool->IsActive ()) + { + pool->CreateTunnels (); + pool->TestTunnels (); + } + } + } + + void Tunnels::PostTunnelData (I2NPMessage * msg) + { + if (msg) m_Queue.Put (msg); + } + + template + TTunnel * Tunnels::CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel) + { + TTunnel * newTunnel = new TTunnel (config); + uint32_t replyMsgID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (); + m_PendingTunnels[replyMsgID] = newTunnel; + newTunnel->Build (replyMsgID, outboundTunnel); + return newTunnel; + } + + void Tunnels::AddOutboundTunnel (OutboundTunnel * newTunnel) + { + std::unique_lock l(m_OutboundTunnelsMutex); + m_OutboundTunnels.push_back (newTunnel); + auto pool = newTunnel->GetTunnelPool (); + if (pool && pool->IsActive ()) + pool->TunnelCreated (newTunnel); + else + newTunnel->SetTunnelPool (nullptr); + } + + void Tunnels::AddInboundTunnel (InboundTunnel * newTunnel) + { + std::unique_lock l(m_InboundTunnelsMutex); + m_InboundTunnels[newTunnel->GetTunnelID ()] = newTunnel; + auto pool = newTunnel->GetTunnelPool (); + if (!pool) + { + // build symmetric outbound tunnel + CreateTunnel (newTunnel->GetTunnelConfig ()->Invert (), GetNextOutboundTunnel ()); + } + else + { + if (pool->IsActive ()) + pool->TunnelCreated (newTunnel); + else + newTunnel->SetTunnelPool (nullptr); + } + } + + + void Tunnels::CreateZeroHopsInboundTunnel () + { + CreateTunnel ( + new TunnelConfig (std::vector > + { + i2p::context.GetSharedRouterInfo () + })); + } +} +} diff --git a/Tunnel.h b/Tunnel.h new file mode 100644 index 00000000..d5c48ee7 --- /dev/null +++ b/Tunnel.h @@ -0,0 +1,176 @@ +#ifndef TUNNEL_H__ +#define TUNNEL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "Queue.h" +#include "TunnelConfig.h" +#include "TunnelPool.h" +#include "TransitTunnel.h" +#include "TunnelEndpoint.h" +#include "TunnelGateway.h" +#include "TunnelBase.h" +#include "I2NPProtocol.h" + +namespace i2p +{ +namespace tunnel +{ + const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes + const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute + const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds + const int STANDARD_NUM_RECORDS = 5; // in VariableTunnelBuild message + + enum TunnelState + { + eTunnelStatePending, + eTunnelStateBuildReplyReceived, + eTunnelStateBuildFailed, + eTunnelStateEstablished, + eTunnelStateTestFailed, + eTunnelStateFailed, + eTunnelStateExpiring + }; + + class OutboundTunnel; + class InboundTunnel; + class Tunnel: public TunnelBase + { + public: + + Tunnel (TunnelConfig * config); + ~Tunnel (); + + void Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel = 0); + + TunnelConfig * GetTunnelConfig () const { return m_Config; } + TunnelState GetState () const { return m_State; }; + void SetState (TunnelState state) { m_State = state; }; + bool IsEstablished () const { return m_State == eTunnelStateEstablished; }; + bool IsFailed () const { return m_State == eTunnelStateFailed; }; + + TunnelPool * GetTunnelPool () const { return m_Pool; }; + void SetTunnelPool (TunnelPool * pool) { m_Pool = pool; }; + + bool HandleTunnelBuildResponse (uint8_t * msg, size_t len); + + // implements TunnelBase + void EncryptTunnelMsg (I2NPMessage * tunnelMsg); + uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; }; + const i2p::data::IdentHash& GetNextIdentHash () const { return m_Config->GetFirstHop ()->router->GetIdentHash (); }; + + private: + + TunnelConfig * m_Config; + TunnelPool * m_Pool; // pool, tunnel belongs to, or null + TunnelState m_State; + }; + + class OutboundTunnel: public Tunnel + { + public: + + OutboundTunnel (TunnelConfig * config): Tunnel (config), m_Gateway (this) {}; + + void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg); + void SendTunnelDataMsg (const std::vector& msgs); // multiple messages + std::shared_ptr GetEndpointRouter () const + { return GetTunnelConfig ()->GetLastHop ()->router; }; + size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); }; + + // implements TunnelBase + uint32_t GetTunnelID () const { return GetNextTunnelID (); }; + + private: + + std::mutex m_SendMutex; + TunnelGateway m_Gateway; + }; + + class InboundTunnel: public Tunnel + { + public: + + InboundTunnel (TunnelConfig * config): Tunnel (config), m_Endpoint (true) {}; + void HandleTunnelDataMsg (I2NPMessage * msg); + size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; + + // implements TunnelBase + uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; }; + private: + + TunnelEndpoint m_Endpoint; + }; + + + class Tunnels + { + public: + + Tunnels (); + ~Tunnels (); + void Start (); + void Stop (); + + InboundTunnel * GetInboundTunnel (uint32_t tunnelID); + Tunnel * GetPendingTunnel (uint32_t replyMsgID); + InboundTunnel * GetNextInboundTunnel (); + OutboundTunnel * GetNextOutboundTunnel (); + TunnelPool * GetExploratoryPool () const { return m_ExploratoryPool; }; + TransitTunnel * GetTransitTunnel (uint32_t tunnelID); + void AddTransitTunnel (TransitTunnel * tunnel); + void AddOutboundTunnel (OutboundTunnel * newTunnel); + void AddInboundTunnel (InboundTunnel * newTunnel); + void PostTunnelData (I2NPMessage * msg); + template + TTunnel * CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel = 0); + TunnelPool * CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOuboundHops); + void DeleteTunnelPool (TunnelPool * pool); + void StopTunnelPool (TunnelPool * pool); + + private: + + void Run (); + void ManageTunnels (); + void ManageOutboundTunnels (); + void ManageInboundTunnels (); + void ManageTransitTunnels (); + void ManagePendingTunnels (); + void ManageTunnelPools (); + + void CreateZeroHopsInboundTunnel (); + + private: + + bool m_IsRunning; + std::thread * m_Thread; + std::map m_PendingTunnels; // by replyMsgID + std::mutex m_InboundTunnelsMutex; + std::map m_InboundTunnels; + std::mutex m_OutboundTunnelsMutex; + std::list m_OutboundTunnels; + std::mutex m_TransitTunnelsMutex; + std::map m_TransitTunnels; + std::mutex m_PoolsMutex; + std::list m_Pools; + TunnelPool * m_ExploratoryPool; + i2p::util::Queue m_Queue; + + public: + + // for HTTP only + const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; + const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; + const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; }; + }; + + extern Tunnels tunnels; +} +} + +#endif diff --git a/TunnelBase.h b/TunnelBase.h new file mode 100644 index 00000000..24704d46 --- /dev/null +++ b/TunnelBase.h @@ -0,0 +1,65 @@ +#ifndef TUNNEL_BASE_H__ +#define TUNNEL_BASE_H__ + +#include +#include "Timestamp.h" +#include "I2NPProtocol.h" +#include "Identity.h" + +namespace i2p +{ +namespace tunnel +{ + const size_t TUNNEL_DATA_MSG_SIZE = 1028; + const size_t TUNNEL_DATA_ENCRYPTED_SIZE = 1008; + const size_t TUNNEL_DATA_MAX_PAYLOAD_SIZE = 1003; + + enum TunnelDeliveryType + { + eDeliveryTypeLocal = 0, + eDeliveryTypeTunnel = 1, + eDeliveryTypeRouter = 2 + }; + struct TunnelMessageBlock + { + TunnelDeliveryType deliveryType; + i2p::data::IdentHash hash; + uint32_t tunnelID; + I2NPMessage * data; + }; + + class TunnelBase + { + public: + + //WARNING!!! GetSecondsSinceEpoch() return uint64_t + TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {}; + virtual ~TunnelBase () {}; + + virtual void EncryptTunnelMsg (I2NPMessage * tunnelMsg) = 0; + virtual uint32_t GetNextTunnelID () const = 0; + virtual const i2p::data::IdentHash& GetNextIdentHash () const = 0; + virtual uint32_t GetTunnelID () const = 0; // as known at our side + + uint32_t GetCreationTime () const { return m_CreationTime; }; + void SetCreationTime (uint32_t t) { m_CreationTime = t; }; + + private: + + uint32_t m_CreationTime; // seconds since epoch + }; + + struct TunnelCreationTimeCmp + { + bool operator() (const TunnelBase * t1, const TunnelBase * t2) const + { + if (t1->GetCreationTime () != t2->GetCreationTime ()) + return t1->GetCreationTime () > t2->GetCreationTime (); + else + return t1 < t2; + }; + }; +} +} + +#endif diff --git a/TunnelConfig.h b/TunnelConfig.h new file mode 100644 index 00000000..49a1d622 --- /dev/null +++ b/TunnelConfig.h @@ -0,0 +1,246 @@ +#ifndef TUNNEL_CONFIG_H__ +#define TUNNEL_CONFIG_H__ + +#include +#include +#include +#include +#include "aes.h" +#include "RouterInfo.h" +#include "RouterContext.h" +#include "Timestamp.h" + +namespace i2p +{ +namespace tunnel +{ + struct TunnelHopConfig + { + std::shared_ptr router, nextRouter; + uint32_t tunnelID, nextTunnelID; + uint8_t layerKey[32]; + uint8_t ivKey[32]; + uint8_t replyKey[32]; + uint8_t replyIV[16]; + bool isGateway, isEndpoint; + + TunnelHopConfig * next, * prev; + i2p::crypto::TunnelDecryption decryption; + int recordIndex; // record # in tunnel build message + + TunnelHopConfig (std::shared_ptr r) + { + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (layerKey, 32); + rnd.GenerateBlock (ivKey, 32); + rnd.GenerateBlock (replyIV, 16); + tunnelID = rnd.GenerateWord32 (); + isGateway = true; + isEndpoint = true; + router = r; + //nextRouter = nullptr; + nextTunnelID = 0; + + next = nullptr; + prev = nullptr; + } + + void SetNextRouter (std::shared_ptr r) + { + nextRouter = r; + isEndpoint = false; + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + nextTunnelID = rnd.GenerateWord32 (); + } + + void SetReplyHop (const TunnelHopConfig * replyFirstHop) + { + nextRouter = replyFirstHop->router; + nextTunnelID = replyFirstHop->tunnelID; + isEndpoint = true; + } + + void SetNext (TunnelHopConfig * n) + { + next = n; + if (next) + { + next->prev = this; + next->isGateway = false; + isEndpoint = false; + nextRouter = next->router; + nextTunnelID = next->tunnelID; + } + } + + void SetPrev (TunnelHopConfig * p) + { + prev = p; + if (prev) + { + prev->next = this; + prev->isEndpoint = false; + isGateway = false; + } + } + + void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID) + { + uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); + memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, router->GetIdentHash (), 32); + htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); + memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextRouter->GetIdentHash (), 32); + memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32); + memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); + memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); + memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16); + uint8_t flag = 0; + if (isGateway) flag |= 0x80; + if (isEndpoint) flag |= 0x40; + clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag; + htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ()); + htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); + // TODO: fill padding + router->GetElGamalEncryption ()->Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET); + memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)router->GetIdentHash (), 16); + } + }; + + class TunnelConfig + { + public: + + + TunnelConfig (std::vector > peers, + const TunnelConfig * replyTunnelConfig = nullptr) // replyTunnelConfig=nullptr means inbound + { + TunnelHopConfig * prev = nullptr; + for (auto it: peers) + { + auto hop = new TunnelHopConfig (it); + if (prev) + prev->SetNext (hop); + else + m_FirstHop = hop; + prev = hop; + } + m_LastHop = prev; + + if (replyTunnelConfig) // outbound + { + m_FirstHop->isGateway = false; + m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ()); + } + else // inbound + m_LastHop->SetNextRouter (i2p::context.GetSharedRouterInfo ()); + } + + ~TunnelConfig () + { + TunnelHopConfig * hop = m_FirstHop; + + while (hop) + { + auto tmp = hop; + hop = hop->next; + delete tmp; + } + } + + TunnelHopConfig * GetFirstHop () const + { + return m_FirstHop; + } + + TunnelHopConfig * GetLastHop () const + { + return m_LastHop; + } + + int GetNumHops () const + { + int num = 0; + TunnelHopConfig * hop = m_FirstHop; + while (hop) + { + num++; + hop = hop->next; + } + return num; + } + + void Print (std::stringstream& s) const + { + TunnelHopConfig * hop = m_FirstHop; + if (!m_FirstHop->isGateway) + s << "me"; + s << "-->" << m_FirstHop->tunnelID; + while (hop) + { + s << ":" << hop->router->GetIdentHashAbbreviation () << "-->"; + if (!hop->isEndpoint) + s << hop->nextTunnelID; + else + return; + hop = hop->next; + } + // we didn't reach enpoint that mean we are last hop + s << ":me"; + } + + TunnelConfig * Invert () const + { + TunnelConfig * newConfig = new TunnelConfig (); + TunnelHopConfig * hop = m_FirstHop, * nextNewHop = nullptr; + while (hop) + { + TunnelHopConfig * newHop = new TunnelHopConfig (hop->router); + if (nextNewHop) + newHop->SetNext (nextNewHop); + nextNewHop = newHop; + newHop->isEndpoint = hop->isGateway; + newHop->isGateway = hop->isEndpoint; + + if (!hop->prev) // first hop + { + newConfig->m_LastHop = newHop; + if (hop->isGateway) // inbound tunnel + newHop->SetReplyHop (m_FirstHop); // use it as reply tunnel + else + newHop->SetNextRouter (i2p::context.GetSharedRouterInfo ()); + } + if (!hop->next) newConfig->m_FirstHop = newHop; // last hop + + hop = hop->next; + } + return newConfig; + } + + TunnelConfig * Clone (const TunnelConfig * replyTunnelConfig = nullptr) const + { + std::vector > peers; + TunnelHopConfig * hop = m_FirstHop; + while (hop) + { + peers.push_back (hop->router); + hop = hop->next; + } + return new TunnelConfig (peers, replyTunnelConfig); + } + + private: + + // this constructor can't be called from outside + TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr) + { + } + + private: + + TunnelHopConfig * m_FirstHop, * m_LastHop; + }; +} +} + +#endif diff --git a/TunnelEndpoint.cpp b/TunnelEndpoint.cpp new file mode 100644 index 00000000..d7fa821b --- /dev/null +++ b/TunnelEndpoint.cpp @@ -0,0 +1,270 @@ +#include "I2PEndian.h" +#include +#include "Log.h" +#include "NetDb.h" +#include "I2NPProtocol.h" +#include "Transports.h" +#include "RouterContext.h" +#include "TunnelEndpoint.h" + +namespace i2p +{ +namespace tunnel +{ + TunnelEndpoint::~TunnelEndpoint () + { + for (auto it: m_IncompleteMessages) + i2p::DeleteI2NPMessage (it.second.data); + for (auto it: m_OutOfSequenceFragments) + i2p::DeleteI2NPMessage (it.second.data); + } + + void TunnelEndpoint::HandleDecryptedTunnelDataMsg (I2NPMessage * msg) + { + m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE; + + uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16 + uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // witout 4-byte checksum + if (zero) + { + LogPrint ("TunnelMessage: zero found at ", (int)(zero-decrypted)); + uint8_t * fragment = zero + 1; + // verify checksum + memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end + uint8_t hash[32]; + CryptoPP::SHA256().CalculateDigest (hash, fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16); // payload + iv + if (memcmp (hash, decrypted, 4)) + { + LogPrint ("TunnelMessage: checksum verification failed"); + i2p::DeleteI2NPMessage (msg); + return; + } + // process fragments + while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) + { + uint8_t flag = fragment[0]; + fragment++; + + bool isFollowOnFragment = flag & 0x80, isLastFragment = true; + uint32_t msgID = 0; + int fragmentNum = 0; + TunnelMessageBlockEx m; + if (!isFollowOnFragment) + { + // first fragment + + m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03); + switch (m.deliveryType) + { + case eDeliveryTypeLocal: // 0 + LogPrint ("Delivery type local"); + break; + case eDeliveryTypeTunnel: // 1 + LogPrint ("Delivery type tunnel"); + m.tunnelID = bufbe32toh (fragment); + fragment += 4; // tunnelID + m.hash = i2p::data::IdentHash (fragment); + fragment += 32; // hash + break; + case eDeliveryTypeRouter: // 2 + LogPrint ("Delivery type router"); + m.hash = i2p::data::IdentHash (fragment); + fragment += 32; // to hash + break; + default: + ; + } + + bool isFragmented = flag & 0x08; + if (isFragmented) + { + // Message ID + msgID = bufbe32toh (fragment); + fragment += 4; + LogPrint ("Fragmented message ", msgID); + isLastFragment = false; + } + } + else + { + // follow on + msgID = bufbe32toh (fragment); // MessageID + fragment += 4; + fragmentNum = (flag >> 1) & 0x3F; // 6 bits + isLastFragment = flag & 0x01; + LogPrint ("Follow on fragment ", fragmentNum, " of message ", msgID, isLastFragment ? " last" : " non-last"); + } + + uint16_t size = bufbe16toh (fragment); + fragment += 2; + LogPrint ("Fragment size=", (int)size); + + msg->offset = fragment - msg->buf; + msg->len = msg->offset + size; + if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) + { + // this is not last message. we have to copy it + m.data = NewI2NPMessage (); + m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header + m.data->len += TUNNEL_GATEWAY_HEADER_SIZE; + *(m.data) = *msg; + } + else + m.data = msg; + + if (!isFollowOnFragment && isLastFragment) + HandleNextMessage (m); + else + { + if (msgID) // msgID is presented, assume message is fragmented + { + if (!isFollowOnFragment) // create new incomlete message + { + m.nextFragmentNum = 1; + auto& msg = m_IncompleteMessages[msgID]; + msg = m; + HandleOutOfSequenceFragment (msgID, msg); + } + else + { + m.nextFragmentNum = fragmentNum; + HandleFollowOnFragment (msgID, isLastFragment, m); + } + } + else + LogPrint ("Message is fragmented, but msgID is not presented"); + } + + fragment += size; + } + } + else + { + LogPrint ("TunnelMessage: zero not found"); + i2p::DeleteI2NPMessage (msg); + } + } + + void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m) + { + auto fragment = m.data->GetBuffer (); + auto size = m.data->GetLength (); + auto it = m_IncompleteMessages.find (msgID); + if (it != m_IncompleteMessages.end()) + { + auto& msg = it->second; + if (m.nextFragmentNum == msg.nextFragmentNum) + { + if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if messega is not too long + { + memcpy (msg.data->buf + msg.data->len, fragment, size); // concatenate fragment + msg.data->len += size; + if (isLastFragment) + { + // message complete + HandleNextMessage (msg); + m_IncompleteMessages.erase (it); + } + else + { + msg.nextFragmentNum++; + HandleOutOfSequenceFragment (msgID, msg); + } + } + else + { + LogPrint ("Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped"); + i2p::DeleteI2NPMessage (msg.data); + m_IncompleteMessages.erase (it); + } + i2p::DeleteI2NPMessage (m.data); + } + else + { + LogPrint ("Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ". Saved"); + AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data); + } + } + else + { + LogPrint ("First fragment of message ", msgID, " not found. Saved"); + AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data); + } + } + + void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, I2NPMessage * data) + { + auto it = m_OutOfSequenceFragments.find (msgID); + if (it == m_OutOfSequenceFragments.end ()) + m_OutOfSequenceFragments.insert (std::pair (msgID, {fragmentNum, isLastFragment, data})); + else + i2p::DeleteI2NPMessage (data); + } + + void TunnelEndpoint::HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg) + { + auto it = m_OutOfSequenceFragments.find (msgID); + if (it != m_OutOfSequenceFragments.end ()) + { + if (it->second.fragmentNum == msg.nextFragmentNum) + { + LogPrint ("Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found"); + auto size = it->second.data->GetLength (); + memcpy (msg.data->buf + msg.data->len, it->second.data->GetBuffer (), size); // concatenate out-of-sync fragment + msg.data->len += size; + if (it->second.isLastFragment) + { + // message complete + HandleNextMessage (msg); + m_IncompleteMessages.erase (msgID); + } + else + msg.nextFragmentNum++; + i2p::DeleteI2NPMessage (it->second.data); + m_OutOfSequenceFragments.erase (it); + } + } + } + + void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg) + { + LogPrint ("TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetTypeID ()); + switch (msg.deliveryType) + { + case eDeliveryTypeLocal: + i2p::HandleI2NPMessage (msg.data); + break; + case eDeliveryTypeTunnel: + i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); + break; + case eDeliveryTypeRouter: + if (msg.hash == i2p::context.GetRouterInfo ().GetIdentHash ()) // check if message is sent to us + i2p::HandleI2NPMessage (msg.data); + else + { + // to somebody else + if (!m_IsInbound) // outbound transit tunnel + { + auto typeID = msg.data->GetTypeID (); + if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply ) + { + // catch RI or reply with new list of routers + auto ds = NewI2NPMessage (); + *ds = *(msg.data); + i2p::data::netdb.PostI2NPMsg (ds); + } + i2p::transport::transports.SendMessage (msg.hash, msg.data); + } + else // we shouldn't send this message. possible leakage + { + LogPrint ("Message to another router arrived from an inbound tunnel. Dropped"); + i2p::DeleteI2NPMessage (msg.data); + } + } + break; + default: + LogPrint ("TunnelMessage: Unknown delivery type ", (int)msg.deliveryType); + }; + } +} +} diff --git a/TunnelEndpoint.h b/TunnelEndpoint.h new file mode 100644 index 00000000..61d0a9ab --- /dev/null +++ b/TunnelEndpoint.h @@ -0,0 +1,54 @@ +#ifndef TUNNEL_ENDPOINT_H__ +#define TUNNEL_ENDPOINT_H__ + +#include +#include +#include +#include "I2NPProtocol.h" +#include "TunnelBase.h" + +namespace i2p +{ +namespace tunnel +{ + class TunnelEndpoint + { + struct TunnelMessageBlockEx: public TunnelMessageBlock + { + uint8_t nextFragmentNum; + }; + + struct Fragment + { + uint8_t fragmentNum; + bool isLastFragment; + I2NPMessage * data; + }; + + public: + + TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {}; + ~TunnelEndpoint (); + size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; + + void HandleDecryptedTunnelDataMsg (I2NPMessage * msg); + + private: + + void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m); + void HandleNextMessage (const TunnelMessageBlock& msg); + + void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, I2NPMessage * data); + void HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); + + private: + + std::map m_IncompleteMessages; + std::map m_OutOfSequenceFragments; + bool m_IsInbound; + size_t m_NumReceivedBytes; + }; +} +} + +#endif diff --git a/libi2pd/TunnelGateway.cpp b/TunnelGateway.cpp similarity index 56% rename from libi2pd/TunnelGateway.cpp rename to TunnelGateway.cpp index 9e27d207..146c007c 100644 --- a/libi2pd/TunnelGateway.cpp +++ b/TunnelGateway.cpp @@ -1,14 +1,6 @@ -/* -* Copyright (c) 2013-2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - #include -#include "Crypto.h" #include "I2PEndian.h" +#include #include "Log.h" #include "RouterContext.h" #include "Transports.h" @@ -18,65 +10,34 @@ namespace i2p { namespace tunnel { - TunnelGatewayBuffer::TunnelGatewayBuffer (): - m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0), m_NonZeroRandomBuffer (nullptr) - { - } - - TunnelGatewayBuffer::~TunnelGatewayBuffer () - { - ClearTunnelDataMsgs (); - if (m_NonZeroRandomBuffer) delete[] m_NonZeroRandomBuffer; - } - void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block) { bool messageCreated = false; if (!m_CurrentTunnelDataMsg) - { + { CreateCurrentTunnelDataMessage (); - if (block.data && block.data->onDrop) - { - // onDrop is called for the first fragment in tunnel message - // that's usually true for short TBMs or lookups - m_CurrentTunnelDataMsg->onDrop = block.data->onDrop; - block.data->onDrop = nullptr; - } messageCreated = true; - } + } // create delivery instructions uint8_t di[43]; // max delivery instruction length is 43 for tunnel size_t diLen = 1;// flag if (block.deliveryType != eDeliveryTypeLocal) // tunnel or router - { + { if (block.deliveryType == eDeliveryTypeTunnel) { htobe32buf (di + diLen, block.tunnelID); diLen += 4; // tunnelID } - + memcpy (di + diLen, block.hash, 32); diLen += 32; //len - } + } di[0] = block.deliveryType << 5; // set delivery type // create fragments - const std::shared_ptr & msg = block.data; - size_t fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length - - if (!messageCreated && fullMsgLen > m_RemainingSize) // check if we should complete previous message - { - size_t numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE; - // length of bytes doesn't fit full tunnel message - // every follow-on fragment adds 7 bytes - size_t nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE; - if (!nonFit || nonFit > m_RemainingSize || m_RemainingSize < fullMsgLen/5) - { - CompleteCurrentTunnelDataMessage (); - CreateCurrentTunnelDataMessage (); - } - } + I2NPMessage * msg = block.data; + auto fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length if (fullMsgLen <= m_RemainingSize) { // message fits. First and last fragment @@ -88,9 +49,22 @@ namespace tunnel m_RemainingSize -= diLen + msg->GetLength (); if (!m_RemainingSize) CompleteCurrentTunnelDataMessage (); - } + DeleteI2NPMessage (msg); + } else { + if (!messageCreated) // check if we should complete previous message + { + auto numFollowOnFragments = fullMsgLen / TUNNEL_DATA_MAX_PAYLOAD_SIZE; + // length of bytes don't fit full tunnel message + // every follow-on fragment adds 7 bytes + auto nonFit = (fullMsgLen + numFollowOnFragments*7) % TUNNEL_DATA_MAX_PAYLOAD_SIZE; + if (!nonFit || nonFit > m_RemainingSize) + { + CompleteCurrentTunnelDataMessage (); + CreateCurrentTunnelDataMessage (); + } + } if (diLen + 6 <= m_RemainingSize) { // delivery instructions fit @@ -111,16 +85,16 @@ namespace tunnel // follow on fragments int fragmentNumber = 1; while (size < msg->GetLength ()) - { + { CreateCurrentTunnelDataMessage (); uint8_t * buf = m_CurrentTunnelDataMsg->GetBuffer (); buf[0] = 0x80 | (fragmentNumber << 1); // frag bool isLastFragment = false; size_t s = msg->GetLength () - size; if (s > TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7) // 7 follow on instructions - s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7; + s = TUNNEL_DATA_MAX_PAYLOAD_SIZE - 7; else // last fragment - { + { buf[0] |= 0x01; isLastFragment = true; } @@ -130,114 +104,95 @@ namespace tunnel m_CurrentTunnelDataMsg->len += s+7; if (isLastFragment) { - if(m_RemainingSize < (s+7)) { - LogPrint (eLogError, "TunnelGateway: remaining size overflow: ", m_RemainingSize, " < ", s+7); - } else { - m_RemainingSize -= s+7; - if (m_RemainingSize == 0) - CompleteCurrentTunnelDataMessage (); - } + m_RemainingSize -= s+7; + if (!m_RemainingSize) + CompleteCurrentTunnelDataMessage (); } else CompleteCurrentTunnelDataMessage (); size += s; fragmentNumber++; } - } + DeleteI2NPMessage (msg); + } else { // delivery instructions don't fit. Create new message CompleteCurrentTunnelDataMessage (); PutI2NPMsg (block); // don't delete msg because it's taken care inside - } - } + } + } } - + void TunnelGatewayBuffer::ClearTunnelDataMsgs () { m_TunnelDataMsgs.clear (); - m_CurrentTunnelDataMsg = nullptr; } void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage () { - m_CurrentTunnelDataMsg = NewI2NPTunnelMessage (true); // tunnel endpoint is at least of two tunnel messages size + m_CurrentTunnelDataMsg = NewI2NPMessage (); + m_CurrentTunnelDataMsg->Align (12); // we reserve space for padding m_CurrentTunnelDataMsg->offset += TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE; m_CurrentTunnelDataMsg->len = m_CurrentTunnelDataMsg->offset; m_RemainingSize = TUNNEL_DATA_MAX_PAYLOAD_SIZE; - } - + } + void TunnelGatewayBuffer::CompleteCurrentTunnelDataMessage () { if (!m_CurrentTunnelDataMsg) return; uint8_t * payload = m_CurrentTunnelDataMsg->GetBuffer (); size_t size = m_CurrentTunnelDataMsg->len - m_CurrentTunnelDataMsg->offset; - + m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE; uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload (); - RAND_bytes (buf + 4, 16); // original IV - memcpy (payload + size, buf + 4, 16); // copy IV for checksum + htobe32buf (buf, m_TunnelID); + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + rnd.GenerateBlock (buf + 4, 16); // original IV + memcpy (payload + size, buf + 4, 16); // copy IV for checksum uint8_t hash[32]; - SHA256(payload, size+16, hash); - memcpy (buf+20, hash, 4); // checksum - payload[-1] = 0; // zero - ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1 + CryptoPP::SHA256().CalculateDigest (hash, payload, size+16); + memcpy (buf+20, hash, 4); // checksum + payload[-1] = 0; // zero + ptrdiff_t paddingSize = payload - buf - 25; // 25 = 24 + 1 if (paddingSize > 0) - { - // non-zero padding - if (!m_NonZeroRandomBuffer) // first time? - { - m_NonZeroRandomBuffer = new uint8_t[TUNNEL_DATA_MAX_PAYLOAD_SIZE]; - RAND_bytes (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE); - for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++) - if (!m_NonZeroRandomBuffer[i]) m_NonZeroRandomBuffer[i] = 1; - } - auto randomOffset = rand () % (TUNNEL_DATA_MAX_PAYLOAD_SIZE - paddingSize + 1); - memcpy (buf + 24, m_NonZeroRandomBuffer + randomOffset, paddingSize); - } + memset (buf + 24, 1, paddingSize); // padding TODO: fill with random data // we can't fill message header yet because encryption is required m_TunnelDataMsgs.push_back (m_CurrentTunnelDataMsg); m_CurrentTunnelDataMsg = nullptr; - } - + } + void TunnelGateway::SendTunnelDataMsg (const TunnelMessageBlock& block) { if (block.data) - { + { PutTunnelDataMsg (block); SendBuffer (); - } - } + } + } void TunnelGateway::PutTunnelDataMsg (const TunnelMessageBlock& block) { if (block.data) m_Buffer.PutI2NPMsg (block); - } + } void TunnelGateway::SendBuffer () { - // create list or tunnel messages m_Buffer.CompleteCurrentTunnelDataMessage (); - std::list > newTunnelMsgs; - const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs (); - for (auto& tunnelMsg : tunnelDataMsgs) - { - auto newMsg = CreateEmptyTunnelDataMsg (false); - m_Tunnel.EncryptTunnelMsg (tunnelMsg, newMsg); - htobe32buf (newMsg->GetPayload (), m_Tunnel.GetNextTunnelID ()); - newMsg->FillI2NPMessageHeader (eI2NPTunnelData); - if (tunnelMsg->onDrop) newMsg->onDrop = tunnelMsg->onDrop; - newTunnelMsgs.push_back (newMsg); + auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs (); + for (auto tunnelMsg : tunnelMsgs) + { + m_Tunnel->EncryptTunnelMsg (tunnelMsg); + FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData); + i2p::transport::transports.SendMessage (m_Tunnel->GetNextIdentHash (), tunnelMsg); m_NumSentBytes += TUNNEL_DATA_MSG_SIZE; - } + } m_Buffer.ClearTunnelDataMsgs (); - // send - if (!m_Sender) m_Sender = std::make_unique(); - m_Sender->SendMessagesTo (m_Tunnel.GetNextIdentHash (), std::move (newTunnelMsgs)); - } -} -} + } +} +} + diff --git a/TunnelGateway.h b/TunnelGateway.h new file mode 100644 index 00000000..abed9288 --- /dev/null +++ b/TunnelGateway.h @@ -0,0 +1,55 @@ +#ifndef TUNNEL_GATEWAY_H__ +#define TUNNEL_GATEWAY_H__ + +#include +#include +#include "I2NPProtocol.h" +#include "TunnelBase.h" + +namespace i2p +{ +namespace tunnel +{ + class TunnelGatewayBuffer + { + public: + TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID), + m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0) {}; + void PutI2NPMsg (const TunnelMessageBlock& block); + const std::vector& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; }; + void ClearTunnelDataMsgs (); + void CompleteCurrentTunnelDataMessage (); + + private: + + void CreateCurrentTunnelDataMessage (); + + private: + + uint32_t m_TunnelID; + std::vector m_TunnelDataMsgs; + I2NPMessage * m_CurrentTunnelDataMsg; + size_t m_RemainingSize; + }; + + class TunnelGateway + { + public: + + TunnelGateway (TunnelBase * tunnel): + m_Tunnel (tunnel), m_Buffer (tunnel->GetNextTunnelID ()), m_NumSentBytes (0) {}; + void SendTunnelDataMsg (const TunnelMessageBlock& block); + void PutTunnelDataMsg (const TunnelMessageBlock& block); + void SendBuffer (); + size_t GetNumSentBytes () const { return m_NumSentBytes; }; + + private: + + TunnelBase * m_Tunnel; + TunnelGatewayBuffer m_Buffer; + size_t m_NumSentBytes; + }; +} +} + +#endif diff --git a/TunnelPool.cpp b/TunnelPool.cpp new file mode 100644 index 00000000..fd176841 --- /dev/null +++ b/TunnelPool.cpp @@ -0,0 +1,358 @@ +#include "I2PEndian.h" +#include "CryptoConst.h" +#include "Tunnel.h" +#include "NetDb.h" +#include "Timestamp.h" +#include "Garlic.h" +#include "TunnelPool.h" + +namespace i2p +{ +namespace tunnel +{ + TunnelPool::TunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numTunnels): + m_LocalDestination (localDestination), m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), + m_NumTunnels (numTunnels), m_IsActive (true) + { + } + + TunnelPool::~TunnelPool () + { + DetachTunnels (); + } + + void TunnelPool::DetachTunnels () + { + { + std::unique_lock l(m_InboundTunnelsMutex); + for (auto it: m_InboundTunnels) + it->SetTunnelPool (nullptr); + m_InboundTunnels.clear (); + } + { + std::unique_lock l(m_OutboundTunnelsMutex); + for (auto it: m_OutboundTunnels) + it->SetTunnelPool (nullptr); + m_OutboundTunnels.clear (); + } + m_Tests.clear (); + } + + void TunnelPool::TunnelCreated (InboundTunnel * createdTunnel) + { + if (!m_IsActive) return; + { + std::unique_lock l(m_InboundTunnelsMutex); + m_InboundTunnels.insert (createdTunnel); + } + if (m_LocalDestination) + m_LocalDestination->SetLeaseSetUpdated (); + } + + void TunnelPool::TunnelExpired (InboundTunnel * expiredTunnel) + { + if (expiredTunnel) + { + expiredTunnel->SetTunnelPool (nullptr); + for (auto it: m_Tests) + if (it.second.second == expiredTunnel) it.second.second = nullptr; + RecreateInboundTunnel (expiredTunnel); + + std::unique_lock l(m_InboundTunnelsMutex); + m_InboundTunnels.erase (expiredTunnel); + } + } + + void TunnelPool::TunnelCreated (OutboundTunnel * createdTunnel) + { + if (!m_IsActive) return; + std::unique_lock l(m_OutboundTunnelsMutex); + m_OutboundTunnels.insert (createdTunnel); + } + + void TunnelPool::TunnelExpired (OutboundTunnel * expiredTunnel) + { + if (expiredTunnel) + { + expiredTunnel->SetTunnelPool (nullptr); + for (auto it: m_Tests) + if (it.second.first == expiredTunnel) it.second.first = nullptr; + RecreateOutboundTunnel (expiredTunnel); + + std::unique_lock l(m_OutboundTunnelsMutex); + m_OutboundTunnels.erase (expiredTunnel); + } + } + + std::vector TunnelPool::GetInboundTunnels (int num) const + { + std::vector v; + int i = 0; + std::unique_lock l(m_InboundTunnelsMutex); + for (auto it : m_InboundTunnels) + { + if (i >= num) break; + if (it->IsEstablished ()) + { + v.push_back (it); + i++; + } + } + return v; + } + + OutboundTunnel * TunnelPool::GetNextOutboundTunnel (OutboundTunnel * suggested) const + { + std::unique_lock l(m_OutboundTunnelsMutex); + return GetNextTunnel (m_OutboundTunnels, suggested); + } + + InboundTunnel * TunnelPool::GetNextInboundTunnel (InboundTunnel * suggested) const + { + std::unique_lock l(m_InboundTunnelsMutex); + return GetNextTunnel (m_InboundTunnels, suggested); + } + + template + typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, + typename TTunnels::value_type suggested) const + { + if (tunnels.empty ()) return nullptr; + if (suggested && tunnels.count (suggested) > 0 && suggested->IsEstablished ()) + return suggested; + + CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); + uint32_t ind = rnd.GenerateWord32 (0, tunnels.size ()/2), i = 0; + typename TTunnels::value_type tunnel = nullptr; + for (auto it: tunnels) + { + if (it->IsEstablished ()) + { + tunnel = it; + i++; + } + if (i > ind && tunnel) break; + } + return tunnel; + } + + void TunnelPool::CreateTunnels () + { + int num = 0; + { + std::unique_lock l(m_InboundTunnelsMutex); + for (auto it : m_InboundTunnels) + if (it->IsEstablished ()) num++; + } + for (int i = num; i < m_NumTunnels; i++) + CreateInboundTunnel (); + + num = 0; + { + std::unique_lock l(m_OutboundTunnelsMutex); + for (auto it : m_OutboundTunnels) + if (it->IsEstablished ()) num++; + } + for (int i = num; i < m_NumTunnels; i++) + CreateOutboundTunnel (); + } + + void TunnelPool::TestTunnels () + { + auto& rnd = i2p::context.GetRandomNumberGenerator (); + for (auto it: m_Tests) + { + LogPrint ("Tunnel test ", (int)it.first, " failed"); + // if test failed again with another tunnel we consider it failed + if (it.second.first) + { + if (it.second.first->GetState () == eTunnelStateTestFailed) + { + it.second.first->SetState (eTunnelStateFailed); + std::unique_lock l(m_OutboundTunnelsMutex); + m_OutboundTunnels.erase (it.second.first); + } + else + it.second.first->SetState (eTunnelStateTestFailed); + } + if (it.second.second) + { + if (it.second.second->GetState () == eTunnelStateTestFailed) + { + it.second.second->SetState (eTunnelStateFailed); + { + std::unique_lock l(m_InboundTunnelsMutex); + m_InboundTunnels.erase (it.second.second); + } + if (m_LocalDestination) + m_LocalDestination->SetLeaseSetUpdated (); + } + else + it.second.second->SetState (eTunnelStateTestFailed); + } + } + m_Tests.clear (); + // new tests + auto it1 = m_OutboundTunnels.begin (); + auto it2 = m_InboundTunnels.begin (); + while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ()) + { + bool failed = false; + if ((*it1)->IsFailed ()) + { + failed = true; + it1++; + } + if ((*it2)->IsFailed ()) + { + failed = true; + it2++; + } + if (!failed) + { + uint32_t msgID = rnd.GenerateWord32 (); + m_Tests[msgID] = std::make_pair (*it1, *it2); + (*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (), + CreateDeliveryStatusMsg (msgID)); + it1++; it2++; + } + } + } + + void TunnelPool::ProcessGarlicMessage (I2NPMessage * msg) + { + if (m_LocalDestination) + m_LocalDestination->ProcessGarlicMessage (msg); + else + { + LogPrint (eLogWarning, "Local destination doesn't exist. Dropped"); + DeleteI2NPMessage (msg); + } + } + + void TunnelPool::ProcessDeliveryStatus (I2NPMessage * msg) + { + const uint8_t * buf = msg->GetPayload (); + uint32_t msgID = bufbe32toh (buf); + buf += 4; + uint64_t timestamp = bufbe64toh (buf); + + auto it = m_Tests.find (msgID); + if (it != m_Tests.end ()) + { + // restore from test failed state if any + if (it->second.first->GetState () == eTunnelStateTestFailed) + it->second.first->SetState (eTunnelStateEstablished); + if (it->second.second->GetState () == eTunnelStateTestFailed) + it->second.second->SetState (eTunnelStateEstablished); + LogPrint ("Tunnel test ", it->first, " successive. ", i2p::util::GetMillisecondsSinceEpoch () - timestamp, " milliseconds"); + m_Tests.erase (it); + DeleteI2NPMessage (msg); + } + else + { + if (m_LocalDestination) + m_LocalDestination->ProcessDeliveryStatusMessage (msg); + else + { + LogPrint (eLogWarning, "Local destination doesn't exist. Dropped"); + DeleteI2NPMessage (msg); + } + } + } + + std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop) const + { + bool isExploratory = (m_LocalDestination == &i2p::context); // TODO: implement it better + auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop): + i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop); + + if (!hop) + hop = i2p::data::netdb.GetRandomRouter (); + return hop; + } + + void TunnelPool::CreateInboundTunnel () + { + OutboundTunnel * outboundTunnel = GetNextOutboundTunnel (); + if (!outboundTunnel) + outboundTunnel = tunnels.GetNextOutboundTunnel (); + LogPrint ("Creating destination inbound tunnel..."); + auto prevHop = i2p::context.GetSharedRouterInfo (); + std::vector > hops; + int numHops = m_NumInboundHops; + if (outboundTunnel) + { + // last hop + auto hop = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router; + if (hop->GetIdentHash () != i2p::context.GetIdentHash ()) // outbound shouldn't be zero-hop tunnel + { + prevHop = hop; + hops.push_back (prevHop); + numHops--; + } + } + for (int i = 0; i < numHops; i++) + { + auto hop = SelectNextHop (prevHop); + prevHop = hop; + hops.push_back (hop); + } + std::reverse (hops.begin (), hops.end ()); + auto * tunnel = tunnels.CreateTunnel (new TunnelConfig (hops), outboundTunnel); + tunnel->SetTunnelPool (this); + } + + void TunnelPool::RecreateInboundTunnel (InboundTunnel * tunnel) + { + OutboundTunnel * outboundTunnel = GetNextOutboundTunnel (); + if (!outboundTunnel) + outboundTunnel = tunnels.GetNextOutboundTunnel (); + LogPrint ("Re-creating destination inbound tunnel..."); + auto * newTunnel = tunnels.CreateTunnel (tunnel->GetTunnelConfig ()->Clone (), outboundTunnel); + newTunnel->SetTunnelPool (this); + } + + void TunnelPool::CreateOutboundTunnel () + { + InboundTunnel * inboundTunnel = GetNextInboundTunnel (); + if (!inboundTunnel) + inboundTunnel = tunnels.GetNextInboundTunnel (); + if (inboundTunnel) + { + LogPrint ("Creating destination outbound tunnel..."); + + auto prevHop = i2p::context.GetSharedRouterInfo (); + std::vector > hops; + for (int i = 0; i < m_NumOutboundHops; i++) + { + auto hop = SelectNextHop (prevHop); + prevHop = hop; + hops.push_back (hop); + } + + auto * tunnel = tunnels.CreateTunnel ( + new TunnelConfig (hops, inboundTunnel->GetTunnelConfig ())); + tunnel->SetTunnelPool (this); + } + else + LogPrint ("Can't create outbound tunnel. No inbound tunnels found"); + } + + void TunnelPool::RecreateOutboundTunnel (OutboundTunnel * tunnel) + { + InboundTunnel * inboundTunnel = GetNextInboundTunnel (); + if (!inboundTunnel) + inboundTunnel = tunnels.GetNextInboundTunnel (); + if (inboundTunnel) + { + LogPrint ("Re-creating destination outbound tunnel..."); + auto * newTunnel = tunnels.CreateTunnel ( + tunnel->GetTunnelConfig ()->Clone (inboundTunnel->GetTunnelConfig ())); + newTunnel->SetTunnelPool (this); + } + else + LogPrint ("Can't re-create outbound tunnel. No inbound tunnels found"); + } +} +} diff --git a/TunnelPool.h b/TunnelPool.h new file mode 100644 index 00000000..b2d75950 --- /dev/null +++ b/TunnelPool.h @@ -0,0 +1,85 @@ +#ifndef TUNNEL_POOL__ +#define TUNNEL_POOL__ + +#include +#include +#include +#include +#include +#include "Identity.h" +#include "LeaseSet.h" +#include "RouterInfo.h" +#include "I2NPProtocol.h" +#include "TunnelBase.h" +#include "RouterContext.h" +#include "Garlic.h" + +namespace i2p +{ +namespace tunnel +{ + class Tunnel; + class InboundTunnel; + class OutboundTunnel; + + class TunnelPool // per local destination + { + public: + + TunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numTunnels = 5); + ~TunnelPool (); + + i2p::garlic::GarlicDestination * GetLocalDestination () const { return m_LocalDestination; }; + void SetLocalDestination (i2p::garlic::GarlicDestination * destination) { m_LocalDestination = destination; }; + + void CreateTunnels (); + void TunnelCreated (InboundTunnel * createdTunnel); + void TunnelExpired (InboundTunnel * expiredTunnel); + void TunnelCreated (OutboundTunnel * createdTunnel); + void TunnelExpired (OutboundTunnel * expiredTunnel); + std::vector GetInboundTunnels (int num) const; + OutboundTunnel * GetNextOutboundTunnel (OutboundTunnel * suggested = nullptr) const; + InboundTunnel * GetNextInboundTunnel (InboundTunnel * suggested = nullptr) const; + + void TestTunnels (); + void ProcessGarlicMessage (I2NPMessage * msg); + void ProcessDeliveryStatus (I2NPMessage * msg); + + bool IsActive () const { return m_IsActive; }; + void SetActive (bool isActive) { m_IsActive = isActive; }; + void DetachTunnels (); + + private: + + void CreateInboundTunnel (); + void CreateOutboundTunnel (); + void RecreateInboundTunnel (InboundTunnel * tunnel); + void RecreateOutboundTunnel (OutboundTunnel * tunnel); + template + typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, + typename TTunnels::value_type suggested = nullptr) const; + std::shared_ptr SelectNextHop (std::shared_ptr prevHop) const; + + private: + + i2p::garlic::GarlicDestination * m_LocalDestination; + int m_NumInboundHops, m_NumOutboundHops, m_NumTunnels; + mutable std::mutex m_InboundTunnelsMutex; + std::set m_InboundTunnels; // recent tunnel appears first + mutable std::mutex m_OutboundTunnelsMutex; + std::set m_OutboundTunnels; + std::map > m_Tests; + bool m_IsActive; + + public: + + // for HTTP only + const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; + const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; + + }; +} +} + +#endif + diff --git a/UPnP.cpp b/UPnP.cpp new file mode 100644 index 00000000..e5b8ff79 --- /dev/null +++ b/UPnP.cpp @@ -0,0 +1,270 @@ +#ifdef USE_UPNP +#include +#include + +#ifdef _WIN32 +#include +#endif + +#include +#include +#include + +#include "Log.h" +#include "RouterContext.h" +#include "UPnP.h" +#include "NetDb.h" +#include "util.h" + +#include +#include +#include + +#ifndef UPNPDISCOVER_SUCCESS +/* miniupnpc 1.5 */ +typedef UPNPDev* (*upnp_upnpDiscoverFunc) (int, const char *, const char *, int); +typedef int (*upnp_UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *, + const char *, const char *, const char *, const char *); +#else +/* miniupnpc 1.6 */ +typedef UPNPDev* (*upnp_upnpDiscoverFunc) (int, const char *, const char *, int, int, int *); +typedef int (*upnp_UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *, + const char *, const char *, const char *, const char *, const char *); +#endif +typedef int (*upnp_UPNP_GetValidIGDFunc) (struct UPNPDev *, struct UPNPUrls *, struct IGDdatas *, char *, int); +typedef int (*upnp_UPNP_GetExternalIPAddressFunc) (const char *, const char *, char *); +typedef int (*upnp_UPNP_DeletePortMappingFunc) (const char *, const char *, const char *, const char *, const char *); +typedef void (*upnp_freeUPNPDevlistFunc) (struct UPNPDev *); +typedef void (*upnp_FreeUPNPUrlsFunc) (struct UPNPUrls *); + +namespace i2p +{ +namespace UPnP +{ + UPnP upnpc; + + UPnP::UPnP () : m_Thread (nullptr) , m_IsModuleLoaded (false) + { + } + + void UPnP::Stop () + { + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + + void UPnP::Start() + { + m_Thread = new std::thread (std::bind (&UPnP::Run, this)); + } + + UPnP::~UPnP () + { + } + + void UPnP::Run () + { +#ifdef MAC_OSX + m_Module = dlopen ("libminiupnpc.dylib", RTLD_LAZY); +#elif _WIN32 + m_Module = LoadLibrary ("libminiupnpc.dll"); + if (m_Module == NULL) + { + LogPrint ("Error loading UPNP library. This often happens if there is version mismatch!"); + return; + } + else + { + m_IsModuleLoaded = true; + } +#else + m_Module = dlopen ("libminiupnpc.so", RTLD_LAZY); +#endif +#ifndef _WIN32 + if (!m_Module) + { + LogPrint ("no UPnP module available (", dlerror (), ")"); + return; + } + else + { + m_IsModuleLoaded = true; + } +#endif + for (auto& address : context.GetRouterInfo ().GetAddresses ()) + { + if (!address.host.is_v6 ()) + { + m_Port = std::to_string (util::config::GetArg ("-port", address.port)); + Discover (); + if (address.transportStyle == data::RouterInfo::eTransportSSU ) + { + TryPortMapping (I2P_UPNP_UDP); + } + else if (address.transportStyle == data::RouterInfo::eTransportNTCP ) + { + TryPortMapping (I2P_UPNP_TCP); + } + } + } + } + + void UPnP::Discover () + { + const char *error; +#ifdef _WIN32 + upnp_upnpDiscoverFunc upnpDiscoverFunc = (upnp_upnpDiscoverFunc) GetProcAddress (m_Module, "upnpDiscover"); +#else + upnp_upnpDiscoverFunc upnpDiscoverFunc = (upnp_upnpDiscoverFunc) dlsym (m_Module, "upnpDiscover"); + // reinterpret_cast (dlsym(...)); + if ( (error = dlerror ())) + { + LogPrint ("Error loading UPNP library. This often happens if there is version mismatch!"); + return; + } +#endif // _WIN32 +#ifndef UPNPDISCOVER_SUCCESS + /* miniupnpc 1.5 */ + m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0); +#else + /* miniupnpc 1.6 */ + int nerror = 0; + m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror); +#endif + + int r; +#ifdef _WIN32 + upnp_UPNP_GetValidIGDFunc UPNP_GetValidIGDFunc = (upnp_UPNP_GetValidIGDFunc) GetProcAddress (m_Module, "UPNP_GetValidIGD"); +#else + upnp_UPNP_GetValidIGDFunc UPNP_GetValidIGDFunc = (upnp_UPNP_GetValidIGDFunc) dlsym (m_Module, "UPNP_GetValidIGD"); +#endif + r = (*UPNP_GetValidIGDFunc) (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr)); + if (r == 1) + { + upnp_UPNP_GetExternalIPAddressFunc UPNP_GetExternalIPAddressFunc = (upnp_UPNP_GetExternalIPAddressFunc) dlsym (m_Module, "UPNP_GetExternalIPAddress"); + r = UPNP_GetExternalIPAddressFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress); + if(r != UPNPCOMMAND_SUCCESS) + { + LogPrint ("UPnP: UPNP_GetExternalIPAddress () returned ", r); + return; + } + else + { + if (m_externalIPAddress[0]) + { + LogPrint ("UPnP: ExternalIPAddress = ", m_externalIPAddress); + i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress)); + return; + } + else + { + LogPrint ("UPnP: GetExternalIPAddress failed."); + return; + } + } + } + } + + void UPnP::TryPortMapping (int type) + { + std::string strType; + switch (type) + { + case I2P_UPNP_TCP: + strType = "TCP"; + break; + case I2P_UPNP_UDP: + default: + strType = "UDP"; + } + int r; + std::string strDesc = "I2Pd"; + try { + for (;;) { +#ifdef _WIN32 + upnp_UPNP_AddPortMappingFunc UPNP_AddPortMappingFunc = (upnp_UPNP_AddPortMappingFunc) GetProcAddress (m_Module, "UPNP_AddPortMapping"); +#else + upnp_UPNP_AddPortMappingFunc UPNP_AddPortMappingFunc = (upnp_UPNP_AddPortMappingFunc) dlsym (m_Module, "UPNP_AddPortMapping"); +#endif +#ifndef UPNPDISCOVER_SUCCESS + /* miniupnpc 1.5 */ + r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_Port.c_str (), m_Port.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0); +#else + /* miniupnpc 1.6 */ + r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_Port.c_str (), m_Port.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0"); +#endif + if (r!=UPNPCOMMAND_SUCCESS) + { + LogPrint ("AddPortMapping (", m_Port.c_str () ,", ", m_Port.c_str () ,", ", m_NetworkAddr, ") failed with code ", r); + return; + } + else + { + LogPrint ("UPnP Port Mapping successful. (", m_NetworkAddr ,":", m_Port.c_str(), " type ", strType.c_str () ," -> ", m_externalIPAddress ,":", m_Port.c_str() ,")"); + return; + } + sleep(20*60); + } + } + catch (boost::thread_interrupted) + { + CloseMapping(type); + Close(); + throw; + } + } + + void UPnP::CloseMapping (int type) + { + std::string strType; + switch (type) + { + case I2P_UPNP_TCP: + strType = "TCP"; + break; + case I2P_UPNP_UDP: + default: + strType = "UDP"; + } + int r = 0; +#ifdef _WIN32 + upnp_UPNP_DeletePortMappingFunc UPNP_DeletePortMappingFunc = (upnp_UPNP_DeletePortMappingFunc) GetProcAddress (m_Module, "UPNP_DeletePortMapping"); +#else + upnp_UPNP_DeletePortMappingFunc UPNP_DeletePortMappingFunc = (upnp_UPNP_DeletePortMappingFunc) dlsym (m_Module, "UPNP_DeletePortMapping"); +#endif + r = UPNP_DeletePortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_Port.c_str (), strType.c_str (), 0); + LogPrint ("UPNP_DeletePortMapping() returned : ", r, "\n"); + } + + void UPnP::Close () + { +#ifdef _WIN32 + upnp_freeUPNPDevlistFunc freeUPNPDevlistFunc = (upnp_freeUPNPDevlistFunc) GetProcAddress (m_Module, "freeUPNPDevlist"); +#else + upnp_freeUPNPDevlistFunc freeUPNPDevlistFunc = (upnp_freeUPNPDevlistFunc) dlsym (m_Module, "freeUPNPDevlist"); +#endif + freeUPNPDevlistFunc (m_Devlist); + m_Devlist = 0; +#ifdef _WIN32 + upnp_FreeUPNPUrlsFunc FreeUPNPUrlsFunc = (upnp_FreeUPNPUrlsFunc) GetProcAddress (m_Module, "FreeUPNPUrlsFunc"); +#else + upnp_FreeUPNPUrlsFunc FreeUPNPUrlsFunc = (upnp_FreeUPNPUrlsFunc) dlsym (m_Module, "FreeUPNPUrlsFunc"); +#endif + FreeUPNPUrlsFunc (&m_upnpUrls); +#ifndef _WIN32 + dlclose (m_Module); +#else + FreeLibrary (m_Module); +#endif + } + +} +} + + +#endif + diff --git a/UPnP.h b/UPnP.h new file mode 100644 index 00000000..8dc62b8d --- /dev/null +++ b/UPnP.h @@ -0,0 +1,65 @@ +#ifndef __UPNP_H__ +#define __UPNP_H__ + +#ifdef USE_UPNP +#include +#include + +#include +#include +#include +#include + +#include + +#include "util.h" + +#define I2P_UPNP_TCP 1 +#define I2P_UPNP_UDP 2 + +namespace i2p +{ +namespace UPnP +{ + class UPnP + { + public: + + UPnP (); + ~UPnP (); + void Close (); + + void Start (); + void Stop (); + + void Discover (); + void TryPortMapping (int type); + void CloseMapping (int type); + private: + void Run (); + + std::thread * m_Thread; + struct UPNPUrls m_upnpUrls; + struct IGDdatas m_upnpData; + + // For miniupnpc + char * m_MulticastIf = 0; + char * m_Minissdpdpath = 0; + struct UPNPDev * m_Devlist = 0; + char m_NetworkAddr[64]; + char m_externalIPAddress[40]; + bool m_IsModuleLoaded; + std::string m_Port = std::to_string (util::config::GetArg ("-port", 17070)); +#ifndef _WIN32 + void *m_Module; +#else + HINSTANCE *m_Module; +#endif + }; + extern UPnP upnpc; +} +} + +#endif + +#endif diff --git a/Win32/.gitignore b/Win32/.gitignore new file mode 100644 index 00000000..5aa0538d --- /dev/null +++ b/Win32/.gitignore @@ -0,0 +1,14 @@ +* +!*/ + +!*.h +!*.cpp + +!*.bat + +!*.sln +!*.vcproj +!*.vcxproj +!*.vcxproj.filters +!*.iss +!.gitignore diff --git a/Win32/DaemonWin32.cpp b/Win32/DaemonWin32.cpp deleted file mode 100644 index 48f65c27..00000000 --- a/Win32/DaemonWin32.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* -* Copyright (c) 2013-2023, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include "Config.h" -#include "Daemon.h" -#include "util.h" -#include "Log.h" - -#ifdef _WIN32 -#include "Win32Service.h" -#ifdef WIN32_APP -#include -#include "Win32App.h" -#endif - -namespace i2p -{ -namespace util -{ - bool DaemonWin32::init(int argc, char* argv[]) - { - setlocale(LC_CTYPE, ""); - SetConsoleCP(1251); - SetConsoleOutputCP(1251); - //setlocale(LC_ALL, "Russian"); - setlocale(LC_TIME, "C"); - - i2p::log::SetThrowFunction ([](const std::string& s) - { - MessageBox(0, TEXT(s.c_str ()), TEXT("i2pd"), MB_ICONERROR | MB_TASKMODAL | MB_OK ); - } - ); - - if (!Daemon_Singleton::init(argc, argv)) - return false; - - if (isDaemon) - { - LogPrint(eLogDebug, "Daemon: running as service"); - I2PService service((PSTR)SERVICE_NAME); - if (!I2PService::Run(service)) - { - LogPrint(eLogCritical, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError()); - return false; - } - return false; - } - - return true; - } - - bool DaemonWin32::start() - { - setlocale(LC_CTYPE, ""); - SetConsoleCP(1251); - SetConsoleOutputCP(1251); - //setlocale(LC_ALL, "Russian"); - setlocale(LC_TIME, "C"); -#ifdef WIN32_APP - if (!i2p::win32::StartWin32App (isDaemon)) return false; -#endif - bool ret = Daemon_Singleton::start(); - if (ret && i2p::log::Logger().GetLogType() == eLogFile) - { - // TODO: find out where this garbage to console comes from - SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE); - SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE); - } - bool insomnia; i2p::config::GetOption("insomnia", insomnia); - if (insomnia) - SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); - return ret; - } - - bool DaemonWin32::stop() - { -#ifdef WIN32_APP - i2p::win32::StopWin32App (); -#endif - return Daemon_Singleton::stop(); - } - - void DaemonWin32::run () - { -#ifdef WIN32_APP - i2p::win32::RunWin32App (); -#else - while (running) - { - std::this_thread::sleep_for (std::chrono::seconds(1)); - } -#endif - } -} -} -#endif //_WIN32 diff --git a/Win32/PurpleI2P.nsi b/Win32/PurpleI2P.nsi new file mode 100644 index 00000000..9171c47a --- /dev/null +++ b/Win32/PurpleI2P.nsi @@ -0,0 +1,282 @@ +# NSIS Installer script. (Tested with NSIS 2.64 on Windows 7) +# Author: Mikal Villa (Meeh) +# Version: 1.1 +Name PurpleI2P + +RequestExecutionLevel highest +SetCompressor /SOLID lzma +ShowInstDetails show + +# General Symbol Definitions +!define REGKEY "SOFTWARE\$(^Name)" +!define VERSION 0.3.0.0 +!define COMPANY "The Privacy Solutions Project" +!define URL "https://i2p.io" + +# MUI Symbol Definitions +!define MUI_ICON "ictoopie.ico" +#!define MUI_WELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp" +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_RIGHT +#!define MUI_HEADERIMAGE_BITMAP "../share/pixmaps/nsis-header.bmp" +!define MUI_FINISHPAGE_NOAUTOCLOSE +!define MUI_STARTMENUPAGE_REGISTRY_ROOT HKLM +!define MUI_STARTMENUPAGE_REGISTRY_KEY ${REGKEY} +!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME StartMenuGroup +!define MUI_STARTMENUPAGE_DEFAULTFOLDER PurpleI2P +!define MUI_FINISHPAGE_RUN $INSTDIR\i2pd.exe +!define MUI_FINISHPAGE_SHOWREADME $INSTDIR\Readme.txt + + +!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico" +!define MUI_UNWELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp" +!define MUI_UNFINISHPAGE_NOAUTOCLOSE + +# Included files +!include Sections.nsh +!include MUI2.nsh +!include nsDialogs.nsh +!include winmessages.nsh +!include logiclib.nsh +# Local included files +!include nsi\helper_readme.nsh +;!include nsi\servicelib.nsh + +# Variables +Var StartMenuGroup + +# Installer pages +# Execution flow of installer windows +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_README "../Readme.md" +!insertmacro MUI_PAGE_DIRECTORY +# Disabled for now. Use the bat +;Page custom mode_selection # Meeh's hack for installing and starting service. +!insertmacro MUI_PAGE_STARTMENU Application $StartMenuGroup +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH + +# Uninstall pages +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +# Installer languages +!insertmacro MUI_LANGUAGE English + +# Installer attributes +OutFile PurpleI2P-0.3.0.0-win32-setup.exe +InstallDir $PROGRAMFILES\PurpleI2P +CRCCheck on +XPStyle on +BrandingText " " +ShowInstDetails show +VIProductVersion 0.3.0.0 +VIAddVersionKey ProductName PurpleI2P +VIAddVersionKey ProductVersion "${VERSION}" +VIAddVersionKey CompanyName "${COMPANY}" +VIAddVersionKey CompanyWebsite "${URL}" +VIAddVersionKey FileVersion "${VERSION}" +VIAddVersionKey FileDescription "" +VIAddVersionKey LegalCopyright "" +InstallDirRegKey HKCU "${REGKEY}" Path +ShowUninstDetails show + +# Readme definitions + +;-------------------------------- +;Languages + ;Set up install lang strings for 1st lang + ${ReadmeLanguage} "${LANG_ENGLISH}" \ + "Read Me" \ + "Please review the following important information." \ + "About $(^name):" \ + "$\n Click on scrollbar arrows or press Page Down to review the entire text." + + ;Add 2nd language + !insertmacro MUI_LANGUAGE "Norwegian" + + ;set up install lang strings for second lang + ${ReadmeLanguage} "${LANG_NORWEGIAN}" \ + "Les meg!" \ + "Vennligst les informasjonen om hvordan du skal bruke PurpleI2P." \ + "Om $(^name):" \ + "$\n Klikk på scrollbaren til høyre for å se hele innholdet." + +;-------------------------------- + +# Installer sections +Section -Main SEC0000 + SetOutPath $INSTDIR + SetOverwrite on + File /oname=i2pd.exe Release\i2pd.exe + File /oname=install_service.bat install_service.bat + File /oname=uninstall_service.bat uninstall_service.bat + File /oname=LICENSE.txt ..\LICENSE + File /oname=Readme.txt ..\README.md + SetOutPath $INSTDIR\src + File /r /x *.nsi /x *.rc /x *.exe /x *.obj /x *.nsh /x *.sln /x *.vcxproj /x *.tlog /x *.log /x *.res /x *.pdb /x *.suo /x *.opensdf /x *.filters /x *.sdf /x *.iss /x *.aps /x .gitignore /x *.o ../\*.* + SetOutPath $INSTDIR + RMDir /r /REBOOTOK $INSTDIR\src\.git # Remove git directory + RMDir /r /REBOOTOK $INSTDIR\src\Win32\Release # Removing release directory + RMDir /r /REBOOTOK $INSTDIR\src\Win32\nsi + WriteRegStr HKCU "${REGKEY}\Components" Main 1 +SectionEnd + +Section -post SEC0001 + WriteRegStr HKCU "${REGKEY}" Path $INSTDIR + SetOutPath $INSTDIR + WriteUninstaller $INSTDIR\uninstall.exe + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + CreateDirectory $SMPROGRAMS\$StartMenuGroup + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\PurpleI2P.lnk" $INSTDIR\i2pd.exe + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Install PurpleI2P Service.lnk" $INSTDIR\install_service.bat + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P Service.lnk" $INSTDIR\uninstall_service.bat + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P.lnk" $INSTDIR\uninstall.exe + !insertmacro MUI_STARTMENU_WRITE_END + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "${VERSION}" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\uninstall.exe + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\uninstall.exe + WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1 + WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1 + WriteRegStr HKCR "i2pd" "URL Protocol" "" + WriteRegStr HKCR "i2pd" "" "URL:i2pd" # TODO: if a instance of own is found, relaunch with a proxyfied browser to open webage. (e.g i2pd://meeh.i2p) + WriteRegStr HKCR "i2pd\DefaultIcon" "" $INSTDIR\i2pd.exe + WriteRegStr HKCR "i2pd\shell\open\command" "" '"$INSTDIR\i2pd.exe" "%1"' +SectionEnd + +# Macro for selecting uninstaller sections +!macro SELECT_UNSECTION SECTION_NAME UNSECTION_ID + Push $R0 + ReadRegStr $R0 HKCU "${REGKEY}\Components" "${SECTION_NAME}" + StrCmp $R0 1 0 next${UNSECTION_ID} + !insertmacro SelectSection "${UNSECTION_ID}" + GoTo done${UNSECTION_ID} +next${UNSECTION_ID}: + !insertmacro UnselectSection "${UNSECTION_ID}" +done${UNSECTION_ID}: + Pop $R0 +!macroend + + +# Uninstaller sections +Section /o -un.Main UNSEC0000 + Delete /REBOOTOK $INSTDIR\i2pd.exe + Delete /REBOOTOK $INSTDIR\LICENSE.txt + Delete /REBOOTOK $INSTDIR\Readme.txt + Delete /REBOOTOK $INSTDIR\install_service.bat + Delete /REBOOTOK $INSTDIR\uninstall_service.bat + RMDir /r /REBOOTOK $INSTDIR\src + DeleteRegValue HKCU "${REGKEY}\Components" Main +SectionEnd + +Section -un.post UNSEC0001 + DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P.lnk" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\PurpleI2P.lnk" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Install PurpleI2P Service.lnk" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\UnInstall PurpleI2P Service.lnk" + Delete /REBOOTOK "$SMSTARTUP\PurpleI2P.lnk" + Delete /REBOOTOK $INSTDIR\uninstall.exe + Delete /REBOOTOK $INSTDIR\debug.log + DeleteRegValue HKCU "${REGKEY}" StartMenuGroup + DeleteRegValue HKCU "${REGKEY}" Path + DeleteRegKey /IfEmpty HKCU "${REGKEY}\Components" + DeleteRegKey /IfEmpty HKCU "${REGKEY}" + DeleteRegKey HKCR "i2pd" + RmDir /REBOOTOK $SMPROGRAMS\$StartMenuGroup + RmDir /REBOOTOK $INSTDIR + Push $R0 + StrCpy $R0 $StartMenuGroup 1 + StrCmp $R0 ">" no_smgroup +no_smgroup: + Pop $R0 +SectionEnd + +; var hwndExecModeRadio +; var hwndRunServiceNowRadio + +; Function mode_selection +; nsDialogs::Create 1018 +; Pop $0 +; ${NSD_CreateLabel} 0 10 75% 20u "How would you like PurpleI2P (i2pd) to run?" +; Pop $0 + +; ${NSD_CreateRadioButton} 20 60 80% 25u "Service Mode" +; Pop $hwndExecModeRadio +; ${NSD_AddStyle} $hwndExecModeRadio ${WS_GROUP} + +; ${NSD_CreateRadioButton} 20 90 80% 25u "Command line Mode" +; Pop $0 + +; ${NSD_CreateButton} 20 150 -40 14u "Do it!" +; Pop $0 +; ${NSD_OnClick} $0 perform_mode + +; nsDialogs::Show +; FunctionEnd + +; Function start_now_selection +; nsDialogs::Create 1018 +; Pop $0 +; ${NSD_CreateLabel} 0 10 75% 20u "Enable the service now?" +; Pop $0 + +; ${NSD_CreateRadioButton} 20 60 80% 25u "Yes" +; Pop $hwndRunServiceNowRadio +; ${NSD_AddStyle} $hwndRunServiceNowRadio ${WS_GROUP} + +; ${NSD_CreateRadioButton} 20 90 80% 25u "No" +; Pop $0 + +; ${NSD_CreateButton} 20 150 -40 14u "Do it!" +; Pop $0 +; ${NSD_OnClick} $0 perform_mode + +; nsDialogs::Show +; FunctionEnd + +; Function perform_mode +; ${NSD_GetState} $hwndExecModeRadio $0 +; ${If} $0 = ${BST_CHECKED} +; Call service_mode +; ${EndIF} +; FunctionEnd + +; Function start_now +; ${NSD_GetState} $hwndRunServiceNowRadio $0 +; ${If} $0 = ${BST_CHECKED} +; Call start_now_selection +; ${EndIF} +; FunctionEnd + +; Function service_mode +; Push "create" +; Push "PurpleI2P Service" +; Push "$INSTDIR\i2pd.exe;autostart=1;display=PurpleI2P" +; Call Service +; Pop $0 ; Actually more to write than !insertmacro, but much more fun :D +; Push "start" +; Push "PurpleI2P Service" +; Call Service +; Pop $0 +; Call start_now +; !define MUI_FINISHPAGE_RUN_NOTCHECKED +; !define MUI_FINISHPAGE_RUN_TEXT "No need to run now since we already installed and launched it as a Windows service!" +; FunctionEnd + +# Installer functions +Function .onInit + InitPluginsDir + !insertmacro MUI_LANGDLL_DISPLAY +FunctionEnd + +# Uninstaller functions +Function un.onInit + ReadRegStr $INSTDIR HKCU "${REGKEY}" Path + !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuGroup + !insertmacro SELECT_UNSECTION Main ${UNSEC0000} + !insertmacro MUI_UNGETLANGUAGE +FunctionEnd \ No newline at end of file diff --git a/Win32/README-Build.txt b/Win32/README-Build.txt new file mode 100644 index 00000000..dbdc30a2 --- /dev/null +++ b/Win32/README-Build.txt @@ -0,0 +1,84 @@ +Building i2pd for Windows +========================= + +Requirements for building: + +* Visual Studio 2013 (tested with VS2013 Update 1, Update 3, and Update 4 RC) +* Boost (tested with 1.56 and 1.57) +* Crypto++ (tested with 5.6.2) + + +Building Boost (32-bit) +----------------------- + +Open a Visual Studio x86 command prompt and run the following: + + cd C:\path\to\boost\sources + bootstrap + b2 toolset=msvc-12.0 --build-type=complete --libdir=C:\Boost\lib\Win32 install --with-filesystem --with-program_options --with-regex --with-date_time + + +Building Boost (64-bit) +----------------------- + +Open a Visual Studio x64 command prompt and run the following: + + cd C:\path\to\boost\sources + bootstrap + b2 toolset=msvc-12.0 --build-type=complete --libdir=C:\Boost\lib\x64 architecture=x86 address-model=64 install --with-filesystem --with-program_options --with-regex --with-date_time + +After Boost is compiled, set the environment variable `BOOST` to the directory +Boost was installed to. If you followed the instructions outlined here, you +should set it to `C:\Boost`. Additionally, set the BOOSTVER variable to the +version of Boost that you're using, but instead of a '.' use a '_'. For +example, I have `BOOSTVER` set to `1_57`. + +Building Crypto++ +----------------- + +* Open the crypttest Solution in VS2013 +* Visual Studio will ask to update the Solution/Project. Allow it. +* Build the `cryptopp` project, both the Debug and Release targets and for both + Win32 and x64. +* Create a folder called `cryptopp` in the crypto++ source directory, then copy + the header files to this new directory. +* Set the `CRYPTOPP` environment variable pointing to the Crypto++ source directory. + + +Building i2pd +------------- + +## Prep work ## + +I strongly advise setting up your own `INCLUDES` and `LIBS` instead of relying +on the settings in the i2pd project file. By using your own settings, if the +i2pd devs change the paths in the project file, your builds will still work. + +To do this, create or edit the file +`%localappdata%\Microsoft\MSBuild\v4.0\Microsoft.Cpp.Win32.user`. + +For comparison, my file is reproduced below: + + + + + + + + $(CRYPTOPP)\$(Platform)\Output\$(Configuration);$(BOOST)\lib\$(Platform);$(LibraryPath) + $(CRYPTOPP);$(BOOST)\include\boost-$(BOOSTVER);$(IncludePath) + + + + + + +If you want to build x64 binaries as well, you'll want to edit or create the +file `%localappdata%\Microsoft\MSBuild\v4.0\Microsoft.Cpp.x64.user`. If you +followed the steps outlined earlier you can copy (or link) the win32 file to +the x64 one. + +## Anti-Climatic End ## + +After following the above instructions, you'll be able to build Debug Win32, +Debug x64, Release Win32, and Release x64 i2pd binaries. diff --git a/Win32/Resource.rc b/Win32/Resource.rc index c9266b08..a4fa890b 100644 Binary files a/Win32/Resource.rc and b/Win32/Resource.rc differ diff --git a/Win32/Resource.rc2 b/Win32/Resource.rc2 deleted file mode 100644 index 32744584..00000000 --- a/Win32/Resource.rc2 +++ /dev/null @@ -1,38 +0,0 @@ -#ifdef APSTUDIO_INVOKED -#error this file is not editable by Microsoft Visual C++ -#endif //APSTUDIO_INVOKED - -#include "version.h" - -VS_VERSION_INFO VERSIONINFO - FILEVERSION I2PD_VERSION_MAJOR,I2PD_VERSION_MINOR,I2PD_VERSION_MICRO,I2PD_VERSION_PATCH - PRODUCTVERSION I2P_VERSION_MAJOR,I2P_VERSION_MINOR,I2P_VERSION_MICRO,I2P_VERSION_PATCH - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "Purple I2P" - VALUE "FileDescription", "C++ I2P daemon" - VALUE "FileVersion", I2PD_VERSION - VALUE "InternalName", CODENAME - VALUE "LegalCopyright", "Copyright (C) 2013-2023, The PurpleI2P Project" - VALUE "OriginalFilename", "i2pd" - VALUE "ProductName", "Purple I2P" - VALUE "ProductVersion", I2P_VERSION - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END diff --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp deleted file mode 100644 index 0e29c517..00000000 --- a/Win32/Win32App.cpp +++ /dev/null @@ -1,502 +0,0 @@ -/* -* Copyright (c) 2013-2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include -#include -#include "ClientContext.h" -#include "Config.h" -#include "NetDb.hpp" -#include "RouterContext.h" -#include "Transports.h" -#include "Tunnel.h" -#include "version.h" -#include "resource.h" -#include "Daemon.h" -#include "Win32App.h" -#include "Win32NetState.h" - -#define ID_ABOUT 2000 -#define ID_EXIT 2001 -#define ID_CONSOLE 2002 -#define ID_APP 2003 -#define ID_GRACEFUL_SHUTDOWN 2004 -#define ID_STOP_GRACEFUL_SHUTDOWN 2005 -#define ID_RELOAD 2006 -#define ID_ACCEPT_TRANSIT 2007 -#define ID_DECLINE_TRANSIT 2008 -#define ID_DATADIR 2009 - -#define ID_TRAY_ICON 2050 -#define WM_TRAYICON (WM_USER + 1) - -#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100 -#define FRAME_UPDATE_TIMER 2101 -#define IDT_GRACEFUL_TUNNELCHECK_TIMER 2102 - -namespace i2p -{ -namespace win32 -{ - DWORD g_GracefulShutdownEndtime = 0; - bool g_isWinService; - - static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem) - { - HMENU hPopup = CreatePopupMenu(); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DATADIR, "Open &datadir"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "&Show app"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About..."); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); - if(!i2p::context.AcceptsTunnels()) - InsertMenu (hPopup, -1, - i2p::util::DaemonWin32::Instance ().isGraceful ? MF_BYPOSITION | MF_STRING | MF_GRAYED : MF_BYPOSITION | MF_STRING, - ID_ACCEPT_TRANSIT, "Accept &transit"); - else - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config"); - if (!i2p::util::DaemonWin32::Instance ().isGraceful) - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown"); - else - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown"); - InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit"); - SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE); - SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0); - - POINT p; - if (!curpos) - { - GetCursorPos (&p); - curpos = &p; - } - - WORD cmd = TrackPopupMenu (hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, curpos->x, curpos->y, 0, hWnd, NULL); - SendMessage (hWnd, WM_COMMAND, cmd, 0); - - DestroyMenu(hPopup); - } - - static void AddTrayIcon (HWND hWnd, bool notify = false) - { - NOTIFYICONDATA nid; - memset(&nid, 0, sizeof(nid)); - nid.cbSize = sizeof(nid); - nid.hWnd = hWnd; - nid.uID = ID_TRAY_ICON; - nid.uFlags = notify ? NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO : NIF_ICON | NIF_MESSAGE | NIF_TIP; - nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO; - nid.uCallbackMessage = WM_TRAYICON; - nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON)); - strcpy (nid.szTip, "i2pd"); - if (notify) strcpy (nid.szInfo, "i2pd is starting"); - Shell_NotifyIcon(NIM_ADD, &nid ); - } - - static void RemoveTrayIcon (HWND hWnd) - { - NOTIFYICONDATA nid; - nid.hWnd = hWnd; - nid.uID = ID_TRAY_ICON; - Shell_NotifyIcon (NIM_DELETE, &nid); - } - - static void ShowUptime (std::stringstream& s, int seconds) - { - int num; - - if ((num = seconds / 86400) > 0) { - s << num << " days, "; - seconds -= num * 86400; - } - if ((num = seconds / 3600) > 0) { - s << num << " hours, "; - seconds -= num * 3600; - } - if ((num = seconds / 60) > 0) { - s << num << " min, "; - seconds -= num * 60; - } - s << seconds << " seconds\n"; - } - - template static void ShowTransfered (std::stringstream& s, size transfer) - { - auto bytes = transfer & 0x03ff; - transfer >>= 10; - auto kbytes = transfer & 0x03ff; - transfer >>= 10; - auto mbytes = transfer & 0x03ff; - transfer >>= 10; - auto gbytes = transfer; - - if (gbytes) - s << gbytes << " GB, "; - if (mbytes) - s << mbytes << " MB, "; - if (kbytes) - s << kbytes << " KB, "; - s << bytes << " Bytes\n"; - } - - static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, bool testing, RouterError error) - { - switch (status) - { - case eRouterStatusOK: s << "OK"; break; - case eRouterStatusFirewalled: s << "FW"; break; - case eRouterStatusUnknown: s << "Unk"; break; - case eRouterStatusProxy: s << "Proxy"; break; - case eRouterStatusMesh: s << "Mesh"; break; - default: s << "Unk"; - }; - if (testing) - s << " (Test)"; - if (error != eRouterErrorNone) - { - switch (error) - { - case eRouterErrorClockSkew: - s << " - " << tr("Clock skew"); - break; - case eRouterErrorOffline: - s << " - " << tr("Offline"); - break; - case eRouterErrorSymmetricNAT: - s << " - " << tr("Symmetric NAT"); - break; - case eRouterErrorFullConeNAT: - s << " - " << tr("Full cone NAT"); - break; - case eRouterErrorNoDescriptors: - s << " - " << tr("No Descriptors"); - break; - default: ; - } - } - } - - static void PrintMainWindowText (std::stringstream& s) - { - s << "\n"; - s << "Status: "; - ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetTesting(), i2p::context.GetError ()); - if (i2p::context.SupportsV6 ()) - { - s << " / "; - ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetTestingV6(), i2p::context.GetErrorV6 ()); - } - s << "; "; - s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n"; - s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); - if (g_GracefulShutdownEndtime != 0) - { - DWORD GracefulTimeLeft = (g_GracefulShutdownEndtime - GetTickCount()) / 1000; - s << "Graceful shutdown, time left: "; ShowUptime(s, GracefulTimeLeft); - } - else - s << "\n"; - s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; "; - s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n"; - s << "Received: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes()); - s << "Sent: "; ShowTransfered (s, i2p::transport::transports.GetTotalSentBytes()); - s << "\n"; - s << "Routers: " << i2p::data::netdb.GetNumRouters () << "; "; - s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "; "; - s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "\n"; - s << "Tunnels: "; - s << "In: " << i2p::tunnel::tunnels.CountInboundTunnels() << "; "; - s << "Out: " << i2p::tunnel::tunnels.CountOutboundTunnels() << "; "; - s << "Transit: " << i2p::tunnel::tunnels.CountTransitTunnels() << "\n"; - s << "\n"; - } - - static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - static UINT s_uTaskbarRestart; - - switch (uMsg) - { - case WM_CREATE: - { - s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); - AddTrayIcon (hWnd, true); - break; - } - case WM_CLOSE: - { - RemoveTrayIcon (hWnd); - KillTimer (hWnd, FRAME_UPDATE_TIMER); - KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER); - KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER); - PostQuitMessage (0); - break; - } - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case ID_ABOUT: - { - std::stringstream text; - text << "Version: " << I2PD_VERSION << " " << CODENAME; - MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); - return 0; - } - case ID_EXIT: - { - PostMessage (hWnd, WM_CLOSE, 0, 0); - return 0; - } - case ID_ACCEPT_TRANSIT: - { - i2p::context.SetAcceptsTunnels (true); - std::stringstream text; - text << "I2Pd now accept transit tunnels"; - MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); - return 0; - } - case ID_DECLINE_TRANSIT: - { - i2p::context.SetAcceptsTunnels (false); - std::stringstream text; - text << "I2Pd now decline new transit tunnels"; - MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); - return 0; - } - case ID_GRACEFUL_SHUTDOWN: - { - i2p::context.SetAcceptsTunnels (false); - SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes - SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second - g_GracefulShutdownEndtime = GetTickCount() + 10*60*1000; - i2p::util::DaemonWin32::Instance ().isGraceful = true; - return 0; - } - case ID_STOP_GRACEFUL_SHUTDOWN: - { - i2p::context.SetAcceptsTunnels (true); - KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER); - KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER); - g_GracefulShutdownEndtime = 0; - i2p::util::DaemonWin32::Instance ().isGraceful = false; - return 0; - } - case ID_RELOAD: - { - i2p::client::context.ReloadConfig(); - std::stringstream text; - text << "I2Pd reloading configs..."; - MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK ); - return 0; - } - case ID_CONSOLE: - { - char buf[30]; - std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); - uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); - snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort); - ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL); - return 0; - } - case ID_APP: - { - ShowWindow(hWnd, SW_SHOW); - SetTimer(hWnd, FRAME_UPDATE_TIMER, 3000, NULL); - return 0; - } - case ID_DATADIR: - { - std::string datadir(i2p::fs::GetDataDir()); - ShellExecute(NULL, "explore", datadir.c_str(), NULL, NULL, SW_SHOWNORMAL); - return 0; - } - } - break; - } - case WM_SYSCOMMAND: - { - switch (wParam) - { - case SC_MINIMIZE: - { - ShowWindow(hWnd, SW_HIDE); - KillTimer (hWnd, FRAME_UPDATE_TIMER); - return 0; - } - case SC_CLOSE: - { - std::string close; i2p::config::GetOption("close", close); - if (0 == close.compare("ask")) - switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?" - " You can add 'close' configuration option. Valid values are: ask, minimize, exit.", - "Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1)) - { - case IDYES: close = "minimize"; break; - case IDNO: close = "exit"; break; - default: return 0; - } - if (0 == close.compare("minimize")) - { - ShowWindow(hWnd, SW_HIDE); - KillTimer (hWnd, FRAME_UPDATE_TIMER); - return 0; - } - if (0 != close.compare("exit")) - { - ::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING); - return 0; - } - } - } - [[fallthrough]]; - } - case WM_TRAYICON: - { - switch (lParam) - { - case WM_LBUTTONUP: - case WM_RBUTTONUP: - { - SetForegroundWindow (hWnd); - ShowPopupMenu(hWnd, NULL, -1); - PostMessage (hWnd, WM_APP + 1, 0, 0); - break; - } - } - break; - } - case WM_TIMER: - { - switch(wParam) - { - case IDT_GRACEFUL_SHUTDOWN_TIMER: - { - g_GracefulShutdownEndtime = 0; - PostMessage (hWnd, WM_CLOSE, 0, 0); // exit - return 0; - } - case IDT_GRACEFUL_TUNNELCHECK_TIMER: - { - if (i2p::tunnel::tunnels.CountTransitTunnels() == 0) - PostMessage (hWnd, WM_CLOSE, 0, 0); - else - SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); - return 0; - } - case FRAME_UPDATE_TIMER: - { - InvalidateRect(hWnd, NULL, TRUE); - return 0; - } - } - break; - } - case WM_PAINT: - { - HDC hDC; - PAINTSTRUCT ps; - RECT rp; - HFONT hFont; - std::stringstream s; PrintMainWindowText (s); - hDC = BeginPaint (hWnd, &ps); - GetClientRect(hWnd, &rp); - SetTextColor(hDC, 0x00D43B69); - hFont = CreateFont(18,0,0,0,0,0,0,0,DEFAULT_CHARSET,0,0,0,0,TEXT("Times New Roman")); - SelectObject(hDC,hFont); - DrawText(hDC, TEXT(s.str().c_str()), s.str().length(), &rp, DT_CENTER|DT_VCENTER); - DeleteObject(hFont); - EndPaint(hWnd, &ps); - break; - } - default: - { - if (uMsg == s_uTaskbarRestart) - AddTrayIcon (hWnd, false); - break; - } - } - return DefWindowProc( hWnd, uMsg, wParam, lParam); - } - - bool StartWin32App (bool isWinService) - { - g_isWinService = isWinService; - if (FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"))) - { - MessageBox(NULL, TEXT("I2Pd is running already"), TEXT("Warning"), MB_OK); - return false; - } - // register main window - auto hInst = GetModuleHandle(NULL); - WNDCLASSEX wclx; - memset (&wclx, 0, sizeof(wclx)); - wclx.cbSize = sizeof(wclx); - wclx.style = 0; - wclx.lpfnWndProc = WndProc; - //wclx.cbClsExtra = 0; - //wclx.cbWndExtra = 0; - wclx.hInstance = hInst; - wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON)); - wclx.hCursor = LoadCursor (NULL, IDC_ARROW); - //wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); - wclx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wclx.lpszMenuName = NULL; - wclx.lpszClassName = I2PD_WIN32_CLASSNAME; - RegisterClassEx (&wclx); - // create new window - if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 100, 100, 350, 210, NULL, NULL, hInst, NULL)) - { - MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST); - return false; - } - // COM requires message loop to work, which is not implemented in service mode - if (!g_isWinService) - SubscribeToEvents(); - return true; - } - - int RunWin32App () - { - MSG msg; - while (GetMessage (&msg, NULL, 0, 0 )) - { - TranslateMessage (&msg); - DispatchMessage (&msg); - } - return msg.wParam; - } - - void StopWin32App () - { - HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")); - if (hWnd) - PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_EXIT, 0), 0); - else if(!g_isWinService) - UnSubscribeFromEvents(); - UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL)); - } - - bool GracefulShutdown () - { - HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")); - if (hWnd) - PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0); - return hWnd; - } - - bool StopGracefulShutdown () - { - HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")); - if (hWnd) - PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_STOP_GRACEFUL_SHUTDOWN, 0), 0); - return hWnd; - } -} -} diff --git a/Win32/Win32App.h b/Win32/Win32App.h deleted file mode 100644 index 614de738..00000000 --- a/Win32/Win32App.h +++ /dev/null @@ -1,27 +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 WIN32APP_H__ -#define WIN32APP_H__ - -#define I2PD_WIN32_CLASSNAME "i2pd main window" - -namespace i2p -{ -namespace win32 -{ - extern DWORD g_GracefulShutdownEndtime; - - bool StartWin32App (bool isWinService); - void StopWin32App (); - int RunWin32App (); - bool GracefulShutdown (); - bool StopGracefulShutdown (); -} -} -#endif // WIN32APP_H__ diff --git a/Win32/Win32NetState.cpp b/Win32/Win32NetState.cpp deleted file mode 100644 index 4ef768c8..00000000 --- a/Win32/Win32NetState.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* -* Copyright (c) 2013-2024, 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 -*/ - -#if WINVER != 0x0501 // supported since Vista -#include "Win32NetState.h" -#include -#include "Log.h" - -IUnknown *pUnknown = nullptr; -INetworkListManager *pNetworkListManager = nullptr; -IConnectionPointContainer *pCPContainer = nullptr; -IConnectionPoint *pConnectPoint = nullptr; -CNetworkListManagerEvent *pNetEvent = nullptr; -DWORD Cookie = 0; - -void SubscribeToEvents() -{ - LogPrint(eLogInfo, "NetState: Trying to subscribe to NetworkListManagerEvents"); - CoInitialize(NULL); - - HRESULT Result = CoCreateInstance(CLSID_NetworkListManager, NULL, CLSCTX_ALL, IID_IUnknown, (void **)&pUnknown); - if (SUCCEEDED(Result)) - { - Result = pUnknown->QueryInterface(IID_INetworkListManager, (void **)&pNetworkListManager); - if (SUCCEEDED(Result)) - { - VARIANT_BOOL IsConnect = VARIANT_FALSE; -#if defined(_MSC_VER) - Result = pNetworkListManager->get_IsConnectedToInternet(&IsConnect); -#else - Result = pNetworkListManager->IsConnectedToInternet(&IsConnect); -#endif - if (SUCCEEDED(Result)) { - i2p::transport::transports.SetOnline (true); - LogPrint(eLogInfo, "NetState: Current state: ", IsConnect == VARIANT_TRUE ? "connected" : "disconnected"); - } - - Result = pNetworkListManager->QueryInterface(IID_IConnectionPointContainer, (void **)&pCPContainer); - if (SUCCEEDED(Result)) - { - Result = pCPContainer->FindConnectionPoint(IID_INetworkListManagerEvents, &pConnectPoint); - if(SUCCEEDED(Result)) - { - pNetEvent = new CNetworkListManagerEvent; - Result = pConnectPoint->Advise((IUnknown *)pNetEvent, &Cookie); - if (SUCCEEDED(Result)) - LogPrint(eLogInfo, "NetState: Successfully subscribed to NetworkListManagerEvent messages"); - else - LogPrint(eLogError, "NetState: Unable to subscribe to NetworkListManagerEvent messages"); - } else - LogPrint(eLogError, "NetState: Unable to find interface connection point"); - } else - LogPrint(eLogError, "NetState: Unable to query NetworkListManager interface"); - } else - LogPrint(eLogError, "NetState: Unable to query global interface"); - } else - LogPrint(eLogError, "NetState: Unable to create INetworkListManager interface"); -} - -void UnSubscribeFromEvents() -{ - LogPrint(eLogInfo, "NetState: Unsubscribing from NetworkListManagerEvents"); - try - { - if (pConnectPoint) { - pConnectPoint->Unadvise(Cookie); - pConnectPoint->Release(); - } - - if (pNetEvent) - { - pNetEvent->Release(); - } - - if (pCPContainer) - { - pCPContainer->Release(); - } - - if (pNetworkListManager) - { - pNetworkListManager->Release(); - } - - if (pUnknown) - { - pUnknown->Release(); - } - - CoUninitialize(); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "NetState: Received exception: ", ex.what ()); - } -} - -#endif // WINVER diff --git a/Win32/Win32NetState.h b/Win32/Win32NetState.h deleted file mode 100644 index c1f47a24..00000000 --- a/Win32/Win32NetState.h +++ /dev/null @@ -1,92 +0,0 @@ -/* -* Copyright (c) 2013-2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef WIN_32_NETSTATE_H__ -#define WIN_32_NETSTATE_H__ - -#if WINVER != 0x0501 // supported since Vista -#include -#include -#include "Log.h" -#include "Transports.h" - -class CNetworkListManagerEvent final : public INetworkListManagerEvents -{ -public: - CNetworkListManagerEvent() : m_ref(1) { } - ~CNetworkListManagerEvent() { } - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) - { - if (IsEqualIID(riid, IID_IUnknown)) { - *ppvObject = (IUnknown *)this; - } else if (IsEqualIID(riid ,IID_INetworkListManagerEvents)) { - *ppvObject = (INetworkListManagerEvents *)this; - } else { - return E_NOINTERFACE; - } - AddRef(); - return S_OK; - } - - ULONG STDMETHODCALLTYPE AddRef() - { - return (ULONG)InterlockedIncrement(&m_ref); - } - - ULONG STDMETHODCALLTYPE Release() - { - LONG Result = InterlockedDecrement(&m_ref); - if (Result == 0) - delete this; - return (ULONG)Result; - } - - virtual HRESULT STDMETHODCALLTYPE ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) - { - if (newConnectivity == NLM_CONNECTIVITY_DISCONNECTED) { - i2p::transport::transports.SetOnline (false); - LogPrint(eLogInfo, "NetState: disconnected from network"); - } - - if (((int)newConnectivity & (int)NLM_CONNECTIVITY_IPV4_INTERNET) != 0) { - i2p::transport::transports.SetOnline (true); - LogPrint(eLogInfo, "NetState: connected to internet with IPv4 capability"); - } - - if (((int)newConnectivity & (int)NLM_CONNECTIVITY_IPV6_INTERNET) != 0) { - i2p::transport::transports.SetOnline (true); - LogPrint(eLogInfo, "NetState: connected to internet with IPv6 capability"); - } - - if ( - (((int)newConnectivity & (int)NLM_CONNECTIVITY_IPV4_INTERNET) == 0) && - (((int)newConnectivity & (int)NLM_CONNECTIVITY_IPV6_INTERNET) == 0) - ) { - i2p::transport::transports.SetOnline (false); - LogPrint(eLogInfo, "NetState: connected without internet access"); - } - - return S_OK; - } - -private: - - LONG m_ref; -}; - -void SubscribeToEvents(); -void UnSubscribeFromEvents(); - -#else // WINVER == 0x0501 - -void SubscribeToEvents() { } -void UnSubscribeFromEvents() { } - -#endif // WINVER -#endif diff --git a/Win32/Win32Service.cpp b/Win32/Win32Service.cpp index 057a1edd..e5abc72a 100644 --- a/Win32/Win32Service.cpp +++ b/Win32/Win32Service.cpp @@ -1,27 +1,25 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS // to use freopen +#endif #include "Win32Service.h" #include +#include #include -#include "Daemon.h" -#include "Log.h" +#include "../Daemon.h" +#include "../Log.h" I2PService *I2PService::s_service = NULL; BOOL I2PService::isService() { BOOL bIsService = FALSE; + HWINSTA hWinStation = GetProcessWindowStation(); if (hWinStation != NULL) { - USEROBJECTFLAGS uof = { FALSE, FALSE, 0 }; + USEROBJECTFLAGS uof = { 0 }; if (GetUserObjectInformation(hWinStation, UOI_FLAGS, &uof, sizeof(USEROBJECTFLAGS), NULL) && ((uof.dwFlags & WSF_VISIBLE) == 0)) { bIsService = TRUE; @@ -33,23 +31,28 @@ BOOL I2PService::isService() BOOL I2PService::Run(I2PService &service) { s_service = &service; + SERVICE_TABLE_ENTRY serviceTable[] = { { service.m_name, ServiceMain }, { NULL, NULL } }; + return StartServiceCtrlDispatcher(serviceTable); } + void WINAPI I2PService::ServiceMain(DWORD dwArgc, PSTR *pszArgv) { assert(s_service != NULL); + s_service->m_statusHandle = RegisterServiceCtrlHandler( s_service->m_name, ServiceCtrlHandler); if (s_service->m_statusHandle == NULL) { throw GetLastError(); } + s_service->Start(dwArgc, pszArgv); } @@ -58,23 +61,27 @@ void WINAPI I2PService::ServiceCtrlHandler(DWORD dwCtrl) { switch (dwCtrl) { - case SERVICE_CONTROL_STOP: s_service->Stop(); break; - case SERVICE_CONTROL_PAUSE: s_service->Pause(); break; - case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break; - case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break; - case SERVICE_CONTROL_INTERROGATE: break; - default: break; + case SERVICE_CONTROL_STOP: s_service->Stop(); break; + case SERVICE_CONTROL_PAUSE: s_service->Pause(); break; + case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break; + case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break; + case SERVICE_CONTROL_INTERROGATE: break; + default: break; } } + I2PService::I2PService(PSTR pszServiceName, BOOL fCanStop, BOOL fCanShutdown, BOOL fCanPauseContinue) { - m_name = (pszServiceName == NULL) ? (PSTR)"" : pszServiceName; + m_name = (pszServiceName == NULL) ? "" : pszServiceName; + m_statusHandle = NULL; + m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + m_status.dwCurrentState = SERVICE_START_PENDING; DWORD dwControlsAccepted = 0; @@ -84,14 +91,16 @@ I2PService::I2PService(PSTR pszServiceName, dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN; if (fCanPauseContinue) dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE; - m_status.dwControlsAccepted = dwControlsAccepted; + m_status.dwWin32ExitCode = NO_ERROR; m_status.dwServiceSpecificExitCode = 0; m_status.dwCheckPoint = 0; m_status.dwWaitHint = 0; + m_fStopping = FALSE; - // Create a manual-reset event that is not signaled at first to indicate + + // Create a manual-reset event that is not signaled at first to indicate // the stopped signal of the service. m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (m_hStoppedEvent == NULL) @@ -100,6 +109,7 @@ I2PService::I2PService(PSTR pszServiceName, } } + I2PService::~I2PService(void) { if (m_hStoppedEvent) @@ -109,69 +119,93 @@ I2PService::~I2PService(void) } } + void I2PService::Start(DWORD dwArgc, PSTR *pszArgv) { try { SetServiceStatus(SERVICE_START_PENDING); + OnStart(dwArgc, pszArgv); + SetServiceStatus(SERVICE_RUNNING); } catch (DWORD dwError) { - LogPrint(eLogCritical, "Win32Service: Start error: ", dwError); + LogPrint("Win32Service Start", dwError); + SetServiceStatus(SERVICE_STOPPED, dwError); } catch (...) { - LogPrint(eLogCritical, "Win32Service: failed to start: ", EVENTLOG_ERROR_TYPE); + LogPrint("Win32Service failed to start.", EVENTLOG_ERROR_TYPE); + SetServiceStatus(SERVICE_STOPPED); } } + void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv) { - LogPrint(eLogInfo, "Win32Service: in OnStart (", EVENTLOG_INFORMATION_TYPE, ")"); + LogPrint("Win32Service in OnStart", + EVENTLOG_INFORMATION_TYPE); + Daemon.start(); + + //i2p::util::config::OptionParser(dwArgc, pszArgv); + //i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs); + //i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"), + // i2p::util::config::GetArg("-port", 17070)); + _worker = new std::thread(std::bind(&I2PService::WorkerThread, this)); } + void I2PService::WorkerThread() { while (!m_fStopping) { - ::Sleep(1000); // Simulate some lengthy operations. + ::Sleep(1000); // Simulate some lengthy operations. } + // Signal the stopped event. SetEvent(m_hStoppedEvent); } + void I2PService::Stop() { DWORD dwOriginalState = m_status.dwCurrentState; try { SetServiceStatus(SERVICE_STOP_PENDING); + OnStop(); + SetServiceStatus(SERVICE_STOPPED); } catch (DWORD dwError) { - LogPrint(eLogInfo, "Win32Service: Stop error: ", dwError); + LogPrint("Win32Service Stop", dwError); + SetServiceStatus(dwOriginalState); } catch (...) { - LogPrint(eLogCritical, "Win32Service: Failed to stop: ", EVENTLOG_ERROR_TYPE); + LogPrint("Win32Service failed to stop.", EVENTLOG_ERROR_TYPE); + SetServiceStatus(dwOriginalState); } } + void I2PService::OnStop() { // Log a service stop message to the Application log. - LogPrint(eLogInfo, "Win32Service: in OnStop (", EVENTLOG_INFORMATION_TYPE, ")"); + LogPrint("Win32Service in OnStop", EVENTLOG_INFORMATION_TYPE); + Daemon.stop(); + m_fStopping = TRUE; if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0) { @@ -181,83 +215,102 @@ void I2PService::OnStop() delete _worker; } + void I2PService::Pause() { try { SetServiceStatus(SERVICE_PAUSE_PENDING); + OnPause(); + SetServiceStatus(SERVICE_PAUSED); } catch (DWORD dwError) { - LogPrint(eLogCritical, "Win32Service: Pause error: ", dwError); + LogPrint("Win32Service Pause", dwError); + SetServiceStatus(SERVICE_RUNNING); } catch (...) { - LogPrint(eLogCritical, "Win32Service: Failed to pause: ", EVENTLOG_ERROR_TYPE); + LogPrint("Win32Service failed to pause.", EVENTLOG_ERROR_TYPE); + SetServiceStatus(SERVICE_RUNNING); } } + void I2PService::OnPause() { } + void I2PService::Continue() { try { SetServiceStatus(SERVICE_CONTINUE_PENDING); + OnContinue(); + SetServiceStatus(SERVICE_RUNNING); } catch (DWORD dwError) { - LogPrint(eLogCritical, "Win32Service: Continue error: ", dwError); + LogPrint("Win32Service Continue", dwError); + SetServiceStatus(SERVICE_PAUSED); } catch (...) { - LogPrint(eLogCritical, "Win32Service: Failed to resume: ", EVENTLOG_ERROR_TYPE); + LogPrint("Win32Service failed to resume.", EVENTLOG_ERROR_TYPE); + SetServiceStatus(SERVICE_PAUSED); } } + void I2PService::OnContinue() { } + void I2PService::Shutdown() { try { OnShutdown(); + SetServiceStatus(SERVICE_STOPPED); } catch (DWORD dwError) { - LogPrint(eLogCritical, "Win32Service: Shutdown error: ", dwError); + LogPrint("Win32Service Shutdown", dwError); } catch (...) { - LogPrint(eLogCritical, "Win32Service: Failed to shut down: ", EVENTLOG_ERROR_TYPE); + LogPrint("Win32Service failed to shut down.", EVENTLOG_ERROR_TYPE); } } + void I2PService::OnShutdown() { } + void I2PService::SetServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) { static DWORD dwCheckPoint = 1; + + m_status.dwCurrentState = dwCurrentState; m_status.dwWin32ExitCode = dwWin32ExitCode; m_status.dwWaitHint = dwWaitHint; + m_status.dwCheckPoint = ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) ? @@ -281,3 +334,129 @@ void FreeHandles(SC_HANDLE schSCManager, SC_HANDLE schService) schService = NULL; } } + +void InstallService(PSTR pszServiceName, + PSTR pszDisplayName, + DWORD dwStartType, + PSTR pszDependencies, + PSTR pszAccount, + PSTR pszPassword) +{ + printf("Try to install Win32Service (%s).\n", pszServiceName); + + char szPath[MAX_PATH]; + SC_HANDLE schSCManager = NULL; + SC_HANDLE schService = NULL; + + if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)) == 0) + { + printf("GetModuleFileName failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + // Open the local default service control manager database + schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | + SC_MANAGER_CREATE_SERVICE); + if (schSCManager == NULL) + { + printf("OpenSCManager failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + // Install the service into SCM by calling CreateService + schService = CreateService( + schSCManager, // SCManager database + pszServiceName, // Name of service + pszDisplayName, // Name to display + SERVICE_QUERY_STATUS, // Desired access + SERVICE_WIN32_OWN_PROCESS, // Service type + dwStartType, // Service start type + SERVICE_ERROR_NORMAL, // Error control type + szPath, // Service's binary + NULL, // No load ordering group + NULL, // No tag identifier + pszDependencies, // Dependencies + pszAccount, // Service running account + pszPassword // Password of the account + ); + if (schService == NULL) + { + printf("CreateService failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + printf("Win32Service is installed as %s.\n", pszServiceName); + + // Centralized cleanup for all allocated resources. + FreeHandles(schSCManager, schService); +} + +void UninstallService(PSTR pszServiceName) +{ + printf("Try to uninstall Win32Service (%s).\n", pszServiceName); + + SC_HANDLE schSCManager = NULL; + SC_HANDLE schService = NULL; + SERVICE_STATUS ssSvcStatus = {}; + + // Open the local default service control manager database + schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (schSCManager == NULL) + { + printf("OpenSCManager failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + // Open the service with delete, stop, and query status permissions + schService = OpenService(schSCManager, pszServiceName, SERVICE_STOP | + SERVICE_QUERY_STATUS | DELETE); + if (schService == NULL) + { + printf("OpenService failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + // Try to stop the service + if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus)) + { + printf("Stopping %s.\n", pszServiceName); + Sleep(1000); + + while (QueryServiceStatus(schService, &ssSvcStatus)) + { + if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING) + { + printf("."); + Sleep(1000); + } + else break; + } + + if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED) + { + printf("\n%s is stopped.\n", pszServiceName); + } + else + { + printf("\n%s failed to stop.\n", pszServiceName); + } + } + + // Now remove the service by calling DeleteService. + if (!DeleteService(schService)) + { + printf("DeleteService failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + printf("%s is removed.\n", pszServiceName); + + // Centralized cleanup for all allocated resources. + FreeHandles(schSCManager, schService); +} diff --git a/Win32/Win32Service.h b/Win32/Win32Service.h index 5cedbed6..868528f6 100644 --- a/Win32/Win32Service.h +++ b/Win32/Win32Service.h @@ -1,63 +1,85 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - #ifndef WIN_32_SERVICE_H__ #define WIN_32_SERVICE_H__ #include +#define WIN32_LEAN_AND_MEAN #include -#define SERVICE_NAME "i2pdService" + +#ifdef _WIN32 +// Internal name of the service +#define SERVICE_NAME "i2pService" + +// Displayed name of the service +#define SERVICE_DISPLAY_NAME "i2p router service" + +// Service start options. +#define SERVICE_START_TYPE SERVICE_DEMAND_START + +// List of service dependencies - "dep1\0dep2\0\0" +#define SERVICE_DEPENDENCIES "" + +// The name of the account under which the service should run +#define SERVICE_ACCOUNT "NT AUTHORITY\\LocalService" + +// The password to the service account name +#define SERVICE_PASSWORD NULL +#endif + class I2PService { - public: +public: - I2PService(PSTR pszServiceName, - BOOL fCanStop = TRUE, - BOOL fCanShutdown = TRUE, - BOOL fCanPauseContinue = FALSE); + I2PService(PSTR pszServiceName, + BOOL fCanStop = TRUE, + BOOL fCanShutdown = TRUE, + BOOL fCanPauseContinue = FALSE); - virtual ~I2PService(void); + virtual ~I2PService(void); - static BOOL isService(); - static BOOL Run(I2PService &service); - void Stop(); + static BOOL isService(); + static BOOL Run(I2PService &service); + void Stop(); - protected: +protected: - virtual void OnStart(DWORD dwArgc, PSTR *pszArgv); - virtual void OnStop(); - virtual void OnPause(); - virtual void OnContinue(); - virtual void OnShutdown(); - void SetServiceStatus(DWORD dwCurrentState, - DWORD dwWin32ExitCode = NO_ERROR, - DWORD dwWaitHint = 0); + virtual void OnStart(DWORD dwArgc, PSTR *pszArgv); + virtual void OnStop(); + virtual void OnPause(); + virtual void OnContinue(); + virtual void OnShutdown(); + void SetServiceStatus(DWORD dwCurrentState, + DWORD dwWin32ExitCode = NO_ERROR, + DWORD dwWaitHint = 0); - private: +private: - static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv); - static void WINAPI ServiceCtrlHandler(DWORD dwCtrl); - void WorkerThread(); - void Start(DWORD dwArgc, PSTR *pszArgv); - void Pause(); - void Continue(); - void Shutdown(); - static I2PService* s_service; - PSTR m_name; - SERVICE_STATUS m_status; - SERVICE_STATUS_HANDLE m_statusHandle; + static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv); + static void WINAPI ServiceCtrlHandler(DWORD dwCtrl); + void WorkerThread(); + void Start(DWORD dwArgc, PSTR *pszArgv); + void Pause(); + void Continue(); + void Shutdown(); + static I2PService* s_service; + PSTR m_name; + SERVICE_STATUS m_status; + SERVICE_STATUS_HANDLE m_statusHandle; - BOOL m_fStopping; - HANDLE m_hStoppedEvent; + BOOL m_fStopping; + HANDLE m_hStoppedEvent; - std::thread* _worker; + std::thread* _worker; }; -#endif // WIN_32_SERVICE_H__ +void InstallService(PSTR pszServiceName, + PSTR pszDisplayName, + DWORD dwStartType, + PSTR pszDependencies, + PSTR pszAccount, + PSTR pszPassword); + +void UninstallService(PSTR pszServiceName); + +#endif // WIN_32_SERVICE_H__ \ No newline at end of file diff --git a/Win32/i2pd.sln b/Win32/i2pd.sln new file mode 100644 index 00000000..4606b24b --- /dev/null +++ b/Win32/i2pd.sln @@ -0,0 +1,30 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "i2pd", "i2pd.vcxproj", "{930568EC-31C9-406A-AD1C-9636DF5D8FAA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|Win32.ActiveCfg = Debug|Win32 + {930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|Win32.Build.0 = Debug|Win32 + {930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|Win32.Deploy.0 = Debug|Win32 + {930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|x64.ActiveCfg = Debug|x64 + {930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|x64.Build.0 = Debug|x64 + {930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|Win32.ActiveCfg = Release|Win32 + {930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|Win32.Build.0 = Release|Win32 + {930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|Win32.Deploy.0 = Release|Win32 + {930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|x64.ActiveCfg = Release|x64 + {930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Win32/i2pd.vcxproj b/Win32/i2pd.vcxproj new file mode 100644 index 00000000..bba9a6a7 --- /dev/null +++ b/Win32/i2pd.vcxproj @@ -0,0 +1,283 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {930568EC-31C9-406A-AD1C-9636DF5D8FAA} + i2pd + + + + Application + true + v120_xp + NotSet + + + Application + true + v120_xp + NotSet + + + Application + false + v120_xp + true + NotSet + + + Application + false + v120_xp + true + NotSet + + + + + + + + + + + + + + + + + + + ./..;$(IncludePath);$(BOOST);$(CRYPTOPP);C:\build-lib\cryptopp;C:\build-lib\boost_1_57_0\ + $(BOOST)\stage\lib;C:\build-lib\cryptopp;C:\build-lib\boost_1_57_0\stage\lib;$(CRYPTOPP)\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath) + ./..;$(VC_SourcePath); + $(ProjectName)_d + + + ./..;$(IncludePath);$(BOOST);$(CRYPTOPP) + $(BOOST)\stage\lib;$(CRYPTOPP)\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath) + ./..;$(VC_SourcePath); + $(ProjectName)_d + + + ./..;$(IncludePath);$(BOOST);C:\build-lib\boost_1_57_0\;C:\build-lib + C:\build-lib\boost_1_57_0\stage\lib;C:\build-lib\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath) + ./..;$(VC_SourcePath); + + + ./..;$(IncludePath);$(BOOST);$(CRYPTOPP) + $(BOOST)\stage\lib;$(CRYPTOPP)\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath) + ./..;$(VC_SourcePath); + + + + Level3 + Disabled + true + MultiThreadedDebug + _MBCS;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions) + + + true + cryptlib.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + AsInvoker + 0.2 + Console + + + + + Level3 + Disabled + true + MultiThreadedDebug + _MBCS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions) + + + true + cryptlib.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + AsInvoker + 0.2 + Console + + + + + Level2 + MaxSpeed + true + true + MultiThreaded + _WIN32_WINNT=0x0501;%(PreprocessorDefinitions) + true + true + + + false + true + false + cryptlib.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + AsInvoker + + + Console + 5.01 + NoErrorReport + + + + + + + + + + + Level3 + MaxSpeed + true + true + MultiThreaded + _WIN32_WINNT=0x0502;%(PreprocessorDefinitions) + true + true + + + false + true + false + cryptlib.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + AsInvoker + + + Console + 5.02 + NoErrorReport + + + + + + + + + + + + diff --git a/Win32/i2pd.vcxproj.filters b/Win32/i2pd.vcxproj.filters new file mode 100644 index 00000000..31d2a995 --- /dev/null +++ b/Win32/i2pd.vcxproj.filters @@ -0,0 +1,284 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {a880a08c-16b8-4243-82ea-6bfc63bb7dab} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Win32 + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Win32 + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/Win32/ictoopie.ico b/Win32/ictoopie.ico new file mode 100644 index 00000000..d92a598a Binary files /dev/null and b/Win32/ictoopie.ico differ diff --git a/Win32/inno_installer.iss b/Win32/inno_installer.iss new file mode 100644 index 00000000..67acc431 --- /dev/null +++ b/Win32/inno_installer.iss @@ -0,0 +1,149 @@ + +#define I2Pd_AppName "i2pd" +#define I2Pd_ver "0.2" + +[Setup] +AppName={#I2Pd_AppName} +AppVersion={#I2Pd_ver} +DefaultDirName={pf}\I2Pd +DefaultGroupName=I2Pd +UninstallDisplayIcon={app}\I2Pd.exe +Compression=lzma2 +SolidCompression=yes +OutputDir=. +LicenseFile=.\..\LICENSE +OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_ver} +ArchitecturesInstallIn64BitMode=x64 + + +[Files] +Source: "x64\Release\i2pd.exe"; DestDir: "{app}"; DestName: "i2pd.exe"; Check: Is64BitInstallMode +Source: "Release\i2pd.exe"; DestDir: "{app}"; Check: not Is64BitInstallMode +Source: "..\README.md"; DestDir: "{app}"; DestName: "Readme.txt"; AfterInstall: ConvertLineEndings + +[Icons] +Name: "{group}\I2Pd"; Filename: "{app}\i2pd.exe" +Name: "{group}\Readme"; Filename: "{app}\Readme.txt" + + +[Registry] +Root: HKCU; Subkey: "Environment"; ValueName: "Path"; ValueType: "string"; ValueData: "{app};{olddata}"; Check: NotOnPathAlready(); Flags: preservestringtype; + +[Code] + +var + DefaultTop, + DefaultLeft, + DefaultHeight, + DefaultBackTop, + DefaultNextTop, + DefaultCancelTop, + DefaultBevelTop, + DefaultOuterHeight: Integer; + +const + LicenseHeight = 400; + LF = #10; + CR = #13; + CRLF = CR + LF; + +procedure ConvertLineEndings(); + var + FilePath : String; + FileContents : String; +begin + FilePath := ExpandConstant(CurrentFileName) + LoadStringFromFile(FilePath, FileContents); + StringChangeEx(FileContents, LF, CRLF, False); + SaveStringToFile(FilePath, FileContents, False); +end; + +procedure InitializeWizard(); +begin + DefaultTop := WizardForm.Top; + DefaultLeft := WizardForm.Left; + DefaultHeight := WizardForm.Height; + DefaultBackTop := WizardForm.BackButton.Top; + DefaultNextTop := WizardForm.NextButton.Top; + DefaultCancelTop := WizardForm.CancelButton.Top; + DefaultBevelTop := WizardForm.Bevel.Top; + DefaultOuterHeight := WizardForm.OuterNotebook.Height; + + WizardForm.InnerPage.Height := WizardForm.InnerPage.Height + (LicenseHeight - DefaultHeight); + WizardForm.InnerNotebook.Height := WizardForm.InnerNotebook.Height + (LicenseHeight - DefaultHeight); + WizardForm.LicensePage.Height := WizardForm.LicensePage.Height + (LicenseHeight - DefaultHeight); + WizardForm.LicenseMemo.Height := WizardForm.LicenseMemo.Height + (LicenseHeight - DefaultHeight); + WizardForm.LicenseNotAcceptedRadio.Top := WizardForm.LicenseNotAcceptedRadio.Top + (LicenseHeight - DefaultHeight); + WizardForm.LicenseAcceptedRadio.Top := WizardForm.LicenseAcceptedRadio.Top + (LicenseHeight - DefaultHeight); + +end; + +procedure CurPageChanged(CurPageID: Integer); +begin + if CurPageID = wpLicense then + begin + WizardForm.Top := DefaultTop - (LicenseHeight - DefaultHeight) div 2; + WizardForm.Height := LicenseHeight; + WizardForm.OuterNotebook.Height := WizardForm.OuterNotebook.Height + (LicenseHeight - DefaultHeight); + WizardForm.CancelButton.Top := DefaultCancelTop + (LicenseHeight - DefaultHeight); + WizardForm.NextButton.Top := DefaultNextTop + (LicenseHeight - DefaultHeight); + WizardForm.BackButton.Top := DefaultBackTop + (LicenseHeight - DefaultHeight); + WizardForm.Bevel.Top := DefaultBevelTop + (LicenseHeight - DefaultHeight); + end + else + begin + WizardForm.Top := DefaultTop; + WizardForm.Left := DefaultLeft; + WizardForm.Height := DefaultHeight; + WizardForm.OuterNotebook.Height := DefaultOuterHeight; + WizardForm.CancelButton.Top := DefaultCancelTop; + WizardForm.NextButton.Top := DefaultNextTop; + WizardForm.BackButton.Top := DefaultBackTop; + WizardForm.Bevel.Top := DefaultBevelTop; + end; +end; + +function NotOnPathAlready(): Boolean; +var + BinDir, Path: String; +begin + Log('Checking if i2pd dir is already in the %PATH%'); + if RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Path) then + begin // Successfully read the value + Log('HKCUEnvironmentPATH = ' + Path); + BinDir := ExpandConstant('{app}'); + Log('Looking for i2pd dir in %PATH%: ' + BinDir + ' in ' + Path); + if Pos(LowerCase(BinDir), Lowercase(Path)) = 0 then + begin + Log('Did not find i2pd dir in %PATH% so I will add it'); + Result := True; + end + else + begin + Log('Found i2pd dir in %PATH% so will not add it again'); + Result := False; + end + end + else // The key probably doesn't exist + begin + Log('Could not access HKCUEnvironmentPATH so I assume that it is OK to add it'); + Result := True; + end; +end; + + +procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); +var + BinDir, Path: String; +begin + if (CurUninstallStep = usPostUninstall) + and (RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path)) then + begin + BinDir := ExpandConstant('{app}'); + if Pos(LowerCase(BinDir) + ';', Lowercase(Path)) <> 0 then + begin + StringChange(Path, BinDir + ';', ''); + RegWriteStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path); + end; + end; +end; diff --git a/Win32/install_service.bat b/Win32/install_service.bat new file mode 100644 index 00000000..b03c11b6 --- /dev/null +++ b/Win32/install_service.bat @@ -0,0 +1 @@ +i2pd --service=install \ No newline at end of file diff --git a/Win32/mask.ico b/Win32/mask.ico deleted file mode 100644 index f5807de5..00000000 Binary files a/Win32/mask.ico and /dev/null differ diff --git a/Win32/nsi/helper_readme.nsh b/Win32/nsi/helper_readme.nsh new file mode 100644 index 00000000..a3baaca1 --- /dev/null +++ b/Win32/nsi/helper_readme.nsh @@ -0,0 +1,57 @@ +!verbose push +!verbose 3 + +!ifndef _MUI_EXTRAPAGES_NSH +!define _MUI_EXTRAPAGES_NSH + +!ifmacrondef MUI_EXTRAPAGE_README & MUI_PAGE_README & MUI_UNPAGE_README & ReadmeLangStrings + +!macro MUI_EXTRAPAGE_README UN ReadmeFile +!verbose push +!verbose 3 + !define MUI_PAGE_HEADER_TEXT "$(${UN}ReadmeHeader)" + !define MUI_PAGE_HEADER_SUBTEXT "$(${UN}ReadmeSubHeader)" + !define MUI_LICENSEPAGE_TEXT_TOP "$(${UN}ReadmeTextTop)" + !define MUI_LICENSEPAGE_TEXT_BOTTOM "$(${UN}ReadmeTextBottom)" + !define MUI_LICENSEPAGE_BUTTON "$(^NextBtn)" + !insertmacro MUI_${UN}PAGE_LICENSE "${ReadmeFile}" +!verbose pop +!macroend + +!define ReadmeRun "!insertmacro MUI_EXTRAPAGE_README" + + +!macro MUI_PAGE_README ReadmeFile +!verbose push +!verbose 3 + ${ReadmeRun} "" "${ReadmeFile}" +!verbose pop +!macroend + + +!macro MUI_UNPAGE_README ReadmeFile +!verbose push +!verbose 3 + ${ReadmeRun} "UN" "${ReadmeFile}" +!verbose pop +!macroend + + +!macro ReadmeLangStrings UN MUI_LANG ReadmeHeader ReadmeSubHeader ReadmeTextTop ReadmeTextBottom +!verbose push +!verbose 3 + LangString ${UN}ReadmeHeader ${MUI_LANG} "${ReadmeHeader}" + LangString ${UN}ReadmeSubHeader ${MUI_LANG} "${ReadmeSubHeader}" + LangString ${UN}ReadmeTextTop ${MUI_LANG} "${ReadmeTextTop}" + LangString ${UN}ReadmeTextBottom ${MUI_LANG} "${ReadmeTextBottom}" +!verbose pop +!macroend + +!define ReadmeLanguage `!insertmacro ReadmeLangStrings ""` + +!define Un.ReadmeLanguage `!insertmacro ReadmeLangStrings "UN"` + +!endif +!endif + +!verbose pop \ No newline at end of file diff --git a/Win32/nsi/servicelib.nsh b/Win32/nsi/servicelib.nsh new file mode 100644 index 00000000..7f4b5861 --- /dev/null +++ b/Win32/nsi/servicelib.nsh @@ -0,0 +1,419 @@ +; NSIS SERVICE LIBRARY - servicelib.nsh +; Version 1.8.1 - Jun 21th, 2013 +; Questions/Comments - dselkirk@hotmail.com +; +; Description: +; Provides an interface to window services +; +; Inputs: +; action - systemlib action ie. create, delete, start, stop, pause, +; continue, installed, running, status +; name - name of service to manipulate +; param - action parameters; usage: var1=value1;var2=value2;...etc. +; (don't forget to add a ';' after the last value!) +; +; Actions: +; create - creates a new windows service +; Parameters: +; path - path to service executable +; autostart - automatically start with system ie. 1|0 +; interact - interact with the desktop ie. 1|0 +; depend - service dependencies +; user - user that runs the service +; password - password of the above user +; display - display name in service's console +; description - Description of service +; starttype - start type (supersedes autostart) +; servicetype - service type (supersedes interact) +; +; delete - deletes a windows service +; start - start a stopped windows service +; stop - stops a running windows service +; pause - pauses a running windows service +; continue - continues a paused windows service +; installed - is the provided service installed +; Parameters: +; action - if true then invokes the specified action +; running - is the provided service running +; Parameters: +; action - if true then invokes the specified action +; status - check the status of the provided service +; +; Usage: +; Method 1: +; Push "action" +; Push "name" +; Push "param" +; Call Service +; Pop $0 ;response +; +; Method 2: +; !insertmacro SERVICE "action" "name" "param" +; +; History: +; 1.0 - 09/15/2003 - Initial release +; 1.1 - 09/16/2003 - Changed &l to i, thx brainsucker +; 1.2 - 02/29/2004 - Fixed documentation. +; 1.3 - 01/05/2006 - Fixed interactive flag and pop order (Kichik) +; 1.4 - 12/07/2006 - Added display and depend, fixed datatypes (Vitoco) +; 1.5 - 06/25/2008 - Added description of service.(DeSafe.com/liuqixing#gmail.com) +; 1.5.1 - 06/12/2009 - Added use of __UNINSTALL__ +; 1.6 - 08/02/2010 - Fixed description implementation (Anders) +; 1.7 - 04/11/2010 - Added get running service process id (Nico) +; 1.8 - 24/03/2011 - Added starttype and servicetype (Sergius) +; 1.8.1 - 21/06/2013 - Added dynamic ASCII & Unicode support (Zinthose) + +!ifndef SERVICELIB + !define SERVICELIB + + !define SC_MANAGER_ALL_ACCESS 0x3F + !define SC_STATUS_PROCESS_INFO 0x0 + !define SERVICE_ALL_ACCESS 0xF01FF + + !define SERVICE_CONTROL_STOP 1 + !define SERVICE_CONTROL_PAUSE 2 + !define SERVICE_CONTROL_CONTINUE 3 + + !define SERVICE_STOPPED 0x1 + !define SERVICE_START_PENDING 0x2 + !define SERVICE_STOP_PENDING 0x3 + !define SERVICE_RUNNING 0x4 + !define SERVICE_CONTINUE_PENDING 0x5 + !define SERVICE_PAUSE_PENDING 0x6 + !define SERVICE_PAUSED 0x7 + + !define SERVICE_KERNEL_DRIVER 0x00000001 + !define SERVICE_FILE_SYSTEM_DRIVER 0x00000002 + !define SERVICE_WIN32_OWN_PROCESS 0x00000010 + !define SERVICE_WIN32_SHARE_PROCESS 0x00000020 + !define SERVICE_INTERACTIVE_PROCESS 0x00000100 + + + !define SERVICE_BOOT_START 0x00000000 + !define SERVICE_SYSTEM_START 0x00000001 + !define SERVICE_AUTO_START 0x00000002 + !define SERVICE_DEMAND_START 0x00000003 + !define SERVICE_DISABLED 0x00000004 + + ## Added by Zinthose for Native Unicode Support + !ifdef NSIS_UNICODE + !define APITAG "W" + !else + !define APITAG "A" + !endif + + !macro SERVICE ACTION NAME PARAM + Push '${ACTION}' + Push '${NAME}' + Push '${PARAM}' + !ifdef __UNINSTALL__ + Call un.Service + !else + Call Service + !endif + !macroend + + !macro FUNC_GETPARAM + Push $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + Push $7 + Exch 8 + Pop $1 ;name + Exch 8 + Pop $2 ;source + StrCpy $0 "" + StrLen $7 $2 + StrCpy $3 0 + lbl_loop: + IntCmp $3 $7 0 0 lbl_done + StrLen $4 "$1=" + StrCpy $5 $2 $4 $3 + StrCmp $5 "$1=" 0 lbl_next + IntOp $5 $3 + $4 + StrCpy $3 $5 + lbl_loop2: + IntCmp $3 $7 0 0 lbl_done + StrCpy $6 $2 1 $3 + StrCmp $6 ";" 0 lbl_next2 + IntOp $6 $3 - $5 + StrCpy $0 $2 $6 $5 + Goto lbl_done + lbl_next2: + IntOp $3 $3 + 1 + Goto lbl_loop2 + lbl_next: + IntOp $3 $3 + 1 + Goto lbl_loop + lbl_done: + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Exch 2 + Pop $6 + Pop $7 + Exch $0 + !macroend + + !macro CALL_GETPARAM VAR NAME DEFAULT LABEL + Push $1 + Push ${NAME} + Call ${UN}GETPARAM + Pop $6 + StrCpy ${VAR} "${DEFAULT}" + StrCmp $6 "" "${LABEL}" 0 + StrCpy ${VAR} $6 + !macroend + + !macro FUNC_SERVICE UN + Push $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + Push $7 + Exch 8 + Pop $1 ;param + Exch 8 + Pop $2 ;name + Exch 8 + Pop $3 ;action + ;$0 return + ;$4 OpenSCManager + ;$5 OpenService + + StrCpy $0 "false" + System::Call 'advapi32::OpenSCManager${APITAG}(n, n, i ${SC_MANAGER_ALL_ACCESS}) i.r4' + IntCmp $4 0 lbl_done + StrCmp $3 "create" lbl_create + System::Call 'advapi32::OpenService${APITAG}(i r4, t r2, i ${SERVICE_ALL_ACCESS}) i.r5' + IntCmp $5 0 lbl_done + + lbl_select: + StrCmp $3 "delete" lbl_delete + StrCmp $3 "start" lbl_start + StrCmp $3 "stop" lbl_stop + StrCmp $3 "pause" lbl_pause + StrCmp $3 "continue" lbl_continue + StrCmp $3 "installed" lbl_installed + StrCmp $3 "running" lbl_running + StrCmp $3 "status" lbl_status + StrCmp $3 "processid" lbl_processid + Goto lbl_done + + ; create service + lbl_create: + Push $R1 ;depend + Push $R2 ;user + Push $R3 ;password + Push $R4 ;servicetype/interact + Push $R5 ;starttype/autostart + Push $R6 ;path + Push $R7 ;display + Push $R8 ;description + + !insertmacro CALL_GETPARAM $R1 "depend" "n" "lbl_depend" + StrCpy $R1 't "$R1"' + lbl_depend: + StrCmp $R1 "n" 0 lbl_machine ;old name of depend param + !insertmacro CALL_GETPARAM $R1 "machine" "n" "lbl_machine" + StrCpy $R1 't "$R1"' + lbl_machine: + + !insertmacro CALL_GETPARAM $R2 "user" "n" "lbl_user" + StrCpy $R2 't "$R2"' + lbl_user: + + !insertmacro CALL_GETPARAM $R3 "password" "n" "lbl_password" + StrCpy $R3 't "$R3"' + lbl_password: + + !insertmacro CALL_GETPARAM $R4 "interact" "${SERVICE_WIN32_OWN_PROCESS}" "lbl_interact" + StrCpy $6 ${SERVICE_WIN32_OWN_PROCESS} + IntCmp $R4 0 +2 + IntOp $6 $6 | ${SERVICE_INTERACTIVE_PROCESS} + StrCpy $R4 $6 + lbl_interact: + + !insertmacro CALL_GETPARAM $R4 "servicetype" "$R4" "lbl_servicetype" + lbl_servicetype: + + !insertmacro CALL_GETPARAM $R5 "autostart" "${SERVICE_DEMAND_START}" "lbl_autostart" + StrCpy $6 ${SERVICE_DEMAND_START} + IntCmp $R5 0 +2 + StrCpy $6 ${SERVICE_AUTO_START} + StrCpy $R5 $6 + lbl_autostart: + + !insertmacro CALL_GETPARAM $R5 "starttype" "$R5" "lbl_starttype" + lbl_starttype: + + !insertmacro CALL_GETPARAM $R6 "path" "n" "lbl_path" + lbl_path: + + !insertmacro CALL_GETPARAM $R7 "display" "$2" "lbl_display" + lbl_display: + + !insertmacro CALL_GETPARAM $R8 "description" "$2" "lbl_description" + lbl_description: + + System::Call 'advapi32::CreateService${APITAG}(i r4, t r2, t R7, i ${SERVICE_ALL_ACCESS}, \ + i R4, i R5, i 0, t R6, n, n, $R1, $R2, $R3) i.r6' + + ; write description of service (SERVICE_CONFIG_DESCRIPTION) + System::Call 'advapi32::ChangeServiceConfig2${APITAG}(ir6,i1,*t "$R8")i.R7' + strcmp $R7 "error" 0 lbl_descriptioncomplete + WriteRegStr HKLM "SYSTEM\CurrentControlSet\Services\$2" "Description" $R8 + lbl_descriptioncomplete: + + Pop $R8 + Pop $R7 + Pop $R6 + Pop $R5 + Pop $R4 + Pop $R3 + Pop $R2 + Pop $R1 + StrCmp $6 0 lbl_done lbl_good + + ; delete service + lbl_delete: + System::Call 'advapi32::DeleteService(i r5) i.r6' + StrCmp $6 0 lbl_done lbl_good + + ; start service + lbl_start: + System::Call 'advapi32::StartService${APITAG}(i r5, i 0, i 0) i.r6' + StrCmp $6 0 lbl_done lbl_good + + ; stop service + lbl_stop: + Push $R1 + System::Call '*(i,i,i,i,i,i,i) i.R1' + System::Call 'advapi32::ControlService(i r5, i ${SERVICE_CONTROL_STOP}, i $R1) i' + System::Free $R1 + Pop $R1 + StrCmp $6 0 lbl_done lbl_good + + ; pause service + lbl_pause: + Push $R1 + System::Call '*(i,i,i,i,i,i,i) i.R1' + System::Call 'advapi32::ControlService(i r5, i ${SERVICE_CONTROL_PAUSE}, i $R1) i' + System::Free $R1 + Pop $R1 + StrCmp $6 0 lbl_done lbl_good + + ; continue service + lbl_continue: + Push $R1 + System::Call '*(i,i,i,i,i,i,i) i.R1' + System::Call 'advapi32::ControlService(i r5, i ${SERVICE_CONTROL_CONTINUE}, i $R1) i' + System::Free $R1 + Pop $R1 + StrCmp $6 0 lbl_done lbl_good + + ; is installed + lbl_installed: + !insertmacro CALL_GETPARAM $7 "action" "" "lbl_good" + StrCpy $3 $7 + Goto lbl_select + + ; is service running + lbl_running: + Push $R1 + System::Call '*(i,i,i,i,i,i,i) i.R1' + System::Call 'advapi32::QueryServiceStatus(i r5, i $R1) i' + System::Call '*$R1(i, i.r6)' + System::Free $R1 + Pop $R1 + IntFmt $6 "0x%X" $6 + StrCmp $6 ${SERVICE_RUNNING} 0 lbl_done + !insertmacro CALL_GETPARAM $7 "action" "" "lbl_good" + StrCpy $3 $7 + Goto lbl_select + + lbl_status: + Push $R1 + System::Call '*(i,i,i,i,i,i,i) i.R1' + System::Call 'advapi32::QueryServiceStatus(i r5, i $R1) i' + System::Call '*$R1(i, i .r6)' + System::Free $R1 + Pop $R1 + IntFmt $6 "0x%X" $6 + StrCpy $0 "running" + IntCmp $6 ${SERVICE_RUNNING} lbl_done + StrCpy $0 "stopped" + IntCmp $6 ${SERVICE_STOPPED} lbl_done + StrCpy $0 "start_pending" + IntCmp $6 ${SERVICE_START_PENDING} lbl_done + StrCpy $0 "stop_pending" + IntCmp $6 ${SERVICE_STOP_PENDING} lbl_done + StrCpy $0 "running" + IntCmp $6 ${SERVICE_RUNNING} lbl_done + StrCpy $0 "continue_pending" + IntCmp $6 ${SERVICE_CONTINUE_PENDING} lbl_done + StrCpy $0 "pause_pending" + IntCmp $6 ${SERVICE_PAUSE_PENDING} lbl_done + StrCpy $0 "paused" + IntCmp $6 ${SERVICE_PAUSED} lbl_done + StrCpy $0 "unknown" + Goto lbl_done + + lbl_processid: + Push $R1 + Push $R2 + System::Call '*(i,i,i,i,i,i,i,i,i) i.R1' + System::Call '*(i 0) i.R2' + System::Call "advapi32::QueryServiceStatusEx(i r5, i ${SC_STATUS_PROCESS_INFO}, i $R1, i 36, i $R2) i" + System::Call "*$R1(i,i,i,i,i,i,i, i .r0)" + System::Free $R2 + System::Free $R1 + Pop $R2 + Pop $R1 + Goto lbl_done + + lbl_good: + StrCpy $0 "true" + lbl_done: + IntCmp $5 0 +2 + System::Call 'advapi32::CloseServiceHandle(i r5) n' + IntCmp $4 0 +2 + System::Call 'advapi32::CloseServiceHandle(i r4) n' + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Exch 3 + Pop $5 + Pop $7 + Pop $6 + Exch $0 + !macroend + + Function Service + !insertmacro FUNC_SERVICE "" + FunctionEnd + + Function un.Service + !insertmacro FUNC_SERVICE "un." + FunctionEnd + + Function GetParam + !insertmacro FUNC_GETPARAM + FunctionEnd + + Function un.GetParam + !insertmacro FUNC_GETPARAM + FunctionEnd + + !undef APITAG +!endif \ No newline at end of file diff --git a/Win32/resource.h b/Win32/resource.h index daa1ddcb..661a5baf 100644 Binary files a/Win32/resource.h and b/Win32/resource.h differ diff --git a/Win32/uninstall_service.bat b/Win32/uninstall_service.bat new file mode 100644 index 00000000..0289c24a --- /dev/null +++ b/Win32/uninstall_service.bat @@ -0,0 +1 @@ +i2pd --service=remove \ No newline at end of file diff --git a/Win32/winres.h b/Win32/winres.h deleted file mode 100644 index e9afee91..00000000 --- a/Win32/winres.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef WINRES_H__ -#define WINRES_H__ - -#include - -#endif diff --git a/aes.cpp b/aes.cpp new file mode 100644 index 00000000..d1c9d984 --- /dev/null +++ b/aes.cpp @@ -0,0 +1,351 @@ +#include +#include "TunnelBase.h" +#include "aes.h" + +namespace i2p +{ +namespace crypto +{ + +#ifdef AESNI + + #define KeyExpansion256(round0,round1) \ + "pshufd $0xff, %%xmm2, %%xmm2 \n" \ + "movaps %%xmm1, %%xmm4 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm1 \n" \ + "pxor %%xmm2, %%xmm1 \n" \ + "movaps %%xmm1, "#round0"(%[sched]) \n" \ + "aeskeygenassist $0, %%xmm1, %%xmm4 \n" \ + "pshufd $0xaa, %%xmm4, %%xmm2 \n" \ + "movaps %%xmm3, %%xmm4 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pslldq $4, %%xmm4 \n" \ + "pxor %%xmm4, %%xmm3 \n" \ + "pxor %%xmm2, %%xmm3 \n" \ + "movaps %%xmm3, "#round1"(%[sched]) \n" + + void ECBCryptoAESNI::ExpandKey (const AESKey& key) + { + __asm__ + ( + "movups (%[key]), %%xmm1 \n" + "movups 16(%[key]), %%xmm3 \n" + "movaps %%xmm1, (%[sched]) \n" + "movaps %%xmm3, 16(%[sched]) \n" + "aeskeygenassist $1, %%xmm3, %%xmm2 \n" + KeyExpansion256(32,48) + "aeskeygenassist $2, %%xmm3, %%xmm2 \n" + KeyExpansion256(64,80) + "aeskeygenassist $4, %%xmm3, %%xmm2 \n" + KeyExpansion256(96,112) + "aeskeygenassist $8, %%xmm3, %%xmm2 \n" + KeyExpansion256(128,144) + "aeskeygenassist $16, %%xmm3, %%xmm2 \n" + KeyExpansion256(160,176) + "aeskeygenassist $32, %%xmm3, %%xmm2 \n" + KeyExpansion256(192,208) + "aeskeygenassist $64, %%xmm3, %%xmm2 \n" + // key expansion final + "pshufd $0xff, %%xmm2, %%xmm2 \n" + "movaps %%xmm1, %%xmm4 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pslldq $4, %%xmm4 \n" + "pxor %%xmm4, %%xmm1 \n" + "pxor %%xmm2, %%xmm1 \n" + "movups %%xmm1, 224(%[sched]) \n" + : // output + : [key]"r"((const uint8_t *)key), [sched]"r"(GetKeySchedule ()) // input + : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "memory" // clogged + ); + } + + #define EncryptAES256(sched) \ + "pxor (%["#sched"]), %%xmm0 \n" \ + "aesenc 16(%["#sched"]), %%xmm0 \n" \ + "aesenc 32(%["#sched"]), %%xmm0 \n" \ + "aesenc 48(%["#sched"]), %%xmm0 \n" \ + "aesenc 64(%["#sched"]), %%xmm0 \n" \ + "aesenc 80(%["#sched"]), %%xmm0 \n" \ + "aesenc 96(%["#sched"]), %%xmm0 \n" \ + "aesenc 112(%["#sched"]), %%xmm0 \n" \ + "aesenc 128(%["#sched"]), %%xmm0 \n" \ + "aesenc 144(%["#sched"]), %%xmm0 \n" \ + "aesenc 160(%["#sched"]), %%xmm0 \n" \ + "aesenc 176(%["#sched"]), %%xmm0 \n" \ + "aesenc 192(%["#sched"]), %%xmm0 \n" \ + "aesenc 208(%["#sched"]), %%xmm0 \n" \ + "aesenclast 224(%["#sched"]), %%xmm0 \n" + + void ECBEncryptionAESNI::Encrypt (const ChipherBlock * in, ChipherBlock * out) + { + __asm__ + ( + "movups (%[in]), %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" + ); + } + + #define DecryptAES256(sched) \ + "pxor 224(%["#sched"]), %%xmm0 \n" \ + "aesdec 208(%["#sched"]), %%xmm0 \n" \ + "aesdec 192(%["#sched"]), %%xmm0 \n" \ + "aesdec 176(%["#sched"]), %%xmm0 \n" \ + "aesdec 160(%["#sched"]), %%xmm0 \n" \ + "aesdec 144(%["#sched"]), %%xmm0 \n" \ + "aesdec 128(%["#sched"]), %%xmm0 \n" \ + "aesdec 112(%["#sched"]), %%xmm0 \n" \ + "aesdec 96(%["#sched"]), %%xmm0 \n" \ + "aesdec 80(%["#sched"]), %%xmm0 \n" \ + "aesdec 64(%["#sched"]), %%xmm0 \n" \ + "aesdec 48(%["#sched"]), %%xmm0 \n" \ + "aesdec 32(%["#sched"]), %%xmm0 \n" \ + "aesdec 16(%["#sched"]), %%xmm0 \n" \ + "aesdeclast (%["#sched"]), %%xmm0 \n" + + void ECBDecryptionAESNI::Decrypt (const ChipherBlock * in, ChipherBlock * out) + { + __asm__ + ( + "movups (%[in]), %%xmm0 \n" + DecryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + : : [sched]"r"(GetKeySchedule ()), [in]"r"(in), [out]"r"(out) : "%xmm0", "memory" + ); + } + + #define CallAESIMC(offset) \ + "movaps "#offset"(%[shed]), %%xmm0 \n" \ + "aesimc %%xmm0, %%xmm0 \n" \ + "movaps %%xmm0, "#offset"(%[shed]) \n" + + void ECBDecryptionAESNI::SetKey (const AESKey& key) + { + ExpandKey (key); // expand encryption key first + // then invert it using aesimc + __asm__ + ( + CallAESIMC(16) + CallAESIMC(32) + CallAESIMC(48) + CallAESIMC(64) + CallAESIMC(80) + CallAESIMC(96) + CallAESIMC(112) + CallAESIMC(128) + CallAESIMC(144) + CallAESIMC(160) + CallAESIMC(176) + CallAESIMC(192) + CallAESIMC(208) + : : [shed]"r"(GetKeySchedule ()) : "%xmm0", "memory" + ); + } + +#endif + + + void CBCEncryption::Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) + { +#ifdef AESNI + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "1: \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[out]) \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz 1b \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "cc", "memory" + ); +#else + for (int i = 0; i < numBlocks; i++) + { + m_LastBlock ^= in[i]; + m_ECBEncryption.Encrypt (&m_LastBlock, &m_LastBlock); + out[i] = m_LastBlock; + } +#endif + } + + void CBCEncryption::Encrypt (const uint8_t * in, std::size_t len, uint8_t * out) + { + // len/16 + Encrypt (len >> 4, (const ChipherBlock *)in, (ChipherBlock *)out); + } + + void CBCEncryption::Encrypt (const uint8_t * in, uint8_t * out) + { +#ifdef AESNI + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched) + "movups %%xmm0, (%[out]) \n" + "movups %%xmm0, (%[iv]) \n" + : + : [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); +#else + Encrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); +#endif + } + + void CBCDecryption::Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out) + { +#ifdef AESNI + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "1: \n" + "movups (%[in]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + "movaps %%xmm2, %%xmm1 \n" + "add $16, %[in] \n" + "add $16, %[out] \n" + "dec %[num] \n" + "jnz 1b \n" + "movups %%xmm1, (%[iv]) \n" + : + : [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out), [num]"r"(numBlocks) + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); +#else + for (int i = 0; i < numBlocks; i++) + { + ChipherBlock tmp = in[i]; + m_ECBDecryption.Decrypt (in + i, out + i); + out[i] ^= m_IV; + m_IV = tmp; + } +#endif + } + + void CBCDecryption::Decrypt (const uint8_t * in, std::size_t len, uint8_t * out) + { + Decrypt (len >> 4, (const ChipherBlock *)in, (ChipherBlock *)out); + } + + void CBCDecryption::Decrypt (const uint8_t * in, uint8_t * out) + { +#ifdef AESNI + __asm__ + ( + "movups (%[iv]), %%xmm1 \n" + "movups (%[in]), %%xmm0 \n" + "movups %%xmm0, (%[iv]) \n" + DecryptAES256(sched) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[out]) \n" + : + : [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()), + [in]"r"(in), [out]"r"(out) + : "%xmm0", "%xmm1", "memory" + ); +#else + Decrypt (1, (const ChipherBlock *)in, (ChipherBlock *)out); +#endif + } + + void TunnelEncryption::Encrypt (uint8_t * payload) + { +#ifdef AESNI + __asm__ + ( + // encrypt IV + "movups (%[payload]), %%xmm0 \n" + EncryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + EncryptAES256(sched_iv) + "movups %%xmm0, (%[payload]) \n" + // encrypt data, IV is xmm1 + "1: \n" + "add $16, %[payload] \n" + "movups (%[payload]), %%xmm0 \n" + "pxor %%xmm1, %%xmm0 \n" + EncryptAES256(sched_l) + "movaps %%xmm0, %%xmm1 \n" + "movups %%xmm0, (%[payload]) \n" + "dec %[num] \n" + "jnz 1b \n" + : + : [sched_iv]"r"(m_IVEncryption.GetKeySchedule ()), [sched_l]"r"(m_LayerEncryption.GetKeySchedule ()), + [payload]"r"(payload), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "cc", "memory" + ); +#else + m_IVEncryption.Encrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // iv + m_LayerEncryption.SetIV (payload); + m_LayerEncryption.Encrypt (payload + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, payload + 16); // data + m_IVEncryption.Encrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // double iv +#endif + } + + void TunnelDecryption::Decrypt (uint8_t * payload) + { +#ifdef AESNI + __asm__ + ( + // decrypt IV + "movups (%[payload]), %%xmm0 \n" + DecryptAES256(sched_iv) + "movaps %%xmm0, %%xmm1 \n" + // double IV encryption + DecryptAES256(sched_iv) + "movups %%xmm0, (%[payload]) \n" + // decrypt data, IV is xmm1 + "1: \n" + "add $16, %[payload] \n" + "movups (%[payload]), %%xmm0 \n" + "movaps %%xmm0, %%xmm2 \n" + DecryptAES256(sched_l) + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[payload]) \n" + "movaps %%xmm2, %%xmm1 \n" + "dec %[num] \n" + "jnz 1b \n" + : + : [sched_iv]"r"(m_IVDecryption.GetKeySchedule ()), [sched_l]"r"(m_LayerDecryption.GetKeySchedule ()), + [payload]"r"(payload), [num]"r"(63) // 63 blocks = 1008 bytes + : "%xmm0", "%xmm1", "%xmm2", "cc", "memory" + ); +#else + m_IVDecryption.Decrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // iv + m_LayerDecryption.SetIV (payload); + m_LayerDecryption.Decrypt (payload + 16, i2p::tunnel::TUNNEL_DATA_ENCRYPTED_SIZE, payload + 16); // data + m_IVDecryption.Decrypt ((ChipherBlock *)payload, (ChipherBlock *)payload); // double iv +#endif + } +} +} + diff --git a/aes.h b/aes.h new file mode 100644 index 00000000..95180fd6 --- /dev/null +++ b/aes.h @@ -0,0 +1,225 @@ +#ifndef AES_H__ +#define AES_H__ + +#include +#include +#include +#include "Identity.h" + +namespace i2p +{ +namespace crypto +{ + struct ChipherBlock + { + uint8_t buf[16]; + + void operator^=(const ChipherBlock& other) // XOR + { +#if defined(__x86_64__) // for Intel x64 + __asm__ + ( + "movups (%[buf]), %%xmm0 \n" + "movups (%[other]), %%xmm1 \n" + "pxor %%xmm1, %%xmm0 \n" + "movups %%xmm0, (%[buf]) \n" + : + : [buf]"r"(buf), [other]"r"(other.buf) + : "%xmm0", "%xmm1", "memory" + ); +#else + // TODO: implement it better + for (int i = 0; i < 16; i++) + buf[i] ^= other.buf[i]; +#endif + } + }; + + typedef i2p::data::Tag<32> AESKey; + + template + class AESAlignedBuffer // 16 bytes alignment + { + public: + + AESAlignedBuffer () + { + m_Buf = m_UnalignedBuffer; + uint8_t rem = ((size_t)m_Buf) & 0x0f; + if (rem) + m_Buf += (16 - rem); + } + + operator uint8_t * () { return m_Buf; }; + operator const uint8_t * () const { return m_Buf; }; + + private: + + uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment + uint8_t * m_Buf; + }; + + +#ifdef AESNI + class ECBCryptoAESNI + { + public: + + uint8_t * GetKeySchedule () { return m_KeySchedule; }; + + protected: + + void ExpandKey (const AESKey& key); + + private: + + AESAlignedBuffer<240> m_KeySchedule; // 14 rounds for AES-256, 240 bytes + }; + + class ECBEncryptionAESNI: public ECBCryptoAESNI + { + public: + + void SetKey (const AESKey& key) { ExpandKey (key); }; + void Encrypt (const ChipherBlock * in, ChipherBlock * out); + }; + + class ECBDecryptionAESNI: public ECBCryptoAESNI + { + public: + + void SetKey (const AESKey& key); + void Decrypt (const ChipherBlock * in, ChipherBlock * out); + }; + + typedef ECBEncryptionAESNI ECBEncryption; + typedef ECBDecryptionAESNI ECBDecryption; + +#else // use crypto++ + + class ECBEncryption + { + public: + + void SetKey (const AESKey& key) + { + m_Encryption.SetKey (key, 32); + } + void Encrypt (const ChipherBlock * in, ChipherBlock * out) + { + m_Encryption.ProcessData (out->buf, in->buf, 16); + } + + private: + + CryptoPP::ECB_Mode::Encryption m_Encryption; + }; + + class ECBDecryption + { + public: + + void SetKey (const AESKey& key) + { + m_Decryption.SetKey (key, 32); + } + void Decrypt (const ChipherBlock * in, ChipherBlock * out) + { + m_Decryption.ProcessData (out->buf, in->buf, 16); + } + + private: + + CryptoPP::ECB_Mode::Decryption m_Decryption; + }; + + +#endif + + class CBCEncryption + { + public: + + CBCEncryption () { memset (m_LastBlock.buf, 0, 16); }; + + void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes + void SetIV (const uint8_t * iv) { memcpy (m_LastBlock.buf, iv, 16); }; // 16 bytes + + void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); + void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out); + void Encrypt (const uint8_t * in, uint8_t * out); // one block + + private: + + ChipherBlock m_LastBlock; + + ECBEncryption m_ECBEncryption; + }; + + class CBCDecryption + { + public: + + CBCDecryption () { memset (m_IV.buf, 0, 16); }; + + void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes + void SetIV (const uint8_t * iv) { memcpy (m_IV.buf, iv, 16); }; // 16 bytes + + void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out); + void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out); + void Decrypt (const uint8_t * in, uint8_t * out); // one block + + private: + + ChipherBlock m_IV; + ECBDecryption m_ECBDecryption; + }; + + class TunnelEncryption // with double IV encryption + { + public: + + void SetKeys (const AESKey& layerKey, const AESKey& ivKey) + { + m_LayerEncryption.SetKey (layerKey); + m_IVEncryption.SetKey (ivKey); + } + + void Encrypt (uint8_t * payload); // 1024 bytes (16 IV + 1008 data) + + private: + + ECBEncryption m_IVEncryption; +#ifdef AESNI + ECBEncryption m_LayerEncryption; +#else + CBCEncryption m_LayerEncryption; +#endif + }; + + class TunnelDecryption // with double IV encryption + { + public: + + void SetKeys (const AESKey& layerKey, const AESKey& ivKey) + { + m_LayerDecryption.SetKey (layerKey); + m_IVDecryption.SetKey (ivKey); + } + + void Decrypt (uint8_t * payload); // 1024 bytes (16 IV + 1008 data) + + private: + + ECBDecryption m_IVDecryption; +#ifdef AESNI + ECBDecryption m_LayerDecryption; +#else + CBCDecryption m_LayerDecryption; +#endif + }; +} +} + +#endif + diff --git a/api.cpp b/api.cpp new file mode 100644 index 00000000..3cddf4f5 --- /dev/null +++ b/api.cpp @@ -0,0 +1,112 @@ +#include +#include +#include "Log.h" +#include "NetDb.h" +#include "Transports.h" +#include "Tunnel.h" +#include "RouterContext.h" +#include "Identity.h" +#include "Destination.h" +#include "util.h" +#include "api.h" + +namespace i2p +{ +namespace api +{ + void InitI2P (int argc, char* argv[], const char * appName) + { + i2p::util::filesystem::SetAppName (appName); + i2p::util::config::OptionParser(argc, argv); + i2p::context.Init (); + } + + void StartI2P (std::ostream * logStream) + { + if (logStream) + StartLog (logStream); + else + StartLog (i2p::util::filesystem::GetAppName () + ".log"); + i2p::data::netdb.Start(); + LogPrint("NetDB started"); + i2p::transport::transports.Start(); + LogPrint("Transports started"); + i2p::tunnel::tunnels.Start(); + LogPrint("Tunnels started"); + } + + void StopI2P () + { + LogPrint("Shutdown started."); + i2p::tunnel::tunnels.Stop(); + LogPrint("Tunnels stoped"); + i2p::transport::transports.Stop(); + LogPrint("Transports stoped"); + i2p::data::netdb.Stop(); + LogPrint("NetDB stoped"); + StopLog (); + } + + i2p::client::ClientDestination * CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, + const std::map * params) + { + auto localDestination = new i2p::client::ClientDestination (keys, isPublic, params); + localDestination->Start (); + return localDestination; + } + + i2p::client::ClientDestination * CreateLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType, + const std::map * params) + { + i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); + auto localDestination = new i2p::client::ClientDestination (keys, isPublic, params); + localDestination->Start (); + return localDestination; + } + + void DestroyLocalDestination (i2p::client::ClientDestination * dest) + { + if (dest) + { + dest->Stop (); + delete dest; + } + } + + void RequestLeaseSet (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote) + { + if (dest) + dest->RequestDestination (remote); + } + + std::shared_ptr CreateStream (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote) + { + if (!dest) return nullptr; + auto leaseSet = dest->FindLeaseSet (remote); + if (leaseSet) + { + auto stream = dest->CreateStream (*leaseSet); + stream->Send (nullptr, 0); // connect + return stream; + } + else + { + RequestLeaseSet (dest, remote); + return nullptr; + } + } + + void AcceptStream (i2p::client::ClientDestination * dest, const i2p::stream::StreamingDestination::Acceptor& acceptor) + { + if (dest) + dest->AcceptStreams (acceptor); + } + + void DestroyStream (std::shared_ptr stream) + { + if (stream) + stream->Close (); + } +} +} + diff --git a/api.h b/api.h new file mode 100644 index 00000000..894aff49 --- /dev/null +++ b/api.h @@ -0,0 +1,36 @@ +#ifndef API_H__ +#define API_H__ + +#include +#include +#include "Identity.h" +#include "Destination.h" +#include "Streaming.h" + +namespace i2p +{ +namespace api +{ + // initialization start and stop + void InitI2P (int argc, char* argv[], const char * appName); + void StartI2P (std::ostream * logStream = nullptr); + // write system log to logStream, if not specified to .log in application's folder + void StopI2P (); + + // destinations + i2p::client::ClientDestination * CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, + const std::map * params = nullptr); + i2p::client::ClientDestination * CreateLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256, + const std::map * params = nullptr); // transient destinations usually not published + void DestroyLocalDestination (i2p::client::ClientDestination * dest); + + // streams + void RequestLeaseSet (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote); + std::shared_ptr CreateStream (i2p::client::ClientDestination * dest, const i2p::data::IdentHash& remote); + void AcceptStream (i2p::client::ClientDestination * dest, const i2p::stream::StreamingDestination::Acceptor& acceptor); + void DestroyStream (std::shared_ptr stream); +} +} + +#endif + diff --git a/base64.cpp b/base64.cpp new file mode 100644 index 00000000..3d1ec07e --- /dev/null +++ b/base64.cpp @@ -0,0 +1,269 @@ +#include +#include "base64.h" + +namespace i2p +{ +namespace data +{ + + static void iT64Build(void); + + /* + * + * BASE64 Substitution Table + * ------------------------- + * + * Direct Substitution Table + */ + + static char T64[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '~' + }; + + const char * GetBase64SubstitutionTable () + { + return T64; + } + + /* + * Reverse Substitution Table (built in run time) + */ + + static char iT64[256]; + static int isFirstTime = 1; + + /* + * Padding + */ + + static char P64 = '='; + + /* + * + * ByteStreamToBase64 + * ------------------ + * + * Converts binary encoded data to BASE64 format. + * + */ + + size_t /* Number of bytes in the encoded buffer */ + ByteStreamToBase64 ( + const uint8_t * InBuffer, /* Input buffer, binary data */ + size_t InCount, /* Number of bytes in the input buffer */ + char * OutBuffer, /* output buffer */ + size_t len /* length of output buffer */ + ) + + { + unsigned char * ps; + unsigned char * pd; + unsigned char acc_1; + unsigned char acc_2; + int i; + int n; + int m; + size_t outCount; + + ps = (unsigned char *)InBuffer; + n = InCount/3; + m = InCount%3; + if (!m) + outCount = 4*n; + else + outCount = 4*(n+1); + if (outCount > len) return 0; + pd = (unsigned char *)OutBuffer; + for ( i = 0; i>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + acc_1 = *ps++; + acc_2 |= acc_1 >> 4; /* base64 digit #2 */ + *pd++ = T64[acc_2]; + acc_1 &= 0x0f; + acc_1 <<=2; + acc_2 = *ps++; + acc_1 |= acc_2>>6; /* base64 digit #3 */ + *pd++ = T64[acc_1]; + acc_2 &= 0x3f; /* base64 digit #4 */ + *pd++ = T64[acc_2]; + } + if ( m == 1 ){ + acc_1 = *ps++; + acc_2 = (acc_1<<4)&0x3f; /* base64 digit #2 */ + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + *pd++ = T64[acc_2]; + *pd++ = P64; + *pd++ = P64; + + } + else if ( m == 2 ){ + acc_1 = *ps++; + acc_2 = (acc_1<<4)&0x3f; + acc_1 >>= 2; /* base64 digit #1 */ + *pd++ = T64[acc_1]; + acc_1 = *ps++; + acc_2 |= acc_1 >> 4; /* base64 digit #2 */ + *pd++ = T64[acc_2]; + acc_1 &= 0x0f; + acc_1 <<=2; /* base64 digit #3 */ + *pd++ = T64[acc_1]; + *pd++ = P64; + } + + return outCount; + } + + /* + * + * Base64ToByteStream + * ------------------ + * + * Converts BASE64 encoded data to binary format. If input buffer is + * not properly padded, buffer of negative length is returned + * + */ + + size_t /* Number of output bytes */ + Base64ToByteStream ( + const char * InBuffer, /* BASE64 encoded buffer */ + size_t InCount, /* Number of input bytes */ + uint8_t * OutBuffer, /* output buffer length */ + size_t len /* length of output buffer */ + ) + { + unsigned char * ps; + unsigned char * pd; + unsigned char acc_1; + unsigned char acc_2; + int i; + int n; + int m; + size_t outCount; + + if (isFirstTime) iT64Build(); + n = InCount/4; + m = InCount%4; + if (!m) + outCount = 3*n; + else { + outCount = 0; + return 0; + } + + ps = (unsigned char *)(InBuffer + InCount - 1); + while ( *ps-- == P64 ) outCount--; + ps = (unsigned char *)InBuffer; + + if (outCount > len) return -1; + pd = OutBuffer; + auto endOfOutBuffer = OutBuffer + outCount; + for ( i = 0; i < n; i++ ){ + acc_1 = iT64[*ps++]; + acc_2 = iT64[*ps++]; + acc_1 <<= 2; + acc_1 |= acc_2>>4; + *pd++ = acc_1; + if (pd >= endOfOutBuffer) break; + + acc_2 <<= 4; + acc_1 = iT64[*ps++]; + acc_2 |= acc_1 >> 2; + *pd++ = acc_2; + if (pd >= endOfOutBuffer) break; + + acc_2 = iT64[*ps++]; + acc_2 |= acc_1 << 6; + *pd++ = acc_2; + } + + return outCount; + } + + /* + * + * iT64 + * ---- + * Reverse table builder. P64 character is replaced with 0 + * + * + */ + + static void iT64Build() + { + int i; + isFirstTime = 0; + for ( i=0; i<256; i++ ) iT64[i] = -1; + for ( i=0; i<64; i++ ) iT64[(int)T64[i]] = i; + iT64[(int)P64] = 0; + } + + size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen) + { + int tmp = 0, bits = 0; + size_t ret = 0; + for (size_t i = 0; i < len; i++) + { + char ch = inBuf[i]; + if (ch >= '2' && ch <= '7') // digit + ch = (ch - '2') + 26; // 26 means a-z + else if (ch >= 'a' && ch <= 'z') + ch = ch - 'a'; // a = 0 + else + return 0; // unexpected character + + tmp |= ch; + bits += 5; + if (bits >= 8) + { + if (ret >= outLen) return ret; + outBuf[ret] = tmp >> (bits - 8); + bits -= 8; + ret++; + } + tmp <<= 5; + } + return ret; + } + + size_t ByteStreamToBase32 (const uint8_t * inBuf, size_t len, char * outBuf, size_t outLen) + { + size_t ret = 0, pos = 1; + int bits = 8, tmp = inBuf[0]; + while (ret < outLen && (bits > 0 || pos < len)) + { + if (bits < 5) + { + if (pos < len) + { + tmp <<= 8; + tmp |= inBuf[pos] & 0xFF; + pos++; + bits += 8; + } + else // last byte + { + tmp <<= (5 - bits); + bits = 5; + } + } + + bits -= 5; + int ind = (tmp >> bits) & 0x1F; + outBuf[ret] = (ind < 26) ? (ind + 'a') : ((ind - 26) + '2'); + ret++; + } + return ret; + } +} +} diff --git a/base64.h b/base64.h new file mode 100644 index 00000000..c0ce1495 --- /dev/null +++ b/base64.h @@ -0,0 +1,22 @@ +#ifndef BASE64_H +#define BASE64_H + +#include +#include + +namespace i2p +{ +namespace data +{ + + size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len); + size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len ); + const char * GetBase64SubstitutionTable (); + + size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen); + size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen); +} +} + +#endif + diff --git a/build/.gitignore b/build/.gitignore deleted file mode 100644 index 39b8094c..00000000 --- a/build/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# Various generated files -/CMakeFiles/ -/Testing/ -/tests/ -/.ninja_* -/arch.c -/build.ninja -/i2pd -/i2pd.exe -/i2pd.exe.debug -/libi2pd.a -/libi2pdclient.a -/libi2pdlang.a -/cmake_install.cmake -/CMakeCache.txt -/CPackConfig.cmake -/CPackSourceConfig.cmake -/CTestTestfile.cmake -/install_manifest.txt -/Makefile -# windows build script -i2pd*.zip -build*.log -# MVS project files -*.vcxproj -*.vcxproj.filters -*.sln diff --git a/build/BUILD_NOTES.md b/build/BUILD_NOTES.md new file mode 100644 index 00000000..aea939da --- /dev/null +++ b/build/BUILD_NOTES.md @@ -0,0 +1,50 @@ +Build notes +=========== + +Common build/install process: + +* git clone https://github.com/PrivacySolutions/i2pd.git +* cd i2pd/build +* cmake -DCMAKE_BUILD_TYPE=Release . +* make +* make install + +Available cmake options: + +* CMAKE_BUILD_TYPE -- build profile (Debug/Release) +* WITH_AESNI -- AES-NI support (ON/OFF) +* WITH_HARDENING -- enable hardening features (ON/OFF) (gcc only) + +Debian +------ + +Required "-dev" packages: +* cmake +* libboost-filesystem-dev +* libboost-program-options-dev +* libboost-regex-dev +* libboost-system-dev +* libcrypto++-dev + +FreeBSD +------- + +Branch 9.X has gcc v4.2, that knows nothing about required c++11 standart. + +Required ports: + +* devel/cmake +* devel/boost-libs +* lang/gcc47 # or later version +* security/cryptopp + +To use newer compiler you should set these variables: + + export CC=/usr/local/bin/gcc47 + export CXX=/usr/local/bin/g++47 + +Replace "47" with your actual gcc version + +Branch 10.X has more reliable clang version, that can finally build i2pd, +but i still recommend to use gcc, otherwise you will fight it's bugs by +your own. diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index bc936e18..bad1daf9 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -1,318 +1,134 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required ( VERSION 2.8.5 ) +project ( "i2pd" ) -if(${CMAKE_VERSION} VERSION_LESS 3.22) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -else() - cmake_policy(VERSION 3.22) -endif() - -# for debugging -#set(CMAKE_VERBOSE_MAKEFILE on) +# configurale options +option(WITH_AESNI "Use AES-NI instructions set" OFF) +option(WITH_HARDENING "Use hardening compiler flags" OFF) +option(WITH_LIBRARY "Build library" ON) +option(WITH_BINARY "Build binary" ON) +option(WITH_STATIC "Static build" OFF) # paths -set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules") -set(CMAKE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") +set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" ) +set ( CMAKE_SOURCE_DIR ".." ) -set(LIBI2PD_SRC_DIR ${CMAKE_SOURCE_DIR}/libi2pd) -set(LIBI2PD_CLIENT_SRC_DIR ${CMAKE_SOURCE_DIR}/libi2pd_client) -set(LANG_SRC_DIR ${CMAKE_SOURCE_DIR}/i18n) -set(DAEMON_SRC_DIR ${CMAKE_SOURCE_DIR}/daemon) - -include(Version) -set_version("${LIBI2PD_SRC_DIR}/version.h" PROJECT_VERSION) - -project( - i2pd - VERSION ${PROJECT_VERSION} - HOMEPAGE_URL "https://i2pd.website/" - LANGUAGES C CXX +set (COMMON_SRC + "${CMAKE_SOURCE_DIR}/AddressBook.cpp" + "${CMAKE_SOURCE_DIR}/CryptoConst.cpp" + "${CMAKE_SOURCE_DIR}/Garlic.cpp" + "${CMAKE_SOURCE_DIR}/I2NPProtocol.cpp" + "${CMAKE_SOURCE_DIR}/Identity.cpp" + "${CMAKE_SOURCE_DIR}/LeaseSet.cpp" + "${CMAKE_SOURCE_DIR}/Log.cpp" + "${CMAKE_SOURCE_DIR}/NTCPSession.cpp" + "${CMAKE_SOURCE_DIR}/NetDb.cpp" + "${CMAKE_SOURCE_DIR}/Reseed.cpp" + "${CMAKE_SOURCE_DIR}/RouterContext.cpp" + "${CMAKE_SOURCE_DIR}/RouterInfo.cpp" + "${CMAKE_SOURCE_DIR}/SSU.cpp" + "${CMAKE_SOURCE_DIR}/SSUData.cpp" + "${CMAKE_SOURCE_DIR}/SSUSession.cpp" + "${CMAKE_SOURCE_DIR}/Streaming.cpp" + "${CMAKE_SOURCE_DIR}/Destination.cpp" + "${CMAKE_SOURCE_DIR}/TransitTunnel.cpp" + "${CMAKE_SOURCE_DIR}/Tunnel.cpp" + "${CMAKE_SOURCE_DIR}/TunnelGateway.cpp" + "${CMAKE_SOURCE_DIR}/Transports.cpp" + "${CMAKE_SOURCE_DIR}/TunnelEndpoint.cpp" + "${CMAKE_SOURCE_DIR}/TunnelPool.cpp" + "${CMAKE_SOURCE_DIR}/aes.cpp" + "${CMAKE_SOURCE_DIR}/base64.cpp" + "${CMAKE_SOURCE_DIR}/util.cpp" + "${CMAKE_SOURCE_DIR}/Datagram.cpp" ) -# configurable options -option(WITH_HARDENING "Use hardening compiler flags" OFF) -option(WITH_LIBRARY "Build library" ON) -option(WITH_BINARY "Build binary" ON) -option(WITH_STATIC "Static build" OFF) -option(WITH_UPNP "Include support for UPnP client" OFF) -option(WITH_GIT_VERSION "Use git commit info as version" OFF) -option(WITH_ADDRSANITIZER "Build with address sanitizer unix only" OFF) -option(WITH_THREADSANITIZER "Build with thread sanitizer unix only" OFF) -option(BUILD_TESTING "Build tests" OFF) - -IF(BUILD_TESTING) - enable_testing() -ENDIF() - -# Handle paths nicely -include(GNUInstallDirs) - -# Architecture -include(TargetArch) -target_architecture(ARCHITECTURE) - -include(CheckAtomic) - -if(WITH_STATIC) - if(MSVC) - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - endif() -endif() - -include_directories(${LIBI2PD_SRC_DIR}) -FILE(GLOB LIBI2PD_SRC ${LIBI2PD_SRC_DIR}/*.cpp) -add_library(libi2pd ${LIBI2PD_SRC}) -set_target_properties(libi2pd PROPERTIES PREFIX "") - -if(WITH_LIBRARY) - install(TARGETS libi2pd - EXPORT libi2pd - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - COMPONENT Libraries) -endif() - -include_directories(${LIBI2PD_CLIENT_SRC_DIR}) -FILE(GLOB CLIENT_SRC ${LIBI2PD_CLIENT_SRC_DIR}/*.cpp) -add_library(libi2pdclient ${CLIENT_SRC}) -set_target_properties(libi2pdclient PROPERTIES PREFIX "") - -if(WITH_LIBRARY) - install(TARGETS libi2pdclient - EXPORT libi2pdclient - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - COMPONENT Libraries) -endif() - -include_directories(${LANG_SRC_DIR}) -FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp) -add_library(libi2pdlang ${LANG_SRC}) -set_target_properties(libi2pdlang PROPERTIES PREFIX "") - -if(WITH_LIBRARY) - install(TARGETS libi2pdlang - EXPORT libi2pdlang - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - COMPONENT Libraries) -endif() - -include_directories(${DAEMON_SRC_DIR}) - -set(DAEMON_SRC - "${DAEMON_SRC_DIR}/Daemon.cpp" - "${DAEMON_SRC_DIR}/HTTPServer.cpp" - "${DAEMON_SRC_DIR}/I2PControl.cpp" - "${DAEMON_SRC_DIR}/I2PControlHandlers.cpp" - "${DAEMON_SRC_DIR}/i2pd.cpp" - "${DAEMON_SRC_DIR}/UPnP.cpp" +set (DAEMON_SRC + "${CMAKE_SOURCE_DIR}/BOB.cpp" + "${CMAKE_SOURCE_DIR}/ClientContext.cpp" + "${CMAKE_SOURCE_DIR}/Daemon.cpp" + "${CMAKE_SOURCE_DIR}/HTTPProxy.cpp" + "${CMAKE_SOURCE_DIR}/HTTPServer.cpp" + "${CMAKE_SOURCE_DIR}/I2PTunnel.cpp" + "${CMAKE_SOURCE_DIR}/SAM.cpp" + "${CMAKE_SOURCE_DIR}/SOCKS.cpp" + "${CMAKE_SOURCE_DIR}/UPnP.cpp" + "${CMAKE_SOURCE_DIR}/i2p.cpp" ) -if(WIN32) - set(WIN32_SRC_DIR ${CMAKE_SOURCE_DIR}/Win32) - include_directories(${WIN32_SRC_DIR}) +set (LIBRARY_SRC + "${CMAKE_SOURCE_DIR}/api.cpp" +) - list(APPEND DAEMON_SRC - "${WIN32_SRC_DIR}/DaemonWin32.cpp" - "${WIN32_SRC_DIR}/Win32App.cpp" - "${WIN32_SRC_DIR}/Win32Service.cpp" - "${WIN32_SRC_DIR}/Win32NetState.cpp" - ) +file (GLOB HEADERS "${CMAKE_SOURCE_DIR}/*.h") - file(GLOB WIN32_RC ${WIN32_SRC_DIR}/*.rc) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWIN32_APP -DWIN32_LEAN_AND_MEAN -DNOMINMAX") +# MSVS grouping +source_group ("Header Files" FILES ${HEADERS}) +source_group ("Source Files" FILES ${COMMON_SRC} ${DAEMON_SRC} ${LIBRARY_SRC}) -endif() +# Default build is Debug +if (CMAKE_BUILD_TYPE STREQUAL "Release") + add_definitions( "-pedantic" ) +else () + set(CMAKE_BUILD_TYPE Debug) +endif () -if(WITH_UPNP) - add_definitions(-DUSE_UPNP) -endif() +# compiler flags customization (by vendor) +add_definitions ( "-Wall -Wextra -fPIC" ) -if(WITH_GIT_VERSION) - include(GetGitRevisionDescription) - git_describe(GIT_VERSION) - add_definitions(-DGITVER=${GIT_VERSION}) -endif() +# check for c++11 support +include(CheckCXXCompilerFlag) +CHECK_CXX_COMPILER_FLAG("-std=c++11" CXX11_SUPPORTED) +CHECK_CXX_COMPILER_FLAG("-std=c++0x" CXX0X_SUPPORTED) +if (CXX11_SUPPORTED) + add_definitions( "-std=c++11" ) +elseif (CXX0X_SUPPORTED) # gcc 4.6 + add_definitions( "-std=c++0x" ) +else () + message(SEND_ERROR "C++11 standart not seems to be supported by compiler. Too old version?") +endif () -if(APPLE) - add_definitions(-DMAC_OSX) -endif() - -if(HAIKU) - add_definitions(-D_DEFAULT_SOURCE -D_GNU_SOURCE) -endif() - -if(MSVC) - add_definitions(-DWINVER=0x0600) - add_definitions(-D_WIN32_WINNT=0x0600) -else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Winvalid-pch -Wno-unused-parameter -Wno-uninitialized") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -pedantic") - # TODO: The following is incompatible with static build and enabled hardening for OpenWRT. - # Multiple definitions of __stack_chk_fail(libssp & libc) - if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -flto -s") - endif() - set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -ffunction-sections -fdata-sections") - set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections") # -flto is added from above -endif() - -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pipe") - if(WITH_HARDENING) - add_definitions("-D_FORTIFY_SOURCE=2") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat -Wformat-security -Werror=format-security") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector --param ssp-buffer-size=4") - endif() -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if (WITH_HARDENING) + add_definitions( "-D_FORTIFY_SOURCE=2" ) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat -Wformat-security -Werror=format-security" ) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector --param ssp-buffer-size=4" ) + endif () +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # more tweaks - if(LINUX) - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libstdc++") # required for - list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++") # required to link with -stdlib=libstdc++ - endif() - if(NOT APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-const-variable -Wno-overloaded-virtual -Wno-c99-extensions") - endif() -endif() +endif () -# compiler flags customization(by system) -if(UNIX) - list(APPEND DAEMON_SRC "${DAEMON_SRC_DIR}/UnixDaemon.cpp") - if(NOT(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR APPLE)) - # "'sleep_for' is not a member of 'std::this_thread'" in gcc 4.7/4.8 - add_definitions("-D_GLIBCXX_USE_NANOSLEEP=1") - endif() -endif() +# compiler flags customization (by system) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + list (APPEND DAEMON_SRC "${CMAKE_SOURCE_DIR}/DaemonLinux.cpp") +elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + list (APPEND DAEMON_SRC "${CMAKE_SOURCE_DIR}/DaemonLinux.cpp") + # "'sleep_for' is not a member of 'std::this_thread'" in gcc 4.7/4.8 + add_definitions( "-D_GLIBCXX_USE_NANOSLEEP=1" ) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + list (APPEND DAEMON_SRC "${CMAKE_SOURCE_DIR}/DaemonLinux.cpp") +elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows") + list (APPEND DAEMON_SRC "${CMAKE_SOURCE_DIR}/DaemonWin32.cpp") +endif () -if(WITH_ADDRSANITIZER) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") -endif() - -if(WITH_THREADSANITIZER) - if(WITH_ADDRSANITIZER) - message(FATAL_ERROR "thread sanitizer option cannot be combined with address sanitizer") - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread") - endif() -endif() - -if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) # gcc 8-9 - list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++fs") -endif() - -# Use std::atomic instead of GCC builtins on macOS PowerPC: -# For more information refer to: https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111 -# This has been fixed in Boost 1.81, nevertheless we retain the setting for the sake of compatibility. -if(APPLE AND CMAKE_OSX_ARCHITECTURES MATCHES "ppc") - add_definitions(-DBOOST_SP_USE_STD_ATOMIC) +if (WITH_AESNI) + add_definitions ( "-maes -DAESNI" ) endif() # libraries -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) +find_package ( Threads REQUIRED ) -if(WITH_STATIC) - if(NOT MSVC) - set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") - endif() - - set(Boost_USE_STATIC_LIBS ON) - if(MSVC) - set(Boost_USE_STATIC_RUNTIME ON) - else() - set(Boost_USE_STATIC_RUNTIME OFF) - endif() - - if(MSVC) - set(OPENSSL_MSVC_STATIC_RT ON) - endif() - set(OPENSSL_USE_STATIC_LIBS ON) - - set(ZLIB_USE_STATIC_LIBS ON) - if(MSVC) - set(ZLIB_NAMES zlibstatic zlibstat) - else() - set(ZLIB_NAMES libz zlibstatic zlibstat zlib z) - endif() - - if(WITH_UPNP) - set(MINIUPNPC_USE_STATIC_LIBS ON) - add_definitions(-DMINIUPNP_STATICLIB) - endif() - - set(BUILD_SHARED_LIBS OFF) - - if(${CMAKE_CXX_COMPILER} MATCHES ".*-openwrt-.*") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") - # set(CMAKE_THREAD_LIBS_INIT "gcc_eh -Wl,--whole-archive -lpthread -Wl,--no-whole-archive") - set(CMAKE_THREAD_LIBS_INIT "gcc_eh -Wl,-u,pthread_create,-u,pthread_once,-u,pthread_mutex_lock,-u,pthread_mutex_unlock,-u,pthread_join,-u,pthread_equal,-u,pthread_detach,-u,pthread_cond_wait,-u,pthread_cond_signal,-u,pthread_cond_destroy,-u,pthread_cond_broadcast,-u,pthread_cancel") - endif() -else() - # TODO: Consider separate compilation for LIBI2PD_SRC for library. - # No need in -fPIC overhead for binary if not interested in library - # HINT: revert c266cff CMakeLists.txt: compilation speed up - if(NOT MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") - endif() - add_definitions(-DBOOST_ATOMIC_DYN_LINK -DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK) - if(WIN32) - set(Boost_USE_STATIC_LIBS OFF) - set(Boost_USE_STATIC_RUNTIME OFF) - endif() +find_package ( Boost COMPONENTS system filesystem regex program_options REQUIRED ) +if(NOT DEFINED Boost_INCLUDE_DIRS) + message(SEND_ERROR "Boost is not found, or your boost version was bellow 1.46. Please download Boost!") endif() -find_package(Boost REQUIRED COMPONENTS system filesystem program_options) -if(NOT DEFINED Boost_FOUND) - message(SEND_ERROR "Boost is not found, or your boost version was below 1.46. Please download Boost!") -endif() - -find_package(OpenSSL REQUIRED) -if(NOT DEFINED OPENSSL_FOUND) - message(SEND_ERROR "Could not find OpenSSL. Please download and install it first!") -endif() - -if(OPENSSL_VERSION VERSION_GREATER_EQUAL "3.0.0") - add_definitions(-DOPENSSL_SUPPRESS_DEPRECATED) -endif() - -if(WITH_UPNP) - find_package(MiniUPnPc REQUIRED) - if(NOT MINIUPNPC_FOUND) - message(SEND_ERROR "Could not find MiniUPnPc. Please download and install it first!") - else() - include_directories(SYSTEM ${MINIUPNPC_INCLUDE_DIR}) - endif() -endif() - -find_package(ZLIB) -if(ZLIB_FOUND) - link_directories(${ZLIB_ROOT}/lib) -endif() - -# C++ standard to use, based on compiler and version of boost -if(NOT MSVC) -# check for c++20 & c++17 support - include(CheckCXXCompilerFlag) - - if(Boost_VERSION VERSION_GREATER_EQUAL "1.83") # min boost version for c++20 - CHECK_CXX_COMPILER_FLAG("-std=c++20" CXX20_SUPPORTED) - endif() - CHECK_CXX_COMPILER_FLAG("-std=c++17" CXX17_SUPPORTED) - - - if(CXX20_SUPPORTED) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") - elseif(CXX17_SUPPORTED) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") - else() - message(SEND_ERROR "C++20 nor C++17 standard not seems to be supported by compiler. Too old version?") - endif() +find_package ( CryptoPP REQUIRED ) +if(NOT DEFINED CRYPTO++_INCLUDE_DIR) + message(SEND_ERROR "Could not find Crypto++. Please download and install it first!") endif() # load includes -include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) +include_directories( ${Boost_INCLUDE_DIRS} ${CRYPTO++_INCLUDE_DIR} "${CMAKE_SOURCE_DIR}/..") # show summary message(STATUS "---------------------------------------") @@ -320,73 +136,37 @@ message(STATUS "Build type : ${CMAKE_BUILD_TYPE}") message(STATUS "Compiler vendor : ${CMAKE_CXX_COMPILER_ID}") message(STATUS "Compiler version : ${CMAKE_CXX_COMPILER_VERSION}") message(STATUS "Compiler path : ${CMAKE_CXX_COMPILER}") -message(STATUS "Architecture : ${ARCHITECTURE}") -message(STATUS "Compiler flags : ${CMAKE_CXX_FLAGS}") message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}") message(STATUS "Options:") +message(STATUS " AESNI : ${WITH_AESNI}") message(STATUS " HARDENING : ${WITH_HARDENING}") message(STATUS " LIBRARY : ${WITH_LIBRARY}") message(STATUS " BINARY : ${WITH_BINARY}") message(STATUS " STATIC BUILD : ${WITH_STATIC}") -message(STATUS " UPnP : ${WITH_UPNP}") -if(WITH_GIT_VERSION) -message(STATUS " GIT VERSION : ${WITH_GIT_VERSION} (${GIT_VERSION})") -else() -message(STATUS " GIT VERSION : ${WITH_GIT_VERSION}") -endif() -message(STATUS " ADDRSANITIZER : ${WITH_ADDRSANITIZER}") -message(STATUS " THREADSANITIZER : ${WITH_THREADSANITIZER}") message(STATUS "---------------------------------------") -if(WITH_BINARY) - if(WIN32) - add_executable("${PROJECT_NAME}" WIN32 ${DAEMON_SRC} ${WIN32_RC}) - else() - add_executable("${PROJECT_NAME}" ${DAEMON_SRC}) - endif() +#Handle paths nicely +include(GNUInstallDirs) - if(WIN32) - list(APPEND MINGW_EXTRA "wsock32" "ws2_32" "iphlpapi") - # OpenSSL may require Crypt32 library on MSVC build, which is not added by CMake lesser than 3.21 - if(MSVC AND ${CMAKE_VERSION} VERSION_LESS 3.21) - list(APPEND MINGW_EXTRA "crypt32") - endif() - endif() +if (WITH_BINARY) + add_executable ( "${PROJECT_NAME}-bin" ${COMMON_SRC} ${DAEMON_SRC}) + set_target_properties("${PROJECT_NAME}-bin" PROPERTIES OUTPUT_NAME "${PROJECT_NAME}") - if(WITH_STATIC) - if(NOT MSVC) - set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static") - endif() - endif() + if (WITH_HARDENING AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set_target_properties("${PROJECT_NAME}-bin" PROPERTIES LINK_FLAGS "-z relro -z now" ) + endif () - if(WITH_HARDENING AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-z relro -z now") - endif() + if (WITH_STATIC) + set(BUILD_SHARED_LIBS OFF) + set_target_properties("${PROJECT_NAME}-bin" PROPERTIES LINK_FLAGS "-static" ) + endif () - # FindBoost pulls pthread for thread which is broken for static linking at least on Ubuntu 15.04 - list(GET Boost_LIBRARIES -1 LAST_Boost_LIBRARIES) - if(${LAST_Boost_LIBRARIES} MATCHES ".*pthread.*") - list(REMOVE_AT Boost_LIBRARIES -1) - endif() + target_link_libraries( "${PROJECT_NAME}-bin" ${Boost_LIBRARIES} ${CRYPTO++_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) - # synchronization library is incompatible with Windows 7 - if(WIN32) - get_target_property(BOOSTFSLIBS Boost::filesystem INTERFACE_LINK_LIBRARIES) - list(REMOVE_ITEM BOOSTFSLIBS synchronization) - set_target_properties(Boost::filesystem PROPERTIES INTERFACE_LINK_LIBRARIES "${BOOSTFSLIBS}") - endif() + install(TARGETS "${PROJECT_NAME}-bin" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) +endif () - if(WITH_STATIC) - set(DL_LIB ${CMAKE_DL_LIBS}) - endif() - - target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto ${MINIUPNPC_LIBRARY} ZLIB::ZLIB Threads::Threads ${MINGW_EXTRA} ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES}) - - install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime) - set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}") - set(DIRS "${Boost_LIBRARY_DIR};${OPENSSL_INCLUDE_DIR}/../bin;${ZLIB_INCLUDE_DIR}/../bin;/mingw32/bin") -endif() - -if(BUILD_TESTING) - add_subdirectory(${CMAKE_SOURCE_DIR}/tests ${CMAKE_CURRENT_BINARY_DIR}/tests) -endif() +if (WITH_LIBRARY) + add_library(${PROJECT_NAME} SHARED ${COMMON_SRC} ${LIBRARY_SRC}) + install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) +endif () diff --git a/build/autotools/.gitignore b/build/autotools/.gitignore new file mode 100644 index 00000000..a0fc14e4 --- /dev/null +++ b/build/autotools/.gitignore @@ -0,0 +1,230 @@ +# i2pd +obj/*.o +router.info +router.keys +i2p +netDb + +# Autotools +autom4te.cache +.deps +stamp-h1 +Makefile +config.h +config.h.in~ +config.log +config.status + +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +[Bb]in/ +[Oo]bj/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +############# +## Windows detritus +############# + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist/ +eggs/ +parts/ +var/ +sdist/ +develop-eggs/ +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg diff --git a/build/autotools/Makefile.am b/build/autotools/Makefile.am new file mode 100644 index 00000000..fe1af15a --- /dev/null +++ b/build/autotools/Makefile.am @@ -0,0 +1,26 @@ +bin_PROGRAMS = i2p +i2p_SOURCES = AddressBook.cpp CryptoConst.cpp Daemon.cpp \ + DaemonLinux.cpp DaemonWin32.cpp Garlic.cpp \ + HTTPProxy.cpp HTTPServer.cpp I2NPProtocol.cpp \ + I2PTunnel.cpp Identity.cpp LeaseSet.cpp Log.cpp \ + NTCPSession.cpp NetDb.cpp Reseed.cpp \ + RouterContext.cpp RouterInfo.cpp SOCKS.cpp SSU.cpp \ + SSUData.cpp Streaming.cpp TransitTunnel.cpp \ + Transports.cpp Tunnel.cpp TunnelEndpoint.cpp \ + TunnelGateway.cpp TunnelPool.cpp UPnP.cpp aes.cpp \ + base64.cpp i2p.cpp util.cpp \ + \ + AddressBook.h CryptoConst.h Daemon.h ElGamal.h \ + Garlic.h HTTPProxy.h HTTPServer.h I2NPProtocol.h \ + I2PEndian.h I2PTunnel.h Identity.h LeaseSet.h \ + LittleBigEndian.h Log.h NTCPSession.h NetDb.h Queue.h \ + Reseed.h RouterContext.h RouterInfo.h SOCKS.h SSU.h \ + SSUData.h Signature.h Streaming.h Timestamp.h \ + TransitTunnel.h Transports.h Tunnel.h TunnelBase.h \ + TunnelConfig.h TunnelEndpoint.h TunnelGateway.h \ + TunnelPool.h UPnP.h aes.h base64.h config.h hmac.h \ + util.h version.h + +AM_LDFLAGS = @BOOST_DATE_TIME_LIB@ @BOOST_FILESYSTEM_LIB@ \ + @BOOST_PROGRAM_OPTIONS_LIB@ @BOOST_REGEX_LIB@ \ + @BOOST_SYSTEM_LIB@ diff --git a/build/autotools/Makefile.in b/build/autotools/Makefile.in new file mode 100644 index 00000000..6c861710 --- /dev/null +++ b/build/autotools/Makefile.in @@ -0,0 +1,854 @@ +# Makefile.in generated by automake 1.13.4 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = i2p$(EXEEXT) +subdir = . +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/configure $(am__configure_deps) \ + $(srcdir)/config.h.in depcomp config.guess config.sub \ + install-sh missing +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_boost_base.m4 \ + $(top_srcdir)/m4/ax_boost_date_time.m4 \ + $(top_srcdir)/m4/ax_boost_filesystem.m4 \ + $(top_srcdir)/m4/ax_boost_program_options.m4 \ + $(top_srcdir)/m4/ax_boost_regex.m4 \ + $(top_srcdir)/m4/ax_boost_system.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am_i2p_OBJECTS = AddressBook.$(OBJEXT) CryptoConst.$(OBJEXT) \ + Daemon.$(OBJEXT) DaemonLinux.$(OBJEXT) DaemonWin32.$(OBJEXT) \ + Garlic.$(OBJEXT) HTTPProxy.$(OBJEXT) HTTPServer.$(OBJEXT) \ + I2NPProtocol.$(OBJEXT) I2PTunnel.$(OBJEXT) Identity.$(OBJEXT) \ + LeaseSet.$(OBJEXT) Log.$(OBJEXT) NTCPSession.$(OBJEXT) \ + NetDb.$(OBJEXT) Reseed.$(OBJEXT) RouterContext.$(OBJEXT) \ + RouterInfo.$(OBJEXT) SOCKS.$(OBJEXT) SSU.$(OBJEXT) \ + SSUData.$(OBJEXT) Streaming.$(OBJEXT) TransitTunnel.$(OBJEXT) \ + Transports.$(OBJEXT) Tunnel.$(OBJEXT) TunnelEndpoint.$(OBJEXT) \ + TunnelGateway.$(OBJEXT) TunnelPool.$(OBJEXT) UPnP.$(OBJEXT) \ + aes.$(OBJEXT) base64.$(OBJEXT) i2p.$(OBJEXT) util.$(OBJEXT) \ + SAM.$(OBJEXT) Destination.$(OBJEXT) ClientContext.$(OBJEXT) \ + Datagram.$(OBJEXT) SSUSession.$(OBJEXT) BOB.$(OBJEXT) +i2p_OBJECTS = $(am_i2p_OBJECTS) +i2p_LDADD = $(LDADD) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(i2p_SOURCES) +DIST_SOURCES = $(i2p_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ + $(LISP)config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +AM_RECURSIVE_TARGETS = cscope +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_DATE_TIME_LIB = @BOOST_DATE_TIME_LIB@ +BOOST_FILESYSTEM_LIB = @BOOST_FILESYSTEM_LIB@ +BOOST_LDFLAGS = @BOOST_LDFLAGS@ +BOOST_PROGRAM_OPTIONS_LIB = @BOOST_PROGRAM_OPTIONS_LIB@ +BOOST_REGEX_LIB = @BOOST_REGEX_LIB@ +BOOST_SYSTEM_LIB = @BOOST_SYSTEM_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +i2p_SOURCES = AddressBook.cpp CryptoConst.cpp Daemon.cpp \ + DaemonLinux.cpp DaemonWin32.cpp Garlic.cpp \ + HTTPProxy.cpp HTTPServer.cpp I2NPProtocol.cpp \ + I2PTunnel.cpp Identity.cpp LeaseSet.cpp Log.cpp \ + NTCPSession.cpp NetDb.cpp Reseed.cpp \ + RouterContext.cpp RouterInfo.cpp SOCKS.cpp SSU.cpp \ + SSUData.cpp Streaming.cpp TransitTunnel.cpp \ + Transports.cpp Tunnel.cpp TunnelEndpoint.cpp \ + TunnelGateway.cpp TunnelPool.cpp UPnP.cpp aes.cpp \ + base64.cpp i2p.cpp util.cpp SAM.cpp Destination.cpp \ + ClientContext.cpp DataFram.cpp SSUSession.cpp BOB.cpp \ + \ + AddressBook.h CryptoConst.h Daemon.h ElGamal.h \ + Garlic.h HTTPProxy.h HTTPServer.h I2NPProtocol.h \ + I2PEndian.h I2PTunnel.h Identity.h LeaseSet.h \ + LittleBigEndian.h Log.h NTCPSession.h NetDb.h Queue.h \ + Reseed.h RouterContext.h RouterInfo.h SOCKS.h SSU.h \ + SSUData.h Signature.h Streaming.h Timestamp.h \ + TransitTunnel.h Transports.h Tunnel.h TunnelBase.h \ + TunnelConfig.h TunnelEndpoint.h TunnelGateway.h \ + TunnelPool.h UPnP.h aes.h base64.h config.h hmac.h \ + util.h version.h Destination.h ClientContext.h \ + TransportSession.h Datagram.h SSUSession.h BOB.h + +AM_LDFLAGS = @BOOST_DATE_TIME_LIB@ @BOOST_FILESYSTEM_LIB@ \ + @BOOST_PROGRAM_OPTIONS_LIB@ @BOOST_REGEX_LIB@ \ + @BOOST_SYSTEM_LIB@ + +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +config.h: stamp-h1 + @if test ! -f $@; then rm -f stamp-h1; else :; fi + @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) stamp-h1; else :; fi + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.h.in: $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) + +i2p$(EXEEXT): $(i2p_OBJECTS) $(i2p_DEPENDENCIES) $(EXTRA_i2p_DEPENDENCIES) + @rm -f i2p$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(i2p_OBJECTS) $(i2p_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AddressBook.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CryptoConst.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Daemon.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DaemonLinux.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DaemonWin32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Garlic.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HTTPProxy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HTTPServer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/I2NPProtocol.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/I2PTunnel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Identity.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LeaseSet.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Log.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NTCPSession.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetDb.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Reseed.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RouterContext.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RouterInfo.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SOCKS.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SSU.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SSUData.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Streaming.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Destination.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TransitTunnel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Transports.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Tunnel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TunnelEndpoint.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TunnelGateway.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TunnelPool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UPnP.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aes.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i2p.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SAM.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BOB.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ClientContext.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Datagram.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SSUSession.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) config.h +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: all install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-am clean \ + clean-binPROGRAMS clean-cscope clean-generic cscope \ + cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ + dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ + distcheck distclean distclean-compile distclean-generic \ + distclean-hdr distclean-tags distcleancheck distdir \ + distuninstallcheck dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-binPROGRAMS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/build/autotools/aclocal.m4 b/build/autotools/aclocal.m4 new file mode 100644 index 00000000..f6192c71 --- /dev/null +++ b/build/autotools/aclocal.m4 @@ -0,0 +1,1039 @@ +# generated automatically by aclocal 1.13.4 -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# Copyright (C) 2002-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.13' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.13.4], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.13.4])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[dnl Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50])dnl +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each '.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2013 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + diff --git a/build/autotools/config.guess b/build/autotools/config.guess new file mode 100755 index 00000000..9c9d05bb --- /dev/null +++ b/build/autotools/config.guess @@ -0,0 +1,1431 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2014 Free Software Foundation, Inc. + +timestamp='2014-02-12' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# +# Please send patches with a ChangeLog entry to config-patches@gnu.org. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2014 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + else + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; + or1k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) + eval $set_cc_for_build + X86_64_ABI= + # If there is a compiler, see if it is configured for 32-bit objects. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_X32 >/dev/null + then + X86_64_ABI=x32 + fi + fi + echo x86_64-unknown-linux-gnu${X86_64_ABI} + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; +esac + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/build/autotools/config.h.in b/build/autotools/config.h.in new file mode 100644 index 00000000..bb48fdc2 --- /dev/null +++ b/build/autotools/config.h.in @@ -0,0 +1,175 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* define if the Boost library is available */ +#undef HAVE_BOOST + +/* define if the Boost::Date_Time library is available */ +#undef HAVE_BOOST_DATE_TIME + +/* define if the Boost::Filesystem library is available */ +#undef HAVE_BOOST_FILESYSTEM + +/* define if the Boost::PROGRAM_OPTIONS library is available */ +#undef HAVE_BOOST_PROGRAM_OPTIONS + +/* define if the Boost::Regex library is available */ +#undef HAVE_BOOST_REGEX + +/* define if the Boost::System library is available */ +#undef HAVE_BOOST_SYSTEM + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `memchr' function. */ +#undef HAVE_MEMCHR + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Have PTHREAD_PRIO_INHERIT. */ +#undef HAVE_PTHREAD_PRIO_INHERIT + +/* Define to 1 if the system has the type `ptrdiff_t'. */ +#undef HAVE_PTRDIFF_T + +/* Define to 1 if you have the `setlocale' function. */ +#undef HAVE_SETLOCALE + +/* Define to 1 if you have the `socket' function. */ +#undef HAVE_SOCKET + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strstr' function. */ +#undef HAVE_STRSTR + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define for Solaris 2.5.1 so the uint32_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT32_T + +/* Define for Solaris 2.5.1 so the uint64_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT64_T + +/* Define for Solaris 2.5.1 so the uint8_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT8_T + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to the type of a signed integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef int32_t + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to the type of an unsigned integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef uint16_t + +/* Define to the type of an unsigned integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef uint32_t + +/* Define to the type of an unsigned integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef uint64_t + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +#undef uint8_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork diff --git a/build/autotools/config.sub b/build/autotools/config.sub new file mode 100755 index 00000000..34c510d3 --- /dev/null +++ b/build/autotools/config.sub @@ -0,0 +1,1811 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2014 Free Software Foundation, Inc. + +timestamp='2014-01-01' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2014 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx | dvp \ + | epiphany \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 \ + | or1k | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mipsEE* | ee | ps2) + basic_machine=mips64r5900el-scei + case $os in + -linux*) + ;; + *) + os=-elf + ;; + esac + ;; + iop) + basic_machine=mipsel-scei + os=-irx + ;; + dvp) + basic_machine=dvp-scei + os=-elf + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -irx* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or1k-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/build/autotools/configure b/build/autotools/configure new file mode 100755 index 00000000..50560353 --- /dev/null +++ b/build/autotools/configure @@ -0,0 +1,9297 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for i2pd 0.0.0. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: https://track.privacysolutions.no/projects/i2pd/issues +$0: about your system, including any error possibly output +$0: before this message. Then install a modern shell, or +$0: manually run the script under such a shell if you do +$0: have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='i2pd' +PACKAGE_TARNAME='i2pd' +PACKAGE_VERSION='0.0.0' +PACKAGE_STRING='i2pd 0.0.0' +PACKAGE_BUGREPORT='https://track.privacysolutions.no/projects/i2pd/issues' +PACKAGE_URL='' + +ac_unique_file="I2NPProtocol.cpp" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +EGREP +GREP +CPP +PTHREAD_CFLAGS +PTHREAD_LIBS +PTHREAD_CC +ax_pthread_config +BOOST_SYSTEM_LIB +BOOST_REGEX_LIB +BOOST_PROGRAM_OPTIONS_LIB +BOOST_FILESYSTEM_LIB +BOOST_DATE_TIME_LIB +BOOST_LDFLAGS +BOOST_CPPFLAGS +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +HAVE_CXX11 +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +ac_ct_CC +CFLAGS +CC +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CXX +CPPFLAGS +LDFLAGS +CXXFLAGS +CXX +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_silent_rules +enable_dependency_tracking +with_boost +with_boost_libdir +with_boost_date_time +with_boost_filesystem +with_boost_program_options +with_boost_regex +with_boost_system +' + ac_precious_vars='build_alias +host_alias +target_alias +CXX +CXXFLAGS +LDFLAGS +LIBS +CPPFLAGS +CCC +CC +CFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures i2pd 0.0.0 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/i2pd] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of i2pd 0.0.0:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-boost[=ARG] use Boost library from a standard location + (ARG=yes), from the specified location (ARG=), + or disable it (ARG=no) [ARG=yes] + --with-boost-libdir=LIB_DIR + Force given directory for boost libraries. Note that + this will override library path detection, so use + this parameter only if default library detection + fails and you know exactly where your boost + libraries are located. + --with-boost-date-time[=special-lib] + use the Date_Time library from boost - it is + possible to specify a certain library for the linker + e.g. + --with-boost-date-time=boost_date_time-gcc-mt-d-1_33_1 + --with-boost-filesystem[=special-lib] + use the Filesystem library from boost - it is + possible to specify a certain library for the linker + e.g. --with-boost-filesystem=boost_filesystem-gcc-mt + --with-boost-program-options[=special-lib] + use the program options library from boost - it is + possible to specify a certain library for the linker + e.g. + --with-boost-program-options=boost_program_options-gcc-mt-1_33_1 + --with-boost-regex[=special-lib] + use the Regex library from boost - it is possible to + specify a certain library for the linker e.g. + --with-boost-regex=boost_regex-gcc-mt-d-1_33_1 + --with-boost-system[=special-lib] + use the System library from boost - it is possible + to specify a certain library for the linker e.g. + --with-boost-system=boost_system-gcc-mt + +Some influential environment variables: + CXX C++ compiler command + CXXFLAGS C++ compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CC C compiler command + CFLAGS C compiler flags + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +i2pd configure 0.0.0 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## --------------------------------------------------------------------- ## +## Report this to https://track.privacysolutions.no/projects/i2pd/issues ## +## --------------------------------------------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_find_intX_t LINENO BITS VAR +# ----------------------------------- +# Finds a signed integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_intX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 +$as_echo_n "checking for int$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in int$2_t 'int' 'long int' \ + 'long long int' 'short int' 'signed char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) + < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + case $ac_type in #( + int$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_intX_t + +# ac_fn_c_find_uintX_t LINENO BITS VAR +# ------------------------------------ +# Finds an unsigned integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_uintX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 +$as_echo_n "checking for uint$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ + 'unsigned long long int' 'unsigned short int' 'unsigned char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + case $ac_type in #( + uint$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_uintX_t + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by i2pd $as_me 0.0.0, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +am__api_version='1.13' + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='i2pd' + VERSION='0.0.0' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + + + +ac_config_headers="$ac_config_headers config.h" + + +# Checks for programs. +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 +$as_echo_n "checking whether the C++ compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C++ compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 +$as_echo_n "checking for C++ compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + +depcc="$CXX" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CXX_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +# Check for C++11 +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++11 +# standard; if necessary, add switches to CXXFLAGS to enable support. +# +# The first argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The second argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline C++11 support is required and that the macro +# should error out if no mode with that support is found. If specified +# 'optional', then configuration proceeds regardless, after defining +# HAVE_CXX11 if and only if a supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014 Alexey Sokolov +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 4 + + + + + + + ax_cxx_compile_cxx11_required=true + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + ac_success=no + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5 +$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; } +if ${ax_cv_cxx_compile_cxx11+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + struct Base { + virtual void f() {} + }; + struct Child : public Base { + virtual void f() {} + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_cxx_compile_cxx11=yes +else + ax_cv_cxx_compile_cxx11=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5 +$as_echo "$ax_cv_cxx_compile_cxx11" >&6; } + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + if test x$ac_success = xno; then + for switch in -std=gnu++11 -std=gnu++0x; do + cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 +$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } +if eval \${$cachevar+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + struct Base { + virtual void f() {} + }; + struct Child : public Base { + virtual void f() {} + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval $cachevar=yes +else + eval $cachevar=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS="$ac_save_CXXFLAGS" +fi +eval ac_res=\$$cachevar + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi + + if test x$ac_success = xno; then + for switch in -std=c++11 -std=c++0x; do + cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 +$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } +if eval \${$cachevar+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + struct Base { + virtual void f() {} + }; + struct Child : public Base { + virtual void f() {} + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval $cachevar=yes +else + eval $cachevar=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS="$ac_save_CXXFLAGS" +fi +eval ac_res=\$$cachevar + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5 + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5 +$as_echo "$as_me: No compiler with C++11 support was found" >&6;} + else + HAVE_CXX11=1 + +$as_echo "#define HAVE_CXX11 1" >>confdefs.h + + fi + + + fi + + +# Set platform specific flags +if test "$(uname -s)" = "Darwin" +then + CXXFLAGS="-DCRYPTOPP_DISABLE_ASM -DAESNI ${CXXFLAGS}" + +elif test "$(uname -s)" = "Linux" +then + if test -n "$(grep aes /proc/cpuinfo)" + then + CXXFLAGS="-DAESNI ${CXXFLAGS}" + fi +else + # emtpy + true +fi + +# Checks for libraries. + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing s_sosemanukMulTables" >&5 +$as_echo_n "checking for library containing s_sosemanukMulTables... " >&6; } +if ${ac_cv_search_s_sosemanukMulTables+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char s_sosemanukMulTables (); +int +main () +{ +return s_sosemanukMulTables (); + ; + return 0; +} +_ACEOF +for ac_lib in '' cryptopp; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_s_sosemanukMulTables=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_s_sosemanukMulTables+:} false; then : + break +fi +done +if ${ac_cv_search_s_sosemanukMulTables+:} false; then : + +else + ac_cv_search_s_sosemanukMulTables=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_s_sosemanukMulTables" >&5 +$as_echo "$ac_cv_search_s_sosemanukMulTables" >&6; } +ac_res=$ac_cv_search_s_sosemanukMulTables +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + as_fn_error $? "Unable to find crypto++" "$LINENO" 5 +fi + + +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# Test for the Boost C++ libraries of a particular version (or newer) +# +# If no path to the installed boost library is given the macro searchs +# under /usr, /usr/local, /opt and /opt/local and evaluates the +# $BOOST_ROOT environment variable. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) +# +# And sets: +# +# HAVE_BOOST +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2009 Peter Adolphs +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 23 + + + +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_date_time.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_DATE_TIME +# +# DESCRIPTION +# +# Test for Date_Time library from the Boost C++ libraries. The macro +# requires a preceding call to AX_BOOST_BASE. Further documentation is +# available at . +# +# This macro calls: +# +# AC_SUBST(BOOST_DATE_TIME_LIB) +# +# And sets: +# +# HAVE_BOOST_DATE_TIME +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2008 Michael Tindal +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 21 + + + +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_filesystem.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_FILESYSTEM +# +# DESCRIPTION +# +# Test for Filesystem library from the Boost C++ libraries. The macro +# requires a preceding call to AX_BOOST_BASE. Further documentation is +# available at . +# +# This macro calls: +# +# AC_SUBST(BOOST_FILESYSTEM_LIB) +# +# And sets: +# +# HAVE_BOOST_FILESYSTEM +# +# LICENSE +# +# Copyright (c) 2009 Thomas Porschberg +# Copyright (c) 2009 Michael Tindal +# Copyright (c) 2009 Roman Rybalko +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 26 + + + +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_boost_program_options.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_BOOST_PROGRAM_OPTIONS +# +# DESCRIPTION +# +# Test for program options library from the Boost C++ libraries. The macro +# requires a preceding call to AX_BOOST_BASE. Further documentation is +# available at . +# +# This macro calls: +# +# AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) +# +# And sets: +# +# HAVE_BOOST_PROGRAM_OPTIONS +# +# LICENSE +# +# Copyright (c) 2009 Thomas Porschberg +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 22 + + + +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_regex.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_REGEX +# +# DESCRIPTION +# +# Test for Regex library from the Boost C++ libraries. The macro requires +# a preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_REGEX_LIB) +# +# And sets: +# +# HAVE_BOOST_REGEX +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2008 Michael Tindal +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 22 + + + +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_system.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_SYSTEM +# +# DESCRIPTION +# +# Test for System library from the Boost C++ libraries. The macro requires +# a preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_SYSTEM_LIB) +# +# And sets: +# +# HAVE_BOOST_SYSTEM +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2008 Michael Tindal +# Copyright (c) 2008 Daniel Casimiro +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 17 + + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + + + +# Check whether --with-boost was given. +if test "${with_boost+set}" = set; then : + withval=$with_boost; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ac_boost_path="" + else + want_boost="yes" + ac_boost_path="$withval" + fi + +else + want_boost="yes" +fi + + + + +# Check whether --with-boost-libdir was given. +if test "${with_boost_libdir+set}" = set; then : + withval=$with_boost_libdir; + if test -d "$withval" + then + ac_boost_lib_path="$withval" + else + as_fn_error $? "--with-boost-libdir expected directory name" "$LINENO" 5 + fi + +else + ac_boost_lib_path="" + +fi + + +if test "x$want_boost" = "xyes"; then + boost_lib_version_req=1.46 + boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([0-9]*\.[0-9]*\)'` + boost_lib_version_req_major=`expr $boost_lib_version_req : '\([0-9]*\)'` + boost_lib_version_req_minor=`expr $boost_lib_version_req : '[0-9]*\.\([0-9]*\)'` + boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[0-9]*\.[0-9]*\.\([0-9]*\)'` + if test "x$boost_lib_version_req_sub_minor" = "x" ; then + boost_lib_version_req_sub_minor="0" + fi + WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for boostlib >= $boost_lib_version_req" >&5 +$as_echo_n "checking for boostlib >= $boost_lib_version_req... " >&6; } + succeeded=no + + libsubdirs="lib" + ax_arch=`uname -m` + case $ax_arch in + x86_64|ppc64|s390x|sparc64|aarch64) + libsubdirs="lib64 lib lib64" + ;; + esac + + + libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" + + case ${host_cpu} in + i?86) + libsubdirs="lib/i386-${host_os} $libsubdirs" + ;; + esac + + if test "$ac_boost_path" != ""; then + BOOST_CPPFLAGS="-I$ac_boost_path/include" + for ac_boost_path_tmp in $libsubdirs; do + if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then + BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" + break + fi + done + elif test "$cross_compiling" != yes; then + for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then + for libsubdir in $libsubdirs ; do + if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" + BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" + break; + fi + done + fi + + if test "$ac_boost_lib_path" != ""; then + BOOST_LDFLAGS="-L$ac_boost_lib_path" + fi + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ + + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + succeeded=yes + found_system=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + if test "x$succeeded" != "xyes"; then + _version=0 + if test "$ac_boost_path" != ""; then + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + fi + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" + done + fi + else + if test "$cross_compiling" != yes; then + for ac_boost_path in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + best_path=$ac_boost_path + fi + done + fi + done + + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" + if test "$ac_boost_lib_path" = ""; then + for libsubdir in $libsubdirs ; do + if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$best_path/$libsubdir" + fi + fi + + if test "x$BOOST_ROOT" != "x"; then + for libsubdir in $libsubdirs ; do + if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then + version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` + stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` + stage_version_shorten=`expr $stage_version : '\([0-9]*\.[0-9]*\)'` + V_CHECK=`expr $stage_version_shorten \>\= $_version` + if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: We will use a staged boost library from $BOOST_ROOT" >&5 +$as_echo "$as_me: We will use a staged boost library from $BOOST_ROOT" >&6;} + BOOST_CPPFLAGS="-I$BOOST_ROOT" + BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" + fi + fi + fi + fi + + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ + + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + succeeded=yes + found_system=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + fi + + if test "$succeeded" != "yes" ; then + if test "$_version" = "0" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation." >&5 +$as_echo "$as_me: We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation." >&6;} + else + { $as_echo "$as_me:${as_lineno-$LINENO}: Your boost libraries seems to old (version $_version)." >&5 +$as_echo "$as_me: Your boost libraries seems to old (version $_version)." >&6;} + fi + # execute ACTION-IF-NOT-FOUND (if present): + : + else + + + +$as_echo "#define HAVE_BOOST /**/" >>confdefs.h + + # execute ACTION-IF-FOUND (if present): + : + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" +fi + + + + +# Check whether --with-boost-date-time was given. +if test "${with_boost_date_time+set}" = set; then : + withval=$with_boost_date_time; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_date_time_lib="" + else + want_boost="yes" + ax_boost_user_date_time_lib="$withval" + fi + +else + want_boost="yes" + +fi + + + if test "x$want_boost" = "xyes"; then + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::Date_Time library is available" >&5 +$as_echo_n "checking whether the Boost::Date_Time library is available... " >&6; } +if ${ax_cv_boost_date_time+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +using namespace boost::gregorian; date d(2002,Jan,10); + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_boost_date_time=yes +else + ax_cv_boost_date_time=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_date_time" >&5 +$as_echo "$ax_cv_boost_date_time" >&6; } + if test "x$ax_cv_boost_date_time" = "xyes"; then + +$as_echo "#define HAVE_BOOST_DATE_TIME /**/" >>confdefs.h + + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` + if test "x$ax_boost_user_date_time_lib" = "x"; then + for libextension in `ls $BOOSTLIBDIR/libboost_date_time*.so* $BOOSTLIBDIR/libboost_date_time*.dylib* $BOOSTLIBDIR/libboost_date_time*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_date_time.*\)\.so.*$;\1;' -e 's;^lib\(boost_date_time.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_date_time.*\)\.a*$;\1;'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_DATE_TIME_LIB="-l$ax_lib"; link_date_time="yes"; break +else + link_date_time="no" +fi + + done + if test "x$link_date_time" != "xyes"; then + for libextension in `ls $BOOSTLIBDIR/boost_date_time*.dll* $BOOSTLIBDIR/boost_date_time*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_date_time.*\)\.dll.*$;\1;' -e 's;^\(boost_date_time.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_DATE_TIME_LIB="-l$ax_lib"; link_date_time="yes"; break +else + link_date_time="no" +fi + + done + fi + + else + for ax_lib in $ax_boost_user_date_time_lib boost_date_time-$ax_boost_user_date_time_lib; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$ax_lib" >&5 +$as_echo_n "checking for main in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_DATE_TIME_LIB="-l$ax_lib"; link_date_time="yes"; break +else + link_date_time="no" +fi + + done + + fi + if test "x$ax_lib" = "x"; then + as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 + fi + if test "x$link_date_time" != "xyes"; then + as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi + + + +# Check whether --with-boost-filesystem was given. +if test "${with_boost_filesystem+set}" = set; then : + withval=$with_boost_filesystem; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_filesystem_lib="" + else + want_boost="yes" + ax_boost_user_filesystem_lib="$withval" + fi + +else + want_boost="yes" + +fi + + + if test "x$want_boost" = "xyes"; then + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + LIBS_SAVED=$LIBS + LIBS="$LIBS $BOOST_SYSTEM_LIB" + export LIBS + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::Filesystem library is available" >&5 +$as_echo_n "checking whether the Boost::Filesystem library is available... " >&6; } +if ${ax_cv_boost_filesystem+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +using namespace boost::filesystem; + path my_path( "foo/bar/data.txt" ); + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_boost_filesystem=yes +else + ax_cv_boost_filesystem=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_filesystem" >&5 +$as_echo "$ax_cv_boost_filesystem" >&6; } + if test "x$ax_cv_boost_filesystem" = "xyes"; then + +$as_echo "#define HAVE_BOOST_FILESYSTEM /**/" >>confdefs.h + + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` + if test "x$ax_boost_user_filesystem_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_filesystem* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_FILESYSTEM_LIB="-l$ax_lib"; link_filesystem="yes"; break +else + link_filesystem="no" +fi + + done + if test "x$link_filesystem" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_filesystem* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_FILESYSTEM_LIB="-l$ax_lib"; link_filesystem="yes"; break +else + link_filesystem="no" +fi + + done + fi + else + for ax_lib in $ax_boost_user_filesystem_lib boost_filesystem-$ax_boost_user_filesystem_lib; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_FILESYSTEM_LIB="-l$ax_lib"; link_filesystem="yes"; break +else + link_filesystem="no" +fi + + done + + fi + if test "x$ax_lib" = "x"; then + as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 + fi + if test "x$link_filesystem" != "xyes"; then + as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + LIBS="$LIBS_SAVED" + fi + + + +# Check whether --with-boost-program-options was given. +if test "${with_boost_program_options+set}" = set; then : + withval=$with_boost_program_options; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_program_options_lib="" + else + want_boost="yes" + ax_boost_user_program_options_lib="$withval" + fi + +else + want_boost="yes" + +fi + + + if test "x$want_boost" = "xyes"; then + + export want_boost + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::Program_Options library is available" >&5 +$as_echo_n "checking whether the Boost::Program_Options library is available... " >&6; } +if ${ax_cv_boost_program_options+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +boost::program_options::options_description generic("Generic options"); + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_boost_program_options=yes +else + ax_cv_boost_program_options=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_program_options" >&5 +$as_echo "$ax_cv_boost_program_options" >&6; } + if test "$ax_cv_boost_program_options" = yes; then + +$as_echo "#define HAVE_BOOST_PROGRAM_OPTIONS /**/" >>confdefs.h + + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` + if test "x$ax_boost_user_program_options_lib" = "x"; then + for libextension in `ls $BOOSTLIBDIR/libboost_program_options*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.dylib* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.dylib.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; link_program_options="yes"; break +else + link_program_options="no" +fi + + done + if test "x$link_program_options" != "xyes"; then + for libextension in `ls $BOOSTLIBDIR/boost_program_options*.dll* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.dll.*$;\1;'` `ls $BOOSTLIBDIR/boost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; link_program_options="yes"; break +else + link_program_options="no" +fi + + done + fi + else + for ax_lib in $ax_boost_user_program_options_lib boost_program_options-$ax_boost_user_program_options_lib; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$ax_lib" >&5 +$as_echo_n "checking for main in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; link_program_options="yes"; break +else + link_program_options="no" +fi + + done + fi + if test "x$ax_lib" = "x"; then + as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 + fi + if test "x$link_program_options" != "xyes"; then + as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 + fi + fi + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi + + + +# Check whether --with-boost-regex was given. +if test "${with_boost_regex+set}" = set; then : + withval=$with_boost_regex; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_regex_lib="" + else + want_boost="yes" + ax_boost_user_regex_lib="$withval" + fi + +else + want_boost="yes" + +fi + + + if test "x$want_boost" = "xyes"; then + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::Regex library is available" >&5 +$as_echo_n "checking whether the Boost::Regex library is available... " >&6; } +if ${ax_cv_boost_regex+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +boost::regex r(); return 0; + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_boost_regex=yes +else + ax_cv_boost_regex=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_regex" >&5 +$as_echo "$ax_cv_boost_regex" >&6; } + if test "x$ax_cv_boost_regex" = "xyes"; then + +$as_echo "#define HAVE_BOOST_REGEX /**/" >>confdefs.h + + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` + if test "x$ax_boost_user_regex_lib" = "x"; then + for libextension in `ls $BOOSTLIBDIR/libboost_regex*.so* $BOOSTLIBDIR/libboost_regex*.dylib* $BOOSTLIBDIR/libboost_regex*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_regex.*\)\.so.*$;\1;' -e 's;^lib\(boost_regex.*\)\.dylib.*;\1;' -e 's;^lib\(boost_regex.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_REGEX_LIB="-l$ax_lib"; link_regex="yes"; break +else + link_regex="no" +fi + + done + if test "x$link_regex" != "xyes"; then + for libextension in `ls $BOOSTLIBDIR/boost_regex*.dll* $BOOSTLIBDIR/boost_regex*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_regex.*\)\.dll.*$;\1;' -e 's;^\(boost_regex.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_REGEX_LIB="-l$ax_lib"; link_regex="yes"; break +else + link_regex="no" +fi + + done + fi + + else + for ax_lib in $ax_boost_user_regex_lib boost_regex-$ax_boost_user_regex_lib; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_main" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -l$ax_lib" >&5 +$as_echo_n "checking for main in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_REGEX_LIB="-l$ax_lib"; link_regex="yes"; break +else + link_regex="no" +fi + + done + fi + if test "x$ax_lib" = "x"; then + as_fn_error $? "Could not find a version of the Boost::Regex library!" "$LINENO" 5 + fi + if test "x$link_regex" != "xyes"; then + as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi + + + +# Check whether --with-boost-system was given. +if test "${with_boost_system+set}" = set; then : + withval=$with_boost_system; + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_system_lib="" + else + want_boost="yes" + ax_boost_user_system_lib="$withval" + fi + +else + want_boost="yes" + +fi + + + if test "x$want_boost" = "xyes"; then + + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Boost::System library is available" >&5 +$as_echo_n "checking whether the Boost::System library is available... " >&6; } +if ${ax_cv_boost_system+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + CXXFLAGS_SAVE=$CXXFLAGS + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +boost::system::system_category + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_boost_system=yes +else + ax_cv_boost_system=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS=$CXXFLAGS_SAVE + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_system" >&5 +$as_echo "$ax_cv_boost_system" >&6; } + if test "x$ax_cv_boost_system" = "xyes"; then + + + +$as_echo "#define HAVE_BOOST_SYSTEM /**/" >>confdefs.h + + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'` + + LDFLAGS_SAVE=$LDFLAGS + if test "x$ax_boost_user_system_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break +else + link_system="no" +fi + + done + if test "x$link_system" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do + ax_lib=${libextension} + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break +else + link_system="no" +fi + + done + fi + + else + for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do + as_ac_Lib=`$as_echo "ac_cv_lib_$ax_lib''_exit" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5 +$as_echo_n "checking for exit in -l$ax_lib... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-l$ax_lib $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char exit (); +int +main () +{ +return exit (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break +else + link_system="no" +fi + + done + + fi + if test "x$ax_lib" = "x"; then + as_fn_error $? "Could not find a version of the library!" "$LINENO" 5 + fi + if test "x$link_system" = "xno"; then + as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5 + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi + + +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also link it with them as well. e.g. you should link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threads programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name +# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 21 + +# This is what autoupdate's m4 run will expand. It fires +# the warning (with _au_warn_XXX), outputs it into the +# updated configure.ac (with AC_DIAGNOSE), and then outputs +# the replacement expansion. + + +# This is an auxiliary macro that is also run when +# autoupdate runs m4. It simply calls m4_warning, but +# we need a wrapper so that each warning is emitted only +# once. We break the quoting in m4_warning's argument in +# order to expand this macro's arguments, not AU_DEFUN's. + + +# Finally, this is the expansion that is picked up by +# autoconf. It tells the user to run autoupdate, and +# then outputs the replacement expansion. We do not care +# about autoupdate's warning because that contains +# information on what to do *after* running autoupdate. + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5 +$as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_join (); +int +main () +{ +return pthread_join (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +$as_echo "$ax_pthread_ok" >&6; } + if test x"$ax_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case ${host_os} in + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" + ;; + + darwin*) + ax_pthread_flags="-pthread $ax_pthread_flags" + ;; +esac + +# Clang doesn't consider unrecognized options an error unless we specify +# -Werror. We throw in some extra Clang-specific options to ensure that +# this doesn't happen for GCC, which also accepts -Werror. + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler needs -Werror to reject unknown flags" >&5 +$as_echo_n "checking if compiler needs -Werror to reject unknown flags... " >&6; } +save_CFLAGS="$CFLAGS" +ax_pthread_extra_flags="-Werror" +CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo(void); +int +main () +{ +foo() + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + ax_pthread_extra_flags= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +CFLAGS="$save_CFLAGS" + +if test x"$ax_pthread_ok" = xno; then +for flag in $ax_pthread_flags; do + + case $flag in + none) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 +$as_echo_n "checking whether pthreads work without any flags... " >&6; } + ;; + + -*) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5 +$as_echo_n "checking whether pthreads work with $flag... " >&6; } + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + # Extract the first word of "pthread-config", so it can be a program name with args. +set dummy pthread-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ax_pthread_config+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ax_pthread_config"; then + ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ax_pthread_config="yes" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no" +fi +fi +ax_pthread_config=$ac_cv_prog_ax_pthread_config +if test -n "$ax_pthread_config"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5 +$as_echo "$ax_pthread_config" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test x"$ax_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5 +$as_echo_n "checking for the pthreads library -l$flag... " >&6; } + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; } +int +main () +{ +pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +$as_echo "$ax_pthread_ok" >&6; } + if test "x$ax_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 +$as_echo_n "checking for joinable pthread attribute... " >&6; } + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +int attr = $attr; return attr /* ; */ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + attr_name=$attr; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5 +$as_echo "$attr_name" >&6; } + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + +cat >>confdefs.h <<_ACEOF +#define PTHREAD_CREATE_JOINABLE $attr_name +_ACEOF + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5 +$as_echo_n "checking if more special flags are required for pthreads... " >&6; } + flag=no + case ${host_os} in + aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; + osf* | hpux*) flag="-D_REENTRANT";; + solaris*) + if test "$GCC" = "yes"; then + flag="-D_REENTRANT" + else + # TODO: What about Clang on Solaris? + flag="-mt -D_REENTRANT" + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $flag" >&5 +$as_echo "$flag" >&6; } + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5 +$as_echo_n "checking for PTHREAD_PRIO_INHERIT... " >&6; } +if ${ax_cv_PTHREAD_PRIO_INHERIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +int i = PTHREAD_PRIO_INHERIT; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_cv_PTHREAD_PRIO_INHERIT=yes +else + ax_cv_PTHREAD_PRIO_INHERIT=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5 +$as_echo "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; } + if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"; then : + +$as_echo "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h + +fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != xyes; then + case $host_os in + aix*) + case "x/$CC" in #( + x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) : + #handle absolute path differently from PATH based program lookup + case "x$CC" in #( + x/*) : + if as_fn_executable_p ${CC}_r; then : + PTHREAD_CC="${CC}_r" +fi ;; #( + *) : + for ac_prog in ${CC}_r +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_PTHREAD_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$PTHREAD_CC"; then + ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_PTHREAD_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PTHREAD_CC=$ac_cv_prog_PTHREAD_CC +if test -n "$PTHREAD_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 +$as_echo "$PTHREAD_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$PTHREAD_CC" && break +done +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + ;; +esac ;; #( + *) : + ;; +esac + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + + + + + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then + +$as_echo "#define HAVE_PTHREAD 1" >>confdefs.h + + : +else + ax_pthread_ok=no + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +# Checks for header files. +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in fcntl.h inttypes.h stdlib.h string.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Checks for typedefs, structures, and compiler characteristics. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5 +$as_echo_n "checking for stdbool.h that conforms to C99... " >&6; } +if ${ac_cv_header_stdbool_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + #ifndef bool + "error: bool is not defined" + #endif + #ifndef false + "error: false is not defined" + #endif + #if false + "error: false is not 0" + #endif + #ifndef true + "error: true is not defined" + #endif + #if true != 1 + "error: true is not 1" + #endif + #ifndef __bool_true_false_are_defined + "error: __bool_true_false_are_defined is not defined" + #endif + + struct s { _Bool s: 1; _Bool t; } s; + + char a[true == 1 ? 1 : -1]; + char b[false == 0 ? 1 : -1]; + char c[__bool_true_false_are_defined == 1 ? 1 : -1]; + char d[(bool) 0.5 == true ? 1 : -1]; + /* See body of main program for 'e'. */ + char f[(_Bool) 0.0 == false ? 1 : -1]; + char g[true]; + char h[sizeof (_Bool)]; + char i[sizeof s.t]; + enum { j = false, k = true, l = false * true, m = true * 256 }; + /* The following fails for + HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ + _Bool n[m]; + char o[sizeof n == m * sizeof n[0] ? 1 : -1]; + char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; + /* Catch a bug in an HP-UX C compiler. See + http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html + */ + _Bool q = true; + _Bool *pq = &q; + +int +main () +{ + + bool e = &s; + *pq |= q; + *pq |= ! q; + /* Refer to every declared value, to avoid compiler optimizations. */ + return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l + + !m + !n + !o + !p + !q + !pq); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdbool_h=yes +else + ac_cv_header_stdbool_h=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5 +$as_echo "$ac_cv_header_stdbool_h" >&6; } + ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" +if test "x$ac_cv_type__Bool" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE__BOOL 1 +_ACEOF + + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t" +case $ac_cv_c_int32_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int32_t $ac_cv_c_int32_t +_ACEOF +;; +esac + +ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" +if test "x$ac_cv_type_pid_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" +case $ac_cv_c_uint16_t in #( + no|yes) ;; #( + *) + + +cat >>confdefs.h <<_ACEOF +#define uint16_t $ac_cv_c_uint16_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" +case $ac_cv_c_uint32_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT32_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint32_t $ac_cv_c_uint32_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" +case $ac_cv_c_uint64_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT64_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint64_t $ac_cv_c_uint64_t +_ACEOF +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" +case $ac_cv_c_uint8_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT8_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint8_t $ac_cv_c_uint8_t +_ACEOF +;; + esac + +ac_fn_c_check_type "$LINENO" "ptrdiff_t" "ac_cv_type_ptrdiff_t" "$ac_includes_default" +if test "x$ac_cv_type_ptrdiff_t" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_PTRDIFF_T 1 +_ACEOF + + +fi + + +# Checks for library functions. +for ac_header in vfork.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default" +if test "x$ac_cv_header_vfork_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_VFORK_H 1 +_ACEOF + +fi + +done + +for ac_func in fork vfork +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 +$as_echo_n "checking for working fork... " >&6; } +if ${ac_cv_func_fork_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_fork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* By Ruediger Kuhlmann. */ + return fork () < 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_fork_works=yes +else + ac_cv_func_fork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5 +$as_echo "$ac_cv_func_fork_works" >&6; } + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 +$as_echo_n "checking for working vfork... " >&6; } +if ${ac_cv_func_vfork_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_vfork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +$ac_includes_default +#include +#ifdef HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + return ( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_vfork_works=yes +else + ac_cv_func_vfork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5 +$as_echo "$ac_cv_func_vfork_works" >&6; } + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h + +else + +$as_echo "#define vfork fork" >>confdefs.h + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h + +fi + +for ac_func in memchr memset setlocale socket strstr +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by i2pd $as_me 0.0.0, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +i2pd config.status 0.0.0 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/build/autotools/configure.ac b/build/autotools/configure.ac new file mode 100644 index 00000000..c44eed29 --- /dev/null +++ b/build/autotools/configure.ac @@ -0,0 +1,72 @@ +AC_PREREQ([2.69]) +AC_INIT([i2pd], [0.0.0], + [https://track.privacysolutions.no/projects/i2pd/issues]) +AM_INIT_AUTOMAKE([-Wall foreign]) +AC_CONFIG_SRCDIR([I2NPProtocol.cpp]) +AC_CONFIG_HEADERS([config.h]) + +# Checks for programs. +AC_PROG_CXX +AC_PROG_CC + +# Check for C++11 +m4_include([m4/ax_cxx_compile_stdcxx_11.m4]) +AX_CXX_COMPILE_STDCXX_11([],[mandatory]) + +# Set platform specific flags +if test "$(uname -s)" = "Darwin" +then + CXXFLAGS="-DCRYPTOPP_DISABLE_ASM -DAESNI ${CXXFLAGS}" + +elif test "$(uname -s)" = "Linux" +then + if test -n "$(grep aes /proc/cpuinfo)" + then + CXXFLAGS="-DAESNI ${CXXFLAGS}" + fi +else + # emtpy + true +fi + +# Checks for libraries. +AC_SEARCH_LIBS([s_sosemanukMulTables], [cryptopp], + [], [AC_MSG_ERROR([Unable to find crypto++])]) + +m4_include([m4/ax_boost_base.m4]) +m4_include([m4/ax_boost_date_time.m4]) +m4_include([m4/ax_boost_filesystem.m4]) +m4_include([m4/ax_boost_program_options.m4]) +m4_include([m4/ax_boost_regex.m4]) +m4_include([m4/ax_boost_system.m4]) +AX_BOOST_BASE([1.46]) +AX_BOOST_DATE_TIME +AX_BOOST_FILESYSTEM +AX_BOOST_PROGRAM_OPTIONS +AX_BOOST_REGEX +AX_BOOST_SYSTEM + +m4_include([m4/ax_pthread.m4]) +AX_PTHREAD() + +# Checks for header files. +AC_CHECK_HEADERS([fcntl.h inttypes.h stdlib.h string.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_CHECK_HEADER_STDBOOL +AC_C_INLINE +AC_TYPE_INT32_T +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_UINT8_T +AC_CHECK_TYPES([ptrdiff_t]) + +# Checks for library functions. +AC_FUNC_FORK +AC_CHECK_FUNCS([memchr memset setlocale socket strstr]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/build/autotools/depcomp b/build/autotools/depcomp new file mode 100755 index 00000000..4ebd5b3a --- /dev/null +++ b/build/autotools/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2013-05-30.07; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/build/autotools/install-sh b/build/autotools/install-sh new file mode 100755 index 00000000..377bb868 --- /dev/null +++ b/build/autotools/install-sh @@ -0,0 +1,527 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2011-11-20.07; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/build/autotools/m4/ax_boost_base.m4 b/build/autotools/m4/ax_boost_base.m4 new file mode 100644 index 00000000..8e6ee9a9 --- /dev/null +++ b/build/autotools/m4/ax_boost_base.m4 @@ -0,0 +1,272 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# Test for the Boost C++ libraries of a particular version (or newer) +# +# If no path to the installed boost library is given the macro searchs +# under /usr, /usr/local, /opt and /opt/local and evaluates the +# $BOOST_ROOT environment variable. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) +# +# And sets: +# +# HAVE_BOOST +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2009 Peter Adolphs +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 23 + +AC_DEFUN([AX_BOOST_BASE], +[ +AC_ARG_WITH([boost], + [AS_HELP_STRING([--with-boost@<:@=ARG@:>@], + [use Boost library from a standard location (ARG=yes), + from the specified location (ARG=), + or disable it (ARG=no) + @<:@ARG=yes@:>@ ])], + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ac_boost_path="" + else + want_boost="yes" + ac_boost_path="$withval" + fi + ], + [want_boost="yes"]) + + +AC_ARG_WITH([boost-libdir], + AS_HELP_STRING([--with-boost-libdir=LIB_DIR], + [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]), + [ + if test -d "$withval" + then + ac_boost_lib_path="$withval" + else + AC_MSG_ERROR(--with-boost-libdir expected directory name) + fi + ], + [ac_boost_lib_path=""] +) + +if test "x$want_boost" = "xyes"; then + boost_lib_version_req=ifelse([$1], ,1.20.0,$1) + boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` + boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` + boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` + boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` + if test "x$boost_lib_version_req_sub_minor" = "x" ; then + boost_lib_version_req_sub_minor="0" + fi + WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` + AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) + succeeded=no + + dnl On 64-bit systems check for system libraries in both lib64 and lib. + dnl The former is specified by FHS, but e.g. Debian does not adhere to + dnl this (as it rises problems for generic multi-arch support). + dnl The last entry in the list is chosen by default when no libraries + dnl are found, e.g. when only header-only libraries are installed! + libsubdirs="lib" + ax_arch=`uname -m` + case $ax_arch in + x86_64|ppc64|s390x|sparc64|aarch64) + libsubdirs="lib64 lib lib64" + ;; + esac + + dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give + dnl them priority over the other paths since, if libs are found there, they + dnl are almost assuredly the ones desired. + AC_REQUIRE([AC_CANONICAL_HOST]) + libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" + + case ${host_cpu} in + i?86) + libsubdirs="lib/i386-${host_os} $libsubdirs" + ;; + esac + + dnl first we check the system location for boost libraries + dnl this location ist chosen if boost libraries are installed with the --layout=system option + dnl or if you install boost with RPM + if test "$ac_boost_path" != ""; then + BOOST_CPPFLAGS="-I$ac_boost_path/include" + for ac_boost_path_tmp in $libsubdirs; do + if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then + BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" + break + fi + done + elif test "$cross_compiling" != yes; then + for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then + for libsubdir in $libsubdirs ; do + if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" + BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" + break; + fi + done + fi + + dnl overwrite ld flags if we have required special directory with + dnl --with-boost-libdir parameter + if test "$ac_boost_lib_path" != ""; then + BOOST_LDFLAGS="-L$ac_boost_lib_path" + fi + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_REQUIRE([AC_PROG_CXX]) + AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + ]])],[ + AC_MSG_RESULT(yes) + succeeded=yes + found_system=yes + ],[ + ]) + AC_LANG_POP([C++]) + + + + dnl if we found no boost with system layout we search for boost libraries + dnl built and installed without the --layout=system option or for a staged(not installed) version + if test "x$succeeded" != "xyes"; then + _version=0 + if test "$ac_boost_path" != ""; then + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + fi + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" + done + fi + else + if test "$cross_compiling" != yes; then + for ac_boost_path in /usr /usr/local /opt /opt/local ; do + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + best_path=$ac_boost_path + fi + done + fi + done + + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" + if test "$ac_boost_lib_path" = ""; then + for libsubdir in $libsubdirs ; do + if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$best_path/$libsubdir" + fi + fi + + if test "x$BOOST_ROOT" != "x"; then + for libsubdir in $libsubdirs ; do + if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then + version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` + stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` + stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` + V_CHECK=`expr $stage_version_shorten \>\= $_version` + if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then + AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) + BOOST_CPPFLAGS="-I$BOOST_ROOT" + BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" + fi + fi + fi + fi + + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + ]])],[ + AC_MSG_RESULT(yes) + succeeded=yes + found_system=yes + ],[ + ]) + AC_LANG_POP([C++]) + fi + + if test "$succeeded" != "yes" ; then + if test "$_version" = "0" ; then + AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) + else + AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) + fi + # execute ACTION-IF-NOT-FOUND (if present): + ifelse([$3], , :, [$3]) + else + AC_SUBST(BOOST_CPPFLAGS) + AC_SUBST(BOOST_LDFLAGS) + AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) + # execute ACTION-IF-FOUND (if present): + ifelse([$2], , :, [$2]) + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" +fi + +]) diff --git a/build/autotools/m4/ax_boost_date_time.m4 b/build/autotools/m4/ax_boost_date_time.m4 new file mode 100644 index 00000000..ec9c0445 --- /dev/null +++ b/build/autotools/m4/ax_boost_date_time.m4 @@ -0,0 +1,113 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_date_time.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_DATE_TIME +# +# DESCRIPTION +# +# Test for Date_Time library from the Boost C++ libraries. The macro +# requires a preceding call to AX_BOOST_BASE. Further documentation is +# available at . +# +# This macro calls: +# +# AC_SUBST(BOOST_DATE_TIME_LIB) +# +# And sets: +# +# HAVE_BOOST_DATE_TIME +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2008 Michael Tindal +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 21 + +AC_DEFUN([AX_BOOST_DATE_TIME], +[ + AC_ARG_WITH([boost-date-time], + AS_HELP_STRING([--with-boost-date-time@<:@=special-lib@:>@], + [use the Date_Time library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-date-time=boost_date_time-gcc-mt-d-1_33_1 ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_date_time_lib="" + else + want_boost="yes" + ax_boost_user_date_time_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_CACHE_CHECK(whether the Boost::Date_Time library is available, + ax_cv_boost_date_time, + [AC_LANG_PUSH([C++]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], + [[using namespace boost::gregorian; date d(2002,Jan,10); + return 0; + ]])], + ax_cv_boost_date_time=yes, ax_cv_boost_date_time=no) + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_date_time" = "xyes"; then + AC_DEFINE(HAVE_BOOST_DATE_TIME,,[define if the Boost::Date_Time library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + if test "x$ax_boost_user_date_time_lib" = "x"; then + for libextension in `ls $BOOSTLIBDIR/libboost_date_time*.so* $BOOSTLIBDIR/libboost_date_time*.dylib* $BOOSTLIBDIR/libboost_date_time*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_date_time.*\)\.so.*$;\1;' -e 's;^lib\(boost_date_time.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_date_time.*\)\.a*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_DATE_TIME_LIB="-l$ax_lib"; AC_SUBST(BOOST_DATE_TIME_LIB) link_date_time="yes"; break], + [link_date_time="no"]) + done + if test "x$link_date_time" != "xyes"; then + for libextension in `ls $BOOSTLIBDIR/boost_date_time*.dll* $BOOSTLIBDIR/boost_date_time*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_date_time.*\)\.dll.*$;\1;' -e 's;^\(boost_date_time.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_DATE_TIME_LIB="-l$ax_lib"; AC_SUBST(BOOST_DATE_TIME_LIB) link_date_time="yes"; break], + [link_date_time="no"]) + done + fi + + else + for ax_lib in $ax_boost_user_date_time_lib boost_date_time-$ax_boost_user_date_time_lib; do + AC_CHECK_LIB($ax_lib, main, + [BOOST_DATE_TIME_LIB="-l$ax_lib"; AC_SUBST(BOOST_DATE_TIME_LIB) link_date_time="yes"; break], + [link_date_time="no"]) + done + + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_date_time" != "xyes"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/build/autotools/m4/ax_boost_filesystem.m4 b/build/autotools/m4/ax_boost_filesystem.m4 new file mode 100644 index 00000000..f162163c --- /dev/null +++ b/build/autotools/m4/ax_boost_filesystem.m4 @@ -0,0 +1,118 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_filesystem.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_FILESYSTEM +# +# DESCRIPTION +# +# Test for Filesystem library from the Boost C++ libraries. The macro +# requires a preceding call to AX_BOOST_BASE. Further documentation is +# available at . +# +# This macro calls: +# +# AC_SUBST(BOOST_FILESYSTEM_LIB) +# +# And sets: +# +# HAVE_BOOST_FILESYSTEM +# +# LICENSE +# +# Copyright (c) 2009 Thomas Porschberg +# Copyright (c) 2009 Michael Tindal +# Copyright (c) 2009 Roman Rybalko +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 26 + +AC_DEFUN([AX_BOOST_FILESYSTEM], +[ + AC_ARG_WITH([boost-filesystem], + AS_HELP_STRING([--with-boost-filesystem@<:@=special-lib@:>@], + [use the Filesystem library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-filesystem=boost_filesystem-gcc-mt ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_filesystem_lib="" + else + want_boost="yes" + ax_boost_user_filesystem_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + LIBS_SAVED=$LIBS + LIBS="$LIBS $BOOST_SYSTEM_LIB" + export LIBS + + AC_CACHE_CHECK(whether the Boost::Filesystem library is available, + ax_cv_boost_filesystem, + [AC_LANG_PUSH([C++]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], + [[using namespace boost::filesystem; + path my_path( "foo/bar/data.txt" ); + return 0;]])], + ax_cv_boost_filesystem=yes, ax_cv_boost_filesystem=no) + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_filesystem" = "xyes"; then + AC_DEFINE(HAVE_BOOST_FILESYSTEM,,[define if the Boost::Filesystem library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + if test "x$ax_boost_user_filesystem_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_filesystem* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_FILESYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_FILESYSTEM_LIB) link_filesystem="yes"; break], + [link_filesystem="no"]) + done + if test "x$link_filesystem" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_filesystem* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_FILESYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_FILESYSTEM_LIB) link_filesystem="yes"; break], + [link_filesystem="no"]) + done + fi + else + for ax_lib in $ax_boost_user_filesystem_lib boost_filesystem-$ax_boost_user_filesystem_lib; do + AC_CHECK_LIB($ax_lib, exit, + [BOOST_FILESYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_FILESYSTEM_LIB) link_filesystem="yes"; break], + [link_filesystem="no"]) + done + + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_filesystem" != "xyes"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + LIBS="$LIBS_SAVED" + fi +]) diff --git a/build/autotools/m4/ax_boost_program_options.m4 b/build/autotools/m4/ax_boost_program_options.m4 new file mode 100644 index 00000000..65a39c8c --- /dev/null +++ b/build/autotools/m4/ax_boost_program_options.m4 @@ -0,0 +1,108 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_boost_program_options.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_BOOST_PROGRAM_OPTIONS +# +# DESCRIPTION +# +# Test for program options library from the Boost C++ libraries. The macro +# requires a preceding call to AX_BOOST_BASE. Further documentation is +# available at . +# +# This macro calls: +# +# AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) +# +# And sets: +# +# HAVE_BOOST_PROGRAM_OPTIONS +# +# LICENSE +# +# Copyright (c) 2009 Thomas Porschberg +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 22 + +AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS], +[ + AC_ARG_WITH([boost-program-options], + AS_HELP_STRING([--with-boost-program-options@<:@=special-lib@:>@], + [use the program options library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-program-options=boost_program_options-gcc-mt-1_33_1 ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_program_options_lib="" + else + want_boost="yes" + ax_boost_user_program_options_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + export want_boost + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + AC_CACHE_CHECK([whether the Boost::Program_Options library is available], + ax_cv_boost_program_options, + [AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include + ]], + [[boost::program_options::options_description generic("Generic options"); + return 0;]])], + ax_cv_boost_program_options=yes, ax_cv_boost_program_options=no) + AC_LANG_POP([C++]) + ]) + if test "$ax_cv_boost_program_options" = yes; then + AC_DEFINE(HAVE_BOOST_PROGRAM_OPTIONS,,[define if the Boost::PROGRAM_OPTIONS library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + if test "x$ax_boost_user_program_options_lib" = "x"; then + for libextension in `ls $BOOSTLIBDIR/libboost_program_options*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.dylib* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.dylib.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break], + [link_program_options="no"]) + done + if test "x$link_program_options" != "xyes"; then + for libextension in `ls $BOOSTLIBDIR/boost_program_options*.dll* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.dll.*$;\1;'` `ls $BOOSTLIBDIR/boost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break], + [link_program_options="no"]) + done + fi + else + for ax_lib in $ax_boost_user_program_options_lib boost_program_options-$ax_boost_user_program_options_lib; do + AC_CHECK_LIB($ax_lib, main, + [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break], + [link_program_options="no"]) + done + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_program_options" != "xyes"; then + AC_MSG_ERROR([Could not link against [$ax_lib] !]) + fi + fi + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/build/autotools/m4/ax_boost_regex.m4 b/build/autotools/m4/ax_boost_regex.m4 new file mode 100644 index 00000000..918f16a4 --- /dev/null +++ b/build/autotools/m4/ax_boost_regex.m4 @@ -0,0 +1,111 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_regex.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_REGEX +# +# DESCRIPTION +# +# Test for Regex library from the Boost C++ libraries. The macro requires +# a preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_REGEX_LIB) +# +# And sets: +# +# HAVE_BOOST_REGEX +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2008 Michael Tindal +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 22 + +AC_DEFUN([AX_BOOST_REGEX], +[ + AC_ARG_WITH([boost-regex], + AS_HELP_STRING([--with-boost-regex@<:@=special-lib@:>@], + [use the Regex library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-regex=boost_regex-gcc-mt-d-1_33_1 ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_regex_lib="" + else + want_boost="yes" + ax_boost_user_regex_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_CACHE_CHECK(whether the Boost::Regex library is available, + ax_cv_boost_regex, + [AC_LANG_PUSH([C++]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include + ]], + [[boost::regex r(); return 0;]])], + ax_cv_boost_regex=yes, ax_cv_boost_regex=no) + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_regex" = "xyes"; then + AC_DEFINE(HAVE_BOOST_REGEX,,[define if the Boost::Regex library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + if test "x$ax_boost_user_regex_lib" = "x"; then + for libextension in `ls $BOOSTLIBDIR/libboost_regex*.so* $BOOSTLIBDIR/libboost_regex*.dylib* $BOOSTLIBDIR/libboost_regex*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_regex.*\)\.so.*$;\1;' -e 's;^lib\(boost_regex.*\)\.dylib.*;\1;' -e 's;^lib\(boost_regex.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_REGEX_LIB="-l$ax_lib"; AC_SUBST(BOOST_REGEX_LIB) link_regex="yes"; break], + [link_regex="no"]) + done + if test "x$link_regex" != "xyes"; then + for libextension in `ls $BOOSTLIBDIR/boost_regex*.dll* $BOOSTLIBDIR/boost_regex*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_regex.*\)\.dll.*$;\1;' -e 's;^\(boost_regex.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_REGEX_LIB="-l$ax_lib"; AC_SUBST(BOOST_REGEX_LIB) link_regex="yes"; break], + [link_regex="no"]) + done + fi + + else + for ax_lib in $ax_boost_user_regex_lib boost_regex-$ax_boost_user_regex_lib; do + AC_CHECK_LIB($ax_lib, main, + [BOOST_REGEX_LIB="-l$ax_lib"; AC_SUBST(BOOST_REGEX_LIB) link_regex="yes"; break], + [link_regex="no"]) + done + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the Boost::Regex library!) + fi + if test "x$link_regex" != "xyes"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/build/autotools/m4/ax_boost_system.m4 b/build/autotools/m4/ax_boost_system.m4 new file mode 100644 index 00000000..c4c45559 --- /dev/null +++ b/build/autotools/m4/ax_boost_system.m4 @@ -0,0 +1,120 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_system.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_SYSTEM +# +# DESCRIPTION +# +# Test for System library from the Boost C++ libraries. The macro requires +# a preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_SYSTEM_LIB) +# +# And sets: +# +# HAVE_BOOST_SYSTEM +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2008 Michael Tindal +# Copyright (c) 2008 Daniel Casimiro +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 17 + +AC_DEFUN([AX_BOOST_SYSTEM], +[ + AC_ARG_WITH([boost-system], + AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@], + [use the System library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-system=boost_system-gcc-mt ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_system_lib="" + else + want_boost="yes" + ax_boost_user_system_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_BUILD]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_CACHE_CHECK(whether the Boost::System library is available, + ax_cv_boost_system, + [AC_LANG_PUSH([C++]) + CXXFLAGS_SAVE=$CXXFLAGS + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], + [[boost::system::system_category]])], + ax_cv_boost_system=yes, ax_cv_boost_system=no) + CXXFLAGS=$CXXFLAGS_SAVE + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_system" = "xyes"; then + AC_SUBST(BOOST_CPPFLAGS) + + AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + + LDFLAGS_SAVE=$LDFLAGS + if test "x$ax_boost_user_system_lib" = "x"; then + for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], + [link_system="no"]) + done + if test "x$link_system" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], + [link_system="no"]) + done + fi + + else + for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do + AC_CHECK_LIB($ax_lib, exit, + [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], + [link_system="no"]) + done + + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_system" = "xno"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/build/autotools/m4/ax_cxx_compile_stdcxx_11.m4 b/build/autotools/m4/ax_cxx_compile_stdcxx_11.m4 new file mode 100644 index 00000000..af31b86b --- /dev/null +++ b/build/autotools/m4/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1,141 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++11 +# standard; if necessary, add switches to CXXFLAGS to enable support. +# +# The first argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The second argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline C++11 support is required and that the macro +# should error out if no mode with that support is found. If specified +# 'optional', then configuration proceeds regardless, after defining +# HAVE_CXX11 if and only if a supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014 Alexey Sokolov +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 4 + +m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[ + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + struct Base { + virtual void f() {} + }; + struct Child : public Base { + virtual void f() {} + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; +]]) + +AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl + m4_if([$1], [], [], + [$1], [ext], [], + [$1], [noext], [], + [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl + m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], + [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], + [$2], [optional], [ax_cxx_compile_cxx11_required=false], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++11 features by default, + ax_cv_cxx_compile_cxx11, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [ax_cv_cxx_compile_cxx11=yes], + [ax_cv_cxx_compile_cxx11=no])]) + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + m4_if([$1], [noext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=gnu++11 -std=gnu++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + + m4_if([$1], [ext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=c++11 -std=c++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + AC_MSG_NOTICE([No compiler with C++11 support was found]) + else + HAVE_CXX11=1 + AC_DEFINE(HAVE_CXX11,1, + [define if the compiler supports basic C++11 syntax]) + fi + + AC_SUBST(HAVE_CXX11) + fi +]) diff --git a/build/autotools/m4/ax_pthread.m4 b/build/autotools/m4/ax_pthread.m4 new file mode 100644 index 00000000..d383ad5c --- /dev/null +++ b/build/autotools/m4/ax_pthread.m4 @@ -0,0 +1,332 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also link it with them as well. e.g. you should link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threads programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name +# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 21 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test x"$ax_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case ${host_os} in + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" + ;; + + darwin*) + ax_pthread_flags="-pthread $ax_pthread_flags" + ;; +esac + +# Clang doesn't consider unrecognized options an error unless we specify +# -Werror. We throw in some extra Clang-specific options to ensure that +# this doesn't happen for GCC, which also accepts -Werror. + +AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags]) +save_CFLAGS="$CFLAGS" +ax_pthread_extra_flags="-Werror" +CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])], + [AC_MSG_RESULT([yes])], + [ax_pthread_extra_flags= + AC_MSG_RESULT([no])]) +CFLAGS="$save_CFLAGS" + +if test x"$ax_pthread_ok" = xno; then +for flag in $ax_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + if test x"$ax_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $attr; return attr /* ; */])], + [attr_name=$attr; break], + []) + done + AC_MSG_RESULT([$attr_name]) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case ${host_os} in + aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; + osf* | hpux*) flag="-D_REENTRANT";; + solaris*) + if test "$GCC" = "yes"; then + flag="-D_REENTRANT" + else + # TODO: What about Clang on Solaris? + flag="-mt -D_REENTRANT" + fi + ;; + esac + AC_MSG_RESULT([$flag]) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != xyes; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], + [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/build/autotools/missing b/build/autotools/missing new file mode 100755 index 00000000..cdea5149 --- /dev/null +++ b/build/autotools/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2012-06-26.16; # UTC + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=http://www.perl.org/ +flex_URL=http://flex.sourceforge.net/ +gnu_software_URL=http://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'automa4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/build/build_mingw.cmd b/build/build_mingw.cmd deleted file mode 100644 index e8bb2b84..00000000 --- a/build/build_mingw.cmd +++ /dev/null @@ -1,149 +0,0 @@ -@echo off -setlocal enableextensions enabledelayedexpansion -title Building i2pd - -REM Copyright (c) 2013-2022, The PurpleI2P Project -REM This file is part of Purple i2pd project and licensed under BSD3 -REM See full license text in LICENSE file at top of project tree - -REM To use that script, you must have installed in your MSYS installation these packages: -REM Base: git make zip -REM UCRT64: mingw-w64-ucrt-x86_64-boost mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-gcc -REM MINGW32: mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-gcc - -REM setting up variables for MSYS -REM Note: if you installed MSYS64 to different path, edit WD variable (only C:\msys64 needed to edit) -set MSYS2_PATH_TYPE=inherit -set CHERE_INVOKING=enabled_from_arguments -set MSYSTEM=MINGW32 - -set "WD=C:\msys64\usr\bin\" -set "xSH=%WD%bash -lc" - -set "FILELIST=i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates contrib/tunnels.d contrib/webconsole" - -REM detecting number of processors -set /a threads=%NUMBER_OF_PROCESSORS% - -REM we must work in root of repo -cd .. - -REM deleting old log files -del /S build_*.log >> nul 2>&1 - -echo Receiving latest commit and cleaning up... -%xSH% "git checkout contrib/* && git pull && make clean" > build\build.log 2>&1 - -REM set to variable current commit hash -for /F "usebackq" %%a in (`%xSH% "git describe --tags"`) DO ( - set tag=%%a -) - -REM set to variable latest released tag -for /F "usebackq" %%b in (`%xSH% "git describe --abbrev=0"`) DO ( - set reltag=%%b -) - -echo Preparing configuration files and README for packaging... - -%xSH% "echo To use configs and certificates, move all files and certificates folder from contrib directory here. > README.txt" >> nul - -REM converting configuration files to DOS format (make usable in Windows Notepad) -%xSH% "unix2dos contrib/i2pd.conf contrib/tunnels.conf contrib/tunnels.d/* contrib/webconsole/style.css" >> build\build.log 2>&1 - -REM Prepare binary signing command if signing key and password provided -if defined SIGN ( - echo Signing enabled - - for %%X in (signtool.exe) do (set xSIGNTOOL=%%~$PATH:X) - if not defined xSIGNTOOL ( - if not defined SIGNTOOL ( - echo Error: Can't find signtool. Please provide path to binary using SIGNTOOL variable. - exit /b 1 - ) else ( - set "xSIGNTOOL=%SIGNTOOL%" - ) - ) - - if defined SIGNKEY ( - set "xSIGNKEYOPTS=/f ^"%SIGNKEY%^"" - ) - - if defined SIGNPASS ( - set "xSIGNPASSOPTS=/p ^"%SIGNPASS%^"" - ) - - set "xSIGNOPTS=sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 %xSIGNKEYOPTS% %xSIGNPASSOPTS%" -) - -REM starting building -set MSYSTEM=MINGW32 -set bitness=32 -call :BUILDING - -set MSYSTEM=UCRT64 -set bitness=64 -call :BUILDING - -REM build for Windows XP -if exist C:\msys64-xp\ ( call :BUILDING_XP ) - -echo. - -REM compile installer -echo Building installer... -C:\PROGRA~2\INNOSE~1\ISCC.exe /dI2Pd_TextVer="%tag%" /dI2Pd_Ver="%reltag%.0" build\win_installer.iss >> build\build.log 2>&1 - -REM Sign binary -if defined xSIGNOPTS ( - "%xSIGNTOOL%" %xSIGNOPTS% build\setup_i2pd_v%tag%.exe -) - -%xSH% "git checkout contrib/*" >> build\build.log 2>&1 -del README.txt i2pd_x32.exe i2pd_x64.exe i2pd_xp.exe >> nul - -echo Build complete... -pause -exit /b 0 - -:BUILDING -%xSH% "make clean" >> nul -echo Building i2pd %tag% for win%bitness%... -REM Build i2pd -%xSH% "make DEBUG=no USE_UPNP=yes -j%threads%" > build\build_win%bitness%_%tag%.log 2>&1 - -REM Sign binary -if defined xSIGNOPTS ( - "%xSIGNTOOL%" %xSIGNOPTS% i2pd.exe -) - -REM Copy binary for installer and create distribution archive -%xSH% "cp i2pd.exe i2pd_x%bitness%.exe && zip -r9 build/i2pd_%tag%_win%bitness%_mingw.zip %FILELIST%" >> build\build_win%bitness%_%tag%.log 2>&1 - -REM Clean work directory -%xSH% "make clean" >> build\build_win%bitness%_%tag%.log 2>&1 -goto EOF - -:BUILDING_XP -set MSYSTEM=MINGW32 -set bitness=32 -set "WD=C:\msys64-xp\usr\bin\" -set "xSH=%WD%bash -lc" - -%xSH% "make clean" >> nul -echo Building i2pd %tag% for winxp... -%xSH% "make DEBUG=no USE_UPNP=yes USE_WINXP_FLAGS=yes -j%threads%" > build\build_winxp_%tag%.log 2>&1 - -REM Sign binary -if defined xSIGNOPTS ( - "%xSIGNTOOL%" %xSIGNOPTS% i2pd.exe -) - -REM Copy binary for installer and create distribution archive -%xSH% "cp i2pd.exe i2pd_xp.exe && zip -r9 build/i2pd_%tag%_winxp_mingw.zip %FILELIST%" >> build\build_winxp_%tag%.log 2>&1 - -REM Clean work directory -%xSH% "make clean" >> build\build_winxp_%tag%.log 2>&1 -goto EOF - -:EOF diff --git a/build/cmake_modules/CheckAtomic.cmake b/build/cmake_modules/CheckAtomic.cmake deleted file mode 100644 index 4954e3e5..00000000 --- a/build/cmake_modules/CheckAtomic.cmake +++ /dev/null @@ -1,112 +0,0 @@ -# atomic builtins are required for threading support. - -INCLUDE(CheckCXXSourceCompiles) -INCLUDE(CheckLibraryExists) - -# Sometimes linking against libatomic is required for atomic ops, if -# the platform doesn't support lock-free atomics. - -function(check_working_cxx_atomics varname) - set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++17") - CHECK_CXX_SOURCE_COMPILES(" -#include -std::atomic x; -std::atomic y; -std::atomic z; -int main() { - ++z; - ++y; - return ++x; -} -" ${varname}) - set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) -endfunction(check_working_cxx_atomics) - -function(check_working_cxx_atomics64 varname) - set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) - set(CMAKE_REQUIRED_FLAGS "-std=c++17 ${CMAKE_REQUIRED_FLAGS}") - CHECK_CXX_SOURCE_COMPILES(" -#include -#include -std::atomic x (0); -int main() { - uint64_t i = x.load(std::memory_order_relaxed); - (void)i; - return 0; -} -" ${varname}) - set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) -endfunction(check_working_cxx_atomics64) - - -# Check for (non-64-bit) atomic operations. -if(MSVC) - set(HAVE_CXX_ATOMICS_WITHOUT_LIB True) -else() - # First check if atomics work without the library. - check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB) - # If not, check if the library exists, and atomics work with it. - if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) - check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC) - if(HAVE_LIBATOMIC) - list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") - check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB) - if (NOT HAVE_CXX_ATOMICS_WITH_LIB) - message(FATAL_ERROR "Host compiler must support std::atomic!") - endif() - else() - message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.") - endif() - endif() -endif() - -# Check for 64 bit atomic operations. -if(MSVC) - set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True) -else() - # First check if atomics work without the library. - check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB) - # If not, check if the library exists, and atomics work with it. - if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) - check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64) - if(HAVE_CXX_LIBATOMICS64) - list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") - check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) - if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) - message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!") - endif() - else() - message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.") - endif() - endif() -endif() - -## TODO: This define is only used for the legacy atomic operations in -## llvm's Atomic.h, which should be replaced. Other code simply -## assumes C++11 works. -CHECK_CXX_SOURCE_COMPILES(" -#ifdef _MSC_VER -#include -#endif -int main() { -#ifdef _MSC_VER - volatile LONG val = 1; - MemoryBarrier(); - InterlockedCompareExchange(&val, 0, 1); - InterlockedIncrement(&val); - InterlockedDecrement(&val); -#else - volatile unsigned long val = 1; - __sync_synchronize(); - __sync_val_compare_and_swap(&val, 1, 0); - __sync_add_and_fetch(&val, 1); - __sync_sub_and_fetch(&val, 1); -#endif - return 0; - } -" LLVM_HAS_ATOMICS) - -if( NOT LLVM_HAS_ATOMICS ) - message(STATUS "Warning: LLVM will be built thread-unsafe because atomic builtins are missing") -endif() diff --git a/build/cmake_modules/FindCheck.cmake b/build/cmake_modules/FindCheck.cmake deleted file mode 100644 index 8ad818f4..00000000 --- a/build/cmake_modules/FindCheck.cmake +++ /dev/null @@ -1,55 +0,0 @@ -# - Try to find the CHECK libraries -# Once done this will define -# -# CHECK_FOUND - system has check -# CHECK_INCLUDE_DIRS - the check include directory -# CHECK_LIBRARIES - check library -# -# Copyright (c) 2007 Daniel Gollub -# Copyright (c) 2007-2009 Bjoern Ricks -# -# Redistribution and use is allowed according to the terms of the New -# BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - - -INCLUDE( FindPkgConfig ) - -IF ( Check_FIND_REQUIRED ) - SET( _pkgconfig_REQUIRED "REQUIRED" ) -ELSE( Check_FIND_REQUIRED ) - SET( _pkgconfig_REQUIRED "" ) -ENDIF ( Check_FIND_REQUIRED ) - -IF ( CHECK_MIN_VERSION ) - PKG_SEARCH_MODULE( CHECK ${_pkgconfig_REQUIRED} check>=${CHECK_MIN_VERSION} ) -ELSE ( CHECK_MIN_VERSION ) - PKG_SEARCH_MODULE( CHECK ${_pkgconfig_REQUIRED} check ) -ENDIF ( CHECK_MIN_VERSION ) - -# Look for CHECK include dir and libraries -IF( NOT CHECK_FOUND AND NOT PKG_CONFIG_FOUND ) - - FIND_PATH( CHECK_INCLUDE_DIRS check.h ) - - FIND_LIBRARY( CHECK_LIBRARIES NAMES check ) - - IF ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES ) - SET( CHECK_FOUND 1 ) - IF ( NOT Check_FIND_QUIETLY ) - MESSAGE ( STATUS "Found CHECK: ${CHECK_LIBRARIES}" ) - ENDIF ( NOT Check_FIND_QUIETLY ) - ELSE ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES ) - IF ( Check_FIND_REQUIRED ) - MESSAGE( FATAL_ERROR "Could NOT find CHECK" ) - ELSE ( Check_FIND_REQUIRED ) - IF ( NOT Check_FIND_QUIETLY ) - MESSAGE( STATUS "Could NOT find CHECK" ) - ENDIF ( NOT Check_FIND_QUIETLY ) - ENDIF ( Check_FIND_REQUIRED ) - ENDIF ( CHECK_INCLUDE_DIRS AND CHECK_LIBRARIES ) -ENDIF( NOT CHECK_FOUND AND NOT PKG_CONFIG_FOUND ) - -# Hide advanced variables from CMake GUIs -MARK_AS_ADVANCED( CHECK_INCLUDE_DIRS CHECK_LIBRARIES ) - diff --git a/build/cmake_modules/FindCryptoPP.cmake b/build/cmake_modules/FindCryptoPP.cmake new file mode 100644 index 00000000..7a8ac317 --- /dev/null +++ b/build/cmake_modules/FindCryptoPP.cmake @@ -0,0 +1,35 @@ +# - Find Crypto++ + +if(CRYPTO++_INCLUDE_DIR AND CRYPTO++_LIBRARIES) + set(CRYPTO++_FOUND TRUE) + +else(CRYPTO++_INCLUDE_DIR AND CRYPTO++_LIBRARIES) + find_path(CRYPTO++_INCLUDE_DIR cryptlib.h + /usr/include/crypto++ + /usr/include/cryptopp + /usr/local/include/crypto++ + /usr/local/include/cryptopp + /opt/local/include/crypto++ + /opt/local/include/cryptopp + $ENV{SystemDrive}/Crypto++/include + ) + + find_library(CRYPTO++_LIBRARIES NAMES cryptopp + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + $ENV{SystemDrive}/Crypto++/lib + ) + + if(CRYPTO++_INCLUDE_DIR AND CRYPTO++_LIBRARIES) + set(CRYPTO++_FOUND TRUE) + message(STATUS "Found Crypto++: ${CRYPTO++_INCLUDE_DIR}, ${CRYPTO++_LIBRARIES}") + else(CRYPTO++_INCLUDE_DIR AND CRYPTO++_LIBRARIES) + set(CRYPTO++_FOUND FALSE) + message(STATUS "Crypto++ not found.") + endif(CRYPTO++_INCLUDE_DIR AND CRYPTO++_LIBRARIES) + + mark_as_advanced(CRYPTO++_INCLUDE_DIR CRYPTO++_LIBRARIES) + +endif(CRYPTO++_INCLUDE_DIR AND CRYPTO++_LIBRARIES) diff --git a/build/cmake_modules/FindMiniUPnPc.cmake b/build/cmake_modules/FindMiniUPnPc.cmake deleted file mode 100644 index 17731912..00000000 --- a/build/cmake_modules/FindMiniUPnPc.cmake +++ /dev/null @@ -1,28 +0,0 @@ -# - Find MINIUPNPC - -if(MINIUPNPC_INCLUDE_DIR AND MINIUPNPC_LIBRARY) - set(MINIUPNPC_FOUND TRUE) - -else() - find_path(MINIUPNPC_INCLUDE_DIR miniupnpc/miniupnpc.h - /usr/include - /usr/local/include - /opt/local/include - $ENV{SystemDrive} - ${PROJECT_SOURCE_DIR}/../.. - ) - - find_library(MINIUPNPC_LIBRARY miniupnpc) - - if(MINIUPNPC_INCLUDE_DIR AND MINIUPNPC_LIBRARY) - set(MINIUPNPC_FOUND TRUE) - message(STATUS "Found MiniUPnP headers: ${MINIUPNPC_INCLUDE_DIR}") - message(STATUS "Found MiniUPnP library: ${MINIUPNPC_LIBRARY}") - else() - set(MINIUPNPC_FOUND FALSE) - message(STATUS "MiniUPnP not found.") - endif() - - mark_as_advanced(MINIUPNPC_INCLUDE_DIR MINIUPNPC_LIBRARY) - -endif() diff --git a/build/cmake_modules/GetGitRevisionDescription.cmake b/build/cmake_modules/GetGitRevisionDescription.cmake deleted file mode 100644 index a08895c6..00000000 --- a/build/cmake_modules/GetGitRevisionDescription.cmake +++ /dev/null @@ -1,284 +0,0 @@ -# - Returns a version string from Git -# -# These functions force a re-configure on each git commit so that you can -# trust the values of the variables in your build system. -# -# get_git_head_revision( [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR]) -# -# Returns the refspec and sha hash of the current head revision -# -# git_describe( [ ...]) -# -# Returns the results of git describe on the source tree, and adjusting -# the output so that it tests false if an error occurs. -# -# git_describe_working_tree( [ ...]) -# -# Returns the results of git describe on the working tree (--dirty option), -# and adjusting the output so that it tests false if an error occurs. -# -# git_get_exact_tag( [ ...]) -# -# Returns the results of git describe --exact-match on the source tree, -# and adjusting the output so that it tests false if there was no exact -# matching tag. -# -# git_local_changes() -# -# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. -# Uses the return code of "git diff-index --quiet HEAD --". -# Does not regard untracked files. -# -# Requires CMake 2.6 or newer (uses the 'function' command) -# -# Original Author: -# 2009-2020 Ryan Pavlik -# http://academic.cleardefinition.com -# -# Copyright 2009-2013, Iowa State University. -# Copyright 2013-2020, Ryan Pavlik -# Copyright 2013-2020, Contributors -# SPDX-License-Identifier: BSL-1.0 -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -if(__get_git_revision_description) - return() -endif() -set(__get_git_revision_description YES) - -# We must run the following at "include" time, not at function call time, -# to find the path to this module rather than the path to a calling list file -get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) - -# Function _git_find_closest_git_dir finds the next closest .git directory -# that is part of any directory in the path defined by _start_dir. -# The result is returned in the parent scope variable whose name is passed -# as variable _git_dir_var. If no .git directory can be found, the -# function returns an empty string via _git_dir_var. -# -# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and -# neither foo nor bar contain a file/directory .git. This will return -# C:/bla/.git -# -function(_git_find_closest_git_dir _start_dir _git_dir_var) - set(cur_dir "${_start_dir}") - set(git_dir "${_start_dir}/.git") - while(NOT EXISTS "${git_dir}") - # .git dir not found, search parent directories - set(git_previous_parent "${cur_dir}") - get_filename_component(cur_dir "${cur_dir}" DIRECTORY) - if(cur_dir STREQUAL git_previous_parent) - # We have reached the root directory, we are not in git - set(${_git_dir_var} - "" - PARENT_SCOPE) - return() - endif() - set(git_dir "${cur_dir}/.git") - endwhile() - set(${_git_dir_var} - "${git_dir}" - PARENT_SCOPE) -endfunction() - -function(get_git_head_revision _refspecvar _hashvar) - _git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR) - - if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR") - set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE) - else() - set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE) - endif() - if(NOT "${GIT_DIR}" STREQUAL "") - file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}" - "${GIT_DIR}") - if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR) - # We've gone above the CMake root dir. - set(GIT_DIR "") - endif() - endif() - if("${GIT_DIR}" STREQUAL "") - set(${_refspecvar} - "GITDIR-NOTFOUND" - PARENT_SCOPE) - set(${_hashvar} - "GITDIR-NOTFOUND" - PARENT_SCOPE) - return() - endif() - - # Check if the current source dir is a git submodule or a worktree. - # In both cases .git is a file instead of a directory. - # - if(NOT IS_DIRECTORY ${GIT_DIR}) - # The following git command will return a non empty string that - # points to the super project working tree if the current - # source dir is inside a git submodule. - # Otherwise the command will return an empty string. - # - execute_process( - COMMAND "${GIT_EXECUTABLE}" rev-parse - --show-superproject-working-tree - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT "${out}" STREQUAL "") - # If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule - file(READ ${GIT_DIR} submodule) - string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE - ${submodule}) - string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) - get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) - get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} - ABSOLUTE) - set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") - else() - # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree - file(READ ${GIT_DIR} worktree_ref) - # The .git directory contains a path to the worktree information directory - # inside the parent git repo of the worktree. - # - string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir - ${worktree_ref}) - string(STRIP ${git_worktree_dir} git_worktree_dir) - _git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR) - set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") - endif() - else() - set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") - endif() - set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") - if(NOT EXISTS "${GIT_DATA}") - file(MAKE_DIRECTORY "${GIT_DATA}") - endif() - - if(NOT EXISTS "${HEAD_SOURCE_FILE}") - return() - endif() - set(HEAD_FILE "${GIT_DATA}/HEAD") - configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) - - configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" - "${GIT_DATA}/grabRef.cmake" @ONLY) - include("${GIT_DATA}/grabRef.cmake") - - set(${_refspecvar} - "${HEAD_REF}" - PARENT_SCOPE) - set(${_hashvar} - "${HEAD_HASH}" - PARENT_SCOPE) -endfunction() - -function(git_describe _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - get_git_head_revision(refspec hash) - if(NOT GIT_FOUND) - set(${_var} - "GIT-NOTFOUND" - PARENT_SCOPE) - return() - endif() - if(NOT hash) - set(${_var} - "HEAD-HASH-NOTFOUND" - PARENT_SCOPE) - return() - endif() - - # TODO sanitize - #if((${ARGN}" MATCHES "&&") OR - # (ARGN MATCHES "||") OR - # (ARGN MATCHES "\\;")) - # message("Please report the following error to the project!") - # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") - #endif() - - #message(STATUS "Arguments to execute_process: ${ARGN}") - - execute_process( - COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE res - OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT res EQUAL 0) - set(out "${out}-${res}-NOTFOUND") - endif() - - set(${_var} - "${out}" - PARENT_SCOPE) -endfunction() - -function(git_describe_working_tree _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - if(NOT GIT_FOUND) - set(${_var} - "GIT-NOTFOUND" - PARENT_SCOPE) - return() - endif() - - execute_process( - COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE res - OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT res EQUAL 0) - set(out "${out}-${res}-NOTFOUND") - endif() - - set(${_var} - "${out}" - PARENT_SCOPE) -endfunction() - -function(git_get_exact_tag _var) - git_describe(out --exact-match ${ARGN}) - set(${_var} - "${out}" - PARENT_SCOPE) -endfunction() - -function(git_local_changes _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - get_git_head_revision(refspec hash) - if(NOT GIT_FOUND) - set(${_var} - "GIT-NOTFOUND" - PARENT_SCOPE) - return() - endif() - if(NOT hash) - set(${_var} - "HEAD-HASH-NOTFOUND" - PARENT_SCOPE) - return() - endif() - - execute_process( - COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE res - OUTPUT_VARIABLE out - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(res EQUAL 0) - set(${_var} - "CLEAN" - PARENT_SCOPE) - else() - set(${_var} - "DIRTY" - PARENT_SCOPE) - endif() -endfunction() diff --git a/build/cmake_modules/GetGitRevisionDescription.cmake.in b/build/cmake_modules/GetGitRevisionDescription.cmake.in deleted file mode 100644 index 116efc4e..00000000 --- a/build/cmake_modules/GetGitRevisionDescription.cmake.in +++ /dev/null @@ -1,43 +0,0 @@ -# -# Internal file for GetGitRevisionDescription.cmake -# -# Requires CMake 2.6 or newer (uses the 'function' command) -# -# Original Author: -# 2009-2010 Ryan Pavlik -# http://academic.cleardefinition.com -# Iowa State University HCI Graduate Program/VRAC -# -# Copyright 2009-2012, Iowa State University -# Copyright 2011-2015, Contributors -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) -# SPDX-License-Identifier: BSL-1.0 - -set(HEAD_HASH) - -file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) - -string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) -if(HEAD_CONTENTS MATCHES "ref") - # named branch - string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") - if(EXISTS "@GIT_DIR@/${HEAD_REF}") - configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) - else() - configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) - file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) - if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") - set(HEAD_HASH "${CMAKE_MATCH_1}") - endif() - endif() -else() - # detached HEAD - configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) -endif() - -if(NOT HEAD_HASH) - file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) - string(STRIP "${HEAD_HASH}" HEAD_HASH) -endif() diff --git a/build/cmake_modules/TargetArch.cmake b/build/cmake_modules/TargetArch.cmake deleted file mode 100644 index e611c4f7..00000000 --- a/build/cmake_modules/TargetArch.cmake +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) 2017-2023, The PurpleI2P Project -# This file is part of Purple i2pd project and licensed under BSD3 -# See full license text in LICENSE file at top of project tree - -# Based on the Qt 5 processor detection code, so should be very accurate -# https://github.com/qt/qtbase/blob/dev/src/corelib/global/qprocessordetection.h -# Currently handles arm (v5, v6, v7, v8), x86 (32/64), ia64, mips (32/64, mipsel, mips64el) and ppc (32/64) - -# Regarding POWER/PowerPC, just as is noted in the Qt source, -# "There are many more known variants/revisions that we do not handle/detect." - -set(archdetect_c_code " -#if defined(__arm__) || defined(__TARGET_ARCH_ARM)|| defined(_M_ARM) || defined(_M_ARM64) || defined(__aarch64__) || defined(__ARM64__) - #if defined(__ARM64_ARCH_8__) \\ - || defined(__aarch64__) \\ - || defined(__ARMv8__) \\ - || defined(__ARMv8_A__) \\ - || defined(_M_ARM64) \\ - || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 8) - #error cmake_ARCH arm64 - #elif defined(__ARM_ARCH_7__) \\ - || defined(__ARM_ARCH_7A__) \\ - || defined(__ARM_ARCH_7R__) \\ - || defined(__ARM_ARCH_7M__) \\ - || defined(__ARM_ARCH_7S__) \\ - || defined(_ARM_ARCH_7) \\ - || defined(__CORE_CORTEXA__) \\ - || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 7) - #error cmake_ARCH armv7 - #elif defined(__ARM_ARCH_6__) \\ - || defined(__ARM_ARCH_6J__) \\ - || defined(__ARM_ARCH_6T2__) \\ - || defined(__ARM_ARCH_6Z__) \\ - || defined(__ARM_ARCH_6K__) \\ - || defined(__ARM_ARCH_6ZK__) \\ - || defined(__ARM_ARCH_6M__) \\ - || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 6) - #error cmake_ARCH armv6 - #elif defined(__ARM_ARCH_5TEJ__) \\ - || defined(__ARM_ARCH_5TE__) \\ - || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 5) - #error cmake_ARCH armv5 - #else - #error cmake_ARCH arm - #endif -#elif defined(__i386) || defined(__i386__) || defined(_M_IX86) - #error cmake_ARCH i386 -#elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) - #error cmake_ARCH x86_64 -#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64) - #error cmake_ARCH ia64 -#elif defined(__mips) || defined(__mips__) || defined(_M_MRX000) - #if defined(_MIPS_ARCH_MIPS64) || defined(__mips64) - #if defined(__MIPSEL__) - #error cmake_ARCH mips64el - #else - #error cmake_ARCH mips64 - #endif - #elif defined(__MIPSEL__) - #error cmake_ARCH mipsel - #else - #error cmake_ARCH mips - #endif -#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) || defined(__POWERPC__) \\ - || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \\ - || defined(_M_MPPC) || defined(_M_PPC) - #if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__) - #error cmake_ARCH ppc64 - #else - #error cmake_ARCH ppc - #endif -#endif - -#error cmake_ARCH unknown -") - -# Set ppc_support to TRUE before including this file on ppc and ppc64 -# will be treated as invalid architectures since they are no longer supported by Apple - -function(target_architecture output_var) - if(APPLE AND CMAKE_OSX_ARCHITECTURES) - # On OS X we use CMAKE_OSX_ARCHITECTURES *if* it was set - # First let's normalize the order of the values - - # Note that it's not possible to compile PowerPC applications if you are using - # the OS X SDK version 10.7 or later - you'll need 10.4/10.5/10.6 for that, so we - # disable it by default. Also, ppc64 is not supported in 10.6. - # See this page for more information: - # http://stackoverflow.com/questions/5333490/how-can-we-restore-ppc-ppc64-as-well-as-full-10-4-10-5-sdk-support-to-xcode-4 - - # Architecture defaults to i386 or ppc on OS X 10.5 and earlier, depending on the CPU type detected at runtime. - # On OS X 10.6+ the default is x86_64 if the CPU supports it, i386 otherwise; 10.6 also supports ppc. - - foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES}) - if("${osx_arch}" STREQUAL "ppc" AND ppc_support) - set(osx_arch_ppc TRUE) - elseif("${osx_arch}" STREQUAL "ppc64" AND ppc_support) - set(osx_arch_ppc64 TRUE) - elseif("${osx_arch}" STREQUAL "i386") - set(osx_arch_i386 TRUE) - elseif("${osx_arch}" STREQUAL "x86_64") - set(osx_arch_x86_64 TRUE) - elseif("${osx_arch}" STREQUAL "arm64") - set(osx_arch_arm64 TRUE) - else() - message(FATAL_ERROR "Invalid OS X arch name: ${osx_arch}") - endif() - endforeach() - - # Now add all the architectures in our normalized order - if(osx_arch_ppc) - list(APPEND ARCH ppc) - endif() - - if(osx_arch_ppc64) - list(APPEND ARCH ppc64) - endif() - - if(osx_arch_i386) - list(APPEND ARCH i386) - endif() - - if(osx_arch_x86_64) - list(APPEND ARCH x86_64) - endif() - - if(osx_arch_arm64) - list(APPEND ARCH arm64) - endif() - else() - file(WRITE "${CMAKE_BINARY_DIR}/arch.c" "${archdetect_c_code}") - - enable_language(C) - - # Detect the architecture in a rather creative way... - # This compiles a small C program which is a series of ifdefs that selects - # a particular #error preprocessor directive whose message string contains - # the target architecture. The program will always fail to compile (both because - # file is not a valid C program, and obviously because of the presence of - # the #error preprocessor directives... but by exploiting the preprocessor in this - # way, we can detect the correct target architecture even when cross-compiling, - # since the program itself never needs to be run (only the compiler/preprocessor) - try_run( - run_result_unused - compile_result_unused - "${CMAKE_BINARY_DIR}" - "${CMAKE_BINARY_DIR}/arch.c" - COMPILE_OUTPUT_VARIABLE ARCH - CMAKE_FLAGS CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} - ) - - # Parse the architecture name from the compiler output - string(REGEX MATCH "cmake_ARCH ([a-zA-Z0-9_]+)" ARCH "${ARCH}") - - # Get rid of the value marker leaving just the architecture name - string(REPLACE "cmake_ARCH " "" ARCH "${ARCH}") - - # If we are compiling with an unknown architecture this variable should - # already be set to "unknown" but in the case that it's empty (i.e. due - # to a typo in the code), then set it to unknown - if (NOT ARCH) - set(ARCH unknown) - endif() - endif() - - set(${output_var} "${ARCH}" PARENT_SCOPE) -endfunction() diff --git a/build/cmake_modules/Version.cmake b/build/cmake_modules/Version.cmake deleted file mode 100644 index cb9551db..00000000 --- a/build/cmake_modules/Version.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# read version - -function(set_version version_file output_var) - file(READ "${version_file}" version_data) - - string(REGEX MATCH "I2PD_VERSION_MAJOR ([0-9]*)" _ ${version_data}) - set(version_major ${CMAKE_MATCH_1}) - - string(REGEX MATCH "I2PD_VERSION_MINOR ([0-9]*)" _ ${version_data}) - set(version_minor ${CMAKE_MATCH_1}) - - string(REGEX MATCH "I2PD_VERSION_MICRO ([0-9]*)" _ ${version_data}) - set(version_micro ${CMAKE_MATCH_1}) - - set(${output_var} "${version_major}.${version_minor}.${version_micro}" PARENT_SCOPE) -endfunction() diff --git a/build/win_installer.iss b/build/win_installer.iss deleted file mode 100644 index a4b67ad2..00000000 --- a/build/win_installer.iss +++ /dev/null @@ -1,54 +0,0 @@ -#define I2Pd_AppName "i2pd" -#define I2Pd_Publisher "PurpleI2P" - -[Setup] -AppName={#I2Pd_AppName} -AppVersion={#I2Pd_TextVer} -AppPublisher={#I2Pd_Publisher} - -DefaultDirName={pf}\I2Pd -DefaultGroupName=I2Pd -UninstallDisplayIcon={app}\I2Pd.exe -OutputDir=. -OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_TextVer} - -LicenseFile=..\LICENSE -SetupIconFile=..\Win32\mask.ico - -InternalCompressLevel=ultra64 -Compression=lzma/ultra64 -SolidCompression=true - -ArchitecturesInstallIn64BitMode=x64 -ExtraDiskSpaceRequired=15 - -AppID={{621A23E0-3CF4-4BD6-97BC-4835EA5206A2} -AppVerName={#I2Pd_AppName} -AppCopyright=Copyright (c) 2013-2024, The PurpleI2P Project -AppPublisherURL=http://i2pd.website/ -AppSupportURL=https://github.com/PurpleI2P/i2pd/issues -AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases - -VersionInfoProductVersion={#I2Pd_Ver} -VersionInfoVersion={#I2Pd_Ver} - -CloseApplications=yes - -[Files] -Source: ..\i2pd_x32.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: not IsWin64; MinVersion: 6.0 -Source: ..\i2pd_x64.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: IsWin64; MinVersion: 6.0 -Source: ..\i2pd_xp.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: IsWin64; OnlyBelowVersion: 6.0 -Source: ..\README.md; DestDir: {app}; DestName: Readme.txt; Flags: onlyifdoesntexist -Source: ..\contrib\i2pd.conf; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist -Source: ..\contrib\subscriptions.txt; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist -Source: ..\contrib\tunnels.conf; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist -Source: ..\contrib\certificates\*; DestDir: {userappdata}\i2pd\certificates; Flags: onlyifdoesntexist recursesubdirs createallsubdirs -Source: ..\contrib\tunnels.d\*; DestDir: {userappdata}\i2pd\tunnels.d; Flags: onlyifdoesntexist recursesubdirs createallsubdirs -Source: ..\contrib\webconsole\*; DestDir: {userappdata}\i2pd\webconsole; Flags: onlyifdoesntexist recursesubdirs createallsubdirs - -[Icons] -Name: {group}\I2Pd; Filename: {app}\i2pd.exe -Name: {group}\Readme; Filename: {app}\Readme.txt - -[UninstallDelete] -Type: filesandordirs; Name: {app} diff --git a/contrib/apparmor/docker-i2pd b/contrib/apparmor/docker-i2pd deleted file mode 100644 index 8e34298a..00000000 --- a/contrib/apparmor/docker-i2pd +++ /dev/null @@ -1,42 +0,0 @@ -# _________________________________________ -# / Copy this file to the right location \ -# | then load with: | -# | | -# | apparmor_parser -r -W | -# | /etc/apparmor.d/docker-i2pd | -# | | -# | docker run --security-opt | -# | "apparmor=docker-i2pd" ... | -# | purplei2p/i2pd | -# | | -# \ And "aa-status" to verify it's loaded. / -# ----------------------------------------- -# \ ^__^ -# \ (oo)\_______ -# (__)\ )\/\ -# ||----w | -# || || - -#include - -profile docker-i2pd flags=(attach_disconnected,mediate_deleted) { - #include - #include - #include - - /bin/busybox ix, - /usr/local/bin/i2pd ix, - /entrypoint.sh ixr, - - /i2pd_certificates/** r, - - /home/i2pd/data/** rw, - - /home/i2pd/data/i2pd.pid k, - - deny /home/i2pd/data/i2pd.conf w, - deny /home/i2pd/data/tunnels.conf w, - deny /home/i2pd/data/tunnels.d/** w, - deny /home/i2pd/data/certificates/** w, - deny /home/i2pd/data/i2pd.log r, -} diff --git a/contrib/apparmor/usr.bin.i2pd b/contrib/apparmor/usr.bin.i2pd deleted file mode 100644 index 4d370f3c..00000000 --- a/contrib/apparmor/usr.bin.i2pd +++ /dev/null @@ -1,25 +0,0 @@ -# Basic profile for i2pd -# Should work without modifications with Ubuntu/Debian packages -# Author: Darknet Villain -# -#include - -profile i2pd /{usr/,}bin/i2pd { - #include - #include - #include - - # path specific (feel free to modify if you have another paths) - /etc/i2pd/** r, - /var/lib/i2pd/** rw, - /var/log/i2pd/i2pd.log w, - /{var/,}run/i2pd/i2pd.pid rwk, - /{usr/,}bin/i2pd mr, - @{system_share_dirs}/i2pd/** r, - - # user homedir (if started not by init.d or systemd) - owner @{HOME}/.i2pd/ rw, - owner @{HOME}/.i2pd/** rwk, - - #include if exists -} diff --git a/contrib/certificates/family/gostcoin.crt b/contrib/certificates/family/gostcoin.crt deleted file mode 100644 index 5fa531e1..00000000 --- a/contrib/certificates/family/gostcoin.crt +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB6jCCAY+gAwIBAgIJAPeWi4iUKLBJMAoGCCqGSM49BAMCMHoxCzAJBgNVBAYT -AlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxHjAcBgNVBAoMFUkyUCBBbm9u -eW1vdXMgTmV0d29yazEPMA0GA1UECwwGZmFtaWx5MSAwHgYDVQQDDBdnb3N0Y29p -bi5mYW1pbHkuaTJwLm5ldDAeFw0xNzA4MDExMzQ4MzdaFw0yNzA3MzAxMzQ4Mzda -MHoxCzAJBgNVBAYTAlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxHjAcBgNV -BAoMFUkyUCBBbm9ueW1vdXMgTmV0d29yazEPMA0GA1UECwwGZmFtaWx5MSAwHgYD -VQQDDBdnb3N0Y29pbi5mYW1pbHkuaTJwLm5ldDBZMBMGByqGSM49AgEGCCqGSM49 -AwEHA0IABC+9iIYumUNnsqKbnTluHimV8OdGvo7yeGxuqhfNNB2b3jvbFJ81scgH -dsZtMQmUxgKM5nH+NQJMoCxHhSlRy2QwCgYIKoZIzj0EAwIDSQAwRgIhANNh7mOp -nBBPRh2a/ipG1VYS0d+mNjSrpz8xWcG3CXPLAiEAjM5MTfv9sOJ74PeZVhFZ02w4 -vhgyZCeLJ57f123Lm1A= ------END CERTIFICATE----- diff --git a/contrib/certificates/family/i2p-dev.crt b/contrib/certificates/family/i2p-dev.crt deleted file mode 100644 index a5796514..00000000 --- a/contrib/certificates/family/i2p-dev.crt +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICCjCCAa2gAwIBAgIEfT9YJTAMBggqhkjOPQQDAgUAMHkxCzAJBgNVBAYTAlhY -MQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9ueW1v -dXMgTmV0d29yazEPMA0GA1UECxMGZmFtaWx5MR8wHQYDVQQDExZpMnAtZGV2LmZh -bWlseS5pMnAubmV0MB4XDTE1MTIwOTIxNDIzM1oXDTI1MTIwODIxNDIzM1oweTEL -MAkGA1UEBhMCWFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMV -STJQIEFub255bW91cyBOZXR3b3JrMQ8wDQYDVQQLEwZmYW1pbHkxHzAdBgNVBAMT -FmkycC1kZXYuZmFtaWx5LmkycC5uZXQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC -AAR7FPSglYrxeSPzv74A1fTwjajZWV0TljqEMBS/56juZQB/7xOwrsHFHA0eEEF9 -dTH64wx3lhV/9sh/stwPU2MToyEwHzAdBgNVHQ4EFgQUQh4uRP1aaX8TJX5dljrS -CeFNjcAwDAYIKoZIzj0EAwIFAANJADBGAiEAhXlEKGCjJ4urpi2db3OIMl9pB+9t -M+oVtAqBamWvVBICIQDBaIqfwLzFameO5ULgGRMysKQkL0O5mH6xo910YQV8jQ== ------END CERTIFICATE----- diff --git a/contrib/certificates/family/i2pd-dev.crt b/contrib/certificates/family/i2pd-dev.crt deleted file mode 100644 index 3bb6f429..00000000 --- a/contrib/certificates/family/i2pd-dev.crt +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB6TCCAY+gAwIBAgIJAI7G9MXxh7OjMAoGCCqGSM49BAMCMHoxCzAJBgNVBAYT -AlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxHjAcBgNVBAoMFUkyUCBBbm9u -eW1vdXMgTmV0d29yazEPMA0GA1UECwwGZmFtaWx5MSAwHgYDVQQDDBdpMnBkLWRl -di5mYW1pbHkuaTJwLm5ldDAeFw0xNjAyMjAxNDE2MzhaFw0yNjAyMTcxNDE2Mzha -MHoxCzAJBgNVBAYTAlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxHjAcBgNV -BAoMFUkyUCBBbm9ueW1vdXMgTmV0d29yazEPMA0GA1UECwwGZmFtaWx5MSAwHgYD -VQQDDBdpMnBkLWRldi5mYW1pbHkuaTJwLm5ldDBZMBMGByqGSM49AgEGCCqGSM49 -AwEHA0IABMlWL3loKVOfsA8Rm91QR53Il69mQiaB7n3rUhfPkJb9MYc1S4198azE -iSnNZSXicKDPIifaCgvONmbACzElHc8wCgYIKoZIzj0EAwIDSAAwRQIgYWmSFuai -TJvVrlB5RlbiiNFCEootjWP8BFM3t/yFeaQCIQDkg4xcQIRGTHhjrCsxmlz9KcRF -G+eIF+ATfI93nPseLw== ------END CERTIFICATE----- diff --git a/contrib/certificates/family/mca2-i2p.crt b/contrib/certificates/family/mca2-i2p.crt deleted file mode 100644 index f5b57303..00000000 --- a/contrib/certificates/family/mca2-i2p.crt +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBwTCCAWigAwIBAgIJAOZBC10+/38EMAkGByqGSM49BAEwZzELMAkGA1UEBhMC -QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp -dHMgUHR5IEx0ZDEgMB4GA1UEAwwXbWNhMi1pMnAuZmFtaWx5LmkycC5uZXQwHhcN -MTYwMzI4MjIwMjMxWhcNMjYwMzI2MjIwMjMxWjBnMQswCQYDVQQGEwJBVTETMBEG -A1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkg -THRkMSAwHgYDVQQDDBdtY2EyLWkycC5mYW1pbHkuaTJwLm5ldDBZMBMGByqGSM49 -AgEGCCqGSM49AwEHA0IABNNyfzJr/rMSUeWliVBbJHRF2+qMypOlHEZ9m1nNATVX -64OhuyuVCmbF9R3oDkcZZJQQK1ovXd/EsbAIWDI8K/gwCQYHKoZIzj0EAQNIADBF -AiEApmv2tvMwzlvPjHJG1/5aXOSjYWw2s4ETeGt4abWPQkACIBbF3RuCHuzg+KN8 -N0n9hAJztAqhRCdG3hilxF4fbVLp ------END CERTIFICATE----- diff --git a/contrib/certificates/family/stormycloud.crt b/contrib/certificates/family/stormycloud.crt deleted file mode 100644 index 4ec4765a..00000000 --- a/contrib/certificates/family/stormycloud.crt +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICKDCCAc6gAwIBAgIUcPHZXtYSqGNRCD6z8gp79WUFtI0wCgYIKoZIzj0EAwIw -gZMxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEPMA0GA1UEBwwGQXVzdGlu -MRgwFgYDVQQKDA9TdG9ybXlDbG91ZCBJbmMxIzAhBgNVBAMMGnN0b3JteWNsb3Vk -LmZhbWlseS5pMnAubmV0MSQwIgYJKoZIhvcNAQkBFhVhZG1pbkBzdG9ybXljbG91 -ZC5vcmcwHhcNMjIwMzE5MTU1MjU2WhcNMzIwMzE2MTU1MjU2WjCBkzELMAkGA1UE -BhMCVVMxDjAMBgNVBAgMBVRleGFzMQ8wDQYDVQQHDAZBdXN0aW4xGDAWBgNVBAoM -D1N0b3JteUNsb3VkIEluYzEjMCEGA1UEAwwac3Rvcm15Y2xvdWQuZmFtaWx5Lmky -cC5uZXQxJDAiBgkqhkiG9w0BCQEWFWFkbWluQHN0b3JteWNsb3VkLm9yZzBZMBMG -ByqGSM49AgEGCCqGSM49AwEHA0IABFUli0hvJEmowNjJVjbKEIWBJhqe973S4VdL -cJuA5yY3dC4Y998abWEox7/Y1BhnBbpJuiodA341bXKkLMXQy/kwCgYIKoZIzj0E -AwIDSAAwRQIgD12F/TfY3iV1/WDF7BSKgbD5g2MfELUIy1dtUlJQuJUCIQD69mZw -V1Z9j2x0ZsuirS3i6AMfVyTDj0RFS3U1jeHzIQ== ------END CERTIFICATE----- diff --git a/contrib/certificates/family/volatile.crt b/contrib/certificates/family/volatile.crt deleted file mode 100644 index 928c7f39..00000000 --- a/contrib/certificates/family/volatile.crt +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBxDCCAWmgAwIBAgIJAJnJIdKHYwWcMAoGCCqGSM49BAMCMGcxCzAJBgNVBAYT -AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn -aXRzIFB0eSBMdGQxIDAeBgNVBAMMF3ZvbGF0aWxlLmZhbWlseS5pMnAubmV0MB4X -DTE2MDQyNjE1MjAyNloXDTI2MDQyNDE1MjAyNlowZzELMAkGA1UEBhMCQVUxEzAR -BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 -IEx0ZDEgMB4GA1UEAwwXdm9sYXRpbGUuZmFtaWx5LmkycC5uZXQwWTATBgcqhkjO -PQIBBggqhkjOPQMBBwNCAARf6LBfbbfL6HInvC/4wAGaN3rj0eeLE/OdBpA93R3L -s8EUp0YTEJHWPo9APiKMmAwQSsMJfjhNrbp+UWEnnx2LMAoGCCqGSM49BAMCA0kA -MEYCIQDpQu2KPV5G1JOFLoZvdj+rcvEnjxM/FxkaqikwkVx8FAIhANP7DkUal+GT -SuiCtcqM4QyIBsfsCJBWEMzovft164Bo ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/acetone_at_mail.i2p.crt b/contrib/certificates/reseed/acetone_at_mail.i2p.crt deleted file mode 100644 index 13f9f17d..00000000 --- a/contrib/certificates/reseed/acetone_at_mail.i2p.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFfzCCA2egAwIBAgIEctG1gDANBgkqhkiG9w0BAQ0FADBwMQswCQYDVQQGEwJY -WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt -b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEZMBcGA1UEAwwQYWNldG9uZUBtYWls -LmkycDAeFw0yMTAxMjUxMDMyMjBaFw0zMTAxMjMxMDMyMjBaMHAxCzAJBgNVBAYT -AlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxHjAcBgNVBAoMFUkyUCBBbm9u -eW1vdXMgTmV0d29yazEMMAoGA1UECwwDSTJQMRkwFwYDVQQDDBBhY2V0b25lQG1h -aWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwqF/BRRmvZ54 -5XArgxbytDi7m7MDjFE/whUADruHj/9jXGCxE8DDiiKTt3yhfakV0SNo5xk7AMD+ -wqiSNC5JCHTm18gd2M4cQLIaOVRqucLLge4XVgk2WPX6OT98wfxh7mqA3wlSdEpj -dY3Txtkf7VfZLicG76/RBtLFW3aBdsn63hZaQqZE4x/5MJyPVZx59+lys5RmMi0o -LpXJy4HOu1/Gl1iKDJoI/ARFG3y7uP/B+ZtZBitJetTs0HcqycnNJq0tVZf2HiGF -JNy67AL4foxNYPXP6QsvXvp6LRpGANaBCkFCBlriSF+x1zO2H3uAkRnuLYXuKIfB -HudejTp4R57VgZGiHYoawHaF17FVAApue9G8O82XYECjhET35B9yFoOBHTvaMxLU -CKrmayH8KMQon95mfe1qpoO3/YDa8DCxkjAfjdtytat7nt2pGZMH6/cLJxcFiofh -RtRVvb+omv/X12j/6iCFrwP4NvBnAZsa736igbjpyee5n+CSyYxd9cJkRX1vQVk7 -WFSqL58Pz+g6CKJmdMPvqNOfUQ6mieBeejmx35B4pLzLcoNxw8R3O1+I2l4dg042 -dEydKRQNwdzOec4jYwnKR40iwIyZxpchXWGRbBdyF5RQCbIIo60QBJlfXMJ2svan -q5lYIeWeY3mlODXu4KH4K09y10KT8FsCAwEAAaMhMB8wHQYDVR0OBBYEFMh+DoIL -APNiu2o+6I9A49joNYQuMA0GCSqGSIb3DQEBDQUAA4ICAQBFeOJi0rmkqN5/E3IB -nE2x4mUeLI82tUcN2D3Yu8J81vy4DnH+oMRQFDtYEHW5pfirRmgSZ7MQwYQnqWLp -iTE7SyCxlqGrmVsYp7PzfS1pUT2QeWPtsNYUDdraG0Zr9BkIGB60VMhjMSa9WUrj -lbchzr6E/j/EsEOE7IK08JxIDKCDZM2LLwis4tAM6tmiylkMf2RlUBIRBs1TCO+q -x3yByttNE2P4nQyQVQpjc1qsaOMvJvbxun37dwo+oTQy+hwkA86BWTDRYdN3xwOk -OfAOtlX6zM/wCKMN0ZRnjZoh59ZCn4JXokt3IjZ4n8qJOuJFRKeKGmGeKA8uaGW8 -ih5tdB99Gu5Z8LOT1FxAJKwQBn5My0JijPoMit4B0WKNC8hy2zc2YvNfflu1ZRj5 -wF4E5ktbtT/LWFSoRPas/GFS8wSXk/kbSB0ArDcRRszb3JHqbALmSQxngz3rfwb3 -SHwQIIg956gjMDueEX5CrGrMqigiK53b9fqtpghUrHDsqtEXqeImpAY65PX1asqo -metDNuETHF7XrAjP7TGJfnrYQyeK90iS7j1G68ScBGkKY2nsTnFoXkSk5s5D338E -SUzPaOlh91spmkVY6gQTVQ7BakADBHw+zBgDA1gBN/4JPvgN36hquj63+aG1cKy3 -3ZUnv2ipo2fpr69NtuBnutK6gw== ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/admin_at_stormycloud.org.crt b/contrib/certificates/reseed/admin_at_stormycloud.org.crt deleted file mode 100644 index ae44521b..00000000 --- a/contrib/certificates/reseed/admin_at_stormycloud.org.crt +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF1zCCA7+gAwIBAgIRAMDqFR09Xuj8ZUu+oetSvAEwDQYJKoZIhvcNAQELBQAw -dTELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwGA1UE -ChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxHjAcBgNVBAMM -FWFkbWluQHN0b3JteWNsb3VkLm9yZzAeFw0yNDAxMjUxNDE1MzBaFw0zNDAxMjUx -NDE1MzBaMHUxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgx -HjAcBgNVBAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMR4w -HAYDVQQDDBVhZG1pbkBzdG9ybXljbG91ZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUA -A4ICDwAwggIKAoICAQDbGX+GikPzQXr9zvkrhfO9g0l49KHLNQhUKYqd6T+PfnGo -Fm0d3ZZVVQZ045vWgroOXDGGZZWxUIlb2inRaR2DF1TxN3pPYt59RgY9ZQ9+TL7o -isY91krCRygY8EcAmHIjlfZQ9dBVcL7CfyT0MYZA5Efee9+NDHSewTfQP9T2faIE -83Fcyd93a2mIHYjKUbJnojng/wgsy8srbsEuuTok4MIQmDj+B5nz+za2FgI0/ydh -srlMt4aGJF4/DIem9z9d0zBCOkwrmtFIzjNF1mOSA8ES4m5YnKA/y9rZlRidLPGu -prbXhPVnqHeOnHMz2QCw1wbVo504kl0bMqyEz2tVWsO9ep7iZoQs2xkFAEaegYNT -QLUpwVGlyuq3wXXwopFRffOSimGSazICwWI6j+K0pOtgefNJaWrqKYvtkj1SbK2L -LBNUIENz6VnB7KPRckuX6zxC8PpOiBK9BcftfO+xAz/wC6qq3riBPw30KKSym0nC -Zp5KciDn4Phtw9PGq8Bkl8SyWl0jtFnfTB1tzJkisf2qKcNHaFTEe2JW763YLbh/ -AU+8X8evFu40qLgvOgKoyy5DLy6i8zetX+3t9K0Fxt9+Vzzq6lm5V/RS8iIPPn+M -q1/3Z5kD0KQBG9h/Gl8BH+lB71ZxPAOZ3SMu8DJZcxBLVmDWqQPCr5CKnoz0swID -AQABo2IwYDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsG -AQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHgYDVR0OBBcEFWFkbWluQHN0b3JteWNs -b3VkLm9yZzANBgkqhkiG9w0BAQsFAAOCAgEARWOJ69vTHMneSXYscha+4Ytjg0RM -faewJNEGj8qy/Qvh9si2bWYNPRK6BlbHFS7pRYBLAnhaeLBGVv1CCR6GUMMe74zQ -UuMeAoWU6qMDmB3GfYoZJh8sIxpwHqyJeTdeccRbZ4sX4F6u3IHPXYiU/AgbYqH7 -pYXQg2lCjXZYaDFAlEf5SlYUDOhhXe5kR8Edhlrsu32/JzA1DQK0JjxKCBp+DQmA -ltdOpQtAg03fHP4ssdj7VvjIDl28iIlATwBvHrdNm7T0tYWn6TWhvxbRqvfTxfaH -MvxnPdIJwNP4/9TyQkwjwHb1h+ucho3CnxI/AxspdOvT1ElMhP6Ce6rcS9pk11Rl -x0ChsqpWwDg7KYpg0qZFSKCTBp4zBq9xoMJ6BQcgMfyl736WbsCzFTEyfifp8beg -NxUa/Qk7w7cuSPGyMIKNOmOR7FLlFbtocy8sXVsUQdqnp/edelufdNe39U9uNtY6 -yoXI9//Tc6NgOwy2Oyia0slZ5qHRkB7e4USXMRzJ3p4q9eCVKjAJs81Utp7O2U+9 -vhbhwWP8CAnNTT1E5WS6EKtfrdqF7wjkV+noPGLDGmrXi01J1fSMAjMfVO+7/LOL -UN+G4ybKWnEhhOO27yidN8Xx6UrCS23DBlPPQAeA74dTsTExiOxf1o1EXzcQiMyO -LAj3/Ojbi1xkWhI= ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/arnavbhatt288_at_mail.i2p.crt b/contrib/certificates/reseed/arnavbhatt288_at_mail.i2p.crt deleted file mode 100644 index 9068afb9..00000000 --- a/contrib/certificates/reseed/arnavbhatt288_at_mail.i2p.crt +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF2TCCA8GgAwIBAgIQIHQPtSoFU+cUpYD8PZaWZjANBgkqhkiG9w0BAQsFADB2 -MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK -ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEfMB0GA1UEAwwW -YXJuYXZiaGF0dDI4OEBtYWlsLmkycDAeFw0yMzAxMjUxODUzNDFaFw0zMzAxMjUx -ODUzNDFaMHYxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgx -HjAcBgNVBAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMR8w -HQYDVQQDDBZhcm5hdmJoYXR0Mjg4QG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEAtwG73sC0jYd3fgEzZh0SveAdUd5yD35nINJRrdPSrSwY -n3i1qGe3fNLj877PvUDU+qiHH0fFZfyFkXTaq3TUp1u4YkmvaoPHy6FZlojB08lK -FBm+iJ1hifQ7MFmvIKUGv+cjlN6xSoQ0U6B2QOy6iZnBgFZ/7jbRY4iZOIj7VJtY -aodeHfy0bWe447VJovbkUi7NJPFZQS65LMcAIWcWTxrC0Gj8SmdxL3a5+hxpmmg0 -+KCQvWQDdxAQjsc16sgUCdUc6cWYO4yw9H6fgdq9GJX+LnXR9OB58GsAjjlLlFoI -CZxdARDpoqcIj6AoKIanALf8yfbIyrqqJE47cuaqV9bht5MWKnXbwHplEkT4ZNkh -PnRDia7B5HY3uwbt39CBm264PEWXvWG2sozTWKQqBjmMN2cj/NFDUEqKv6BggMY1 -HcqxWFKRcgKCtRvrmTmfp5l0/ou+OtUaFUg0a6Qhtb93Hj10vK6wZzidBqj0ggzB -eJDI95b89u8JgzRoOBriuMKTc91WTkOvBLkB3dgUbUpx2p8KHjvf/pppBH9u0oxp -qJFFK840DbnJydEvjKezeVe5Ax6YRSRxyEdKzRoWdvKVxb3qBBKMdCKTYEPxHPBu -JMEQVUCXJMti++1KEiQGhcfWvLyT7OewbcIZNk9XWNrxlKcGrTp9AOwaaNC5m1kC -AwEAAaNjMGEwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr -BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB8GA1UdDgQYBBZhcm5hdmJoYXR0Mjg4 -QG1haWwuaTJwMA0GCSqGSIb3DQEBCwUAA4ICAQAHiK0ld/1PF9DIhutD660/bzBg -mF2Z76hcBqDZ8tnQai/u/RXYrH9wso9BYyrVsvk3fr6tpGT49Ian0MVpPOxMoTU2 -oBEmQlYrfclQLFsOLmA0y2r1ggXzIrt69jB710Vhwdnz09oOE8rS4E2T5oDD8Wvy -Kony+AarRceqtkOlzyquc42KjzdrbHsosF7G2iGhNI6t+T3BfWJ+Q+d5sj3OIh6e -gSfvHL44E4vZt6dtofRN3MAZ60kNLF5YWyaUo3Snv9Lso1IwIz3AVr5ehv+8sFL/ -KxaXdkZ5Yn2YUX7p1t4VQd+eXVPYjf1befg4PvrwSkylu3Jpee3fllZSKXeSVx9x -jpJiq5vIakqk22pnWb1Vn7xzSW1vtEG7QLjobOr1WrcGiwdv+HKiWcXJXDzKoWXs -h3VEfr51Kap8cIJv+D6lJIG9IcIhiQ6CXWBmtjWJvbdVwFBy1/3Fhaou9liHi+gK -4Yh5a5OGCzc7xjtpGaTmoLEz7NzDNOdd/r840qRDOh70izzmFZd5Gwq4hoVcPJcS -EAySwtgqK0/4d0zDd2Wg9ASJV9DnDf8QuSmHZgZ9Efs47XcWz9TvkWUS1E66AJsN -mmI1NDQ3mv3dv5+WPq+dqqYFsnx3xWL1g5Z3buk0opeuXMzoHwM7UfN8h7Q1M5+t -+XBgkaYA4iEwYKqlCQ== ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/backup_at_mail.i2p.crt b/contrib/certificates/reseed/backup_at_mail.i2p.crt new file mode 100644 index 00000000..73b08eaa --- /dev/null +++ b/contrib/certificates/reseed/backup_at_mail.i2p.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFfTCCA2WgAwIBAgIEOprmhjANBgkqhkiG9w0BAQ0FADBvMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEYMBYGA1UEAwwPYmFja3VwQG1haWwu +aTJwMB4XDTEzMTAxMzEzNDQ1NVoXDTIzMTAxMzEzNDQ1NVowbzELMAkGA1UEBhMC +WFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFub255 +bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGDAWBgNVBAMMD2JhY2t1cEBtYWls +LmkycDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIoAkobXwk/Enf1d +roHyqCyvcJfZJVTwb/LgYWAvCBMCr+RGqlSgtk3g69Y3I0xU08fD2kGt3r5Pwsbr +omXIbJAcccyLqmQ5QX6QgL+X9VpMDp9C4h2RogCrqLBAWw4cuZ4RS9VCpP1Yis7H +uejYqENP86p7BsRnuW/4cYnfunAdMpss4LpRGQXt1nTX+kfgCYgnKFbFqwAHt7yV +Ds+Pe6FuBHPlp+sc1amKRcUnSvhXLsv43VicnT7xYL/kUsN83wrtHA3B4aGDx3aA +3/EzuRmIXQB0BlTZILMEyYwG/nc4OsW82QYrvEZ9BIg9A4lF/wS/KZCICPxLF2zo +dGjnmlgkiA4s8eO+va/ElHyELjckVXqmG1eXHhSkEsDvOQJy01IUuwLinvq7cUbJ +HfJBZJllEg+sLDCv3FkEqN+XjBNFfQN4oNew4w6IPY6YH1INVB9LL0Cmdu4DudLv +TY8OcI8eSfez3hmm+pYQ23PJRYYnvRDnRECyIWBegkckWRh8U/WvZUYUvETK6EDl +/0KpTtfzX6MqHA5D6bTAB8Y3ijGMLrZ/B5vj5yCoZbLiGme9X2moR2k1LEhdhtzV +exsqezCpg6dn48FTX7mHjvR5/r4kz2jqBGmdPUWIIxnjFUzDUK3llVQiHihleHpe +jL4LqnhBGKWFRTaVwaIkBG4zAfIzAgMBAAGjITAfMB0GA1UdDgQWBBQNkfW7bSMl +1/4KDbgwrkf9x1Zu/TANBgkqhkiG9w0BAQ0FAAOCAgEAGg3a3rTf0EznQocmio0T +5gCoL0n8h6yKW/PyPAIELrd9wiYjhJFcWvMTcJJJnVqmAL5vpvhaAFVtAfx70MGa +0DZ7FvytK5hEfF4IqOFDyEEVGJR5rIpVK4MeI1nmwEsxdbW+FhODjtRzgYO8XBME +Xj4aY1FWg9vxc3reUj6PSFsZtsB0aLiRgL9JDovJIiRw0Uqr1v2wXBte5yVCxDge +vTREZtpK4cKetoOa68pwSXI32JwKE18j6bfdKVBCcYQKlKP/3gHGduaDrQv3w32S +DRym5s6MREeTUOtAw4wq46KpdOX8yyAqJPrCfMwS6ORd3t+egqOw0PUnsqb97w4O +lUtrRYvb2cOj60SmRx4vJvItyuHbKqIK7o2e1RcUZPXYoAVx2ww4XB2Wk4D7LSAs +cS7nLj8yAqzJ2qqtBzxu+zILJtkVa12dKF0xmS0BxBp4sCYiBtmAVE8AWQqEuSHA +FrMWqoXcjcfdvvyX487FFWWUE7ZBIn0hee2sK9J9+SPtqczJaN7TF3K3nzo65WJG +1epltmq2Ugjb67Gz7v4y7H23DJ/qhm8yLtCHTj69HTta5I08j6Kut924WLZaiMO/ +4YoEL5AE63X0sxYibKFQiq7FW5nUJA280GRlY3xSMFzlB2ggazrUV3YAWVDhfdnI +flpzWXkFM2D36OUaubfe9YY= +-----END CERTIFICATE----- diff --git a/contrib/certificates/reseed/bugme_at_mail.i2p.crt b/contrib/certificates/reseed/bugme_at_mail.i2p.crt new file mode 100644 index 00000000..2b6acac0 --- /dev/null +++ b/contrib/certificates/reseed/bugme_at_mail.i2p.crt @@ -0,0 +1,32 @@ +-----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----- diff --git a/contrib/certificates/reseed/creativecowpat_at_mail.i2p.crt b/contrib/certificates/reseed/creativecowpat_at_mail.i2p.crt deleted file mode 100644 index 07fe75aa..00000000 --- a/contrib/certificates/reseed/creativecowpat_at_mail.i2p.crt +++ /dev/null @@ -1,35 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGAzCCA+ugAwIBAgIRAJNGLpTSm2U3GjXmFkjT/0cwDQYJKoZIhvcNAQELBQAw -dzELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwGA1UE -ChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxIDAeBgNVBAMM -F2NyZWF0aXZlY293cGF0QG1haWwuaTJwMB4XDTE3MDUyNjE5NDQzOVoXDTI3MDUy -NjE5NDQzOVowdzELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJY -WDEeMBwGA1UEChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAx -IDAeBgNVBAMMF2NyZWF0aXZlY293cGF0QG1haWwuaTJwMIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAo3XP4JToVbfM5e4GxyAqzu2DJV7ohpzlLqMLyz/9 -XgZ7ipctNoxVZytoaNgMeAHInJn5OhUC4D+emsgsLJqFjnb2pxf6v45sRZLBMieb -wJlxUmskucpTXwDwuHBk/s3xmH4IluadmzwiCMyycQFH/CNXmu5bonAuZ075rT1Q -a8W0vb8eSfNYXn+FKQBROqsL5Ep+iJM6FX+oWMxJPk/zNluIu9qTdZL7Fts2+ObP -X5WLE4Dtot57vMI2Tg3fjnpgvk3ynQjacS8+CBEbvA/j32PBS1mQB+ebl56CQTBv -glHrXiNdp24TAwy8mwWHknhpt4cvRXOJGZphSVNRYFVk0vv7CTjmQg6WOBGD+d/P -cosvyKxQz4WUSmtaKUftgCBdnemeM7BppZv2URflEOY6Uv3f9xlEC6yVEzSaa2Md -tG6XRkDyupWCBFwmSm1uS+SXXhxAQGn3eMXPFA1XkwNnZtmM9kvSVt34FBE231oN -4oM7rE3ZDyTocZw7cv7bl8idmqsLXDTSFn5Q2iLwvw6ZeTenk8qHrq9kVH1UVE2l -31iKDNdGQkkVcnTWYfiqriwGLpTqbeD/8n9OBgCke1TiKQzP1o66nhkGJTiiRLFK -A8rlSpqBcjGbXDs/X+Ote9MrCxE089eCqN51kzDeQ4Yvy8gDOTBPGEhBLirx+3pp -yWkCAwEAAaOBiTCBhjAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUH -AwIGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wIAYDVR0OBBkEF2NyZWF0aXZl -Y293cGF0QG1haWwuaTJwMCIGA1UdIwQbMBmAF2NyZWF0aXZlY293cGF0QG1haWwu -aTJwMA0GCSqGSIb3DQEBCwUAA4ICAQCYzeYyPYhW+/SZSfpDl6JTzXy8S6NG+yjq -pcinxaIF4XFoXLwWD3uHR4jgpU750mhHJjpGIaltZjFaqLbqtysbqb0vdShyaK/n -Td4CXrNBvEHvLI6DZyDX4BcDlhCI7/dMCSHXwFIhRHhYSnTsJO32BdP5DsUUAlSW -G0FlEEWjlxcdRwIITv70cFNlNOqJeyvtk9DPT+nEzssKWxVZcqN4GK8dvQVWgL91 -8uzrcAYpAEQfmkKzsGmV4v5gWumLZmnzc24hUhVsHhIph4HAmjPMFCppI1tgiwg7 -fH71MYB8b9KBJKipkLdAL292mDLS4G3MGQwMbcjnTyIqOktmyyj/1CorZAKqBtzu -Qyo7z8FM2pd5nzk7QDx/vsJ4bNAYvVu7titDW5mv5JDoQcp2uDVGePlonX3I8iFx -CqKFzGHiR0EU8oWw0Pqf+y2rEV4L74agmUR7VbA+/ovz0UnDUoXIynSwpK7Kfo8D -B7ky9RnmsxJX6TXaMVW06IlYuwIUsAWbMhKvdXbGZur5VVi1ZY1/HgZZnoXejzCe -w3mMl6movkcA0noDXQ+eauUDHjktrVUJdZKYvZNjfnz2rB+MI5wB/hzeBv4KuYFE -oTFt8SwTzs0joM4c7RomTxc+QFe832SvjPAnxQn17qSjD8z4c7Ako6sCKvpdBSDm -Hz8KWVkHZg== ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/echelon3_at_mail.i2p.crt b/contrib/certificates/reseed/echelon3_at_mail.i2p.crt deleted file mode 100644 index 7560a01e..00000000 --- a/contrib/certificates/reseed/echelon3_at_mail.i2p.crt +++ /dev/null @@ -1,33 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFyzCCA7OgAwIBAgIRALWNWsnQ0Vmn/99iCNT7cdQwDQYJKoZIhvcNAQELBQAw -cTELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwGA1UE -ChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGjAYBgNVBAMM -EWVjaGVsb24zQG1haWwuaTJwMB4XDTIxMTEyOTE5MzU1OVoXDTMxMTEyOTE5MzU1 -OVowcTELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwG -A1UEChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGjAYBgNV -BAMMEWVjaGVsb24zQG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC -CgKCAgEA3pccNiQWJUS1t3QHK7rBCNKAsM2dz4szN3+3SrDy1w+rOrK8Vt5aypPU -QYUQwG+odjEPacuoRtO/W14KJl5yAI3eQS+X/cYDXmxvfm4zx5JRumYptXwJD57G -rlPHnFvk8R+Hvh+/UyqgSAZ9ZaKjEzYK4AtbYEXtopaM4U2VYN8xKjvKyWlhPdxo -kI3//qcTlSqGHHeHrkItLG1LubM1EnPu+9zI2WN2zBBRcm8ZtWqHoqFJ1zgJr/49 -nMK8Lnb3I54ctva8x5+gsSk4dbG/mMsOIZekFqYJJs3+u9w5fmOYI7v9GlQr7UhE -G3MwjJ5Cj1LmLVlz/4LApZrDSd2JvwIUdGL3UW8+blaTeCPKIRvmsTeRxo1gORMF -ZH0dg39722lK7ScwOlOUX9ggzRUlYCmvnjQJZGJEUoP68QxjlQfkXZyffmMfvm6K -V6mcZ5aHMGO1lYAl40kWNJ0jGpmxJqTDhNFDEKr0TlRGVxXGWzObEOrcJ8ysRMc1 -x6oXQhh79HXZcKwhZaXLx23ZvVoTfhRm4JH0SSP6XqQm35j4NI1SllEsDns29wU3 -Re4wOWJCCYlPG3CtY32CinwQRoVgtiJk18W8+Pxw7sBFq8sL5L0Z+5bB6nTkBfV6 -7OrZGWL0i344zQE0e3yIsLih+5Wyqw6RSSMysenl3alnUB9EvE0CAwEAAaNeMFww -DgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAP -BgNVHRMBAf8EBTADAQH/MBoGA1UdDgQTBBFlY2hlbG9uM0BtYWlsLmkycDANBgkq -hkiG9w0BAQsFAAOCAgEAaUMnMYtNFBl9dFON6e4EjYo53Wknj61uIVO11dvLqjnh -7X6guPML+GgNZsPQGLu7Bqw4hVgy/cV5AlFc7SXOhzpaYo1ycpjg3Ws1VK2wrk7+ -4bvUThNcS1KZVFDdRE62549rYNfYNfPxXvccOTW9meTCC1kLHerh65ySDr9J02O6 -o5Mf685PgBasBH6dlosOLTtee2gRLNFcAluQYKerawS1gDys5239UNHPCqTgO+Od -FiKfl48OIOzPGLKEf4lXC+lkwZElewShrHhzd8aGueedTi0UHOtQuY7ocsofqXc8 -OnyT/y2X6wn/YkzviKgfxYDSI7FJiUgXCPcT0jUNmuwR168yL5BfzoQmrCvlOOQg -P7ibdBJ6UkL8pRpv/SYpvaX/kf4agYtwh5IL9FzNCwNu54ZC6JilLUhYAU38Eolq -OZ/cGiMoSFQIeBPvB3cdsqEud9W4P+MqN5A76fMzdVV77lGsIS1eCGMceR3CjOiF -6SdAskcBZWhFiRNQweC0iv57/nPCeTCuNAqbZSHd7zC1AKhNmmsKSJUJQCGijcce -P8Gl0AFfZneN2bVEFvJ/zd71pD8ll1Gkju16bfdWn0V4NRaxFiXNr2bL+ah9blud -EXOomE3R6ow1QZk+Gnpy3wh9jfwlrJuFoANvHnv4WREbdjwr//71XjBri5p1wPE= ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/echelon_at_mail.i2p.crt b/contrib/certificates/reseed/echelon_at_mail.i2p.crt new file mode 100644 index 00000000..ad10e3ac --- /dev/null +++ b/contrib/certificates/reseed/echelon_at_mail.i2p.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIESg3kkzANBgkqhkiG9w0BAQ0FADBwMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEZMBcGA1UEAwwQZWNoZWxvbkBtYWls +LmkycDAeFw0xNDA3MzExNjQ3MDJaFw0yNDA3MzAxNjQ3MDJaMHAxCzAJBgNVBAYT +AlhYMQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9u +eW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRkwFwYDVQQDDBBlY2hlbG9uQG1h +aWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmcEgLwwhzLNe +XLOMSrhwB8hWpOhfjo4s6S/wjBtjjUc8nI3D0hSn3HY26p0rvcvNEWexPUpPULmC +exGkU463nu7PiFONiORI1eJAiUFHibRiaA7Wboyo38pO73KirwjG07Y+Ua0jp+HS ++4FQ/I/9H/bPplReTOU/6hmRbgQ69U8nE68HzZHQxP68yVJ2rPHSXMPhF4R1h0G1 +1mCAT+TgTsnwHNGF77XHJnY4/M4e2cgycEZjZow36C3t2mNDVkMgF19QQeb9WmLR +zREn3nq9BJqHpUkn9yWw0kKXTZSds+7UxESfzf3BzK0+hky2fh5H+qbYAo2lz4yj +81MXTAu+4RRkg4DBLlF+2dkclhwQLxxzvkRC6tPkn5i33Yltg7EfzA9IoQ05potJ +I+iOcF+aStfFgFj9u3B5UkcF4P0cH1QD3c6BK4hIezQYqRoPly1gHqg+XdwjG/dr +4as7HA9FTz3p2E8nClpIC1x3hfgwAdfd29aeBxO1WW/z99iMF7TBAF+u5T86XEW1 +WpknqCbTli36yJ8a5fPWxZHrryBRJT5yLxejjFeadtutBSwljiVFq+Y38VqwFivq +VLiBt7IxAsZ8iilgfnnnAvBH6chWfSKb4H7kB4TJvDiV96QmmvoEaWYNHZozMhyK +tO3b5w+xqbJXyCLA3Q75jD0km76hjcECAwEAAaMhMB8wHQYDVR0OBBYEFAHQcAam +QRS/EUhuCSr9pB4Ux0rYMA0GCSqGSIb3DQEBDQUAA4ICAQBq1+1QLmgLAjrTg3tb +4XKgAVICQRoBDNUEobQg3pYeUX9eFNya2RxNljuvYpwT80ilGMPOXcjddmr5ngiK +dbGRcuuJk9MPEHtPaPT3+JJlvKQ3B3g2wva2Wz2OAyLZUGQs389K4nTbwh4QF0n2 +aHFL8BHiD62hiKnCoNaW4ZovUNNvOxo9lMyAiaFU2gqQNcdad8hP9EAllbvbxDx9 +Tjww2UbwQUIHS9rna4Tlu+f0hDXTWIutc2A51W2fJCb7L3+lYO7Wv55ND/WtryLZ +XpMp27+MpuEnN3kQmz/l9R0hIJsWc/x9GQkjm5wEaIZEyTtenqwRKGmVCtAj0Pgv +jn1L3/lWmrNq+OZHb/QeyfKtA3nXfQKVmT98ewQiK/S5i1xIAXCJPytOD887b/o1 +cdurTmCiZMwgiQ+HLJqCg3MDa5mvKqRkRdZXfE6aQWEcSbpAhpV15R17q7L+Fg0W +shLSNucxyGNU8PjiC/nOmqfqUiPiMltJjPmscxBLim8foyxjakC4+6N6m+Jzgznj +PocBehFAfKYj66XEwzIBN7Z2uuXoYH9YptkocFjTzvchcryVulDWZ4FWxreUMhpM +4oyjjhSB4tB9clXlwMqg577q3D6Ms0zLTqsztyPN3zr6jGev3jpVq7Q1GOlciHPv +JNJOWTH/Vas1W6XlwGcOOAARTQ== +-----END CERTIFICATE----- diff --git a/contrib/certificates/reseed/hankhill19580_at_gmail.com.crt b/contrib/certificates/reseed/hankhill19580_at_gmail.com.crt deleted file mode 100644 index 2f177ee1..00000000 --- a/contrib/certificates/reseed/hankhill19580_at_gmail.com.crt +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF3TCCA8WgAwIBAgIRAKye34BRrKyQN6kMVPHddykwDQYJKoZIhvcNAQELBQAw -dzELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwGA1UE -ChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxIDAeBgNVBAMM -F2hhbmtoaWxsMTk1ODBAZ21haWwuY29tMB4XDTIwMDUwNzA1MDkxMFoXDTMwMDUw -NzA1MDkxMFowdzELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJY -WDEeMBwGA1UEChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAx -IDAeBgNVBAMMF2hhbmtoaWxsMTk1ODBAZ21haWwuY29tMIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEA5Vt7c0SeUdVkcXXEYe3M9LmCTUyiCv/PHF2Puys6 -8luLH8lO0U/pQ4j703kFKK7s4rV65jVpGNncjHWbfSCNevvs6VcbAFoo7oJX7Yjt -5+Z4oU1g7JG86feTwU6pzfFjAs0RO2lNq2L8AyLYKWOnPsVrmuGYl2c6N5WDzTxA -Et66IudfGsppTv7oZkgX6VNUMioV8tCjBTLaPCkSfyYKBX7r6ByHY86PflhFgYES -zIB92Ma75YFtCB0ktCM+o6d7wmnt10Iy4I6craZ+z7szCDRF73jhf3Vk7vGzb2cN -aCfr2riwlRJBaKrLJP5m0dGf5RdhviMgxc6JAgkN7Ius5lkxO/p3OSy5co0DrMJ7 -lvwdZ2hu0dnO75unTt6ImR4RQ90Sqj7MUdorKR/8FcYEo+twBV8cV3s9kjuO5jxV -g976Q+GD3zDoixiege3W5UT4ff/Anm4mJpE5PKbNuO+KUjk6WA4B1PeudkEcxkO4 -tQYy0aBzfjeyENee9otd4TgN1epY4wlHIORCa3HUFmFZd9VZMQcxwv7c47wl2kc9 -Cv1L6Nae78wRzRu2CHD8zWhq+tv5q7Md2eRd3mFPI09ljsOgG2TQv6300WvHvI5M -enNdjYjLqOTRCzUJ2Jst4BZsvDxjWYkHsSZc1UORzm2LQmh2bJvbhC3m81qANGw6 -ZhcCAwEAAaNkMGIwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMC -BggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCAGA1UdDgQZBBdoYW5raGlsbDE5 -NTgwQGdtYWlsLmNvbTANBgkqhkiG9w0BAQsFAAOCAgEAVtMF7lrgkDLTNXlavI7h -HJqFxFHjmxPk3iu2Qrgwk302Gowqg5NjVVamT20cXeuJaUa6maTTHzDyyCai3+3e -roaosGxZQRpRf5/RBz2yhdEPLZBV9IqxGgIxvCWNqNIYB1SNk00rwC4q5heW1me0 -EsOK4Mw5IbS2jUjbi9E5th781QDj91elwltghxwtDvpE2vzAJwmxwwBhjySGsKfq -w8SBZOxN+Ih5/IIpDnYGNoN1LSkJnBVGSkjY6OpstuJRIPYWl5zX5tJtYdaxiD+8 -qNbFHBIZ5WrktMopJ3QJJxHdERyK6BFYYSzX/a1gO7woOFCkx8qMCsVzfcE/z1pp -JxJvshT32hnrKZ6MbZMd9JpTFclQ62RV5tNs3FPP3sbDsFtKBUtj87SW7XsimHbZ -OrWlPacSnQDbOoV5TfDDCqWi4PW2EqzDsDcg+Lc8EnBRIquWcAox2+4zmcQI29wO -C1TUpMT5o/wGyL/i9pf6GuTbH0D+aYukULropgSrK57EALbuvqnN3vh5l2QlX/rM -+7lCKsGCNLiJFXb0m6l/B9CC1947XVEbpMEAC/80Shwxl/UB+mKFpJxcNLFtPXzv -FYv2ixarBPbJx/FclOO8G91QC4ZhAKbsVZn5HPMSgtZe+xWM1r0/UJVChsMTafpd -CCOJyu3XtyzFf+tAeixOnuQ= ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt b/contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt deleted file mode 100644 index 3d1c4526..00000000 --- a/contrib/certificates/reseed/i2p-reseed_at_mk16.de.crt +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFzTCCA7WgAwIBAgIQeUqFi0fHNQopg6BZlBLhVzANBgkqhkiG9w0BAQsFADBy -MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK -ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEbMBkGA1UEAwwS -aTJwLXJlc2VlZEBtazE2LmRlMB4XDTIyMDIwNTE3MzkzM1oXDTMyMDIwNTE3Mzkz -M1owcjELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwG -A1UEChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGzAZBgNV -BAMMEmkycC1yZXNlZWRAbWsxNi5kZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC -AgoCggIBAMYxs2D2xpN/8blGawvAlU9DemHIxApOEwaLNfh8aAvqEdB41NTqcx4U -H8VchSormCfkCvezuMHO+K2HX7ihEZ1v6tbr6aX6hY9UZUyDDYsKmJoB1oKEhddv -5UYfcWPE2eSykdFsWgTQD6Z+cRQWHEoCzb7qc+Jrw6KcnHMD0VrmBrEQPzTBxMHW -4HC97PVkSLJTDArnS6ZiX4IbWRPw/mbpJT6EoVZo8J/it0pdn/X4KodEXDcnEMSe -VRulfZH/nSmOOvKhoHPckmgz/u66BlnuSYXEIB0KfDIcAlSYiPDxGnAemTozJYXA -UVMeFMs+YE5wiPgzzu+vpC31xtZLq0gyaCfgEi1P9j2ES/8pH3Gw6W2OH4kBx+jO -TBsfI+ph6qFZ3WWT23MRVyl3ATuI/GHdczTxD9JaOn74lLI+Hnu8wXnyztVWkTMB -4sAnzjdeHkvNDyQ10vSaN0HnGfg6zuAuUSqFQujFF8Vg8ZCcsh8GouWfzYDvi9mj -9pfxx8v6UCC719I4J9CgFjWnn2Hqez3fO8fFulY61VPyCCZp4gKWbI2SIQP/n5gz -ecYJRrJoem+rYfEQ/fwxROsvm3fCO4D6dt7ILRuX286GDIw2qSvP1zZVAioMwSj3 -9CAjKLwD/BhTRiMOlpaVv6IWqjtevbiaIKvbHTnoxvkGsDqe3gJhAgMBAAGjXzBd -MA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEw -DwYDVR0TAQH/BAUwAwEB/zAbBgNVHQ4EFAQSaTJwLXJlc2VlZEBtazE2LmRlMA0G -CSqGSIb3DQEBCwUAA4ICAQAb+x6XpJdjpVYw2bvWIUbatQJwq0YaEW5W61xGLgIG -a37oll3YZbSY9Vk+N1cE0f61L3ya4Ioz6zlH/MO2zUG/dEk8vqdgIPUYJvyF7wwF -w3/G4VMaDKOJx4bAZNmaiRFGYNhCOhCnZx6uZGrLNIJ2Dc+mflrGmGwYphtXVV3e -Iv+ki3gSRgfXuMfKi4B5bLPnz7XDe4TSmwZZSRac4ly4KqmZUyntqbilRxaGTej3 -VYJ1tac8yppyk5N3VopMQNmBarNZG16wSOTD7CtKgn382jgRW8cR7BMeqhORivp0 -ZnPJFhzh4uthdlPdXXo6lxfvZjfiwlDPytvEu2QBz3urTgopGqRLcTBnLucWg9li -OSy9z7hNEnIN3iIJJAwI1wBdDa7K0h3PFBbIUa7X2ybn81VeNSfO25Lo8YTZEKsc -wcThJrNV6qOQv8rM/7aXugi6+VzPlCR+18iKRbebCnlqGR2dT1zFtj3negtOkrjo -LH4H6VUr3q2Ie56IubS2hUKiUkDm0ckP3Vum35GGntyEAzl6uyog0hJFOJb3aq30 -YQLzyVEOz8NnA+32oMRzJJdDxQ7pqG5fgq7EF4d++YSgEfdVXxvfgXQ6m3jAyC7Z -p/gX4rlxNsjeGU3Ds51wkmhH4IB1aSQr52PE6RaBhhh3SmADEv6S/3eGvE4F4MN5 -2Q== ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/igor_at_novg.net.crt b/contrib/certificates/reseed/igor_at_novg.net.crt deleted file mode 100644 index c6a05e8b..00000000 --- a/contrib/certificates/reseed/igor_at_novg.net.crt +++ /dev/null @@ -1,33 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFvjCCA6agAwIBAgIQBnsUOmOu2oZZIwHBmQc1BDANBgkqhkiG9w0BAQsFADBt -MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK -ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEWMBQGA1UEAwwN -aWdvckBub3ZnLm5ldDAeFw0yMzAxMjgxNDM4MzFaFw0zMzAxMjgxNDM4MzFaMG0x -CzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgxHjAcBgNVBAoT -FUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRYwFAYDVQQDDA1p -Z29yQG5vdmcubmV0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvLkf -bM3uiYfp9m0vgdoftyXtk2/9bHf3u5iaM0WfoJIsw1iizo/mxJl+Iy7SxLC16nV0 -v5FpncVv+Z8x9dgoAYVuLq9zKfsAbpj6kuxAqw6vJMlD1TiIL3nSODV9BJLk47X5 -tmvoOSj9BgvemYThTE3nj+DbuJRW5q90KyBV/LdLrQJX3k5R3FFL5tTad2LKFNZ4 -vEOcYwwx6mvrkJ2lly6bAQUCtfc648Jyq+NO3Rba1fmn7gcP9zXXc5KYsj/ovyY2 -OaocSF5wMhzBuPxO+M2HqbYLMAkc6/GesGds8Rm8wofuhJoI5YtqJuLKZm6nQXSc -fx6PKgbKcTIUWNFMsxyfghz9hpbg0rkvC7PtfAjtV0yaDtUum1eZeNEx1HbRWN2n -TQNCVuv0yaKC41qxqzhEybkdjL9JlgUh7VuskaCelB0lz+kgYjGu8ezOa0ua2iKq -4FC/1MbPulxN8NOt4pmbGqqoxmCdShp38wdnOBM3DsAS9f0JaQZd4CDyY4DCSfVn -xPdWk31+VXVt3Ixh1EUqZWYTRSsZApkCyYzkiZ/qPGG6FR9Hq2SuhC5o4P44k7eo -6wwBWD8a5RjsZhvr05E5yBrKXh/PjLwmtG73QC+ouR54/5xtedvdTwNS94FnNctX -FT6QGZnRwCkhPaRe1oQMzP+88pGoCfO33GBAuwUCAwEAAaNaMFgwDgYDVR0PAQH/ -BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8E -BTADAQH/MBYGA1UdDgQPBA1pZ29yQG5vdmcubmV0MA0GCSqGSIb3DQEBCwUAA4IC -AQCteAb5/bqhHr/i5CJbDzlofprXFC826c19GxQ/9Hw0kA52l0J9Q8Vz8Vy7VQyP -QNa8MCv6FeNy8a/wXp6cafyFsBtvehVQO8lFlpCgMEl2Bma43+GaCwkrM6bFNXeW -iQ9h4e1KjsUZ8cQDNEcamiJ80+xbMhBrj5bAZwKmZs8MoGEMyXKEZmcmwA+/fy1c -cx4izsOsmRXmEHXsvB9ydJHZZeKW8+r0DAtgPslwXuXHG6MuBQo7dKCqn+iMxHXV -Jxriq3yvNffdGx4maSLJrjQ1ealt/UMzql7huVSItnVFWoYf7GAELXNJ/PmqVyaK -q11LQ8W/Aud6s/bblaJrFJnK8PbPpaw4RvHoWVLYaZYmQnV2msWs5EuESBlEADbv -UklQXLMc2f9HKWPA5678nvYPrmu8IL5pMkAxgGRqmd+7vCz4lU9M5z3HObU+WRBt -qEMYyXywV8o3tbmnlDS5S5Xxf+tLZn1cxz3ZrmcHPHDbLBNdvszF3CTJH/R2sQvD -bizvYJM+p5F+GWM5mt6w0HrOut5MRlpOws/NRrkbijuVA/A45nzTtKplIFYE3qe8 -q5SAbwYLc8cJcZCN3PxtWwbEv81V33abMt5QcjnWGLH5t2+1Z2KLCgKLSCQTxM8s -zBPHtUe8qtSQaElnNLILYbtJ1w67dPnGYTphHihC+CXjBg== ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/lazygravy_at_mail.i2p.crt b/contrib/certificates/reseed/lazygravy_at_mail.i2p.crt deleted file mode 100644 index 1ff5346c..00000000 --- a/contrib/certificates/reseed/lazygravy_at_mail.i2p.crt +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFzTCCA7WgAwIBAgIQCnVoosrOolXsY+bR5kByeTANBgkqhkiG9w0BAQsFADBy -MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK -ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEbMBkGA1UEAwwS -bGF6eWdyYXZ5QG1haWwuaTJwMB4XDTE2MTIyNzE1NDEzNloXDTI2MTIyNzE1NDEz -NlowcjELMAkGA1UEBhMCWFgxCzAJBgNVBAcTAlhYMQswCQYDVQQJEwJYWDEeMBwG -A1UEChMVSTJQIEFub255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGzAZBgNV -BAMMEmxhenlncmF2eUBtYWlsLmkycDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC -AgoCggIBAN3q+0nUzz9+CBSoXUNf8K6kIc9zF+OP1NVBmOu3zTtkcEnhTtoDNXeU -EV8DhlBhEACbPomA+szQ5zp3O3OYQc2NV50S7KKqlfn5LBBE3BL2grTeBxUMysDd -0TlpxcHKwaog4TZtkHxeNO94F1vgeOkOnlpCQ6H3cMkPEGG3zu1A1ccgPiYO838/ -HNMkSF//VZJLOfPe1vmn9xTB7wZ0DLpEh12QZGg3irA+QDX5zy6Ffl+/Lp+L4tXT -uPZUaC6CL6EABX4DvQcFrOtiWfkbi/ROgYCeTrYw1XbDHfPc+MBxGo1bX7JjnD0o -mFFvo+PjxvWDmCad2TaITh6DwGEeWKu8NtJAyaO5p1ntauuWGB5Xzua4aMmIy7GT -esHQkhW+5IooM0R5bZI8/KXo4Bj52bX5qv+oBiExc6PUUTLWyjoWHb7fKdddwGfc -lUfniV/fw7/9ysIkQZcXLDCXR6O/nH9aGDZ7bxHedw4/LxAXYPfNojb5j7ZVa65o -PWD5xuQfbE+95DdbnKjcjYiam4kjApe7YPwOhtoRJYSGAkrpIMfzFxCXgjTsi3Kw -Ov+sYmBvWBK4ROWQZTgHei3x4FpAGWHCAeTeeQGKmWQ8tT7ZklWD9fBm3J/KXo7I -WCxRW9oedItyqbRuAGxqaoaGSk6TtPVjyPIUExDp1dr4p1nM1TOLAgMBAAGjXzBd -MA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEw -DwYDVR0TAQH/BAUwAwEB/zAbBgNVHQ4EFAQSbGF6eWdyYXZ5QG1haWwuaTJwMA0G -CSqGSIb3DQEBCwUAA4ICAQA2fei/JajeQ7Rn0Hu3IhgF9FDXyxDfcS9Kp+gHE56A -50VOtOcvAQabi/+lt5DqkiBwanj0Ti/ydFRyEmPo45+fUfFuCgXcofro8PGGqFEz -rZGtknH/0hiGfhLR9yQXY8xFS4yvLZvuIcTHa9QPJg3tB9KeYQzF91NQVb5XAyE7 -O3RvollADTV31Xbhxjb7lgra6ff9dZQJE6xtlSk/mnhILjlW80+iPKuj3exBgbJv -ktiR4ZT4xjh1ZgNJX5br86MZrhyyyGWwHWHS0e443eSrrmAPD69zxsfvhoikRX1z -tDz0zB70DwS4pSbVrFuWaIAcbg36vWO8tYPBzV8iBB/tBTURGJjv6Q0EoI5GHmJi -LOhU3B6xublv8Tcoc3tgMqI9STnWROtTiCS6LsWNSXhVpIZqvaiOEtPN4HyL33sf -j5rfPq76gKrTloeLnwLGq0Rs94ScffYkBap3fQ/ALb87LQcwSN4EkObur5pcd7TS -qNdanvCGK8v1UYVzH4l9jekPGsM5euohwAkIl1kZ6+tqGY/MTa7HwTTQyLDTco1t -sPy6neN46+H5DYHADyU5H2G39Kk3WcLmPtfxlPDM6e73+47fJkXnmiaWM0Lrt80y -Enng6bFGMZH01ZsqBk09H+Uswv8h7k69q9uWAS95KE0omCMVtIpoPZXTnRhe6mBC -+g== ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/matt_at_drollette.com.crt b/contrib/certificates/reseed/matt_at_drollette.com.crt new file mode 100644 index 00000000..e490a152 --- /dev/null +++ b/contrib/certificates/reseed/matt_at_drollette.com.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIEB52rdjANBgkqhkiG9w0BAQ0FADByMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEbMBkGA1UEAwwSbWF0dEBkcm9sbGV0 +dGUuY29tMB4XDTE0MDcyMTEzMjYxM1oXDTI0MDcyMDEzMjYxM1owcjELMAkGA1UE +BhMCWFgxCzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFu +b255bW91cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxGzAZBgNVBAMMEm1hdHRAZHJv +bGxldHRlLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL5M9wKT +csNLg4EA3fW7LleTQdrm3stPnoUvFmsNZHGgsKt1Nc1qCNis3kr2QEY+4Z398U7r +7xGEQFa7D/9SPHf6n1uVXc9DIcmwBtEB0FPB1XPFp2h00ZXIv24yiLN3GQT1woAM +yEbBWsUgn8K/iMBeA5dU2vPwAbGO/0ibD62frgGdYqU2EeiJ/U6vBmKxvC+q2noL +gnyfQJEJANXgf+Cw/gBaS6yn5ZsYcenLNenID2TQKQ6Q/NxYrDYRdWdId29iwldt +dmNSmASv8C7g9d/isZkpmtYNkE4J4m0W9wKziOoyvLSMo8ec67QmCKaPaYKTHTjx +aUuja02+mnlV4DSdZo6nPkSdokRY0+5e6q7+dIPefu8ealGEAE5oedEfl5iM5Fnz +phTR+ePodBK3sB+bMi1NMppbWugpFpdqs1hg2KNKSSG8C4/eTqf2nnlDiVvvFANc +imt6tk0pZcKqveRiDSgI8mTzTcrNgVClsCLoInY5Vab7onZjY9bGijPQ2i1P6+qu +5G6LiLFW7xFq2BcX1DnTztcJ8Yu9NYHhR21J6u7Dr8YHntes3mnth1F0BX3FVA1s +9SaE9/pNhdqap9owpEhNoE1Ke3LorVLL8jyQsqgRHx8VdhWdi9Ao0mzzeI9HYX0j +nZ7uXK5DqGG74K6eWoS9jZSDJLj3IBkIr3B/AgMBAAGjITAfMB0GA1UdDgQWBBTK +YjH+9Jv82Zqi86r95/1sXUCOnDANBgkqhkiG9w0BAQ0FAAOCAgEAsDyl3dS/5pR1 +iDN0zE70HN1Sjv55c5um6N39rgz8JSObbAMihhpjRXPR6yl0PdfVcswdCuEaaykp +ppPNY5ObqZIdqI92XOaOhSA3AkZwZffbwaoXFYiawq1aQG1HP7oxXzWwbnbPOxgz +6ThNP5DJan53Mk8TAhxoJkEJxVlMwIiC+QEgqDNYrP8oNOR2J1EXgzsHheEKObyP +xTwRYFqZU/7BQlFeB0LG1LIy9zXAHlb/XIor10w6ChPDW7DiDwGq3zDJw1d8eiUn +RoPRmFjTqn+3rGaEkk+vUFHoWo7cLCEIC3+P9wlY4Kel+ldXMmuJ+BZ1glFXeO3L +VO85n7iVIyBbwo7RLtNaBvrRQIEG3ld5UOKklLlWwhrX/FXksEhdFvmuF9sbiYNr +cg81sbwZlX7Gi7VicXkykFFXwRRr3UblDtfeevouxk4nMVzcDsmzGeAZKQBvcxHa +Pzc70YwnVRqTc87c0bEwPoxK1Vb26+DILyDjKb/AkTw/rwj6vcJZP2ad+hpiz5Ka +nlbY2cI3JJb0TQiDiOIk+xFqC5oHUTSEmfqA6sA5o/RqdwDpkfpgI5mCwhYzDSLD +jfS+263ylhanl7oz0sM+GtH63owVbYJAFT2EozT9siTIErvJESL4Z80yUQG63d/7 +fss8T6gOo19esb/KEMZGZE4pAApakWM= +-----END CERTIFICATE----- diff --git a/contrib/certificates/reseed/meeh_at_mail.i2p.crt b/contrib/certificates/reseed/meeh_at_mail.i2p.crt new file mode 100644 index 00000000..6014c96f --- /dev/null +++ b/contrib/certificates/reseed/meeh_at_mail.i2p.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFeTCCA2GgAwIBAgIEZZozujANBgkqhkiG9w0BAQ0FADBtMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEWMBQGA1UEAwwNbWVlaEBtYWlsLmky +cDAeFw0xNDA2MjgyMjQ5MDlaFw0yNDA2MjcyMjQ5MDlaMG0xCzAJBgNVBAYTAlhY +MQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9ueW1v +dXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRYwFAYDVQQDDA1tZWVoQG1haWwuaTJw +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnVnmPE4uUvCky0yCnnVH +cJEDqzwDPupx0zr0YDlhZk5VOPPecx5haayJ/V6nXPc1aVVWn+CHfedcF2aBgN4K +5aBueS/l6l5WHcv02DofAqlTmyAws3oQeR1qoTuW24cKRtLR7h5bxv63f6bgp6e+ +RihFNez6UxErnRPuJOJEO2Im6EgVp6fz7tQ7R35zxAUeES2YILPySvzy2vYm/EEG +jXX7Ap2A5svVo90xCMOeUZ/55vLsjyIshN+tV87U4xwvAkUmwsmWVHm3BQpHkI6z +zMJie6epB8Bqm0GYm0EcElJH4OCxGTvDLoghpswbuUO7iy3JSfoL7ZCnoiQdK9K4 +yVVChj8lG+r7KaTowK96iZep+sZefjOt5VFGuW2Fi/WBv3ldiLlJAo/ZfrUM4+vG +fyNBXbl6bX87uTCGOT1p3dazo+zJMsAZ+Y93DlM/mDEWFa1kKNrs74syzaWEqF4L +KQE6VoYn80OOzafSigTVQgSwUtQtB0XGhMzJhyxU2XHWe1LFIy7Pta0B+lDiZj7c +I8nXxYjsDfEu/Elj/Ra9N6bH0awmgB5JDa+Tbir+oEM5SyDfpSaCGuatdGxjweGI +kVmFU0SqCZV/8TXbIu6MUVzTZMZVT94edifFSRad4fqw7eZbSXlPu++3d1/btn6h +ibM04nkv0mm+FxCKB/wdAkECAwEAAaMhMB8wHQYDVR0OBBYEFO7jIkSRkoXyJcho +9/Q0gDOINa5EMA0GCSqGSIb3DQEBDQUAA4ICAQBzfWO7+8HWOKLaYWToJ6XZbpNF +3wXv1yC4W/HRR80m4JSsq9r0d7838Nvd7vLVP6MY6MaVb/JnV76FdQ5WQ6ticD0Y +o3zmpqqbKVSspN0lrkig4surT88AjfVQz/vEIzKNQEbpzc3hC2LCiE2u+cK/ix4j +b9RohnaPvwLnew5RNQRpcmk+XejaNITISr2yQIwXL7TEYy8HdGCfzFSSFhKe9vkb +GsWS5ASrUzRoprswmlgRe8gEHI+d51Z7mWgna0/5mBz9bH/3QXtpxlLWm3bVV+kt +pZjQDTHE0GqG2YsD1Gmp4LU/JFhCojMTtiPCXmr9KFtpiVlx06DuKm5PC8Ak+5w+ +m/DQYYfv9z+AA5Y430bjnzwg67bhqVyyek4wcDQinFswv3h4bIB7CJujDcEqXXza +lhG1ufPPCUTMrVjh7AShohZraqlSlyQPY9vEppLwD4W1d+MqDHM7ljOH7gQYaUPi +wE30AdXEOxLZcT3aRKxkKf2esNofSuUC/+NXQvPjpuI4UJKO3eegi+M9dbnKoNWs +MPPLPpycecWPheFYM5K6Ao63cjlUY2wYwCfDTFgjA5q8i/Rp7i6Z6fLE3YWJ4VdR +WOFB7hlluQ//jMW6M1qz6IYXmlUjcXl81VEvlOH/QBNrPvX3I3SYXYgVRnVGUudB +o3eNsanvTU+TIFBh2Q== +-----END CERTIFICATE----- diff --git a/contrib/certificates/reseed/orignal_at_mail.i2p.crt b/contrib/certificates/reseed/orignal_at_mail.i2p.crt deleted file mode 100644 index 799b601b..00000000 --- a/contrib/certificates/reseed/orignal_at_mail.i2p.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFfzCCA2egAwIBAgIEbNbRPjANBgkqhkiG9w0BAQ0FADBwMQswCQYDVQQGEwJY -WDELMAkGA1UECAwCWFgxCzAJBgNVBAcMAlhYMR4wHAYDVQQKDBVJMlAgQW5vbnlt -b3VzIE5ldHdvcmsxDDAKBgNVBAsMA0kyUDEZMBcGA1UEAwwQb3JpZ25hbEBtYWls -LmkycDAeFw0yMTA3MDYyMjExMDFaFw0zMTA3MDQyMjExMDFaMHAxCzAJBgNVBAYT -AlhYMQswCQYDVQQIDAJYWDELMAkGA1UEBwwCWFgxHjAcBgNVBAoMFUkyUCBBbm9u -eW1vdXMgTmV0d29yazEMMAoGA1UECwwDSTJQMRkwFwYDVQQDDBBvcmlnbmFsQG1h -aWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvNJz2KGuAkHP -tGFobfLvpybtxB50fkcEsTc9opmiy7wBKK9rSI01VS616IhABkWKZVfK2A9NqpGv -v/CyhTKoaeSNeXY7+zORUWgWK/zA9fA4GRZFqlW8j4tbompDwcLYNqRBCsn1C0OY -YA5JhXPBixMcnXl8N8x4sXhQ4l9R3+QrydhUHRvgDc8dOxRyIX7zuQAyf8tmA2Xo -xZLdvDcCJdLBIbFwxhIceIhgcOwaOx7oRkZDZdYcLJd3zjyPbu8JtOM2ZkwH7r+0 -ro5PktuDp2LAS6SII5yYNcwcrvPZGPqhLdifIw1BrdTIb/rIkQZ5iXOOdyPmT7e8 -IwAJcPFlfvrS4Vbi9oDqyx3aDUBoubgmFnO1TirL56ck83R/ubcKtdnyzAn5dp+f -ZNYW6/foSBpDDOCViylbFAR5H0HJEbBns7PZx6mGEEI4tUAJdNYl7Ly7Df60a9Rz -cD/gz08U9UwFXYKoT6roEjToADGAzb5MI4cVlAb2AmQaMNXNe04HcDL1bU50mkNU -amqPv8nxf72fBQCEmZz2G57T6QiYTtcCwiWS1QdWsuaOtCo9zO0MKcjzSdUxuxEc -dXhjQdNegsgg/Xk7bJ8lKOsACqMpFftdPmuyeZU2t+3RPuBpV/0j2qUfg/y6kb0z -CxAOYmlcL4kqw4VT+5V/EeZLIG0h9I0CAwEAAaMhMB8wHQYDVR0OBBYEFD/wJObg -CCDuhMJCVWTSTj+B3rsUMA0GCSqGSIb3DQEBDQUAA4ICAQC0PjsTSPWlGbLNeeI8 -F0B5xAwXYJzZ7/LRxh8u42HDUqVIDjqkuls1l3v9D7htty2Gr3Ws2dcvcOr2KcOy -mEWg+jdP/N3vt9IkZeVS4YQoPgq6orn7lVkk00bcKb24f7ZnoQnnVV0/m42Y5P4j -LLh+8MBxsez9azXyZbDVEkgsMUAkdVO6KNz6scqz7wb8egV2GAMAp7cwChC6lanK -gv9ZyJhG/HdTv6VyuMZhJy6rX4geM97tm1iHu1VLsQcIzBKAdEvWJv8ofMeiyINe -hqAP9NYaeowKi975NOrmf+XZwxd0niApIohV684RCVUfL8H7HSPbdXhBJ/WslyDP -cTGhA2BLqEXZBn/nLQknlnl0SZTQxG2n4fEgD1E5YS/aoBrig/uXtWm2Zdf8U3mM -+bNXhbi9s7LneN2ye8LlNJBSRklNn/bNo8OmzLII1RQwf1+vaHT96lASbTVepMZ/ -Y9VcC8fAmho/zfQEKueLEB03K+gr2dGD+1crmMtUBjWJ9vPjtooZArtkDbh+kVYA -cx4N4NXULRwxVWZe5wTQOqcZ3qSS1ClMwaziwychGaj8xRAirHMZnlPOZO1UK4+5 -8F4RMJktyZjNgSLP76XPS4rJK5fobuPqFeA4OpDFn/5+/XeQFF6i6wntx1tzztzH -zc+BrVZOdcYPqu9iLXyRQ9JwwA== ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/r4sas-reseed_at_mail.i2p.crt b/contrib/certificates/reseed/r4sas-reseed_at_mail.i2p.crt deleted file mode 100644 index 850b1afd..00000000 --- a/contrib/certificates/reseed/r4sas-reseed_at_mail.i2p.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFiTCCA3GgAwIBAgIEY2XeQjANBgkqhkiG9w0BAQ0FADB1MQswCQYDVQQGEwJY -WDELMAkGA1UECAwCWFgxHjAcBgNVBAcMFUkyUCBBbm9ueW1vdXMgTmV0d29yazEL -MAkGA1UECgwCWFgxDDAKBgNVBAsMA0kyUDEeMBwGA1UEAwwVcjRzYXMtcmVzZWVk -QG1haWwuaTJwMB4XDTE3MDYyMjEwNTQ1NFoXDTI3MDYyMDEwNTQ1NFowdTELMAkG -A1UEBhMCWFgxCzAJBgNVBAgMAlhYMR4wHAYDVQQHDBVJMlAgQW5vbnltb3VzIE5l -dHdvcmsxCzAJBgNVBAoMAlhYMQwwCgYDVQQLDANJMlAxHjAcBgNVBAMMFXI0c2Fz -LXJlc2VlZEBtYWlsLmkycDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB -ANgsj5LhF4uGG4RDueShqYQZsG5Rz6XUAtK9sVGFdmdJTDZirUMZcCGCGZP/Harz -QaZU9EYxOCztnpLCQksSCpdRsij56MURS0tW/1x7LHIDUOi911Of57jgIHH+3E5n -6tuRxEk6J/9Ji3PI+89kl0sPKMVFMyKkINprVTA5zr/keyYEG0p6HSEYYiJkQH78 -8uoOCAmlk9mxkJFb+zviCk6jsYwdH+ofD6Lw5ueOlYUbeZ9Nd7jfSdf20XM7ofIw -W2COtsbq3J7vNrQJMV7HkHxVx/7OqmjQF02OahZFZREVZqbHpL501iTn9Iqd5qKq -IsxYjk7ZnP4UUCBk8NOU5TuWsy0qNw+TJDI9s55Fi4KPtXWf47HIl6CdpM5y/D5L -eufCojSwPKlrD6x9gTyJdBggBZRIyplXdKffo/95hUhEkv86yfsVVR7Gu1uy0O8T -Gtb8Da/oi5eEZBHWonLVicLPei5jeo+1gbR09PQ6s41uMZlOhMe4RSgiIQj/7UVo -ffKdl1MPNKr1u2fgVj8kxqg8ZivWKQ2taEgimU2EkQcNcE96M9yQlNNpNvqSAQVk -wYXlHt0AN6A1A8u1pItxaTwXnbmx+OBJZoKl4ZQeaC8wtKjTgAgVXp+g5iot2gir -LjxCRx1WLG1c8vRg1W8CDZII8Swc8EWpMhI+0hPv7/4/AgMBAAGjITAfMB0GA1Ud -DgQWBBTN5sKbrNzwE8sgMGDekfOPgX8/JDANBgkqhkiG9w0BAQ0FAAOCAgEAjLaB -bHqvFTs0ikAtesk9r8+8XVIsP5FR57zZCek2vxkHcCQWw8Uqs3ndInRX4FirKSLT -WRb4aSwFCkrmwueecTpXN/RBC+fZj+POCfdILEsA+FGreAM2q5ZXv/Q0jyIXOXEM -+KL0JZXnNS0/dqR3IYbC7f39CL6Sf40gRGTwTWWGg3KnynoS0v1zQcZLTMhHBD2X -tgdIPbroq9t4gXa7Dhm0egYfQOI/7re2wiZT7UWVVwEpYqKf6JApFHa1nNOFMrLF -45JHQIHArkoxpQdfSe9HBoyJiB5vz398rHZeqbJaF3PIg9rxWWY/NvvOVuIk8U5z -0jExhg29a88B32U7ndvQJqIuGiQghzCiLxC/y1+wAdpeDSbD3OAOHqplvMj3BUn9 -yhDSLSjtfBJjnXKxtEcWLR0edHCGEk5mAcL7q1WNxDpxaICwGGpNZN53CtFx7amb -egYil448DmiqoQTCTE9pBz8YjwiVfCYLYv17O0NJyYM9Efy/wL3rFlsPJniWHMuH -imZybVU4ukjvfOZ+LY4COTwz6w4sfA7a+i+2mOynC7eKX8Yg6i1nXlcY1Z8ykNgi -7B3kz1T/DV56CIm6QUWtepfuKTYq4C6QrBBIXLk1d5g95aWA21u1LRqNZ9GLH+eA -gfvIm7v+cELj8a53EQY0LafzZqNC5kQAp916coU= ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/rambler_at_mail.i2p.crt b/contrib/certificates/reseed/rambler_at_mail.i2p.crt deleted file mode 100644 index 40a5c9fe..00000000 --- a/contrib/certificates/reseed/rambler_at_mail.i2p.crt +++ /dev/null @@ -1,33 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFxzCCA6+gAwIBAgIQfKAV7rmoWA8jWpLfMtDQqzANBgkqhkiG9w0BAQsFADBw -MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK -ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEZMBcGA1UEAwwQ -cmFtYmxlckBtYWlsLmkycDAeFw0yMTExMDYwNzEwMzJaFw0zMTExMDYwNzEwMzJa -MHAxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgxHjAcBgNV -BAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRkwFwYDVQQD -DBByYW1ibGVyQG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC -AgEAz4vQlIdjY56uqkFKWld9Oy3E8+06Ag9fUzBVleS2bdJfaFtmEa8xz6Pep7Bb -zJK0Q9t2CW7/xqIWuspWlYn5EYAS7BFiNOX70KX4PMpltj3C4Dpxpjll9LdydU2k -FquCflXNJESnBDdd0qDRMboMf4c9lTz0mTLwAtzInLwHGDrbxEiQ/YqPgPJreOXQ -anhjkpxJcgpLR+9od8EdLNKbShVWEeSBnYp0FcjnZKOb9KC2gjqP0sWdzlw3i1hh -CB38A7a03Q4yUcmxCw4ktM60d/2jCZ+G7KHwcbkfxDjl85r0UgEzgfF7LuIuxxmA -MNLH1eAACnLTl42O72EHdtD9VWWwZF2NuFgAzT3MEFnMKDk+OqZOeZQOEgkIfrNP -O5XYMYxHSWCf/dmSq36ZJwhC40k2S9ArS8BQNY8NvwZG5CSGDU52FKaHzFn6EwLE -4CpsrptUX2itXLaFUiNMw6I+eSgTO7x+gpahZVqpdRSQXmpE0xA5jP/DwPyt3ZVe -/4q4kn3imcSCxBP5NQHWfVszsruRkh9np4R0xVlT8UCwJmY8Yg8zwJG5UddTAck5 -JavDsaXgWMwcZ/qQboZKlH/iAdQnbkte8Yd5GL5nmTeS+vwuluwmA/y9kUzSUhk+ -86kA0eRJ1+e2HdA1/UOTRmyIoIeQ5/fhELMXzhksLcpMGTUCAwEAAaNdMFswDgYD -VR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNV -HRMBAf8EBTADAQH/MBkGA1UdDgQSBBByYW1ibGVyQG1haWwuaTJwMA0GCSqGSIb3 -DQEBCwUAA4ICAQAxRdSTZGEblnNeVuRoEQq/a/6q4egFaOkzXCPKEnDzB5yvm83g -35ImquGFZkgaoc5qUAHVeBwOQrWgUI4xHPofnbM2VsgEUMz6h3ovobPNkN3+lRT5 -30krd0y+A/Q895EHDu0lyf3BHMmtCWiKWQBttuc0dnmoLCRsQxgy+kYJCS/81jCM -4KNnyrtc6a/czqSq758CncjP2nErVucendsguQoA5JUw53YJ4FYHG/f9tYEkhm9C -D6u7L3vTUcMRUrRxSiJyNixH36nEwpM6DNHiPNc+CFKZ/Zx449R1GjcpDhTrXnWP -2H1r3cyKEM8a76VUEs2GQCaaglOR4N1goyqgYEjScf+/4VmARL3VUzfP8Oub70rM -t1fip5QD/4VDQuA/9C9g5Rr2nJ3K2jVnpSSKnBYFYf5z9RZdTOVXjXaEi72lWxpk -mjgK6c5EFOJxYoCaTbKX9Kz9ZIWVOVMrgHWwA/wDW+Qk5zgP9Ysau65xIp9P1RdB -qHgR5BcIrNky9RD8cIzxzMPCSMVgnf0eLFuHmG8uUl/xHHVRprf0pd7DYkQ44HWN -Z/g/gg3DaJdH7vvkShzgjt4iZrmOCHQIKkSGFRYZf0/Mpn6mgK9+grtO9osVgAQr -LBO+5LIxV/S5bcrzWQLOiMABTd2X/0PTOjuXpfinZ3rDSUiNFPq5kLLSlA== ------END CERTIFICATE----- diff --git a/contrib/certificates/reseed/reseed_at_diva.exchange.crt b/contrib/certificates/reseed/reseed_at_diva.exchange.crt deleted file mode 100644 index 04b1524b..00000000 --- a/contrib/certificates/reseed/reseed_at_diva.exchange.crt +++ /dev/null @@ -1,34 +0,0 @@ ------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----- diff --git a/contrib/certificates/reseed/sindu_at_mail.i2p.crt b/contrib/certificates/reseed/sindu_at_mail.i2p.crt new file mode 100644 index 00000000..be1010f9 --- /dev/null +++ b/contrib/certificates/reseed/sindu_at_mail.i2p.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFezCCA2OgAwIBAgIEPSs1SjANBgkqhkiG9w0BAQ0FADBuMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOc2luZHVAbWFpbC5p +MnAwHhcNMTQwNzIxMDAwNjUwWhcNMjQwNzIwMDAwNjUwWjBuMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOc2luZHVAbWFpbC5p +MnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnYMNclgj1gIJfot6f +65jbcXpcqf9hhL/uYiU4+uxVevWyJvNHpMzhPWNN4l3HihJJsINsPs/MsZ83Guva +GW5S/93I617kyjs/ZVpEtfABGewho0m9VCBV2/N1mJpHvvR+9OR+YVuWlTB/8sTG +2smdRj/dkKvewN5PSTQH350yT18NR/DmZUU1Iwa7vrNw8ol3rP3qx9UGpFN3JE7V +Q9cA1nktMiFUm76eOPOoln04WDqW2rvArXzvhSApvt0JsLBrZDzM3cx2Rc2UdjIC +h+Ha+G4CLjszfZfQAFJYPred38Gg6wuXiza/wCBSPiB92i94hIQF/OSeukaMiqwG +dRAcBT84/U9bddqHlIICw14PkNHOGUyJGjGKWQl/2bLX43ghWkUJmsTXS3iVcOTc +gb/7MoCRBdL0q2GyEJXuAoKXD9VqD3g+EdcBTQxS9lhZ0iTR7423pg6FP43VMEUC +HUi/BOX1tCY6iRzD1Su6ISIx7klH/sAWWa+SybLFXWtZJxHXXJICiBHJWRbWgtlu +5V+at66yg/LNpyfW3Am08gDV0kiWUBN2Ct4TX9PAQmNDisNgi2AzdZHIfX6tRpU8 +UnNcnZGOh4+HXQwJtI0y83C8TsXJUFYfGFWqXN69sMEmgtX8/w+YUqjtb2GcX1HN +6z9u9lH40JCFHTA/clPqOSQ+MQIDAQABoyEwHzAdBgNVHQ4EFgQU4R6x7ArVpSVs +b8VTBXmodXzyraEwDQYJKoZIhvcNAQENBQADggIBAJEHLSDBRU2x6EalmN2W952p +MEO5lGD+ZfUVK0c44t1O53naffwZx9QmDmrC4TjeQrLOpAXLQ8GJHAGeZVmYRgkf +OioKde5uuqVcxqNxArO8VyYGwsuNVPCaBV+SyIO+EmWogidSIrOP2WsRRS2NBhLV +2dp3TvMeod9bPwRl00guvv9iqL0UVSpQSlfGkAQTVpyADOaQHOzeoCpmtPOfB6OK +syB/Z/6HElKoUbvhynaASLgmo3wM93PVJQ2Ho294bQHtDl2qcOksJQvWfCgi7Zrt +KuHaM/a2kItzI6JmyNFXgsKQSDJ4UvoppppgD7K48zOtSipGuZAADC5w5HdVvIGJ +1Czva8kTcmC6AMc+4tACGqYZEAEokkeXn+pIIqKVj2eQukT/0dLGGHbKmxp3Z0f2 +pIH2Draq8JPdacr9P/xqEWUuViaOuC5OBjY8Fg3fmVCpwefIuk+DBhbJjEugB0Cu +brJpqNznoYahkbyAXIA8T+QJYMhoGWmaIcaPWK6K3nArvaxzwJbb9Egyivhyp9Rr +r2QMEZ+cPO8p1mEhKpL/wGqAzYyla8SJ06PzLc1lQeGiClu1nbZj5AgkZ1DLa8SD +iO7+e6rS0q1bzc7smE5JzZRiOVqKij/ReKa2uebLLI4wgAhz5ymaD1HfZY+3dV9T +WX89Xn2UyQf5kHifiDKL +-----END CERTIFICATE----- diff --git a/contrib/certificates/reseed/swat_at_mail.i2p.crt b/contrib/certificates/reseed/swat_at_mail.i2p.crt new file mode 100644 index 00000000..276c8be3 --- /dev/null +++ b/contrib/certificates/reseed/swat_at_mail.i2p.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIEU71jgDANBgkqhkiG9w0BAQ0FADBtMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEWMBQGA1UEAwwNc3dhdEBtYWlsLmky +cDAeFw0xNDA3MDkxNTQ1MDRaFw0yNDA3MDgxNTQ1MDRaMG0xCzAJBgNVBAYTAlhY +MQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBBbm9ueW1v +dXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRYwFAYDVQQDDA1zd2F0QG1haWwuaTJw +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjSj53hsbTqtzbnlf5LbR +HmfdC2br9QZaB9e5IQKprlTptdzqTrt2LRS6ZaJ06BKJgX3AfLflvyeUDUPoyg63 +I9a1kb1AsrcxvMkHXTUwPaO09caO5/CiQ3zx/iuTl4e0MmGe3cz7jsvZNdOVH0ba +B691GB2UBq2QjobGx01qjWtACCmoQIcEur2ns/l+VzAextBL70dSPN6EJAonQnWc +JvHf1vhXp5DWasaIovm8haNo48QpCo7NllsAjiONQM9rrJITvzFG9hX9cv/B1Kr4 +LCebTXv58ViXXFsnxYhktAFwP33fn1eCLraJ/BpaDR4+s3ovMC/7S+g5//+sQTd1 +pR/kXx4BmZWZdzs083Z/2skQON75j/qnMhUQqpFCqUImm4lOhlIGbzFJ4GQM6VCR +V4BbvC3XuDc6vlivLzWpUEU7Kc6YnfGgi2G//ZCj2CAoR7qZs/n9997C8oAvGY5z +XGVC/GqIFHuFvnDfPDvxGovYjLJ0KrNtAmp2Rb5812glnVdPwbRYRUBg3ICbNey3 +rmGPURDq0aHMTzX4gtM+/hCYYVnkzNMQvYuw9EZLZrK/XdM1a0U4kajZSKKJsTmW +uQwXSUVjTKQh//yL4zPoELucFk5r7apLePEm6aCeWuY2wVkR8KEFgNanwDckWQAm +Lk9r2t+Y/l2jS2NqBWFyr+cCAwEAATANBgkqhkiG9w0BAQ0FAAOCAgEAgFSquj/0 +iZYpFI1XarSIVpGMo0WLAmb9GZCn5yoXSeE6eypI/hHhXA4Bjdk2Ae33pXPNcCV8 +oT/gHj8943Wx7CTxty2zHzIsd92/HG2EG6U/HPp5l7yIJQaWoe/9tjQoaBhipZOK ++MiytkoBWkyXFqXnKQPExiadWB8axHtt66vrikOcSx6Ur3u5DPKybvY4fsuvo4+I +cLLgoueFm6I1WhmkVmjtm4k2yZ/Z3NEYjg52rv8NuYhRwK2JrQeRzMZv/zt5KhOt +05woHrzymjfFBu0M8uxX7EGZBIsc8zcEY7JL/NSMArw/QCgLU5bQF6+CsyxWUkt1 +obMRXU1oS9GjC/1F0kw52NOz2qzBn9tZBc1zs8+GLpYBUf9KiUMFOfJpkr706VqC +orgxRYwncicq+de2PlesxJb3DNPFuAzUNzAqxcVYDoFPAiL1zCEl0nhBrbN+x93X +ojTfV3UlbMjMkQKveYJxsi5/+jO1dHIkXpzK4bwFwHmJ2RCa6PualWhuXldX6mR+ +APoY6xeoPRlyKk+POrSwU+hywUudyPuFyzDMo8n1w4CyqL+/ky3YsLfGBM1phbb2 +GEnZ0J1HW34Pnie1rzaCak+3RfaZsImCwh1xXl/H7Ka9bLeUIfOuipSSroctdaiG +84wIiEjxgjW2ldM37gTX2XtE/blB1YPIZ5U= +-----END CERTIFICATE----- diff --git a/contrib/certificates/reseed/unixeno_at_cubicchaos.net.crt b/contrib/certificates/reseed/unixeno_at_cubicchaos.net.crt deleted file mode 100644 index c94d319e..00000000 --- a/contrib/certificates/reseed/unixeno_at_cubicchaos.net.crt +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF2TCCA8GgAwIBAgIQVpTNnJZlUTDqmZiHRU4wCjANBgkqhkiG9w0BAQsFADB2 -MQswCQYDVQQGEwJYWDELMAkGA1UEBxMCWFgxCzAJBgNVBAkTAlhYMR4wHAYDVQQK -ExVJMlAgQW5vbnltb3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEfMB0GA1UEAwwW -dW5peGVub0BjdWJpY2NoYW9zLm5ldDAeFw0yNTAzMDQxODU5NDZaFw0zNTAzMDQx -ODU5NDZaMHYxCzAJBgNVBAYTAlhYMQswCQYDVQQHEwJYWDELMAkGA1UECRMCWFgx -HjAcBgNVBAoTFUkyUCBBbm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMR8w -HQYDVQQDDBZ1bml4ZW5vQGN1YmljY2hhb3MubmV0MIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEAr/JoAzLDtHXoAc7QcP4IxO+xNTeiYs78Wlg/Sl/sa6qz -gJoGaKH/X++z4Xe9lBSZalXCamnO4QMTgsWOIeoMy6XVbGzNTXPl8JUcblTIXwkP -pv848b1nxLfgLHzPRz1mJMpMikBugJ3Iz1sQzDVlUdye2fgbGChWliz9P4ClEODv -A/4+7i6uvJgEZ7A+jx3vBCXhiJppE3wTuz5D9BQqG8NuEwwjwBTBByoCC4oxOe0h -Qu1k7kEr+n4qpSEg/1eJ/RYSm+I8BftK1RUfykTwxlfmyEmKsfLBQWczE8Ca9nUB -5V34UH2bRy1cvavJYcNW3EPsGNf4naRs+Gy8XIFrb315GgWC1Z6+tzk+QFli9YeF -0DgtYEZciqu/407o8ZEURTnPjB7GhLDDp1LAQ7CQRhzaraXjHj0hyO+6rFpFdD0D -mXhvI/Eph3QIldsgnQc7nPhU2csN8Vi6bNDgm0HZ8cdmIBpI2Uxn/acZX/9G40oj -czrhsCBEecu/BluLJsfaWCYg90rvONP8Fc4edHAMonzYZR4r0q4hbv7AM8GmDRDN -J9/DZFi+Qs9NAe06jJC3jSsj7IdIs8TMhw8FX3xWOlXwjmVETAgY/dta/MpLJ6tJ -i+E+TH/Ndntj/D6WUwdQq+LyCR6gqHUWR6rl6EDQz+08DWb7j/72JSLb/DaXrDUC -AwEAAaNjMGEwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr -BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB8GA1UdDgQYBBZ1bml4ZW5vQGN1Ymlj -Y2hhb3MubmV0MA0GCSqGSIb3DQEBCwUAA4ICAQBBVoPeuOkmJDUdzIrzmxTmjMyz -gpfrZnjirTQKWhbv53sAWNeJ3kZ9l9m+0YpqEtFDrZPL5LTBXcSci5gGuxPkp+i/ -f/axsdcFMKbI9B/M53fyXLLJY0EM4mdhNAWtph1kTowFPhhReefCdqxYIy9uk2pL -gfb6NYJf+w9//fKYFZXb9SsiRchfv81+lbN+PIprnCpV3cTZWmpLRi2hN86pMW20 -3rh7rqJ4dPnA/NoyM+JstL10IU/4StqInweEvoo4W44+cC0zYGvfkkrKL4LB8w5S -6DKebtk7NQDtzuw2QnA9Ms4bmqWQpbL6/7uGaauS0+nmF+2gkqi9hcv0W5ZoBb/D -IVRauySnCtp9PbYM7pIJP9a1U6naLj7L1VixqsJGfPQ8V9TCOOi5bDc3RTetI/DX -bXHhAqHYzboakptylCp+Ao5h2hu0+w4rqnG63HwsHDJWcETbdVFQfzlzUmbx53yV -GnBsUxDgMOiHTZdKLkEnH4Q/XI76uc0ntTRlK9ktKWZPSISUlHrFnFl6I5UdeBMy -6vpB9sJO5L5RPRi4945K5Xdennywdi508mNXtMMmNCqrk1SMYbwaY6ZtIvXEGam9 -uHQTiTEX9LED/VXzFGqzdyDbG43HgS0PksgzedelHWfVAEnc06U3JX2lqUyihYHa -N4jAXWQ7s5p4GYaf4Q== ------END CERTIFICATE----- diff --git a/contrib/certificates/router/killyourtv_at_mail.i2p.crt b/contrib/certificates/router/killyourtv_at_mail.i2p.crt new file mode 100644 index 00000000..597f455f --- /dev/null +++ b/contrib/certificates/router/killyourtv_at_mail.i2p.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFhTCCA22gAwIBAgIELuRWgDANBgkqhkiG9w0BAQ0FADBzMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEcMBoGA1UEAwwTa2lsbHlvdXJ0dkBt +YWlsLmkycDAeFw0xMzEwMDYyMTM5MzFaFw0yMzEwMDYyMTM5MzFaMHMxCzAJBgNV +BAYTAlhYMQswCQYDVQQIEwJYWDELMAkGA1UEBxMCWFgxHjAcBgNVBAoTFUkyUCBB +bm9ueW1vdXMgTmV0d29yazEMMAoGA1UECxMDSTJQMRwwGgYDVQQDDBNraWxseW91 +cnR2QG1haWwuaTJwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAig3u +niLWm0y/TFJtciHgmWUt20FOdQrxkiSZ87G8xjuGfq7TbGIiVDn7pQZcHidpq+Dk +47sm+Swqhb4psSijj0AXUEVKlV39jF5IZE+VUgmEtMqQbnBkWudaTJPWcEe9T/Kd +8Oz2jgsnrD/EGVTMKBBjt/gk8VqTWvpCdCF1GhqcCeUTFHzjhN9jtoRCaJ2DClpO +Px+86+d3s9PqUFo8gcD/dbbyJCMqUCMBLtIy/Ooouxb9cfWtXfyOlphU+enmdvuA +0BDewb9pOJg2/kVd9/9moDWcBGChLOlfSlxpDwyUtcclcpvwnG7c6o4or6gqLeOf +AbCpse623utV7fWlFWG7M4AQ/2emhhe4YoMJQnflydzV8bPRJxRTeW1j/9UfpvLT +nO5LHp0oBXE0GqAPjxuAr+r5IDXFbkKYNjK5oWQB/Ul3LkexulYdCzHWbGd1Ja5b +sbiOy6t/hH6G8DD75HYb+PQZaNZWBv90EyOq1JDSUPw6nxVbhiBldi3ipc8/1X51 +FbzBqJ+QO1XKrKqxWxBKoTekuy38KRzsmkSCpY+WJ9f0gLOKtxzVO2HNNqqVFGQf +RGIbrNA0JSRQ1fgelccfrcRIXIZ3B8Tk/wxCIzCY6Yvg2jezz2xJkVdqOUsznS2v ++xJe67PYIAeMVtcfO4kmuCvyIYhsUEpob2n/5lkCAwEAAaMhMB8wHQYDVR0OBBYE +FCLneov6QMtvra5FSoSLhdymi++rMA0GCSqGSIb3DQEBDQUAA4ICAQAIcqbiwjdQ +M9VlGBiHe5eVsL6OM9zfRqR1wnRg4Q6ce65XDfEOYleBWaaNJA4BdykcA4fkUN1h +M2D9FDQScsyPTOuzJ6o75TYh0JOtF51yCi9iuemcosxAwsm90ZXGuMDfDYeyND5c +PAkWfyCP+jwLYbNo/hkNqyv+XWHXPQmT2adRnPXINVUQuBxVPC//C9wv2uDYWhgS +f8M425VPp4/R/uks9mlzTx08DwacvouD0YOC+HZE4sWq+2smgeBInMiyr/THYzl+ +baMtYgVs8IKUD2gtjfXZoaQNg3eq5SedSf/5F0S/LCdu9/ccQ8CzSEoVTiQFtO78 +SaU37xai8+QTSVpPuINigxCoXmkubBd+voEmWRcBd/XB5L+u+MFU/jXyyBj2BXVj +6agqVzY53KVYt23/63QliAUWyxT+ns9gRxVN1jrMhHdiDwsdT4NbzHxg1Su4eiHv +C/wjD3Dga0BRTEGylpHZGzb1U1rZRHM3ho3f1QkmRPPLcBUMTyUTxJm+GEeuhPvp ++TBf3Kg/YkdpnEMlagqcyHuIrf3m8Z/pTmpOIbekJWbbA7tluvWbMWw2ARB7dUOE +fHYVISh0DTw2oVXxM82/q8XXHnhEXv2nW3K40x1VabxUN+sF4M/7YA8nJqwsPJei +749STYJRfZXdIe69M9zpM5unxENAsiPJgQ== +-----END CERTIFICATE----- diff --git a/contrib/certificates/router/str4d_at_mail.i2p.crt b/contrib/certificates/router/str4d_at_mail.i2p.crt new file mode 100644 index 00000000..b01c7e32 --- /dev/null +++ b/contrib/certificates/router/str4d_at_mail.i2p.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFezCCA2OgAwIBAgIEHLJfZzANBgkqhkiG9w0BAQ0FADBuMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOc3RyNGRAbWFpbC5p +MnAwHhcNMTMxMDI2MTExODQxWhcNMjMxMDI2MTExODQxWjBuMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEXMBUGA1UEAwwOc3RyNGRAbWFpbC5p +MnAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvw0vTay1IPOgxvwe8 +yt5jGakha20kw9qDb6zbEL87EWEkeOzdu7iUC69lkxVP9Ws8EbLtkeMf/CXg6CC1 +e+w8WpOHj5prOsiOlrIO+2I1tKMaMUuJDX2wK4I5ZSw/Kieimh9xqOBZknDmtwjw +2HPW8rpxMqrScaGAP6sQD8Gh4XKKkLogfxYPzF8NnC6O8vBkFKVU2WSVZ0jPAQfv +6luPdA+5lES+5UPWr9Yhv/CX4siGKUTxchqJRf2VU4o5BzzXae4asVA/NY7lKgEw +eDDufbm0mRFWP4mbmXRlODuJ8GMnJbMQkNcAvZUnUcvpSTnGnIvxyxtXP5P6ic8V +3b9HV2eIsbfO1xrgyr6/9qgGpXcdDJejhvNg6fZgQeO40bOGQYwV8bNvsNQHqnZl +KsVhsMQkOubMxcHTBadcifi8PmdeJ5hxyyqJmyrwkmg2ijnN521M6YkoBzl+8VAi +zLmqKZfvN5t+pb9PZ3U3jHfkeIEwDRYRAOsvVqch5+ZfSv8x/Te6o15zDKPJQtWK +ty42GV1vERw30oSZQdrRRy/+4+HSRs3/Zb368OdAbcr+f/xPvwceYGWPeNNIoZ/x +xkIQE3xgEK+eJyPM9McjlCAezZZclT7fWfiEYNJAiS3fGALi+a+cGYWWULxCXpz+ +y397OHhZBhnh7D9K8aPePB8tCwIDAQABoyEwHzAdBgNVHQ4EFgQUezvGHq3h1gbC +Hs2LLVoll5fIUWMwDQYJKoZIhvcNAQENBQADggIBAF7SG1WBcE1r5eyTp/BLFZfG +iPtvqu+B1L2HutPum/Xf8A5fxR4kcKAKpVdu6vnDzCRAsAC9YvyETgAzI2nfVgLk +l9YZ31tSi6qxnMsQsV5o9lt/q2Rvsf2Zi/Ir8AlWtvnP8YG0Aj/8AG8MyhMLaIdj +M2FuakPs8RqEjoJL9dTOC9VTQpNTwBH9guP9UalWYwlkaXDzMoyO4nswT/GpCpg8 +4m4RO6grzdsEIamD/PCBM5f/vq+y08GaqfXpX9+8CbaX3tdzd3x48wPphmdpkptk +aRELIpLJZiK+Mos7W+0ZS8SHxGDIosjqVsgbZPmk12+VBcVgLOr8W1D7osS4OY59 +2GMUVV/GhoDh8wR/Td5wpZlcPE0NWmljjVg9+1E8ePAyMZy+U1KCiMlRVdRy518O +dOzzUUQGqGQHosRrH0ypS3MGbMLmbuWFRiz7q/3mUmW2xikH9I1t/6ZMNUvh+IWL +kGAaEf2JIv/D8+QsC0Un1W09DgvYz7qmKSeHhBixlLe68vgXtz/Fa+rRMsmPrueo +4wk/u/VyILo0BJP860APJMZbm+DPfGhV9DF9L5Gx9+d/BlduBVGHc+AQSWbU70dS +eH4/rgUYRikWlgwUxjY8/QQTlfx5xl28tG0xdO9libN22z7UwTGfm48BQIdrTyER +hqQ7usTy3oaWD85MbJ0q +-----END CERTIFICATE----- diff --git a/contrib/certificates/router/zzz_at_mail.i2p.crt b/contrib/certificates/router/zzz_at_mail.i2p.crt new file mode 100644 index 00000000..2d6fcba6 --- /dev/null +++ b/contrib/certificates/router/zzz_at_mail.i2p.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFdzCCA1+gAwIBAgIEcwrwsjANBgkqhkiG9w0BAQ0FADBsMQswCQYDVQQGEwJY +WDELMAkGA1UECBMCWFgxCzAJBgNVBAcTAlhYMR4wHAYDVQQKExVJMlAgQW5vbnlt +b3VzIE5ldHdvcmsxDDAKBgNVBAsTA0kyUDEVMBMGA1UEAwwMenp6QG1haWwuaTJw +MB4XDTEzMDkzMDE3NDEyNVoXDTIzMDkzMDE3NDEyNVowbDELMAkGA1UEBhMCWFgx +CzAJBgNVBAgTAlhYMQswCQYDVQQHEwJYWDEeMBwGA1UEChMVSTJQIEFub255bW91 +cyBOZXR3b3JrMQwwCgYDVQQLEwNJMlAxFTATBgNVBAMMDHp6ekBtYWlsLmkycDCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJV4ptvhlfrcize9/ARz4lUy +iLtLMSvSST1BdZjLJTwus05EUs0oiqnv9qXYIWGRB97aKlAmqSxsn4ZgBttCgmev +IkuiZ8kbdqI5YaT98yKW5P2Prt9p9cPbnz5/qjwZ5L9W+k/Itx7bv2pkNEP0NLYo +NrgHHTb1hsyRxc0lfPYk2BwsIi8hIWBHNrRpR41EWFXfqPcdsxS8cQhxVj4zLG/R +aMm4H8T+V1R1Khl4R4qqRgXBP305xqqRoawHmZ/S9/RkF0Ji6IYwBq9iWthWol6W +sMDn1xhZk9765fk+ohAC2XWuGSFCr02JOILRV3x/8OUxT1GYgYjc7FfyWIekg/pZ +yotlhL2I3SMWOH3PdG58iDY121hq/LsSKM9aP20rwtvssnw+8Aex01YDkI3bM6yO +HNi+tRojaJcJciBWv6cuiFKvQdxj/mOhOr0u0lHLlJ4jqES8uvVJkS7X/C4BB7ra +bJYQgumZMYvVQJFIjo8vZxMXue53o65FRidvAUT29ay54UTiL7jRV9w1wHnzLapU +xT1v7kWpWJcZ1zzC8coJjW+6ijkk38cVLb80u1Q4kEbmP2rDxw6jRvmqg6DcCKjK +oqDt+XQ6P5grxAxLT+VMfB404WHHwNs6BB841//4ZnXvy3msMONY/5y0fsblURgh +IS2UG1TAjR+x7+XikGx9AgMBAAGjITAfMB0GA1UdDgQWBBSvx/fCCP8UeHwjN65p +EoHjgRfiIzANBgkqhkiG9w0BAQ0FAAOCAgEAYgVE1Aa/Ok5k+Jvujbx72bktRWXo +Y4UfbWH/426VdgqXt3n9XtJUNM2oI4ODwITM4O15SyXQTLJhnvJz5ELcJV8nqviZ +RjK2HNX1BW7IEta3tacCvVnjzZ265kCT59uW+qmd+5PiaAYI5lYUn8P6pe+6neSa +HW6ecXCrdxJetSYfUUuKeV6YHpdzfjtZClLmwl91sJUBKcjK+Q9G/cE6HnwcDH1s +uXr7SgkBt/qc/OlNuu4fnTqUA58TAumdq9cD+eLBilDFrux1HsUZMuBUp64x5oPi +gme+3VewsczfFEtrxaG6+l6UA40Lerdx9XECZcDCcFsK6MS1uQ2HYjsyZcWnNT3l +6eDNUbjrllwxDdRAk0cbWiMuc21CFq/1v2QMXk88EiBjEajqzyXUPmKzwFhit6pr +5kfjfXNq+pxQSCoaqjpzVKjb3CqMhSlC8cLgrPw6HEgGnjCy4cTLFHlVmD64M778 +tj6rE7CntcmUi8GKmZKyaMyUo3QQUcrjO5IQ4+3iGUgMkZuujyjrZiOJbvircPmK +4IQEXzJ/G00upqtqKstRybaWSbJ/k6iuturtA2n8MJiCBjhLy8dtTgDbFaDaNF7F +NHeqQjIJDLhYDy6mi4gya3A0ort777Inl/rWYLo067pYM+EWDw66GdpbEIB0Bp71 +pwvcQcjIzbUzEK0= +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/193.150.121.66.crt b/contrib/certificates/ssl/193.150.121.66.crt new file mode 100644 index 00000000..450581d2 --- /dev/null +++ b/contrib/certificates/ssl/193.150.121.66.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDgDCCAmgCCQCAKEkFUJcEezANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UEBhMC +Tk8xDTALBgNVBAgMBE9zbG8xDTALBgNVBAcMBE9zbG8xDDAKBgNVBAoMA0kyUDEM +MAoGA1UECwwDSTJQMRcwFQYDVQQDDA4xOTMuMTUwLjEyMS42NjEfMB0GCSqGSIb3 +DQEJARYQbWVlaEBpMnBtYWlsLm9yZzAeFw0xMzA2MjcxODM2MjhaFw0yMDA2MjUx +ODM2MjhaMIGBMQswCQYDVQQGEwJOTzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwE +T3NsbzEMMAoGA1UECgwDSTJQMQwwCgYDVQQLDANJMlAxFzAVBgNVBAMMDjE5My4x +NTAuMTIxLjY2MR8wHQYJKoZIhvcNAQkBFhBtZWVoQGkycG1haWwub3JnMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuBuFY4ZFvsbr5l1/s/GeUBLIWQLB +nqrRkonrwCyxgjSnnG1uz/Z5nf6QDUjiVnFKMXenLaDn4KCmEi4LjWQllhK9r6pj +BRkR7C0DTHq7WqfyvWnGSZZsOJDiH2vLlivV8N9oGdjxvv0N9No3AJcsmLYrxSLi +6/JF8xZ2HGuT/oWW6aWvpIOKpIqti865BJw5P5KgYAS24J8vHRFM3FA4dfLNTBA2 +IGqPqYLQA+2zfOC4z01aArmcYnT1iJLT7krgKnr/BXdJfGQ2GjxkRSt8IwB6WmXA +byz6QdNYM/0eubi102/zpD/DrySTU2kc8xKjknGUqBJvVdsL+iLK98uJrQIDAQAB +MA0GCSqGSIb3DQEBBQUAA4IBAQCTimMu3X7+ztXxlIFhwGh42GfMjeBYT0NHOLAy +ZtQNRqhNvkl3jZ4ERPLxP99+bcAfCX0wgVpgD32OWEZopwveRyMImP8HfFr4NnZ+ +edbM37fRYiVJv57kbi6O0rhEC7J5JF+fnCaZVLCuvYIrIXTdxTjvxuLhyan6Ej7V +7iGDJ8t16tpLVJgcXfRg+dvAa6aDOK6x3w78j0bvh6rhvpOd9sW/Nk3LBKP4Xgkx +PHkqm3hNfDIu8Hubeav9SA1kLVMS/uce52VyYMEDauObfC65ds0GRmCtYhZqMvj+ +FFCbssLraVJE9Hi/ZKGu33jNngDCG+wG+nmleksMYE1yTSRt +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/cert.smartcom.org.crt b/contrib/certificates/ssl/cert.smartcom.org.crt new file mode 100644 index 00000000..960f2657 --- /dev/null +++ b/contrib/certificates/ssl/cert.smartcom.org.crt @@ -0,0 +1,44 @@ +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j +ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js +LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM +BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy +dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh +cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh +YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg +dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp +bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ +YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT +TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ +9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8 +jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW +FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz +ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1 +ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L +EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu +L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC +O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V +um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh +NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/i2p-netdb.innovatio.no.crt b/contrib/certificates/ssl/i2p-netdb.innovatio.no.crt new file mode 100644 index 00000000..d9bac193 --- /dev/null +++ b/contrib/certificates/ssl/i2p-netdb.innovatio.no.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID2zCCApOgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBCMR8wHQYDVQQDExZpMnAt +bmV0ZGIuaW5ub3ZhdGlvLm5vMRIwEAYDVQQKEwlJbm5vdmF0aW8xCzAJBgNVBAYT +Ak5PMCIYDzIwMTQwMTIxMDUzMzMxWhgPMjAyNDAxMTkwNTMzMzFaMEIxHzAdBgNV +BAMTFmkycC1uZXRkYi5pbm5vdmF0aW8ubm8xEjAQBgNVBAoTCUlubm92YXRpbzEL +MAkGA1UEBhMCTk8wggFSMA0GCSqGSIb3DQEBAQUAA4IBPwAwggE6AoIBMQC9WVet +EFeKAHmwgTUxJ/bRI4Gtjke3uj897eeZ15Y0SiqdHzypsEIWtXqx4G3W801xZzhv +UiAculvwRY4kpv3DnQE4sNTzbkAlvC6z4+CpFM2mhZ7o+YmozrIsNmQNCsvlxqJV +AD1mzqTFl/OB7LVtLmpSSd36IQFGmsh24XXa4pVH33e+NCZIGsdVwGsa4GoRuC9a +s/DiLI+x6zYRoY9cfOF2DuuOfKNMjSl65QUe4uHZCsRTb1q08NnPIidEFHr94kZH +Hph+MQs6MUVK1eT4yYt084S3cEWmWBQZVyAvWQ9q8EW+MoniOM7bBG2Bn9wu2F5x +kAKWTYfKSStW5CKSox9VSoopiUAtEIqhwgFGTISqhQyfOfyY97X2M47wvyWsl6dE +NxTgdvLD/o24rejBAgMBAAGjeDB2MAwGA1UdEwEB/wQCMAAwIQYDVR0RBBowGIIW +aTJwLW5ldGRiLmlubm92YXRpby5ubzATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNV +HQ8BAf8EBQMDByAAMB0GA1UdDgQWBBTaTuBpTAinNiT9PfKNRdIn3HrwxTANBgkq +hkiG9w0BAQsFAAOCATEAsYtojwAHiFwUqvCMIMJa5YN0Ms/QpjqZiENuGBpxvmVF +WVImX4s8K/qoBAKED8uKcRbMQd0FeDea7kMisJt5cblDzYuSv6wfeLXYkaT8/9H2 +X1pXhO/eghJ2U42RTgBkaW3mCI8ohk/GehU4tEXnbWRPHt6XDoSYDJdf2X8BPcgB +ZE10owLCw9c80QTuU+LCvbt8/F2USyNplUrogJGThzxrxZvxjGq6EcDj0iA0RRoG +5CUNrCB+JgFc+4bagI3E5B0skk/wn3Nl7mM8/Nf8b1QENmc8eYBZx2InA9769DHL +tNxzvE+OeMNlKy4M9WvLieIh6KmpYhHBG8ubJT8X+bZpJkh4rH6RzVYiXeCpX2iL +eoeSriGO0+8CJTBRbd5cXYd/COT0iAomMTelhcGVTA== +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/i2p.feared.eu.crt b/contrib/certificates/ssl/i2p.feared.eu.crt new file mode 100644 index 00000000..628c6290 --- /dev/null +++ b/contrib/certificates/ssl/i2p.feared.eu.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgIJAPVgXcMcr3zqMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV +BAYTAkVVMQ8wDQYDVQQIDAZFdXJvcGUxDDAKBgNVBAoMA0kyUDETMBEGA1UECwwK +T3V0cHJveGllczEWMBQGA1UEAwwNaTJwLmZlYXJlZC5ldTAeFw0xMjEwMjkxNzMw +MDZaFw0yMTAxMTUxNzMwMDZaMFkxCzAJBgNVBAYTAkVVMQ8wDQYDVQQIDAZFdXJv +cGUxDDAKBgNVBAoMA0kyUDETMBEGA1UECwwKT3V0cHJveGllczEWMBQGA1UEAwwN +aTJwLmZlYXJlZC5ldTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOUh +y2+6Q4RO+b5WPXX/cZ/9fiI7aWGe/C7z0083HOEqnkgGCYgxFWUCed6/eZbYoZ7/ +PV1BAuEereNwTp+Ov7fQB2H73O9sSAEejW6O4C2PZiZWaPxpZiTJNENbLOZxJnIN ++fSqmA5pqvGkYAJ2heZH4v4tayun7Vib58GWuizhzJ4EvhOrOrLq/YHrxMn++r4e +kNNbq4QzWpfxNa7ocDY9OJh5qFzuc+6wKj1m1syK6euDqs5d6X+y0aDTMgRxey2b +tkmNx9wC0flLg1oMcv9o1zN+dENy7Inkd/SqbSjLUqDTJzdq6xURVsgLoV63pb6r +B4gbGIlriYWK/mOPTTkCAwEAAaNQME4wHQYDVR0OBBYEFOI94JZ3Rb2RVmr8QjOp +u3KfVSrNMB8GA1UdIwQYMBaAFOI94JZ3Rb2RVmr8QjOpu3KfVSrNMAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAD7bI05zg9nf9qanq4ZNw/rvEzYQRBmy +MqzZjcwBMGvbcEbS+zYAdAkfxmN3l/AT4I4z138Om0ud4ZJUQTVlRsJkMlmLD4Rt +Jbi2rl7mrY7Qupgu5hvgH+ZaEWr7LTq+tFjPycRS+zijw9NToKeAsgEex9zYIOYD +BxDUn/trvyA41ItvegWh803IsZUBb45Via+bopid9aFFkejRrck9hhcQ6fVh2yju +nuVwHrxNvGc0NmmJ7zI+nPESFS+TAYbWXikDhc5Vtyiuoz47WZU1cgXYYMejK4WA ++3GLvei7qKm4GOJSg7BngF5Iyj/n7ML1rBqTlN3KA1YOgpGCwJlKzto= +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/i2p.mooo.com.crt b/contrib/certificates/ssl/i2p.mooo.com.crt new file mode 100644 index 00000000..ac10e1ff --- /dev/null +++ b/contrib/certificates/ssl/i2p.mooo.com.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDvjCCAyegAwIBAgICZhcwDQYJKoZIhvcNAQEFBQAwdTELMAkGA1UEBhMCVVMx +DTALBgNVBAgMBG5vbmUxDTALBgNVBAcMBG5vbmUxDTALBgNVBAoMBG5vbmUxDTAL +BgNVBAsMBG5vbmUxFTATBgNVBAMMDGkycC5tb29vLmNvbTETMBEGCSqGSIb3DQEJ +ARYEbm9uZTAeFw0xMTEwMjMyMTM2NDFaFw0xOTEwMjMyMTM2NDFaMGYxCzAJBgNV +BAYTAlVTMQ0wCwYDVQQIDARub25lMQ0wCwYDVQQKDARub25lMQ0wCwYDVQQLDARu +b25lMRUwEwYDVQQDDAxpMnAubW9vby5jb20xEzARBgkqhkiG9w0BCQEWBG5vbmUw +ggGPMA0GCSqGSIb3DQEBAQUAA4IBfAAwggF3AoIBbgMG1O7HRVa7UoiKbQTmKy5m +x79Na8vjD3etcOwfc4TSenQFvn+GbAWkJwKpM8uvOcgj1CxNeHWdSaeTFH1OwJsw +vl3leJ7clMdo3hpQDhPeGzBLyOiWwFHVn15YKa9xcM7S9Op5Q6rKBHUyyx1vGSz+ +/NBmkktpI6rcGFfP3ISRL0auR+db+adWv4TS6W8YiwQIVZNbSlKP6FNO9Mv1kxQZ +KoHPn8vT/LtAh1fcI6ryBuy3F5oHfbGumIwsS5dpowryFxQzwg5vtMA7AMCMKyXv +hP/W6OuaaEP5MCIxkWjQs35gOYa8eF1dLoy3AD9yVVhoNrA8Bc5FnVFJ32Qv7agy +qRY85cXBA6hT/Qzs/wWwp7WrrnZuifaSv/u/Ayi5vX42/bf86PSM2IRNIESoA98A +NFz4U2KGq9s1K2JbkQmnFy8IU0w7CMq6PvNEm/uNjSk6OE1rcCXML+EuX0zmXy8d +PjRbLzC9csSg2CqMtQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQf +Fh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUdjuOczdG +hUpYzH0UXqKrOleT8GkwHwYDVR0jBBgwFoAU+SKWC49cM5sCodv89AFin3pkS0Yw +DQYJKoZIhvcNAQEFBQADgYEAKYyWlDIStjjbn/ZzVScKR174I8whTbdqrX/vp9dr +2hMv5m4F+aswX4Jr58WneKg2LvRaL6xEhoL7OAQ6aB/7xVSpDjIrrBLZd513NAam +X6bOPYJ6IH7Vw9ClFY3AlfzsNlgRMXno7rySKKzhg24kusNwKDH2yCphZy4BgjMn +y6A= +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/ieb9oopo.mooo.com.crt b/contrib/certificates/ssl/ieb9oopo.mooo.com.crt new file mode 100644 index 00000000..78772210 --- /dev/null +++ b/contrib/certificates/ssl/ieb9oopo.mooo.com.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIESzCCAzOgAwIBAgIJALGqvElYEEqyMA0GCSqGSIb3DQEBBQUAMIG7MQswCQYD +VQQGEwJERTEaMBgGA1UECAwRaWViOW9vcG8ubW9vby5jb20xGjAYBgNVBAcMEWll +Yjlvb3BvLm1vb28uY29tMRowGAYDVQQKDBFpZWI5b29wby5tb29vLmNvbTEaMBgG +A1UECwwRaWViOW9vcG8ubW9vby5jb20xGjAYBgNVBAMMEWllYjlvb3BvLm1vb28u +Y29tMSAwHgYJKoZIhvcNAQkBFhFpZWI5b29wby5tb29vLmNvbTAeFw0xNDA0MTMx +NDI3MThaFw0zNDA0MDgxNDI3MThaMIG7MQswCQYDVQQGEwJERTEaMBgGA1UECAwR +aWViOW9vcG8ubW9vby5jb20xGjAYBgNVBAcMEWllYjlvb3BvLm1vb28uY29tMRow +GAYDVQQKDBFpZWI5b29wby5tb29vLmNvbTEaMBgGA1UECwwRaWViOW9vcG8ubW9v +by5jb20xGjAYBgNVBAMMEWllYjlvb3BvLm1vb28uY29tMSAwHgYJKoZIhvcNAQkB +FhFpZWI5b29wby5tb29vLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAN1CusoQ3OxJFTytbVMe7LPY8lnR7GIIQt5eCs0XTLl3ECclET1KLRo1c8Mv +vj5AwDRvL3Kw/oeDS+QvSRhgxG3lJ8i0soWDC/1LIX1NS/ZXG7hbycZ7YqAovCxU +958WPjUios73sAWUJrI8BbWFTYPWBB5lbyTaCooxtf6k7yB+CSwZnstEP/lbPNkf +Iupj+9B18ba21D2kQqZXyQRLX02d/rXq963BSkPX14Dxa7abw4lgDltvh2CzcoQH +VbQh3eGfPIIQGfvAAVEGsoy9fFt+xOUxp3KO7Y1VMKzegWqa2vBtWK+2nhpHfswq +eaE1qeVh12cG5kaVNP0o0hOUxkECAwEAAaNQME4wHQYDVR0OBBYEFIQ5U4EKfr0t +4L+RFe/RBFcDVoijMB8GA1UdIwQYMBaAFIQ5U4EKfr0t4L+RFe/RBFcDVoijMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGlNHzxF9tbS/Xr8jGpSdRLm +oIIoTr7+dIfFIy2TMAow0+gx1qEtWASUYIwbCP5mdMfhUjL8POfSwtTvgjw3LMoM +2zsGBvgGWs6VJbMPAgkgfsyjdJTM/IOiJtze6degn2bdZvAr1XiInLGMRMko7WyE +QQ1WJPcTUt8rNYVaCkq3CYioIlkb/C6M6ObHGSia0kwoZa6grD3CNUCa/c/dlFKO +E06qN72fsYihp0ghHkBaCxb+YfYpIAPIpfuuTSGkVYyjpY0a8EQ1JlzbJctQkX5r +sd9jfHsrZriAGCzdsL/diG8bUi9Yex/G0ip8GGEuHs5e2bJ6+7O/mPlL6SL/0tI= +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/ieb9oopo.mooo.com2.crt b/contrib/certificates/ssl/ieb9oopo.mooo.com2.crt new file mode 100644 index 00000000..8be9eef8 --- /dev/null +++ b/contrib/certificates/ssl/ieb9oopo.mooo.com2.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIESzCCAzOgAwIBAgIJAKII1waVnWddMA0GCSqGSIb3DQEBCwUAMIG7MQswCQYD +VQQGEwJERTEaMBgGA1UECAwRaWViOW9vcG8ubW9vby5jb20xGjAYBgNVBAcMEWll +Yjlvb3BvLm1vb28uY29tMRowGAYDVQQKDBFpZWI5b29wby5tb29vLmNvbTEaMBgG +A1UECwwRaWViOW9vcG8ubW9vby5jb20xGjAYBgNVBAMMEWllYjlvb3BvLm1vb28u +Y29tMSAwHgYJKoZIhvcNAQkBFhFpZWI5b29wby5tb29vLmNvbTAeFw0xNDExMjIx +MzQzNThaFw0yMDA1MTQxMzQzNThaMIG7MQswCQYDVQQGEwJERTEaMBgGA1UECAwR +aWViOW9vcG8ubW9vby5jb20xGjAYBgNVBAcMEWllYjlvb3BvLm1vb28uY29tMRow +GAYDVQQKDBFpZWI5b29wby5tb29vLmNvbTEaMBgGA1UECwwRaWViOW9vcG8ubW9v +by5jb20xGjAYBgNVBAMMEWllYjlvb3BvLm1vb28uY29tMSAwHgYJKoZIhvcNAQkB +FhFpZWI5b29wby5tb29vLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMhcnkSifOMw5bd66UlvYVsc42H22Nuy64qhtJHtggofrwBooF38kRCBVFL8 +9Xjzr0xsSshvO6p7E+CEUtA8v55l5vNbUTAvGP9WmzeZyZuCFg9Heo3orNMbIK7m +ppwKhwh6tFEIEpUTz/+xF5NRt0+CqcS4aNHuH3JPwNugfTBuSa86GeSaqL7K4eEZ +bZXqQ16Onvi0yyMqRJDp/ijRFxr2eKGPWb55kuRSET9PxVhlgRKULZkr39Dh9q1c +wb9lAMLMRZIzPVnyvC9jWkIqSDl5bkAAto0n1Jkw92rRp6EVKgSLA/4vl9wTb6xf +WfT5cs7pykAE0WXBr9TqpS3okncCAwEAAaNQME4wHQYDVR0OBBYEFGeEOHhWiKwZ +TGbc7uuK3DD7YjYZMB8GA1UdIwQYMBaAFGeEOHhWiKwZTGbc7uuK3DD7YjYZMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAzRA/0OpJtCO4kQkTn/hux9 +dRi9T6B54Xav5jG53iAPLTeMxsaLkvweh2pZ3kvEUrQhvW0JF8QBrHTsgxzb4Wd6 +FNDHSgJbZv3uCjFtWeuUh+GTG1k9uwgNIEnx7J9Vp0JCi4ezi/HMNI7c+LjinM9f +hrAzclkeRPLYg645DkxckLyDUbrc9v1qWFoTpezXSBPO7n3Wk4sCytdoA1FkTdXh +RF4BWCl/3uOxcrn0TqoC9vCh8RcxnllOiOO5j4+PQ1Z6NkQ/5oRCK/jjaWc3Lr6/ +FicOZJe29BVnrPGynqe0Ky1o+kTdXFflKowfr7g8dwn8k9YavjtGbl1ZSHeuMF8= +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/jp.reseed.i2p2.no.crt b/contrib/certificates/ssl/jp.reseed.i2p2.no.crt new file mode 100644 index 00000000..1cb5abec --- /dev/null +++ b/contrib/certificates/ssl/jp.reseed.i2p2.no.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIJAPsJOCng4aEOMA0GCSqGSIb3DQEBBQUAMGsxCzAJBgNV +BAYTAk5PMQ0wCwYDVQQIDARPc2xvMQ4wDAYDVQQHDAVKYXBhbjEMMAoGA1UECgwD +STJQMRMwEQYDVQQLDApJMlAgUmVzZWVkMRowGAYDVQQDDBFqcC5yZXNlZWQuaTJw +Mi5ubzAeFw0xNDA2MjgyMDQ3MThaFw0yNDA2MjUyMDQ3MThaMGsxCzAJBgNVBAYT +Ak5PMQ0wCwYDVQQIDARPc2xvMQ4wDAYDVQQHDAVKYXBhbjEMMAoGA1UECgwDSTJQ +MRMwEQYDVQQLDApJMlAgUmVzZWVkMRowGAYDVQQDDBFqcC5yZXNlZWQuaTJwMi5u +bzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALlZBIbb4MHqLPpUy298 +PG5z7RFo4bO7CxW2LO8DgE93KNbdkZuYpt6KcAW/gkDfKCiXTxDjyqmzZeBeIbcS +ea96jFc/pTknkiiSm9NX4ATcyRwvKXn6DR/iOiofP3G/sEkxgIdv3BgEYsF5jPQY +KefG0Jvl612cIX+1jC3lRRePYPUZnWxIuokXglLApeyhNzTK/KHIsLzR+56ScltM +pwGlroTky5ekPx4ZnJCxI+qFXWcgqdoNixPUkOtceYm7u5gd1D+mQDuCB5zfB+Pw +Iy9BTARot9M3fMrcRRVfEIGWwN1+bRnZvr0A/sqYMUqGPiUVOMi/mzyqZVQ14CGy +0idRhUKdOb/HNSmcep0Jwp0cP+VD/nCNU4JLptTLdErpTJrmDn+zkuQPPSMTzTWg +Fh3ktRsJ5zKfrnrBGxeKbciZgRkVHyWpv3+0AgbD8C3HvDj4qpkIO6D2gf+TREyM +frDXN6luqkThQcUFv+huMaL3Iul2doYqw5YPAdel6/cCD12n/FoEj5UJ47O/77DA +ITYfKCFRKDh/Ew7Ih3bH66uNaUUp+a0Sd6fNXLmWWr7gcn5B5CvrXhjPPror+Xyz +EZVByPTTfU1BkMQuxv20GG65M9g9wtXrD79N8di2wOfr4EG5i6L9ZvNTThwgWGGd +9f745WCnPL/ulLT9Glnlfk01AgMBAAGjUDBOMB0GA1UdDgQWBBR5Ed38JID7+JwB +hpOLi1Xt1ORyoTAfBgNVHSMEGDAWgBR5Ed38JID7+JwBhpOLi1Xt1ORyoTAMBgNV +HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQBRT/ourx4xT0n/qHF1vy8Ii02v +Hg5lHmzmiVn/3/S4q+t4HrOF6MSjjvVzVNk6JIIYb3+hwGUO31OLNZX60JfSMW+C +ffPiaNDLm/xE4yt4B/QZZDs0kv0RMjGau6k2XJPGvxVNl6LhM6LLvzMEtOGzBr0J +Ai4ZU+WqHk3nnXYHbg7C7Iuu8CT8tBpkaeW/VEN82dcLEulHFxA5Ia6HlwqrgvIW +w77oaOhOh5LKkdS8uHx4OUP8Mv25H2cMBblUdubbeqREyOGRGTkVXfRekD8K95ol +PfT9PhnhUXKRaSwy0nRqvRMiwk/CyIJTbMMflDET1P785diSvtJP0fOhkev9Uprz +FvLsPUTvANUz+vd2KJiLuGbR/d/LaJm18vdWx13vKVO60TBnyFQDS4RZbeuNp73v +x5fPTmiiPZYZj13m2xyes7SXJqhVbms0F39soThpuYrjCaHXyFgqah27+9Ivmbhu +EefPgLmkOx3v0kSfXdJYdji/mrKxERmqT1L34U6M32tCoSbjO7lakAV2opisbHEw +EehCuI83cGp/m3z8yjMoWV8Z4VoB+qMzxzgrXc5C2lxYAT4JggAV2SGcY9MszETI +/S7y5UypV4rutyJvHFrlJZKtp2B7Xi8N8n2NG1WGppYh9UShbFhDaVIMQpjZokAg +MHk7ixe0rSbnHcNF8g== +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/link.mx24.eu.crt b/contrib/certificates/ssl/link.mx24.eu.crt new file mode 100644 index 00000000..8e0d910f --- /dev/null +++ b/contrib/certificates/ssl/link.mx24.eu.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIJAMsPNG1k0yV4MA0GCSqGSIb3DQEBCwUAMIGdMQswCQYD +VQQGEwJERTEVMBMGA1UECAwMbGluay5teDI0LmV1MRUwEwYDVQQHDAxsaW5rLm14 +MjQuZXUxFTATBgNVBAoMDGxpbmsubXgyNC5ldTEVMBMGA1UECwwMbGluay5teDI0 +LmV1MRUwEwYDVQQDDAxsaW5rLm14MjQuZXUxGzAZBgkqhkiG9w0BCQEWDGxpbmsu +bXgyNC5ldTAeFw0xNDExMTkxOTE4NTRaFw0yMDA1MTExOTE4NTRaMIGdMQswCQYD +VQQGEwJERTEVMBMGA1UECAwMbGluay5teDI0LmV1MRUwEwYDVQQHDAxsaW5rLm14 +MjQuZXUxFTATBgNVBAoMDGxpbmsubXgyNC5ldTEVMBMGA1UECwwMbGluay5teDI0 +LmV1MRUwEwYDVQQDDAxsaW5rLm14MjQuZXUxGzAZBgkqhkiG9w0BCQEWDGxpbmsu +bXgyNC5ldTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL8modDBRkyh +SHSm92pTfguO3F6n5ocsBJ4vaVoosYq3ILCsapjqmynMHZUef6gEB7+Gn5cKXsH2 +JaKOeb8DHrOFCaxfj187x1QfZj1UNMQblx2T9q4th12tqp+k4JuLwgemr+2uAUpM +xx/uHRJXD0hf67+fHQFYNVfa+WvT46xlKGsWDQ0LBsA/z4YGnyeaV4PrS5nj3euA +IbdfDj7rJea3bfhSqYA1ZH1cquKlsXOOYO5cIcXsa5dxDWX51QS+i7+ocph+JN1X +dRh6ZirE9OXZVXwXXVRnJSYjgBlP/DQBdE7YkE1R3LyCVZsgxJaaLV/ujijOIK61 +SqEhHvFNRe0CAwEAAaNQME4wHQYDVR0OBBYEFB6XRz6VZlrAE+3xL6AyKrkq+y2X +MB8GA1UdIwQYMBaAFB6XRz6VZlrAE+3xL6AyKrkq+y2XMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQELBQADggEBADhxBA5GHisDVf5a+1hIi7FBGBjJJLqzlaKh+bFB +gTCYfk3F4wYzndr1HpdCZSSYDtY3mXFNMWQCpwvwvy1DM+9AMRY68wKNXHa/WypW +zQSqTfEH8cdaIXUALB7pdWFVr3rx0f7/8I0Gj/ByUbJ94rzd22vduX5riY0Rag6B +dPtW0M9bJrC1AIjexzDcStupj9v/ceGYZQYC4zb2tZ7Ek/6q+vei8TxWZjku7Dl4 +YRPXXufyB24uQ1hJVy2fSyIJ63tIRJoEFLBNaKDOB53i10xLWBcsJpXKY57AOQMn +flqW4HG8uGJ/o1WjhiOB9eI7T9toy08zNzt+kSI/blFIoek= +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/netdb.i2p2.no.crt b/contrib/certificates/ssl/netdb.i2p2.no.crt new file mode 100644 index 00000000..2ab5a131 --- /dev/null +++ b/contrib/certificates/ssl/netdb.i2p2.no.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC0DCCAjmgAwIBAgIJANdBFbakbhhOMA0GCSqGSIb3DQEBBQUAMIGAMQswCQYD +VQQGEwJOTzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwET3NsbzEMMAoGA1UECgwD +STJQMQwwCgYDVQQLDANJMlAxFjAUBgNVBAMMDW5ldGRiLmkycDIubm8xHzAdBgkq +hkiG9w0BCQEWEG1lZWhAaTJwbWFpbC5vcmcwHhcNMTIxMDIzMDExMjIyWhcNMTkx +MDIyMDExMjIyWjCBgDELMAkGA1UEBhMCTk8xDTALBgNVBAgMBE9zbG8xDTALBgNV +BAcMBE9zbG8xDDAKBgNVBAoMA0kyUDEMMAoGA1UECwwDSTJQMRYwFAYDVQQDDA1u +ZXRkYi5pMnAyLm5vMR8wHQYJKoZIhvcNAQkBFhBtZWVoQGkycG1haWwub3JnMIGf +MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCDvmjTpff6/XpiNuqoa9ZEKMlyq1o +kas9fHwnZax/0QTM3xusSQQ9DzeVMSx1ueYxhTZ6VLmE1mTr0aIndzugxGK/g85H +Y+cUl3nw7+5gLPMCUrKAXqQokE3mYxSNY3AUeend7nmHvm9iciw4+Sa2+6ROvQQy +kD31CEN6/I04rwIDAQABo1AwTjAdBgNVHQ4EFgQUV83dJhEcLbfJ+uh+MDYNPdah +RoQwHwYDVR0jBBgwFoAUV83dJhEcLbfJ+uh+MDYNPdahRoQwDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQUFAAOBgQBQQlJym7mUMUM2ryKu20z2PSUzFyq5U4rWHeo3 +elbNaTsFBwi+Ot/Lg/A5I4V8gywH1fBTG5bYKDUYvWohz1qIg66G57B1zT1zK9yh +Byz9go44M3y1/kXXSsJlY9llG9DDicr1y6LfldwZJ5zFAd3iiB8D8UadP5YLqb7v +wb1F1g== +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/reseed.i2p-projekt.de.crt b/contrib/certificates/ssl/reseed.i2p-projekt.de.crt new file mode 100644 index 00000000..75f43dd6 --- /dev/null +++ b/contrib/certificates/ssl/reseed.i2p-projekt.de.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID7TCCAtWgAwIBAgIJAOHakoadaLRiMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD +VQQGEwJBVDEQMA4GA1UECAwHQXVzdHJpYTENMAsGA1UEBwwER3JhejEMMAoGA1UE +CgwDSTJQMQ8wDQYDVQQLDAZSZXNlZWQxHjAcBgNVBAMMFXJlc2VlZC5pMnAtcHJv +amVrdC5kZTEdMBsGCSqGSIb3DQEJARYOcmVzZWVkQGkycDIuZGUwHhcNMTQwNTEw +MTAxOTM3WhcNMjQwNTA3MTAxOTM3WjCBjDELMAkGA1UEBhMCQVQxEDAOBgNVBAgM +B0F1c3RyaWExDTALBgNVBAcMBEdyYXoxDDAKBgNVBAoMA0kyUDEPMA0GA1UECwwG +UmVzZWVkMR4wHAYDVQQDDBVyZXNlZWQuaTJwLXByb2pla3QuZGUxHTAbBgkqhkiG +9w0BCQEWDnJlc2VlZEBpMnAyLmRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA8t5igIeAUZVX9k/A2gudRWVfToIV4yvlxmnH9UTJ8DTkWfHGbY9MmW2+ +b0ZdvIZDcgg1nvcLEKqCDQnIp3wLGdM8fdVSXqxA1dLyHdk6IrGVqb60qpGENeIc +EHiUeB1g0KqP4kLcj2sNlo+Vupjnu7qS8v0/LfZ3fq2m4vtx8dYnvo+JIzGL9K0f +/DOil8QIcdTZupzMbXd6P936Blm/1RdbW/uKROOuuYE38NwYOUCq2/Nd+T86S5DD +9wQBjy0U+9nNayWf6BOSuP6m2mxx/pA1CvKRq7CzI0Gqjo2Msd+i0dTL2WIO2JDp +5uykZ0GabRW3UrMEuyrzzK6U2RZ1dQIDAQABo1AwTjAdBgNVHQ4EFgQUIejD2MMl +6PpcCernYd3ku3sEWfswHwYDVR0jBBgwFoAUIejD2MMl6PpcCernYd3ku3sEWfsw +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAupUg3ZTBSE7iRebjcZ+y +zgnRaClmgrv8Mpa1/weTuXKhJZ65k6+G5mplI5hN/crKi/3b6oyfRrYhgdTdb0rD +2CbrhBkPGGlubhkjkxWjAhibzU6Kt3a7WOjykGnslpCZhwS/hiVB7ZE2JGdphFld +aJTKt12CytyP3GyIQyyX7O2t92dk8cW4tlxRVpaPNr59lk0V50qpvNmNyhxv3yDz +taop/etfjHStq1YrltHWH0d4Dxy8ubb7nV19uvPcE0+MrR2xm7jvOBfGjAf1bQ7Z +rk7RMHio4xWFJZO7TSzL5/8EH2jX6ZqpH+hZ6sV8TmzuRWsPkm0doXWr+HBZ/gMt +5w== +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/reseed.info.crt b/contrib/certificates/ssl/reseed.info.crt new file mode 100644 index 00000000..31302c52 --- /dev/null +++ b/contrib/certificates/ssl/reseed.info.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDRDCCAiwCCQDCm/Zrmali9zANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJB +VTETMBEGA1UECBMKU29tZS1TdGF0ZTELMAkGA1UEBxMCSEgxDDAKBgNVBAoTA0ky +UDEPMA0GA1UECxMGcmVzZWVkMRQwEgYDVQQDEwtyZXNlZWQuaW5mbzAeFw0xMjEw +MjcxODU3NDNaFw0xNjEyMDUxODU3NDNaMGQxCzAJBgNVBAYTAkFVMRMwEQYDVQQI +EwpTb21lLVN0YXRlMQswCQYDVQQHEwJISDEMMAoGA1UEChMDSTJQMQ8wDQYDVQQL +EwZyZXNlZWQxFDASBgNVBAMTC3Jlc2VlZC5pbmZvMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAt9nz0iUvjdX4Hkhfk0FbBOeEP4i/FG3V4VrEdQfcviSF +XgzGYeRtGsvrFWP/5+6bcGnOkIy/jrKJfij3AjKJh8gTzqiNNNnV8VcHwFSNp+hZ +D4BM+UHPACV1Pjd3HQe6f0+LvcTs3HQgIkNkwUyqRuXOm/5Mk6SWSu1740aSwHCj +Kk0x1FByzI0YBvXCPX6TVk6sJqKkQyLzK0CSGSeqUq8GvGCq+jT9k62Su7ooxCwi +GzxaFjMdVYxuI8cuT5Cni+SUw1Ia8vhESnIy6slwzk37xNI80VuMvRT6rD2KcXDH +mK7ml1qL0rJWoF5AE+x/nen4V41mouv1W9rk3wTlTQIDAQABMA0GCSqGSIb3DQEB +BQUAA4IBAQAr6RBviBDW4bnPDTcdtstTDdaYX9yzoh+zzeGB0dUR26GKoOjpSItb +B9nrsW1eJ2wbblfGBUoXhcmNByKHXXHejMhmurHjdei2BuLbTsknN8DPKXu5UF9z +cg4cKQkxgzXOcNYlaF4+sfwFXDHJ4we/8vduVgkyo8R66543/Sh/nIMvq2slRT4w +wIBOVcMb2XxlbdwHW9XALAz9sto+4GH9GAC24f8ngluOpHijMnOOIo4dHibQ5hM9 +KcDpHezP0ugMTAxS2NmtVahwAqa2IjpqR7aEQ2wLvxQzDqrXo93L93+b2FKRUQXH +Duud/n/w0kVV3DaIGikOsJayoanR+9HD +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/uk.reseed.i2p2.no.crt b/contrib/certificates/ssl/uk.reseed.i2p2.no.crt new file mode 100644 index 00000000..84f3f073 --- /dev/null +++ b/contrib/certificates/ssl/uk.reseed.i2p2.no.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFsTCCA5mgAwIBAgIJANgzPow6thRuMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV +BAYTAk5PMQ0wCwYDVQQIDARPc2xvMQswCQYDVQQHDAJVSzETMBEGA1UECgwKSTJQ +IFJlc2VlZDETMBEGA1UECwwKSTJQIFJlc2VlZDEaMBgGA1UEAwwRdWsucmVzZWVk +LmkycDIubm8wHhcNMTQwNjI4MjA0OTA3WhcNMjQwNjI1MjA0OTA3WjBvMQswCQYD +VQQGEwJOTzENMAsGA1UECAwET3NsbzELMAkGA1UEBwwCVUsxEzARBgNVBAoMCkky +UCBSZXNlZWQxEzARBgNVBAsMCkkyUCBSZXNlZWQxGjAYBgNVBAMMEXVrLnJlc2Vl +ZC5pMnAyLm5vMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxlVlXWn5 +Ham6ZqM6FkH6ZoXeXbncY/PnF669mCPcrPH56V2xZXwpeCXHWfu7YiHuhXXZSmzP +zwRrawHZTJulHt4e6j27JnDuEj69gmFpyi4B1djQ0kav0aJeagwCPG2do/UD7Cbr +4nITkU4CifLe47IUW/2K/EBI6bZGsRIDHJ3A+fAQmLnvehEkpvLN+cvtkpJOtZYx +6WvbwLsirkISnaio4//UY8M4poIu9mSG5pvNLagn9uoRPUSuj8jDEysB1Nmh12Zu +gFnt2XcxQB9/0krB5GnDTodrgfsz/UPbk44l4kFmQoLv5ACFndH69RKftogisauj +VVUrqCL3l9TcNsx8GLqZkeWhCwdZycZFjBhK01zihTYPEiU2HXfCNWhzLqxrM2Hh +r1ci+56fyNdn/ssO4o3hrGaWPDiayiHlEGEJxaG/ueKX2c3c0UJKkIGBPTEcdBjW +q42n/7EhY/ISaieQXPRK+gVm18I1OlGUH5FEYELO20bL88J8pr/bYuJyJnC8fiMP +YzKZuiVhey6dPr0zZgNDHyRbOlZqQllzKd1wbzbE4xqdUZfBWYwtRpdOJKDw4eoi +M69TwPQFfudeiudnMcR1gN37OkxS7UTEdsYIB5urgLb6qQD+tYFsxpcVPkedJw62 +3TobhZjucaEZWzePd4u9faT9mQBXBAgY6VcCAwEAAaNQME4wHQYDVR0OBBYEFDTN +QRqhzaLc6XX2gFg26K//e0+8MB8GA1UdIwQYMBaAFDTNQRqhzaLc6XX2gFg26K// +e0+8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggIBACcJ99Z45ghglvL3 +/yMnx6IkOSneEm2/ADQoOabBQSC2grRAMBescKUiqpgbpBFalIPbPJUVrlH9tXYB +izNhqWETBY2tNy7AEHcJcCsAFuC2gOhaFH7FLgPA8V5IJmZ+McjB8REyowcN+CP4 +GDY8s5/yr9S3HpKLD80UV18UX/j5m4b6I1w61QceMOSt6ahTtlnyvNBonFW94L1c +RmkdbhxYWn2eeUas62Q/+9bjr24E0weDKqopa3bbO7MWJ3mKkS4rua42j8GG3Q3q +UWPGh4zm+2+Ncjmz0Ho73RyYDDcp9IjwlAEv+NW86rz/5Pdkhoy+SzQwFYAwNgaQ +FRKb6ltpslxmu3tUdZ7Ydrj6MBGQyH2gRVm9qByro7WGI4UsyzsjP009Iu6dbhdC +2ddTGMisXF3dOmdRWh8dlggmW6gV4iaVgZkzLtrc9S0SK66utKMVXa4EUTm6XogX +F5ImPnVzIMo2qF2pP31aGDzKqJF3GNjGj+xHRVau5whz0a4ESY6V14PLTEL4Vc/H +J9uLCySifvqN+jzs5iY2QvNXjg2zPaTJbnjxxpYQJVSQHX6SyRcszhChqQzxnbyo ++S19BRclqzufRq6pp6VcOiID0BB7qPcrUHM9h1ingMXcZZlGBgHew9cY7tb5TAox +o+aTNc4k/7E543FVbs40dpOD2Fcr +-----END CERTIFICATE----- diff --git a/contrib/certificates/ssl/us.reseed.i2p2.no.crt b/contrib/certificates/ssl/us.reseed.i2p2.no.crt new file mode 100644 index 00000000..eae724e9 --- /dev/null +++ b/contrib/certificates/ssl/us.reseed.i2p2.no.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUDCCAjgCCQCkTcCJMdZV7zANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJO +TzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwET3NsbzENMAsGA1UECgwET3NsbzES +MBAGA1UECwwJTm9yZGNsb3VkMRowGAYDVQQDDBF1cy5yZXNlZWQuaTJwMi5ubzAe +Fw0xNDA2MjcyMjQxMjFaFw0yNDA2MjQyMjQxMjFaMGoxCzAJBgNVBAYTAk5PMQ0w +CwYDVQQIDARPc2xvMQ0wCwYDVQQHDARPc2xvMQ0wCwYDVQQKDARPc2xvMRIwEAYD +VQQLDAlOb3JkY2xvdWQxGjAYBgNVBAMMEXVzLnJlc2VlZC5pMnAyLm5vMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomVoBEc53jzy3xGMfgRaKyX6MaGG +KAmwu0uMTX6bVzGjy56JMMq3luoxOrpvgrNZF52lu7i36Tejo0HM75AHoea1es55 +DNLmrlDeqzlBU2WibOnizbB8G+tlMEbx8eAGAWk/Wv/vH8CAKmxjImslmbajzZC2 +LEH7inp3J5T2sVV7zmXeL9OEPKNyohbu6Mrno2IAlEOr8cu+lWAaFWzpknnR1gBX +NkB/8+7vK5Fq4MT7B0qnXPxmaWDbUOepPPni8u+2L9+qt19vZH4/6KNuH7xd7JLz +FfIdol6jy2cBQyAK7cVKWDHNk7ceB4Dl0mjBDbBIRTtLK+rfdnVmfWn8aQIDAQAB +MA0GCSqGSIb3DQEBBQUAA4IBAQCQH4QJMp5xneh2ah7fiuVdtKbiv6QNunRz7nb/ +mWYyqmBX7EHL8jOG5qmPELDgDt58HmnaYMo05nEJb9JhAoviEDXSYw0s6eN4n4nc +MKqgR/HLLSiXPwT+Wi1MI57OYim5AFTUCYTSaWFUT+dZKYb0QPE1XjGpQXi3ppsJ +3TJG71tOzJmZT6vRPmdTHJO70v6ZEhr5w4SiGx07gNmcgO8WRyb5ajOwSHiGKrj6 +UsuRNhtCyZaAEmelR9mfKBR1J2Nb+9jTz6mJtpT82WY3bst6mFk+A+mMWBQy7Hjt +gpdSDBCcFx9if+AKINGLgFvFKV2q8UzbfXms19NsVt9Hu7W3 +-----END CERTIFICATE----- diff --git a/contrib/debian/README b/contrib/debian/README deleted file mode 100644 index 7dc3a61f..00000000 --- a/contrib/debian/README +++ /dev/null @@ -1,5 +0,0 @@ -This forder contain files required for building debian packages. - -The trunk repository is contains the packaging files for the latest stable version of Debian (if we not forgot to update them). - -Files in subdirectories contains fixes to make possible to build package on specific versions of Debian/Ubuntu. They are used when building the release package. diff --git a/contrib/debian/bionic/compat b/contrib/debian/bionic/compat deleted file mode 100644 index b4de3947..00000000 --- a/contrib/debian/bionic/compat +++ /dev/null @@ -1 +0,0 @@ -11 diff --git a/contrib/debian/bionic/control b/contrib/debian/bionic/control deleted file mode 100644 index d872881c..00000000 --- a/contrib/debian/bionic/control +++ /dev/null @@ -1,18 +0,0 @@ -Source: i2pd -Section: net -Priority: optional -Maintainer: r4sas -Build-Depends: debhelper (>= 11~), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev -Standards-Version: 4.2.0 -Homepage: http://i2pd.website/ -Vcs-Git: git://github.com/PurpleI2P/i2pd.git -Vcs-Browser: https://github.com/PurpleI2P/i2pd - -Package: i2pd -Architecture: any -Pre-Depends: ${misc:Pre-Depends}, adduser -Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base, -Description: Full-featured C++ implementation of I2P client. - I2P (Invisible Internet Protocol) is a universal anonymous network layer. All - communications over I2P are anonymous and end-to-end encrypted, participants - don't reveal their real IP addresses. diff --git a/contrib/debian/i2pd.service b/contrib/debian/i2pd.service deleted file mode 120000 index ca477e3b..00000000 --- a/contrib/debian/i2pd.service +++ /dev/null @@ -1 +0,0 @@ -../i2pd.service \ No newline at end of file diff --git a/contrib/debian/i2pd.tmpfile b/contrib/debian/i2pd.tmpfile deleted file mode 100644 index e9bfebc6..00000000 --- a/contrib/debian/i2pd.tmpfile +++ /dev/null @@ -1,2 +0,0 @@ -d /run/i2pd 0755 i2pd i2pd - - -d /var/log/i2pd 0755 i2pd i2pd - - diff --git a/contrib/debian/trusty/compat b/contrib/debian/trusty/compat deleted file mode 100644 index ec635144..00000000 --- a/contrib/debian/trusty/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/contrib/debian/trusty/control b/contrib/debian/trusty/control deleted file mode 100644 index fc618ddc..00000000 --- a/contrib/debian/trusty/control +++ /dev/null @@ -1,18 +0,0 @@ -Source: i2pd -Section: net -Priority: optional -Maintainer: r4sas -Build-Depends: debhelper (>= 9), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev -Standards-Version: 3.9.8 -Homepage: http://i2pd.website/ -Vcs-Git: git://github.com/PurpleI2P/i2pd.git -Vcs-Browser: https://github.com/PurpleI2P/i2pd - -Package: i2pd -Architecture: any -Pre-Depends: ${misc:Pre-Depends}, adduser -Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base, -Description: Full-featured C++ implementation of I2P client. - I2P (Invisible Internet Protocol) is a universal anonymous network layer. All - communications over I2P are anonymous and end-to-end encrypted, participants - don't reveal their real IP addresses. diff --git a/contrib/debian/trusty/patches/01-upnp.patch b/contrib/debian/trusty/patches/01-upnp.patch deleted file mode 100644 index 74d36c06..00000000 --- a/contrib/debian/trusty/patches/01-upnp.patch +++ /dev/null @@ -1,17 +0,0 @@ -Description: Enable UPnP usage in package -Author: r4sas - -Reviewed-By: r4sas -Last-Update: 2024-12-30 - ---- i2pd.orig/Makefile -+++ i2pd/Makefile -@@ -31,7 +31,7 @@ # import source files lists - include filelist.mk - - USE_STATIC := $(or $(USE_STATIC),no) --USE_UPNP := $(or $(USE_UPNP),no) -+USE_UPNP := $(or $(USE_UPNP),yes) - DEBUG := $(or $(DEBUG),yes) - - # for debugging purposes only, when commit hash needed in trunk builds in i2pd version string diff --git a/contrib/debian/trusty/patches/02-service.patch b/contrib/debian/trusty/patches/02-service.patch deleted file mode 100644 index 546e252f..00000000 --- a/contrib/debian/trusty/patches/02-service.patch +++ /dev/null @@ -1,19 +0,0 @@ -Description: Disable LogsDirectory and LogsDirectoryMode options in service -Author: r4sas - -Reviewed-By: r4sas -Last-Update: 2024-07-19 - ---- a/contrib/i2pd.service -+++ b/contrib/i2pd.service -@@ -8,8 +8,8 @@ User=i2pd - Group=i2pd - RuntimeDirectory=i2pd - RuntimeDirectoryMode=0700 --LogsDirectory=i2pd --LogsDirectoryMode=0700 -+#LogsDirectory=i2pd -+#LogsDirectoryMode=0700 - Type=forking - ExecStart=/usr/bin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service - ExecReload=/bin/sh -c "kill -HUP $MAINPID" diff --git a/contrib/debian/trusty/patches/series b/contrib/debian/trusty/patches/series deleted file mode 100644 index d8caec41..00000000 --- a/contrib/debian/trusty/patches/series +++ /dev/null @@ -1,2 +0,0 @@ -01-upnp.patch -02-service.patch diff --git a/contrib/debian/trusty/rules b/contrib/debian/trusty/rules deleted file mode 100755 index 97a4e008..00000000 --- a/contrib/debian/trusty/rules +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/make -f -#export DH_VERBOSE=1 - -export DEB_BUILD_MAINT_OPTIONS=hardening=+all - -include /usr/share/dpkg/architecture.mk - -ifeq ($(DEB_HOST_ARCH),i386) - export DEB_BUILD_OPTIONS=parallel=1 -endif - -export DEB_CXXFLAGS_MAINT_APPEND=-Wall -pedantic -export DEB_LDFLAGS_MAINT_APPEND= - -%: - dh $@ --parallel - -override_dh_auto_install: diff --git a/contrib/debian/xenial/compat b/contrib/debian/xenial/compat deleted file mode 100644 index ec635144..00000000 --- a/contrib/debian/xenial/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/contrib/debian/xenial/control b/contrib/debian/xenial/control deleted file mode 100644 index fc618ddc..00000000 --- a/contrib/debian/xenial/control +++ /dev/null @@ -1,18 +0,0 @@ -Source: i2pd -Section: net -Priority: optional -Maintainer: r4sas -Build-Depends: debhelper (>= 9), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev -Standards-Version: 3.9.8 -Homepage: http://i2pd.website/ -Vcs-Git: git://github.com/PurpleI2P/i2pd.git -Vcs-Browser: https://github.com/PurpleI2P/i2pd - -Package: i2pd -Architecture: any -Pre-Depends: ${misc:Pre-Depends}, adduser -Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base, -Description: Full-featured C++ implementation of I2P client. - I2P (Invisible Internet Protocol) is a universal anonymous network layer. All - communications over I2P are anonymous and end-to-end encrypted, participants - don't reveal their real IP addresses. diff --git a/contrib/debian/xenial/patches/01-upnp.patch b/contrib/debian/xenial/patches/01-upnp.patch deleted file mode 100644 index 74d36c06..00000000 --- a/contrib/debian/xenial/patches/01-upnp.patch +++ /dev/null @@ -1,17 +0,0 @@ -Description: Enable UPnP usage in package -Author: r4sas - -Reviewed-By: r4sas -Last-Update: 2024-12-30 - ---- i2pd.orig/Makefile -+++ i2pd/Makefile -@@ -31,7 +31,7 @@ # import source files lists - include filelist.mk - - USE_STATIC := $(or $(USE_STATIC),no) --USE_UPNP := $(or $(USE_UPNP),no) -+USE_UPNP := $(or $(USE_UPNP),yes) - DEBUG := $(or $(DEBUG),yes) - - # for debugging purposes only, when commit hash needed in trunk builds in i2pd version string diff --git a/contrib/debian/xenial/patches/02-service.patch b/contrib/debian/xenial/patches/02-service.patch deleted file mode 100644 index 546e252f..00000000 --- a/contrib/debian/xenial/patches/02-service.patch +++ /dev/null @@ -1,19 +0,0 @@ -Description: Disable LogsDirectory and LogsDirectoryMode options in service -Author: r4sas - -Reviewed-By: r4sas -Last-Update: 2024-07-19 - ---- a/contrib/i2pd.service -+++ b/contrib/i2pd.service -@@ -8,8 +8,8 @@ User=i2pd - Group=i2pd - RuntimeDirectory=i2pd - RuntimeDirectoryMode=0700 --LogsDirectory=i2pd --LogsDirectoryMode=0700 -+#LogsDirectory=i2pd -+#LogsDirectoryMode=0700 - Type=forking - ExecStart=/usr/bin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service - ExecReload=/bin/sh -c "kill -HUP $MAINPID" diff --git a/contrib/debian/xenial/patches/series b/contrib/debian/xenial/patches/series deleted file mode 100644 index d8caec41..00000000 --- a/contrib/debian/xenial/patches/series +++ /dev/null @@ -1,2 +0,0 @@ -01-upnp.patch -02-service.patch diff --git a/contrib/debian/xenial/rules b/contrib/debian/xenial/rules deleted file mode 100755 index 11791d9b..00000000 --- a/contrib/debian/xenial/rules +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/make -f -#export DH_VERBOSE=1 -export DEB_BUILD_MAINT_OPTIONS = hardening=+all - -include /usr/share/dpkg/architecture.mk - -export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic -export DEB_LDFLAGS_MAINT_APPEND = - -%: - dh $@ --parallel - -override_dh_auto_install: diff --git a/contrib/dinit/i2pd b/contrib/dinit/i2pd deleted file mode 100644 index 63d877c1..00000000 --- a/contrib/dinit/i2pd +++ /dev/null @@ -1,8 +0,0 @@ -type = bgprocess -run-as = i2pd -command = /usr/bin/i2pd --conf=/var/lib/i2pd/i2pd.conf --pidfile=/var/lib/i2pd/i2pd.pid --daemon --service -smooth-recovery = true -depends-on = ntpd -# uncomment if you want to use i2pd with yggdrasil -# depends-on = yggdrasil -pid-file = /var/lib/i2pd/i2pd.pid diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile deleted file mode 100644 index ead21f10..00000000 --- a/contrib/docker/Dockerfile +++ /dev/null @@ -1,73 +0,0 @@ -# -# Copyright (c) 2017-2022, The PurpleI2P Project -# -# This file is part of Purple i2pd project and licensed under BSD3 -# -# See full license text in LICENSE file at top of project tree -# - -FROM alpine:latest -LABEL authors="Mikal Villa , Darknet Villain " -LABEL maintainer="R4SAS " - -LABEL org.opencontainers.image.source=https://github.com/PurpleI2P/i2pd -LABEL org.opencontainers.image.documentation=https://i2pd.readthedocs.io/en/latest/ -LABEL org.opencontainers.image.licenses=BSD3 - -# Expose git branch, tag and URL variables as arguments -ARG GIT_BRANCH="openssl" -ENV GIT_BRANCH=${GIT_BRANCH} -ARG GIT_TAG="" -ENV GIT_TAG=${GIT_TAG} -ARG REPO_URL="https://github.com/PurpleI2P/i2pd.git" -ENV REPO_URL=${REPO_URL} - -ENV I2PD_HOME="/home/i2pd" -ENV DATA_DIR="${I2PD_HOME}/data" -ENV DEFAULT_ARGS=" --datadir=$DATA_DIR" - -RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \ - && adduser -S -h "$I2PD_HOME" i2pd \ - && chown -R i2pd:nobody "$I2PD_HOME" - - -# 1. Building binary -# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the -# image under 20mb we need to remove all the build dependencies in the same "RUN" / layer. -# -# 1. install deps, clone and build. -# 2. strip binaries. -# 3. Purge all dependencies and other unrelated packages, including build directory. - -RUN apk update \ - && apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl miniupnpc-dev git \ - && mkdir -p /tmp/build \ - && cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \ - && cd i2pd \ - && if [ -n "${GIT_TAG}" ]; then git checkout tags/${GIT_TAG}; fi \ - && make -j$(nproc) USE_UPNP=yes \ - && cp -R contrib/certificates /i2pd_certificates \ - && mkdir -p /usr/local/bin \ - && mv i2pd /usr/local/bin \ - && cd /usr/local/bin \ - && strip i2pd \ - && rm -fr /tmp/build && apk --no-cache --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \ - miniupnpc-dev boost-python3 python3 gdbm boost-unit_test_framework linux-headers boost-prg_exec_monitor \ - boost-serialization boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre2 \ - libtool g++ gcc - -# 2. Adding required libraries to run i2pd to ensure it will run. -RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl miniupnpc musl-utils libstdc++ - -# 3. Copy preconfigured config file and entrypoint -COPY i2pd-docker.conf "$DATA_DIR/i2pd.conf" -RUN chown i2pd:nobody "$DATA_DIR/i2pd.conf" -COPY entrypoint.sh /entrypoint.sh -RUN chmod a+x /entrypoint.sh - -RUN echo "export DATA_DIR=${DATA_DIR}" >> /etc/profile -VOLUME "$DATA_DIR" -EXPOSE 7070 4444 4447 7656 2827 7654 7650 -USER i2pd - -ENTRYPOINT [ "/entrypoint.sh" ] diff --git a/contrib/docker/entrypoint.sh b/contrib/docker/entrypoint.sh deleted file mode 100644 index ce897e3c..00000000 --- a/contrib/docker/entrypoint.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -COMMAND=/usr/local/bin/i2pd -# To make ports exposeable -# Note: $DATA_DIR is defined in /etc/profile - -if [ "$1" = "--help" ]; then - set -- $COMMAND --help -else - ln -s /i2pd_certificates "$DATA_DIR"/certificates - set -- $COMMAND $DEFAULT_ARGS $@ -fi - -exec "$@" diff --git a/contrib/docker/i2pd-docker.conf b/contrib/docker/i2pd-docker.conf deleted file mode 100644 index 3b91a235..00000000 --- a/contrib/docker/i2pd-docker.conf +++ /dev/null @@ -1,52 +0,0 @@ -## Preconfigured i2pd configuration file for a Docker container -## See https://i2pd.readthedocs.io/en/latest/user-guide/configuration/ -## for more options you can use in this file. - -## Note that for exposing ports outside of container you need to bind all services to 0.0.0.0 - -log = file -loglevel = none - -ipv4 = true -ipv6 = false - -# bandwidth = L -# notransit = false -# floodfill = false - -[ntcp2] -enabled = true -published = true - -[ssu2] -enabled = true -published = true - -[http] -enabled = true -address = 0.0.0.0 -port = 7070 - -[httpproxy] -enabled = true -address = 0.0.0.0 -port = 4444 - -[socksproxy] -enabled = true -address = 0.0.0.0 -port = 4447 - -[sam] -enabled = true -address = 0.0.0.0 -port = 7656 - -[upnp] -enabled = false - -[reseed] -verify = true - -[limits] -# transittunnels = 2500 diff --git a/contrib/i18n/English.po b/contrib/i18n/English.po deleted file mode 100644 index 7782187f..00000000 --- a/contrib/i18n/English.po +++ /dev/null @@ -1,784 +0,0 @@ -# i2pd -# Copyright (C) 2021-2023 PurpleI2P team -# This file is distributed under the same license as the i2pd package. -# R4SAS , 2021-2023. -# -msgid "" -msgstr "" -"Project-Id-Version: i2pd\n" -"Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n" -"POT-Creation-Date: 2023-06-10 01:25\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.0\n" -"X-Poedit-SourceCharset: UTF-8\n" -"X-Poedit-Basepath: .\n" -"X-Poedit-KeywordsList: ;tr\n" -"X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n" -"X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n" - -#: daemon/HTTPServer.cpp:107 -#, c-format -msgid "%d day" -msgid_plural "%d days" -msgstr[0] "" -msgstr[1] "" - -#: daemon/HTTPServer.cpp:111 -#, c-format -msgid "%d hour" -msgid_plural "%d hours" -msgstr[0] "" -msgstr[1] "" - -#: daemon/HTTPServer.cpp:115 -#, c-format -msgid "%d minute" -msgid_plural "%d minutes" -msgstr[0] "" -msgstr[1] "" - -#: daemon/HTTPServer.cpp:118 -#, c-format -msgid "%d second" -msgid_plural "%d seconds" -msgstr[0] "" -msgstr[1] "" - -#. tr: Kibibyte -#: daemon/HTTPServer.cpp:126 -#, c-format -msgid "%.2f KiB" -msgstr "" - -#. tr: Mebibyte -#: daemon/HTTPServer.cpp:128 -#, c-format -msgid "%.2f MiB" -msgstr "" - -#. tr: Gibibyte -#: daemon/HTTPServer.cpp:130 -#, c-format -msgid "%.2f GiB" -msgstr "" - -#: daemon/HTTPServer.cpp:147 -msgid "building" -msgstr "" - -#: daemon/HTTPServer.cpp:148 -msgid "failed" -msgstr "" - -#: daemon/HTTPServer.cpp:149 -msgid "expiring" -msgstr "" - -#: daemon/HTTPServer.cpp:150 -msgid "established" -msgstr "" - -#: daemon/HTTPServer.cpp:151 -msgid "unknown" -msgstr "" - -#: daemon/HTTPServer.cpp:153 -msgid "exploratory" -msgstr "" - -#. tr: Webconsole page title -#: daemon/HTTPServer.cpp:185 -msgid "Purple I2P Webconsole" -msgstr "" - -#: daemon/HTTPServer.cpp:190 -msgid "i2pd webconsole" -msgstr "" - -#: daemon/HTTPServer.cpp:193 -msgid "Main page" -msgstr "" - -#: daemon/HTTPServer.cpp:194 daemon/HTTPServer.cpp:742 -msgid "Router commands" -msgstr "" - -#: daemon/HTTPServer.cpp:195 daemon/HTTPServer.cpp:395 -#: daemon/HTTPServer.cpp:407 -msgid "Local Destinations" -msgstr "" - -#: daemon/HTTPServer.cpp:197 daemon/HTTPServer.cpp:365 -#: daemon/HTTPServer.cpp:454 daemon/HTTPServer.cpp:474 -#: daemon/HTTPServer.cpp:636 daemon/HTTPServer.cpp:682 -#: daemon/HTTPServer.cpp:686 -msgid "LeaseSets" -msgstr "" - -#: daemon/HTTPServer.cpp:199 daemon/HTTPServer.cpp:692 -msgid "Tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:201 daemon/HTTPServer.cpp:372 -#: daemon/HTTPServer.cpp:813 daemon/HTTPServer.cpp:830 -msgid "Transit Tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:203 daemon/HTTPServer.cpp:898 -msgid "Transports" -msgstr "" - -#: daemon/HTTPServer.cpp:204 -msgid "I2P tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:206 daemon/HTTPServer.cpp:927 -#: daemon/HTTPServer.cpp:937 -msgid "SAM sessions" -msgstr "" - -#: daemon/HTTPServer.cpp:222 daemon/HTTPServer.cpp:1329 -#: daemon/HTTPServer.cpp:1332 daemon/HTTPServer.cpp:1335 -#: daemon/HTTPServer.cpp:1362 daemon/HTTPServer.cpp:1365 -#: daemon/HTTPServer.cpp:1379 daemon/HTTPServer.cpp:1424 -#: daemon/HTTPServer.cpp:1427 daemon/HTTPServer.cpp:1430 -msgid "ERROR" -msgstr "" - -#: daemon/HTTPServer.cpp:229 -msgid "OK" -msgstr "" - -#: daemon/HTTPServer.cpp:230 -msgid "Testing" -msgstr "" - -#: daemon/HTTPServer.cpp:231 -msgid "Firewalled" -msgstr "" - -#: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:235 -#: daemon/HTTPServer.cpp:336 -msgid "Unknown" -msgstr "" - -#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:382 -#: daemon/HTTPServer.cpp:383 daemon/HTTPServer.cpp:1003 -#: daemon/HTTPServer.cpp:1011 -msgid "Proxy" -msgstr "" - -#: daemon/HTTPServer.cpp:234 -msgid "Mesh" -msgstr "" - -#: daemon/HTTPServer.cpp:242 -msgid "Clock skew" -msgstr "" - -#: daemon/HTTPServer.cpp:245 -msgid "Offline" -msgstr "" - -#: daemon/HTTPServer.cpp:248 -msgid "Symmetric NAT" -msgstr "" - -#: daemon/HTTPServer.cpp:251 -msgid "Full cone NAT" -msgstr "" - -#: daemon/HTTPServer.cpp:254 -msgid "No Descriptors" -msgstr "" - -#: daemon/HTTPServer.cpp:263 -msgid "Uptime" -msgstr "" - -#: daemon/HTTPServer.cpp:266 -msgid "Network status" -msgstr "" - -#: daemon/HTTPServer.cpp:271 -msgid "Network status v6" -msgstr "" - -#: daemon/HTTPServer.cpp:277 daemon/HTTPServer.cpp:284 -msgid "Stopping in" -msgstr "" - -#: daemon/HTTPServer.cpp:291 -msgid "Family" -msgstr "" - -#: daemon/HTTPServer.cpp:292 -msgid "Tunnel creation success rate" -msgstr "" - -#: daemon/HTTPServer.cpp:296 -msgid "Total tunnel creation success rate" -msgstr "" - -#: daemon/HTTPServer.cpp:298 -msgid "Received" -msgstr "" - -#. tr: Kibibyte/s -#: daemon/HTTPServer.cpp:300 daemon/HTTPServer.cpp:303 -#: daemon/HTTPServer.cpp:306 -#, c-format -msgid "%.2f KiB/s" -msgstr "" - -#: daemon/HTTPServer.cpp:301 -msgid "Sent" -msgstr "" - -#: daemon/HTTPServer.cpp:304 -msgid "Transit" -msgstr "" - -#: daemon/HTTPServer.cpp:307 -msgid "Data path" -msgstr "" - -#: daemon/HTTPServer.cpp:310 -msgid "Hidden content. Press on text to see." -msgstr "" - -#: daemon/HTTPServer.cpp:314 -msgid "Router Ident" -msgstr "" - -#: daemon/HTTPServer.cpp:316 -msgid "Router Family" -msgstr "" - -#: daemon/HTTPServer.cpp:317 -msgid "Router Caps" -msgstr "" - -#: daemon/HTTPServer.cpp:318 -msgid "Version" -msgstr "" - -#: daemon/HTTPServer.cpp:319 -msgid "Our external address" -msgstr "" - -#. tr: Shown when router doesn't publish itself and have "Firewalled" state -#: daemon/HTTPServer.cpp:349 -msgid "supported" -msgstr "" - -#: daemon/HTTPServer.cpp:363 -msgid "Routers" -msgstr "" - -#: daemon/HTTPServer.cpp:364 -msgid "Floodfills" -msgstr "" - -#: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:987 -msgid "Client Tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:381 -msgid "Services" -msgstr "" - -#: daemon/HTTPServer.cpp:382 daemon/HTTPServer.cpp:383 -#: daemon/HTTPServer.cpp:384 daemon/HTTPServer.cpp:385 -#: daemon/HTTPServer.cpp:386 daemon/HTTPServer.cpp:387 -msgid "Enabled" -msgstr "" - -#: daemon/HTTPServer.cpp:382 daemon/HTTPServer.cpp:383 -#: daemon/HTTPServer.cpp:384 daemon/HTTPServer.cpp:385 -#: daemon/HTTPServer.cpp:386 daemon/HTTPServer.cpp:387 -msgid "Disabled" -msgstr "" - -#: daemon/HTTPServer.cpp:434 -msgid "Encrypted B33 address" -msgstr "" - -#: daemon/HTTPServer.cpp:442 -msgid "Address registration line" -msgstr "" - -#: daemon/HTTPServer.cpp:447 -msgid "Domain" -msgstr "" - -#: daemon/HTTPServer.cpp:448 -msgid "Generate" -msgstr "" - -#: daemon/HTTPServer.cpp:449 -msgid "" -"Note: result string can be used only for registering 2LD domains " -"(example.i2p). For registering subdomains please use i2pd-tools." -msgstr "" - -#: daemon/HTTPServer.cpp:457 -msgid "Address" -msgstr "" - -#: daemon/HTTPServer.cpp:459 -msgid "Type" -msgstr "" - -#: daemon/HTTPServer.cpp:460 -msgid "EncType" -msgstr "" - -#: daemon/HTTPServer.cpp:467 -msgid "Expire LeaseSet" -msgstr "" - -#: daemon/HTTPServer.cpp:479 daemon/HTTPServer.cpp:697 -msgid "Inbound tunnels" -msgstr "" - -#. tr: Milliseconds -#: daemon/HTTPServer.cpp:494 daemon/HTTPServer.cpp:514 -#: daemon/HTTPServer.cpp:711 daemon/HTTPServer.cpp:731 -#, c-format -msgid "%dms" -msgstr "" - -#: daemon/HTTPServer.cpp:499 daemon/HTTPServer.cpp:716 -msgid "Outbound tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:521 -msgid "Tags" -msgstr "" - -#: daemon/HTTPServer.cpp:522 -msgid "Incoming" -msgstr "" - -#: daemon/HTTPServer.cpp:529 daemon/HTTPServer.cpp:535 -msgid "Outgoing" -msgstr "" - -#: daemon/HTTPServer.cpp:532 daemon/HTTPServer.cpp:551 -msgid "Destination" -msgstr "" - -#: daemon/HTTPServer.cpp:532 daemon/HTTPServer.cpp:814 -msgid "Amount" -msgstr "" - -#: daemon/HTTPServer.cpp:540 -msgid "Incoming Tags" -msgstr "" - -#: daemon/HTTPServer.cpp:548 daemon/HTTPServer.cpp:554 -msgid "Tags sessions" -msgstr "" - -#: daemon/HTTPServer.cpp:551 -msgid "Status" -msgstr "" - -#: daemon/HTTPServer.cpp:561 daemon/HTTPServer.cpp:621 -msgid "Local Destination" -msgstr "" - -#: daemon/HTTPServer.cpp:572 daemon/HTTPServer.cpp:960 -msgid "Streams" -msgstr "" - -#: daemon/HTTPServer.cpp:595 -msgid "Close stream" -msgstr "" - -#: daemon/HTTPServer.cpp:613 daemon/HTTPServer.cpp:1430 -msgid "Such destination is not found" -msgstr "" - -#: daemon/HTTPServer.cpp:626 -msgid "I2CP session not found" -msgstr "" - -#: daemon/HTTPServer.cpp:629 -msgid "I2CP is not enabled" -msgstr "" - -#: daemon/HTTPServer.cpp:658 -msgid "Invalid" -msgstr "" - -#: daemon/HTTPServer.cpp:661 -msgid "Store type" -msgstr "" - -#: daemon/HTTPServer.cpp:662 -msgid "Expires" -msgstr "" - -#: daemon/HTTPServer.cpp:667 -msgid "Non Expired Leases" -msgstr "" - -#: daemon/HTTPServer.cpp:670 -msgid "Gateway" -msgstr "" - -#: daemon/HTTPServer.cpp:671 -msgid "TunnelID" -msgstr "" - -#: daemon/HTTPServer.cpp:672 -msgid "EndDate" -msgstr "" - -#: daemon/HTTPServer.cpp:682 -msgid "floodfill mode is disabled" -msgstr "" - -#: daemon/HTTPServer.cpp:693 -msgid "Queue size" -msgstr "" - -#: daemon/HTTPServer.cpp:743 -msgid "Run peer test" -msgstr "" - -#: daemon/HTTPServer.cpp:744 -msgid "Reload tunnels configuration" -msgstr "" - -#: daemon/HTTPServer.cpp:747 -msgid "Decline transit tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:749 -msgid "Accept transit tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:753 daemon/HTTPServer.cpp:758 -msgid "Cancel graceful shutdown" -msgstr "" - -#: daemon/HTTPServer.cpp:755 daemon/HTTPServer.cpp:760 -msgid "Start graceful shutdown" -msgstr "" - -#: daemon/HTTPServer.cpp:763 -msgid "Force shutdown" -msgstr "" - -#: daemon/HTTPServer.cpp:764 -msgid "Reload external CSS styles" -msgstr "" - -#: daemon/HTTPServer.cpp:767 -msgid "" -"Note: any action done here are not persistent and not changes your " -"config files." -msgstr "" - -#: daemon/HTTPServer.cpp:770 -msgid "Logging level" -msgstr "" - -#: daemon/HTTPServer.cpp:779 -msgid "Transit tunnels limit" -msgstr "" - -#: daemon/HTTPServer.cpp:784 daemon/HTTPServer.cpp:803 -msgid "Change" -msgstr "" - -#: daemon/HTTPServer.cpp:791 -msgid "Change language" -msgstr "" - -#: daemon/HTTPServer.cpp:830 -msgid "no transit tunnels currently built" -msgstr "" - -#: daemon/HTTPServer.cpp:921 daemon/HTTPServer.cpp:944 -msgid "SAM disabled" -msgstr "" - -#: daemon/HTTPServer.cpp:937 -msgid "no sessions currently running" -msgstr "" - -#: daemon/HTTPServer.cpp:950 -msgid "SAM session not found" -msgstr "" - -#: daemon/HTTPServer.cpp:955 -msgid "SAM Session" -msgstr "" - -#: daemon/HTTPServer.cpp:1020 -msgid "Server Tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:1036 -msgid "Client Forwards" -msgstr "" - -#: daemon/HTTPServer.cpp:1050 -msgid "Server Forwards" -msgstr "" - -#: daemon/HTTPServer.cpp:1250 -msgid "Unknown page" -msgstr "" - -#: daemon/HTTPServer.cpp:1269 -msgid "Invalid token" -msgstr "" - -#: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1359 -#: daemon/HTTPServer.cpp:1414 daemon/HTTPServer.cpp:1454 -msgid "SUCCESS" -msgstr "" - -#: daemon/HTTPServer.cpp:1327 -msgid "Stream closed" -msgstr "" - -#: daemon/HTTPServer.cpp:1329 -msgid "Stream not found or already was closed" -msgstr "" - -#: daemon/HTTPServer.cpp:1332 daemon/HTTPServer.cpp:1365 -msgid "Destination not found" -msgstr "" - -#: daemon/HTTPServer.cpp:1335 -msgid "StreamID can't be null" -msgstr "" - -#: daemon/HTTPServer.cpp:1337 daemon/HTTPServer.cpp:1367 -#: daemon/HTTPServer.cpp:1432 -msgid "Return to destination page" -msgstr "" - -#: daemon/HTTPServer.cpp:1338 daemon/HTTPServer.cpp:1368 -#: daemon/HTTPServer.cpp:1381 daemon/HTTPServer.cpp:1456 -#, c-format -msgid "You will be redirected in %d seconds" -msgstr "" - -#: daemon/HTTPServer.cpp:1359 -msgid "LeaseSet expiration time updated" -msgstr "" - -#: daemon/HTTPServer.cpp:1362 -msgid "LeaseSet is not found or already expired" -msgstr "" - -#: daemon/HTTPServer.cpp:1379 -#, c-format -msgid "Transit tunnels count must not exceed %d" -msgstr "" - -#: daemon/HTTPServer.cpp:1380 daemon/HTTPServer.cpp:1455 -msgid "Back to commands list" -msgstr "" - -#: daemon/HTTPServer.cpp:1416 -msgid "Register at reg.i2p" -msgstr "" - -#: daemon/HTTPServer.cpp:1417 -msgid "Description" -msgstr "" - -#: daemon/HTTPServer.cpp:1417 -msgid "A bit information about service on domain" -msgstr "" - -#: daemon/HTTPServer.cpp:1418 -msgid "Submit" -msgstr "" - -#: daemon/HTTPServer.cpp:1424 -msgid "Domain can't end with .b32.i2p" -msgstr "" - -#: daemon/HTTPServer.cpp:1427 -msgid "Domain must end with .i2p" -msgstr "" - -#: daemon/HTTPServer.cpp:1450 -msgid "Unknown command" -msgstr "" - -#: daemon/HTTPServer.cpp:1454 -msgid "Command accepted" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:166 -msgid "Proxy error" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:174 -msgid "Proxy info" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:182 -msgid "Proxy error: Host not found" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:183 -msgid "Remote host not found in router's addressbook" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:184 -msgid "You may try to find this host on jump services below" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:333 libi2pd_client/HTTPProxy.cpp:348 -#: libi2pd_client/HTTPProxy.cpp:417 libi2pd_client/HTTPProxy.cpp:460 -msgid "Invalid request" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:333 -msgid "Proxy unable to parse your request" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:348 -msgid "Addresshelper is not supported" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:373 -#, c-format -msgid "" -"Host %s is already in router's addressbook. Be " -"careful: source of this URL may be harmful! Click here to update record: " -"Continue." -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:375 -msgid "Addresshelper forced update rejected" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:382 -#, c-format -msgid "" -"To add host %s in router's addressbook, click here: Continue." -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:384 -msgid "Addresshelper request" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:393 -#, c-format -msgid "" -"Host %s added to router's addressbook from helper. Click here to proceed: Continue." -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:395 -msgid "Addresshelper adding" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:402 -#, c-format -msgid "" -"Host %s is already in router's addressbook. Click " -"here to update record: Continue." -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:404 -msgid "Addresshelper update" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:417 -msgid "Invalid request URI" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:460 -msgid "Can't detect destination host from request" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:477 libi2pd_client/HTTPProxy.cpp:481 -msgid "Outproxy failure" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:477 -msgid "Bad outproxy settings" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:480 -#, c-format -msgid "Host %s is not inside I2P network, but outproxy is not enabled" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:569 -msgid "Unknown outproxy URL" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:575 -msgid "Cannot resolve upstream proxy" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:583 -msgid "Hostname is too long" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:610 -msgid "Cannot connect to upstream SOCKS proxy" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:616 -msgid "Cannot negotiate with SOCKS proxy" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:658 -msgid "CONNECT error" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:658 -msgid "Failed to connect" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:669 libi2pd_client/HTTPProxy.cpp:695 -msgid "SOCKS proxy error" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:677 -msgid "Failed to send request to upstream" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:698 -msgid "No reply from SOCKS proxy" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:705 -msgid "Cannot connect" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:705 -msgid "HTTP out proxy not implemented" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:706 -msgid "Cannot connect to upstream HTTP proxy" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:739 -msgid "Host is down" -msgstr "" - -#: libi2pd_client/HTTPProxy.cpp:739 -msgid "" -"Can't create connection to requested host, it may be down. Please try again " -"later." -msgstr "" diff --git a/contrib/i18n/README.md b/contrib/i18n/README.md deleted file mode 100644 index ce775ecb..00000000 --- a/contrib/i18n/README.md +++ /dev/null @@ -1,30 +0,0 @@ -`xgettext` command for extracting translation ---- - -``` -xgettext --omit-header -ctr: -ktr -kntr:1,2 daemon/HTTPServer.cpp libi2pd_client/HTTPProxy.cpp -``` - -Regex for transforming gettext translations to our format: ---- - -``` -in: ^(\"|#[:.,]|msgctxt)(.*)$\n -out: -``` - -``` -in: msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\n(msgstr\[1\]\ \"(.*)\"\n)?(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)? -out: #{"$2", {"$3", "$5", "$7", "$9", "$11"}},\n -``` - -``` -in: msgid\ \"(.*)\"\nmsgstr\ \"(.*)\"\n -out: {"$1", "$2"},\n -``` - - -``` -in: \n\n -out: \n -``` diff --git a/contrib/i2pd.conf b/contrib/i2pd.conf deleted file mode 100644 index c26f8af0..00000000 --- a/contrib/i2pd.conf +++ /dev/null @@ -1,279 +0,0 @@ -## Configuration file for a typical i2pd user -## See https://i2pd.readthedocs.io/en/latest/user-guide/configuration/ -## for more options you can use in this file. - -## Lines that begin with "## " try to explain what's going on. Lines -## that begin with just "#" are disabled commands: you can enable them -## by removing the "#" symbol. - -## Tunnels config file -## Default: ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf -# tunconf = /var/lib/i2pd/tunnels.conf - -## Tunnels config files path -## Use that path to store separated tunnels in different config files. -## Default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d -# tunnelsdir = /var/lib/i2pd/tunnels.d - -## Path to certificates used for verifying .su3, families -## Default: ~/.i2pd/certificates or /var/lib/i2pd/certificates -# certsdir = /var/lib/i2pd/certificates - -## Where to write pidfile (default: /run/i2pd.pid, not used in Windows) -# pidfile = /run/i2pd.pid - -## Logging configuration section -## By default logs go to stdout with level 'info' and higher -## For Windows OS by default logs go to file with level 'warn' and higher -## -## Logs destination (valid values: stdout, file, syslog) -## * stdout - print log entries to stdout -## * file - log entries to a file -## * syslog - use syslog, see man 3 syslog -# log = file -## Path to logfile (default: autodetect) -# logfile = /var/log/i2pd/i2pd.log -## Log messages above this level (debug, info, *warn, error, critical, none) -## If you set it to none, logging will be disabled -# loglevel = warn -## Write full CLF-formatted date and time to log (default: write only time) -# logclftime = true - -## Daemon mode. Router will go to background after start. Ignored on Windows -## (default: true) -# daemon = true - -## Specify a family, router belongs to (default - none) -# family = - -## Network interface to bind to -## Updates address4/6 options if they are not set -# ifname = -## You can specify different interfaces for IPv4 and IPv6 -# ifname4 = -# ifname6 = - -## Local address to bind transport sockets to -## Overrides host option if: -## For ipv4: if ipv4 = true and nat = false -## For ipv6: if 'host' is not set or ipv4 = true -# address4 = -# address6 = - -## External IPv4 or IPv6 address to listen for connections -## By default i2pd sets IP automatically -## Sets published NTCP2v4/SSUv4 address to 'host' value if nat = true -## Sets published NTCP2v6/SSUv6 address to 'host' value if ipv4 = false -# host = 1.2.3.4 - -## Port to listen for connections -## By default i2pd picks random port. You MUST pick a random number too, -## don't just uncomment this -# port = 4567 - -## Enable communication through ipv4 (default: true) -ipv4 = true -## Enable communication through ipv6 (default: false) -ipv6 = false - -## Bandwidth configuration -## L limit bandwidth to 32 KB/sec, O - to 256 KB/sec, P - to 2048 KB/sec, -## X - unlimited -## Default is L (regular node) and X if floodfill mode enabled. -## If you want to share more bandwidth without floodfill mode, uncomment -## that line and adjust value to your possibilities. Value can be set to -## integer in kilobytes, it will apply that limit and flag will be used -## from next upper limit (example: if you set 4096 flag will be X, but real -## limit will be 4096 KB/s). Same can be done when floodfill mode is used, -## but keep in mind that low values may be negatively evaluated by Java -## router algorithms. -# bandwidth = L -## Max % of bandwidth limit for transit. 0-100 (default: 100) -# share = 100 - -## Router will not accept transit tunnels, disabling transit traffic completely -## (default: false) -# notransit = true - -## Router will be floodfill (default: false) -## Note: that mode uses much more network connections and CPU! -# floodfill = true - -[ntcp2] -## Enable NTCP2 transport (default: true) -# enabled = true -## Publish address in RouterInfo (default: true) -# published = true -## Port for incoming connections (default is global port option value) -# port = 4567 - -[ssu2] -## Enable SSU2 transport (default: true) -# enabled = true -## Publish address in RouterInfo (default: true) -# published = true -## Port for incoming connections (default is global port option value) -# port = 4567 - -[http] -## Web Console settings -## Enable the Web Console (default: true) -# enabled = true -## Address and port service will listen on (default: 127.0.0.1:7070) -# address = 127.0.0.1 -# port = 7070 -## Path to web console (default: /) -# webroot = / -## Enable Web Console authentication (default: false) -## You should not use Web Console via public networks without additional encryption. -## HTTP authentication is not encryption layer! -# auth = true -# user = i2pd -# pass = changeme -## Select webconsole language -## Currently supported english (default), afrikaans, armenian, chinese, czech, french, -## german, italian, polish, portuguese, russian, spanish, turkish, turkmen, ukrainian -## and uzbek languages -# lang = english - -[httpproxy] -## Enable the HTTP proxy (default: true) -# enabled = true -## Address and port service will listen on (default: 127.0.0.1:4444) -# address = 127.0.0.1 -# port = 4444 -## Optional keys file for proxy local destination (default: http-proxy-keys.dat) -# keys = http-proxy-keys.dat -## Enable address helper for adding .i2p domains with "jump URLs" (default: true) -## You should disable this feature if your i2pd HTTP Proxy is public, -## because anyone could spoof the short domain via addresshelper and forward other users to phishing links -# addresshelper = true -## Address of a proxy server inside I2P, which is used to visit regular Internet -# outproxy = http://false.i2p -## httpproxy section also accepts I2CP parameters, like "inbound.length" etc. - -[socksproxy] -## Enable the SOCKS proxy (default: true) -# enabled = true -## Address and port service will listen on (default: 127.0.0.1:4447) -# address = 127.0.0.1 -# port = 4447 -## Optional keys file for proxy local destination (default: socks-proxy-keys.dat) -# keys = socks-proxy-keys.dat -## Socks outproxy. Example below is set to use Tor for all connections except i2p -## Enable using of SOCKS outproxy (works only with SOCKS4, default: false) -# outproxy.enabled = false -## Address and port of outproxy -# outproxy = 127.0.0.1 -# outproxyport = 9050 -## socksproxy section also accepts I2CP parameters, like "inbound.length" etc. - -[sam] -## Enable the SAM bridge (default: true) -# enabled = false -## Address and ports service will listen on (default: 127.0.0.1:7656, udp: 7655) -# address = 127.0.0.1 -# port = 7656 -# portudp = 7655 - -[bob] -## Enable the BOB command channel (default: false) -# enabled = false -## Address and port service will listen on (default: 127.0.0.1:2827) -# address = 127.0.0.1 -# port = 2827 - -[i2cp] -## Enable the I2CP protocol (default: false) -# enabled = false -## Address and port service will listen on (default: 127.0.0.1:7654) -# address = 127.0.0.1 -# port = 7654 - -[i2pcontrol] -## Enable the I2PControl protocol (default: false) -# enabled = false -## Address and port service will listen on (default: 127.0.0.1:7650) -# address = 127.0.0.1 -# port = 7650 -## Authentication password (default: itoopie) -# password = itoopie - -[precomputation] -## Enable or disable elgamal precomputation table -## By default, enabled on i386 hosts -# elgamal = true - -[upnp] -## Enable or disable UPnP: automatic port forwarding (enabled by default in WINDOWS, ANDROID) -# enabled = false -## Name i2pd appears in UPnP forwardings list (default: I2Pd) -# name = I2Pd - -[meshnets] -## Enable connectivity over the Yggdrasil network (default: false) -# yggdrasil = false -## You can bind address from your Yggdrasil subnet 300::/64 -## The address must first be added to the network interface -# yggaddress = - -[reseed] -## Options for bootstrapping into I2P network, aka reseeding -## Enable reseed data verification (default: true) -verify = true -## URLs to request reseed data from, separated by comma -## Default: "mainline" I2P Network reseeds -# urls = https://reseed.i2p-projekt.de/,https://i2p.mooo.com/netDb/,https://netdb.i2p2.no/ -## Reseed URLs through the Yggdrasil, separated by comma -# yggurls = http://[324:71e:281a:9ed3::ace]:7070/ -## Path to local reseed data file (.su3) for manual reseeding -# file = /path/to/i2pseeds.su3 -## or HTTPS URL to reseed from -# file = https://legit-website.com/i2pseeds.su3 -## Path to local ZIP file or HTTPS URL to reseed from -# zipfile = /path/to/netDb.zip -## If you run i2pd behind a proxy server, set proxy server for reseeding here -## Should be http://address:port or socks://address:port -# proxy = http://127.0.0.1:8118 -## Minimum number of known routers, below which i2pd triggers reseeding (default: 25) -# threshold = 25 - -[addressbook] -## AddressBook subscription URL for initial setup -## Default: reg.i2p at "mainline" I2P Network -# defaulturl = http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt -## Optional subscriptions URLs, separated by comma -# subscriptions = http://reg.i2p/hosts.txt,http://identiguy.i2p/hosts.txt,http://stats.i2p/cgi-bin/newhosts.txt - -[limits] -## Maximum active transit sessions (default: 5000) -## This value is doubled if floodfill mode is enabled! -# transittunnels = 5000 -## Limit number of open file descriptors (0 - use system limit) -# openfiles = 0 -## Maximum size of corefile in Kb (0 - use system limit) -# coresize = 0 - -[trust] -## Enable explicit trust options. (default: false) -# enabled = true -## Make direct I2P connections only to routers in specified Family. -# family = MyFamily -## Make direct I2P connections only to routers specified here. Comma separated list of base64 identities. -# routers = -## Should we hide our router from other routers? (default: false) -# hidden = true - -[exploratory] -## Exploratory tunnels settings with default values -# inbound.length = 2 -# inbound.quantity = 3 -# outbound.length = 2 -# outbound.quantity = 3 - -[persist] -## Save peer profiles on disk (default: true) -# profiles = true -## Save full addresses on disk (default: true) -# addressbook = true - diff --git a/contrib/i2pd.logrotate b/contrib/i2pd.logrotate deleted file mode 100644 index d0ca70ad..00000000 --- a/contrib/i2pd.logrotate +++ /dev/null @@ -1,9 +0,0 @@ -"/var/log/i2pd/*.log" { - copytruncate - daily - rotate 5 - compress - delaycompress - missingok - notifempty -} diff --git a/contrib/i2pd.service b/contrib/i2pd.service deleted file mode 100644 index 1ab46979..00000000 --- a/contrib/i2pd.service +++ /dev/null @@ -1,38 +0,0 @@ -[Unit] -Description=I2P Router written in C++ -Documentation=man:i2pd(1) https://i2pd.readthedocs.io/en/latest/ -Wants=network.target -After=network.target network-online.target - -[Service] -User=i2pd -Group=i2pd -RuntimeDirectory=i2pd -RuntimeDirectoryMode=0700 -LogsDirectory=i2pd -LogsDirectoryMode=0700 -Type=forking -ExecStart=/usr/bin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --tunnelsdir=/etc/i2pd/tunnels.conf.d --pidfile=/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service -ExecReload=/bin/sh -c "kill -HUP $MAINPID" -PIDFile=/run/i2pd/i2pd.pid -### Uncomment, if auto restart needed -#Restart=on-failure - -# Use SIGTERM to stop i2pd immediately. -# Some cleanup processes can delay stopping, so we set 30 seconds timeout and then SIGKILL i2pd. -KillSignal=SIGTERM -TimeoutStopSec=30s -SendSIGKILL=yes - -# If you have the patience waiting 10 min on restarting/stopping it, uncomment this. -# i2pd stops accepting new tunnels and waits ~10 min while old ones do not die. -#KillSignal=SIGINT -#TimeoutStopSec=10m - -# If you have problems with hanging i2pd, you can try increase this -LimitNOFILE=8192 -# To enable write of coredump uncomment this -#LimitCORE=infinity - -[Install] -WantedBy=multi-user.target diff --git a/contrib/openrc/i2pd.openrc b/contrib/openrc/i2pd.openrc deleted file mode 100644 index 0233eed8..00000000 --- a/contrib/openrc/i2pd.openrc +++ /dev/null @@ -1,39 +0,0 @@ -#!/sbin/openrc-run - -pidfile="/var/run/i2pd/i2pd.pid" -logfile="/var/log/i2pd/i2pd.log" -mainconf="/etc/i2pd/i2pd.conf" -tunconf="/etc/i2pd/tunnels.conf" -tundir="/etc/i2pd/tunnels.conf.d" - -name="i2pd" -command="/usr/bin/i2pd" -command_args="--service --daemon --log=file --logfile=$logfile --conf=$mainconf --tunconf=$tunconf --tunnelsdir=$tundir --pidfile=$pidfile" -description="i2p router written in C++" -required_dirs="/var/lib/i2pd" -required_files="$mainconf" -start_stop_daemon_args="--chuid i2pd" - -depend() { - need mountall - use net - after bootmisc -} - -start_pre() { - if [ -r /etc/default/i2pd ]; then - . /etc/default/i2pd - fi - - if [ "x$I2PD_ENABLED" != "xyes" ]; then - ewarn "i2pd disabled in /etc/default/i2pd" - exit 1 - fi - - checkpath -f -o i2pd:adm $logfile - checkpath -f -o i2pd:adm $pidfile - - if [ -n "$DAEMON_OPTS" ]; then - command_args="$command_args $DAEMON_OPTS" - fi -} diff --git a/contrib/rpm/i2pd-git.spec b/contrib/rpm/i2pd-git.spec deleted file mode 100644 index 2083ba18..00000000 --- a/contrib/rpm/i2pd-git.spec +++ /dev/null @@ -1,287 +0,0 @@ -%define git_hash %(git rev-parse HEAD | cut -c -7) - -Name: i2pd-git -Version: 2.56.0 -Release: git%{git_hash}%{?dist} -Summary: I2P router written in C++ -Conflicts: i2pd - -License: BSD -URL: https://github.com/PurpleI2P/i2pd -Source0: https://github.com/PurpleI2P/i2pd/archive/openssl/i2pd-openssl.tar.gz - -%if 0%{?rhel} == 7 -BuildRequires: cmake3 -%else -BuildRequires: cmake -%endif - -BuildRequires: chrpath -BuildRequires: gcc-c++ -BuildRequires: zlib-devel -BuildRequires: boost-devel -BuildRequires: openssl-devel -BuildRequires: miniupnpc-devel -BuildRequires: systemd-units - -%if 0%{?fedora} == 41 -BuildRequires: openssl-devel-engine -%endif - -Requires: logrotate -Requires: systemd -Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd - - -%description -C++ implementation of I2P. - - -%prep -%setup -q -n i2pd-openssl - - -%build -cd build -%if 0%{?rhel} == 7 - %cmake3 \ - -DWITH_LIBRARY=OFF \ - -DWITH_UPNP=ON \ - -DWITH_HARDENING=ON \ - -DBUILD_SHARED_LIBS:BOOL=OFF -%else - %cmake \ - -DWITH_LIBRARY=OFF \ - -DWITH_UPNP=ON \ - -DWITH_HARDENING=ON \ - %if 0%{?fedora} > 29 - -DBUILD_SHARED_LIBS:BOOL=OFF \ - . - %else - -DBUILD_SHARED_LIBS:BOOL=OFF - %endif -%endif - -%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln} - pushd redhat-linux-build -%else - %if 0%{?fedora} >= 33 - pushd %{_target_platform} - %endif - - %if 0%{?mageia} > 7 - pushd build - %endif -%endif - -make %{?_smp_mflags} - -%if 0%{?rhel} == 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7 - popd -%endif - - -%install -pushd build - -%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln} - pushd redhat-linux-build -%else - %if 0%{?fedora} >= 33 - pushd %{_target_platform} - %endif - - %if 0%{?mageia} - pushd build - %endif -%endif - -chrpath -d i2pd -%{__install} -D -m 755 i2pd %{buildroot}%{_bindir}/i2pd -%{__install} -d -m 755 %{buildroot}%{_datadir}/i2pd -%{__install} -d -m 700 %{buildroot}%{_sharedstatedir}/i2pd -%{__install} -d -m 700 %{buildroot}%{_localstatedir}/log/i2pd -%{__install} -D -m 644 %{_builddir}/i2pd-openssl/contrib/i2pd.conf %{buildroot}%{_sysconfdir}/i2pd/i2pd.conf -%{__install} -D -m 644 %{_builddir}/i2pd-openssl/contrib/subscriptions.txt %{buildroot}%{_sysconfdir}/i2pd/subscriptions.txt -%{__install} -D -m 644 %{_builddir}/i2pd-openssl/contrib/tunnels.conf %{buildroot}%{_sysconfdir}/i2pd/tunnels.conf -%{__install} -D -m 644 %{_builddir}/i2pd-openssl/contrib/i2pd.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/i2pd -%{__install} -D -m 644 %{_builddir}/i2pd-openssl/contrib/i2pd.service %{buildroot}%{_unitdir}/i2pd.service -%{__install} -D -m 644 %{_builddir}/i2pd-openssl/debian/i2pd.1 %{buildroot}%{_mandir}/man1/i2pd.1 -%{__cp} -r %{_builddir}/i2pd-openssl/contrib/certificates/ %{buildroot}%{_datadir}/i2pd/certificates -%{__cp} -r %{_builddir}/i2pd-openssl/contrib/tunnels.d/ %{buildroot}%{_sysconfdir}/i2pd/tunnels.conf.d -ln -s %{_datadir}/%{name}/certificates %{buildroot}%{_sharedstatedir}/i2pd/certificates - - -%pre -getent group i2pd >/dev/null || %{_sbindir}/groupadd -r i2pd -getent passwd i2pd >/dev/null || \ - %{_sbindir}/useradd -r -g i2pd -s %{_sbindir}/nologin \ - -d %{_sharedstatedir}/i2pd -c 'I2P Service' i2pd - - -%post -%systemd_post i2pd.service - - -%preun -%systemd_preun i2pd.service - - -%postun -%systemd_postun_with_restart i2pd.service - - -%files -%doc LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf contrib/tunnels.d -%{_bindir}/i2pd -%config(noreplace) %{_sysconfdir}/i2pd/*.conf -%config(noreplace) %{_sysconfdir}/i2pd/tunnels.conf.d/*.conf -%config %{_sysconfdir}/i2pd/subscriptions.txt -%doc %{_sysconfdir}/i2pd/tunnels.conf.d/README -%{_sysconfdir}/logrotate.d/i2pd -%{_unitdir}/i2pd.service -%{_mandir}/man1/i2pd.1* -%dir %attr(0700,i2pd,i2pd) %{_sharedstatedir}/i2pd -%dir %attr(0700,i2pd,i2pd) %{_localstatedir}/log/i2pd -%{_datadir}/i2pd/certificates -%{_sharedstatedir}/i2pd/certificates - - -%changelog -* Tue Feb 11 2025 orignal - 2.56.0 -- update to 2.56.0 - -* Mon Dec 30 2024 orignal - 2.55.0 -- update to 2.55.0 - -* Sun Oct 6 2024 orignal - 2.54.0 -- update to 2.54.0 - -* Tue Jul 30 2024 orignal - 2.53.1 -- update to 2.53.1 - -* Fri Jul 19 2024 orignal - 2.53.0 -- update to 2.53.0 - -* Sun May 12 2024 orignal - 2.52.0 -- update to 2.52.0 - -* Sat Apr 06 2024 orignal - 2.51.0 -- update to 2.51.0 - -* Sat Jan 06 2024 orignal - 2.50.2 -- update to 2.50.2 - -* Sat Dec 23 2023 r4sas - 2.50.1 -- update to 2.50.1 - -* Mon Dec 18 2023 orignal - 2.50.0 -- update to 2.50.0 - -* Mon Sep 18 2023 orignal - 2.49.0 -- update to 2.49.0 - -* Mon Jun 12 2023 orignal - 2.48.0 -- update to 2.48.0 - -* Sat Mar 11 2023 orignal - 2.47.0 -- update to 2.47.0 - -* Mon Feb 20 2023 r4sas - 2.46.1 -- update to 2.46.1 - -* Wed Feb 15 2023 orignal - 2.46.0 -- update to 2.46.0 - -* Wed Jan 11 2023 orignal - 2.45.1 -- update to 2.45.1 - -* Tue Jan 3 2023 orignal - 2.45.0 -- update to 2.45.0 - -* Sun Nov 20 2022 orignal - 2.44.0 -- update to 2.44.0 - -* Mon Aug 22 2022 orignal - 2.43.0 -- update to 2.43.0 - -* Tue May 24 2022 r4sas - 2.42.1 -- update to 2.42.1 - -* Sun May 22 2022 orignal - 2.42.0 -- update to 2.42.0 - -* Sun Feb 20 2022 r4sas - 2.41.0 -- update to 2.41.0 -- fixed build on Fedora Copr over openssl trunk code - -* Mon Nov 29 2021 orignal - 2.40.0 -- update to 2.40.0 - -* Tue Aug 24 2021 r4sas - 2.39.0-2 -- changed if statements to cover fedora 35 - -* Mon Aug 23 2021 orignal - 2.39.0 -- update to 2.39.0 -- fixed build on fedora 36 - -* Mon May 17 2021 orignal - 2.38.0 -- update to 2.38.0 - -* Mon Mar 15 2021 orignal - 2.37.0 -- update to 2.37.0 - -* Mon Feb 15 2021 orignal - 2.36.0 -- update to 2.36.0 - -* Mon Nov 30 2020 orignal - 2.35.0 -- update to 2.35.0 - -* Tue Oct 27 2020 orignal - 2.34.0 -- update to 2.34.0 - -* Mon Aug 24 2020 orignal - 2.33.0 -- update to 2.33.0 - -* Tue Jun 02 2020 r4sas - 2.32.1 -- update to 2.32.1 - -* Mon May 25 2020 r4sas - 2.32.0 -- update to 2.32.0 -- updated systemd service file (#1394) - -* Thu May 7 2020 Anatolii Vorona - 2.31.0-3 -- added RPM logrotate config - -* Fri Apr 10 2020 orignal - 2.31.0 -- update to 2.31.0 - -* Tue Feb 25 2020 orignal - 2.30.0 -- update to 2.30.0 - -* Mon Oct 21 2019 orignal - 2.29.0 -- update to 2.29.0 - -* Tue Aug 27 2019 orignal - 2.28.0 -- update to 2.28.0 - -* Wed Jul 3 2019 orignal - 2.27.0 -- update to 2.27.0 - -* Fri Jun 7 2019 orignal - 2.26.0 -- update to 2.26.0 - -* Thu May 9 2019 orignal - 2.25.0 -- update to 2.25.0 - -* Thu Mar 21 2019 orignal - 2.24.0 -- update to 2.24.0 - -* Mon Jan 21 2019 orignal - 2.23.0 -- update to 2.23.0 - -* Fri Nov 09 2018 r4sas - 2.22.0 -- add support of tunnelsdir option - -* Thu Feb 01 2018 r4sas - 2.18.0 -- Initial i2pd-git based on i2pd 2.18.0-1 spec diff --git a/contrib/rpm/i2pd.spec b/contrib/rpm/i2pd.spec deleted file mode 100644 index 4eb558ba..00000000 --- a/contrib/rpm/i2pd.spec +++ /dev/null @@ -1,358 +0,0 @@ -Name: i2pd -Version: 2.56.0 -Release: 1%{?dist} -Summary: I2P router written in C++ -Conflicts: i2pd-git - -License: BSD -URL: https://github.com/PurpleI2P/i2pd -Source0: https://github.com/PurpleI2P/i2pd/archive/%{version}/%name-%version.tar.gz - -%if 0%{?rhel} == 7 -BuildRequires: cmake3 -%else -BuildRequires: cmake -%endif - -BuildRequires: chrpath -BuildRequires: gcc-c++ -BuildRequires: zlib-devel -BuildRequires: boost-devel -BuildRequires: openssl-devel -BuildRequires: miniupnpc-devel -BuildRequires: systemd-units - -%if 0%{?fedora} == 41 -BuildRequires: openssl-devel-engine -%endif - -Requires: logrotate -Requires: systemd -Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd - - -%description -C++ implementation of I2P. - - -%prep -%setup -q - - -%build -cd build -%if 0%{?rhel} == 7 - %cmake3 \ - -DWITH_LIBRARY=OFF \ - -DWITH_UPNP=ON \ - -DWITH_HARDENING=ON \ - -DBUILD_SHARED_LIBS:BOOL=OFF -%else - %cmake \ - -DWITH_LIBRARY=OFF \ - -DWITH_UPNP=ON \ - -DWITH_HARDENING=ON \ - %if 0%{?fedora} > 29 - -DBUILD_SHARED_LIBS:BOOL=OFF \ - . - %else - -DBUILD_SHARED_LIBS:BOOL=OFF - %endif -%endif - -%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln} - pushd redhat-linux-build -%else - %if 0%{?fedora} >= 33 - pushd %{_target_platform} - %endif - - %if 0%{?mageia} > 7 - pushd build - %endif -%endif - -make %{?_smp_mflags} - -%if 0%{?rhel} == 9 || 0%{?fedora} >= 33 || 0%{?mageia} > 7 - popd -%endif - - -%install -pushd build - -%if 0%{?rhel} == 9 || 0%{?fedora} >= 35 || 0%{?eln} - pushd redhat-linux-build -%else - %if 0%{?fedora} >= 33 - pushd %{_target_platform} - %endif - - %if 0%{?mageia} - pushd build - %endif -%endif - -chrpath -d i2pd -%{__install} -D -m 755 i2pd %{buildroot}%{_bindir}/i2pd -%{__install} -d -m 755 %{buildroot}%{_datadir}/i2pd -%{__install} -d -m 700 %{buildroot}%{_sharedstatedir}/i2pd -%{__install} -d -m 700 %{buildroot}%{_localstatedir}/log/i2pd -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/contrib/i2pd.conf %{buildroot}%{_sysconfdir}/i2pd/i2pd.conf -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/contrib/subscriptions.txt %{buildroot}%{_sysconfdir}/i2pd/subscriptions.txt -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/contrib/tunnels.conf %{buildroot}%{_sysconfdir}/i2pd/tunnels.conf -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/contrib/i2pd.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/i2pd -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/contrib/i2pd.service %{buildroot}%{_unitdir}/i2pd.service -%{__install} -D -m 644 %{_builddir}/%{name}-%{version}/debian/i2pd.1 %{buildroot}%{_mandir}/man1/i2pd.1 -%{__cp} -r %{_builddir}/%{name}-%{version}/contrib/certificates/ %{buildroot}%{_datadir}/i2pd/certificates -%{__cp} -r %{_builddir}/%{name}-%{version}/contrib/tunnels.d/ %{buildroot}%{_sysconfdir}/i2pd/tunnels.conf.d -ln -s %{_datadir}/%{name}/certificates %{buildroot}%{_sharedstatedir}/i2pd/certificates - - -%pre -getent group i2pd >/dev/null || %{_sbindir}/groupadd -r i2pd -getent passwd i2pd >/dev/null || \ - %{_sbindir}/useradd -r -g i2pd -s %{_sbindir}/nologin \ - -d %{_sharedstatedir}/i2pd -c 'I2P Service' i2pd - - -%post -%systemd_post i2pd.service - - -%preun -%systemd_preun i2pd.service - - -%postun -%systemd_postun_with_restart i2pd.service - - -%files -%doc LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf contrib/tunnels.d -%{_bindir}/i2pd -%config(noreplace) %{_sysconfdir}/i2pd/*.conf -%config(noreplace) %{_sysconfdir}/i2pd/tunnels.conf.d/*.conf -%config %{_sysconfdir}/i2pd/subscriptions.txt -%doc %{_sysconfdir}/i2pd/tunnels.conf.d/README -%{_sysconfdir}/logrotate.d/i2pd -%{_unitdir}/i2pd.service -%{_mandir}/man1/i2pd.1* -%dir %attr(0700,i2pd,i2pd) %{_sharedstatedir}/i2pd -%dir %attr(0700,i2pd,i2pd) %{_localstatedir}/log/i2pd -%{_datadir}/i2pd/certificates -%{_sharedstatedir}/i2pd/certificates - - -%changelog -* Tue Feb 11 2025 orignal - 2.56.0 -- update to 2.56.0 - -* Mon Dec 30 2024 orignal - 2.55.0 -- update to 2.55.0 - -* Sun Oct 6 2024 orignal - 2.54.0 -- update to 2.54.0 - -* Tue Jul 30 2024 orignal - 2.53.1 -- update to 2.53.1 - -* Fri Jul 19 2024 orignal - 2.53.0 -- update to 2.53.0 - -* Sun May 12 2024 orignal - 2.52.0 -- update to 2.52.0 - -* Sat Apr 06 2024 orignal - 2.51.0 -- update to 2.51.0 - -* Sat Jan 06 2024 orignal - 2.50.2 -- update to 2.50.2 - -* Sat Dec 23 2023 r4sas - 2.50.1 -- update to 2.50.1 - -* Mon Dec 18 2023 orignal - 2.50.0 -- update to 2.50.0 - -* Mon Sep 18 2023 orignal - 2.49.0 -- update to 2.49.0 - -* Mon Jun 12 2023 orignal - 2.48.0 -- update to 2.48.0 - -* Sat Mar 11 2023 orignal - 2.47.0 -- update to 2.47.0 - -* Mon Feb 20 2023 r4sas - 2.46.1 -- update to 2.46.1 - -* Wed Feb 15 2023 orignal - 2.46.0 -- update to 2.46.0 - -* Wed Jan 11 2023 orignal - 2.45.1 -- update to 2.45.1 - -* Tue Jan 3 2023 orignal - 2.45.0 -- update to 2.45.0 - -* Sun Nov 20 2022 orignal - 2.44.0 -- update to 2.44.0 - -* Mon Aug 22 2022 orignal - 2.43.0 -- update to 2.43.0 - -* Tue May 24 2022 r4sas - 2.42.1 -- update to 2.42.1 - -* Sun May 22 2022 orignal - 2.42.0 -- update to 2.42.0 - -* Sun Feb 20 2022 r4sas - 2.41.0 -- update to 2.41.0 - -* Mon Nov 29 2021 orignal - 2.40.0 -- update to 2.40.0 - -* Tue Aug 24 2021 r4sas - 2.39.0-2 -- changed if statements to cover fedora 35 - -* Mon Aug 23 2021 orignal - 2.39.0 -- update to 2.39.0 -- fixed build on fedora 36 - -* Mon May 17 2021 orignal - 2.38.0 -- update to 2.38.0 - -* Mon Mar 15 2021 orignal - 2.37.0 -- update to 2.37.0 - -* Mon Feb 15 2021 orignal - 2.36.0 -- update to 2.36.0 - -* Mon Nov 30 2020 orignal - 2.35.0 -- update to 2.35.0 - -* Tue Oct 27 2020 orignal - 2.34.0 -- update to 2.34.0 - -* Mon Aug 24 2020 orignal - 2.33.0 -- update to 2.33.0 - -* Tue Jun 02 2020 r4sas - 2.32.1 -- update to 2.32.1 - -* Mon May 25 2020 r4sas - 2.32.0 -- update to 2.32.0 -- updated systemd service file (#1394) - -* Thu May 7 2020 Anatolii Vorona - 2.31.0-3 -- added RPM logrotate config - -* Fri Apr 10 2020 orignal - 2.31.0 -- update to 2.31.0 - -* Tue Feb 25 2020 orignal - 2.30.0 -- update to 2.30.0 - -* Mon Oct 21 2019 orignal - 2.29.0 -- update to 2.29.0 - -* Tue Aug 27 2019 orignal - 2.28.0 -- update to 2.28.0 - -* Wed Jul 3 2019 orignal - 2.27.0 -- update to 2.27.0 - -* Fri Jun 7 2019 orignal - 2.26.0 -- update to 2.26.0 - -* Thu May 9 2019 orignal - 2.25.0 -- update to 2.25.0 - -* Thu Mar 21 2019 orignal - 2.24.0 -- update to 2.24.0 - -* Mon Jan 21 2019 orignal - 2.23.0 -- update to 2.23.0 - -* Fri Nov 09 2018 r4sas - 2.22.0 -- update to 2.22.0 -- add support of tunnelsdir option - -* Mon Oct 22 2018 orignal - 2.21.1 -- update to 2.21.1 - -* Thu Oct 4 2018 orignal - 2.21.0 -- update to 2.21.0 - -* Thu Aug 23 2018 orignal - 2.20.0 -- update to 2.20.0 - -* Tue Jun 26 2018 orignal - 2.19.0 -- update to 2.19.0 - -* Mon Feb 05 2018 r4sas - 2.18.0-2 -- Fixed blocking system shutdown for 10 minutes (#1089) - -* Thu Feb 01 2018 r4sas - 2.18.0-1 -- Added to conflicts i2pd-git package -- Fixed release versioning -- Fixed paths with double slashes - -* Tue Jan 30 2018 orignal - 2.18.0 -- update to 2.18.0 - -* Sat Jan 27 2018 l-n-s - 2.17.0-1 -- Added certificates and default configuration files -- Merge i2pd with i2pd-systemd package -- Fixed package changelogs to comply with guidelines - -* Mon Dec 04 2017 orignal - 2.17.0 -- update to 2.17.0 - -* Mon Nov 13 2017 orignal - 2.16.0 -- update to 2.16.0 - -* Thu Aug 17 2017 orignal - 2.15.0 -- update to 2.15.0 - -* Thu Jun 01 2017 orignal - 2.14.0 -- update to 2.14.0 - -* Thu Apr 06 2017 orignal - 2.13.0 -- update to 2.13.0 - -* Tue Feb 14 2017 orignal - 2.12.0 -- update to 2.12.0 - -* Mon Dec 19 2016 orignal - 2.11.0 -- update to 2.11.0 - -* Thu Oct 20 2016 Anatolii Vorona - 2.10.0-3 -- add support C7 -- move rpm-related files to contrib folder - -* Sun Oct 16 2016 Oleg Girko - 2.10.0-1 -- update to 2.10.0 - -* Sun Aug 14 2016 Oleg Girko - 2.9.0-1 -- update to 2.9.0 - -* Sun Aug 07 2016 Oleg Girko - 2.8.0-2 -- rename daemon subpackage to systemd - -* Sat Aug 06 2016 Oleg Girko - 2.8.0-1 -- update to 2.8.0 -- remove wrong rpath from i2pd binary -- add daemon subpackage with systemd unit file - -* Sat May 21 2016 Oleg Girko - 2.7.0-1 -- update to 2.7.0 - -* Tue Apr 05 2016 Oleg Girko - 2.6.0-1 -- update to 2.6.0 - -* Tue Jan 26 2016 Yaroslav Sidlovsky - 2.3.0-1 -- initial package for version 2.3.0 diff --git a/contrib/subscriptions.txt b/contrib/subscriptions.txt deleted file mode 100644 index 76d73993..00000000 --- a/contrib/subscriptions.txt +++ /dev/null @@ -1,4 +0,0 @@ -http://reg.i2p/hosts.txt -http://identiguy.i2p/hosts.txt -http://stats.i2p/cgi-bin/newhosts.txt -http://i2p-projekt.i2p/hosts.txt diff --git a/contrib/tunnels.conf b/contrib/tunnels.conf deleted file mode 100644 index fc455e79..00000000 --- a/contrib/tunnels.conf +++ /dev/null @@ -1,34 +0,0 @@ -[IRC-ILITA] -type = client -address = 127.0.0.1 -port = 6668 -destination = irc.ilita.i2p -destinationport = 6667 -keys = irc-keys.dat -i2p.streaming.profile=2 - -#[IRC-IRC2P] -#type = client -#address = 127.0.0.1 -#port = 6669 -#destination = irc.postman.i2p -#destinationport = 6667 -#keys = irc-keys.dat - -#[SMTP] -#type = client -#address = 127.0.0.1 -#port = 7659 -#destination = smtp.postman.i2p -#destinationport = 25 -#keys = smtp-keys.dat - -#[POP3] -#type = client -#address = 127.0.0.1 -#port = 7660 -#destination = pop.postman.i2p -#destinationport = 110 -#keys = pop3-keys.dat - -# see more examples at https://i2pd.readthedocs.io/en/latest/user-guide/tunnels/ diff --git a/contrib/tunnels.d/IRC-Ilita.conf b/contrib/tunnels.d/IRC-Ilita.conf deleted file mode 100644 index 74d836aa..00000000 --- a/contrib/tunnels.d/IRC-Ilita.conf +++ /dev/null @@ -1,7 +0,0 @@ -#[IRC-ILITA] -#type = client -#address = 127.0.0.1 -#port = 6669 -#destination = irc.ilita.i2p -#destinationport = 6667 -#keys = irc-keys.dat diff --git a/contrib/tunnels.d/IRC-Irc2P.conf b/contrib/tunnels.d/IRC-Irc2P.conf deleted file mode 100644 index 97da71dc..00000000 --- a/contrib/tunnels.d/IRC-Irc2P.conf +++ /dev/null @@ -1,7 +0,0 @@ -#[IRC-IRC2P] -#type = client -#address = 127.0.0.1 -#port = 6668 -#destination = irc.postman.i2p -#destinationport = 6667 -#keys = irc-keys.dat diff --git a/contrib/tunnels.d/README b/contrib/tunnels.d/README deleted file mode 100644 index 7b07c4be..00000000 --- a/contrib/tunnels.d/README +++ /dev/null @@ -1,4 +0,0 @@ -# In that directory you can store separated config files for every tunnel. -# Please read documentation for more info. -# -# You can find examples in /usr/share/doc/i2pd/tunnels.d directory diff --git a/contrib/webconsole/style.css b/contrib/webconsole/style.css deleted file mode 100644 index 89021539..00000000 --- a/contrib/webconsole/style.css +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (c) 2021-2023, The PurpleI2P Project - * - * This file is part of Purple i2pd project and licensed under BSD3 - * - * See full license text in LICENSE file at top of project tree - * - ****************************************************************** - * - * This is style sheet for webconsole, with @media selectors for adaptive - * view on desktop and mobile devices, respecting preferred user's color - * scheme used in system/browser. - * - * Minified copy of that style sheet is bundled inside i2pd sources. -*/ - -:root { - --main-bg-color: #fafafa; - --main-text-color: #103456; - --main-link-color: #894c84; - --main-link-hover-color: #fafafa; -} - -@media (prefers-color-scheme: dark) { - :root { - --main-bg-color: #242424; - --main-text-color: #17ab5c; - --main-link-color: #bf64b7; - --main-link-hover-color: #000000; - } -} - -body { - font: 100%/1.5em sans-serif; - margin: 0; - padding: 1.5em; - background: var(--main-bg-color); - color: var(--main-text-color); -} - -a, .slide label { - text-decoration: none; - color: var(--main-link-color); -} - -a:hover, a.button.selected, .slide label:hover, button[type=submit]:hover { - color: var(--main-link-hover-color); - background: var(--main-link-color); -} - -a.button { - appearance: button; - text-decoration: none; - padding: 0 5px; - border: 1px solid var(--main-link-color); -} - -.header { - font-size: 2.5em; - text-align: center; - margin: 1em 0; - color: var(--main-link-color); -} - -.wrapper { - margin: 0 auto; - padding: 1em; - max-width: 64em; -} - -.menu { - display: block; - float: left; - overflow: hidden; - padding: 4px; - max-width: 12em; - white-space: nowrap; - text-overflow: ellipsis; -} - -.listitem { - display: block; - font-family: monospace; - font-size: 1.2em; - white-space: nowrap; -} - -.tableitem { - font-family: monospace; - font-size: 1.2em; - white-space: nowrap; -} - -.content { - float: left; - font-size: 1em; - margin-left: 2em; - padding: 4px; - max-width: 50em; - overflow: auto; -} - -.tunnel.established { - color: #56B734; -} - -.tunnel.expiring { - color: #D3AE3F; -} - -.tunnel.failed { - color: #D33F3F; -} - -.tunnel.building { - color: #434343; -} - -caption { - font-size: 1.5em; - text-align: center; - color: var(--main-link-color); -} - -table { - display: table; - border-collapse: collapse; - text-align: center; -} - -table.extaddr { - text-align: left; -} - -table.services { - width: 100%; -} - -textarea { - background-color: var(--main-bg-color); - color: var(--main-text-color); - word-break: break-all; -} - -.streamdest { - width: 120px; - max-width: 240px; - overflow: hidden; - text-overflow: ellipsis; -} - -.slide div.slidecontent, .slide [type="checkbox"] { - display: none; -} - -.slide [type="checkbox"]:checked ~ div.slidecontent { - display: block; - margin-top: 0; - padding: 0; -} - -.disabled { - color: #D33F3F; -} - -.enabled { - color: #56B734; -} - -button[type=submit] { - background-color: transparent; - color: var(--main-link-color); - text-decoration: none; - padding: 5px; - border: 1px solid var(--main-link-color); - font-size: 14px; -} - -input, select, select option { - background-color: var(--main-bg-color); - color: var(--main-link-color); - padding: 5px; - border: 1px solid var(--main-link-color); - font-size: 14px; -} - -input:focus, select:focus, select option:focus { - outline: none; -} - -input[type=number]::-webkit-inner-spin-button { - -webkit-appearance: none; -} - -@media screen and (max-width: 1150px) { /* adaptive style */ - .wrapper { - max-width: 58em; - } - - .content { - max-width: 40em; - } -} - -@media screen and (max-width: 980px) { - body { - font: 100%/1.2em sans-serif; - padding: 1.2em 0 0 0; - } - - .menu { - width: 100%; - max-width: unset; - display: block; - float: none; - position: unset; - font-size: 16px; - text-align: center; - } - - .menu a, .commands a { - display: inline-block; - padding: 4px; - } - - .content { - float: none; - margin-left: unset; - margin-top: 16px; - max-width: 100%; - width: 100%; - text-align: center; - } - - a, .slide label { - display: block; - } - - .header { - margin: unset; - font-size: 1.5em; - } - - small { - display: block - } - - a.button { - appearance: button; - text-decoration: none; - margin-top: 10px; - padding: 6px; - border: 2px solid var(--main-link-color); - border-radius: 5px; - width: -webkit-fill-available; - } - - input, select { - width: 35%; - text-align: center; - padding: 5px; - border: 2px solid var(--main-link-color); - border-radius: 5px; - font-size: 18px; - } - - table.extaddr { - margin: auto; - text-align: unset; - } - - textarea { - width: -webkit-fill-available; - height: auto; - padding: 5px; - border: 2px solid var(--main-link-color); - border-radius: 5px; - font-size: 12px; - } - - button[type=submit] { - padding: 5px 15px; - background: transparent; - border: 2px solid var(--main-link-color); - cursor: pointer; - -webkit-border-radius: 5px; - border-radius: 5px; - position: relative; - height: 36px; - display: -webkit-inline-box; - margin-top: 10px; - } -} diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp deleted file mode 100644 index e2fdf2d4..00000000 --- a/daemon/Daemon.cpp +++ /dev/null @@ -1,408 +0,0 @@ -/* -* Copyright (c) 2013-2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include - -#include "Daemon.h" - -#include "Config.h" -#include "Log.h" -#include "FS.h" -#include "Base.h" -#include "version.h" -#include "Transports.h" -#include "RouterInfo.h" -#include "RouterContext.h" -#include "Tunnel.h" -#include "HTTP.h" -#include "NetDb.hpp" -#include "Garlic.h" -#include "Streaming.h" -#include "Destination.h" -#include "HTTPServer.h" -#include "I2PControl.h" -#include "ClientContext.h" -#include "Crypto.h" -#include "UPnP.h" -#include "Timestamp.h" -#include "I18N.h" - -namespace i2p -{ -namespace util -{ - class Daemon_Singleton::Daemon_Singleton_Private - { - public: - Daemon_Singleton_Private() {}; - ~Daemon_Singleton_Private() {}; - - std::unique_ptr httpServer; - std::unique_ptr m_I2PControlService; - std::unique_ptr UPnP; - std::unique_ptr m_NTPSync; - }; - - Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {} - Daemon_Singleton::~Daemon_Singleton() { - delete &d; - } - - bool Daemon_Singleton::IsService () const - { - bool service = false; - i2p::config::GetOption("service", service); - return service; - } - - void Daemon_Singleton::setDataDir(std::string path) - { - if (path != "") - DaemonDataDir = path; - } - - bool Daemon_Singleton::init(int argc, char* argv[]) { - return init(argc, argv, nullptr); - } - - bool Daemon_Singleton::init(int argc, char* argv[], std::shared_ptr logstream) - { - i2p::config::Init(); - i2p::config::ParseCmdline(argc, argv); - - std::string config; i2p::config::GetOption("conf", config); - std::string datadir; - if(DaemonDataDir != "") { - datadir = DaemonDataDir; - } else { - i2p::config::GetOption("datadir", datadir); - } - - i2p::fs::DetectDataDir(datadir, IsService()); - i2p::fs::Init(); - - datadir = i2p::fs::GetDataDir(); - - if (config == "") - { - config = i2p::fs::DataDirPath("i2pd.conf"); - if (!i2p::fs::Exists (config)) { - // use i2pd.conf only if exists - config = ""; /* reset */ - } - } - - i2p::config::ParseConfig(config); - i2p::config::Finalize(); - - i2p::config::GetOption("daemon", isDaemon); - - std::string certsdir; i2p::config::GetOption("certsdir", certsdir); - i2p::fs::SetCertsDir(certsdir); - - certsdir = i2p::fs::GetCertsDir(); - - std::string logs = ""; i2p::config::GetOption("log", logs); - std::string logfile = ""; i2p::config::GetOption("logfile", logfile); - std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel); - bool logclftime; i2p::config::GetOption("logclftime", logclftime); - - /* setup logging */ - if (logclftime) - i2p::log::Logger().SetTimeFormat ("[%d/%b/%Y:%H:%M:%S %z]"); - -#ifdef WIN32_APP - // Win32 app with GUI supports only logging to file - logs = "file"; -#else - if (isDaemon && (logs == "" || logs == "stdout")) - logs = "file"; -#endif - - i2p::log::Logger().SetLogLevel(loglevel); - if (logstream) { - LogPrint(eLogInfo, "Log: Sending messages to std::ostream"); - i2p::log::Logger().SendTo (logstream); - } else if (logs == "file") { - if (logfile == "") - logfile = i2p::fs::DataDirPath("i2pd.log"); - LogPrint(eLogInfo, "Log: Sending messages to ", logfile); - i2p::log::Logger().SendTo (logfile); -#ifndef _WIN32 - } else if (logs == "syslog") { - LogPrint(eLogInfo, "Log: Sending messages to syslog"); - i2p::log::Logger().SendTo("i2pd", LOG_DAEMON); -#endif - } else { - // use stdout -- default - } - - LogPrint(eLogNone, "i2pd v", VERSION, " (", I2P_VERSION, ") starting..."); - LogPrint(eLogDebug, "FS: Main config file: ", config); - LogPrint(eLogDebug, "FS: Data directory: ", datadir); - LogPrint(eLogDebug, "FS: Certificates directory: ", certsdir); - - bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); - bool ssu; i2p::config::GetOption("ssu", ssu); - if (!ssu && i2p::config::IsDefault ("precomputation.elgamal")) - precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly - i2p::crypto::InitCrypto (precomputation); - - i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces - - int netID; i2p::config::GetOption("netid", netID); - i2p::context.SetNetID (netID); - - bool checkReserved; i2p::config::GetOption("reservedrange", checkReserved); - i2p::transport::transports.SetCheckReserved(checkReserved); - - i2p::context.Init (); - - i2p::transport::InitTransports (); - - bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill); - if (isFloodfill) - { - LogPrint(eLogInfo, "Daemon: Router configured as floodfill"); - i2p::context.SetFloodfill (true); - } - else - i2p::context.SetFloodfill (false); - - bool transit; i2p::config::GetOption("notransit", transit); - i2p::context.SetAcceptsTunnels (!transit); - uint32_t transitTunnels; i2p::config::GetOption("limits.transittunnels", transitTunnels); - if (isFloodfill && i2p::config::IsDefault ("limits.transittunnels")) - transitTunnels *= 2; // double default number of transit tunnels for floodfill - i2p::tunnel::tunnels.SetMaxNumTransitTunnels (transitTunnels); - - /* this section also honors 'floodfill' flag, if set above */ - std::string bandwidth; i2p::config::GetOption("bandwidth", bandwidth); - if (bandwidth.length () > 0) - { - if (bandwidth.length () == 1 && ((bandwidth[0] >= 'K' && bandwidth[0] <= 'P') || bandwidth[0] == 'X' )) - { - i2p::context.SetBandwidth (bandwidth[0]); - LogPrint(eLogInfo, "Daemon: Bandwidth set to ", i2p::context.GetBandwidthLimit (), "KBps"); - } - else - { - auto value = std::atoi(bandwidth.c_str()); - if (value > 0) - { - i2p::context.SetBandwidth (value); - LogPrint(eLogInfo, "Daemon: Bandwidth set to ", i2p::context.GetBandwidthLimit (), " KBps"); - } - else - { - LogPrint(eLogInfo, "Daemon: Unexpected bandwidth ", bandwidth, ". Set to 'low'"); - i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_LOW_BANDWIDTH2); - } - } - } - else if (isFloodfill) - { - LogPrint(eLogInfo, "Daemon: Floodfill bandwidth set to 'extra'"); - i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2); - } - else - { - LogPrint(eLogInfo, "Daemon: bandwidth set to 'low'"); - i2p::context.SetBandwidth (i2p::data::CAPS_FLAG_LOW_BANDWIDTH2); - } - - int shareRatio; i2p::config::GetOption("share", shareRatio); - i2p::context.SetShareRatio (shareRatio); - - std::string family; i2p::config::GetOption("family", family); - i2p::context.SetFamily (family); - if (family.length () > 0) - LogPrint(eLogInfo, "Daemon: Router family set to ", family); - - bool trust; i2p::config::GetOption("trust.enabled", trust); - if (trust) - { - LogPrint(eLogInfo, "Daemon: Explicit trust enabled"); - std::string fam; i2p::config::GetOption("trust.family", fam); - std::string routers; i2p::config::GetOption("trust.routers", routers); - bool restricted = false; - if (fam.length() > 0) - { - std::set fams; - size_t pos = 0, comma; - do - { - comma = fam.find (',', pos); - fams.insert (fam.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos)); - pos = comma + 1; - } - while (comma != std::string::npos); - i2p::transport::transports.RestrictRoutesToFamilies(fams); - restricted = fams.size() > 0; - } - if (routers.length() > 0) { - std::set idents; - size_t pos = 0, comma; - do - { - comma = routers.find (',', pos); - i2p::data::IdentHash ident; - ident.FromBase64 (routers.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos)); - idents.insert (ident); - pos = comma + 1; - } - while (comma != std::string::npos); - LogPrint(eLogInfo, "Daemon: Setting restricted routes to use ", idents.size(), " trusted routers"); - i2p::transport::transports.RestrictRoutesToRouters(idents); - restricted = idents.size() > 0; - } - if(!restricted) - LogPrint(eLogError, "Daemon: No trusted routers of families specified"); - } - - bool hidden; i2p::config::GetOption("trust.hidden", hidden); - if (hidden) - { - LogPrint(eLogInfo, "Daemon: Hidden mode enabled"); - i2p::context.SetHidden(true); - } - - std::string httpLang; i2p::config::GetOption("http.lang", httpLang); - i2p::i18n::SetLanguage(httpLang); - - return true; - } - - bool Daemon_Singleton::start() - { - i2p::log::Logger().Start(); - LogPrint(eLogInfo, "Daemon: Starting NetDB"); - i2p::data::netdb.Start(); - - bool upnp; i2p::config::GetOption("upnp.enabled", upnp); - if (upnp) { - d.UPnP = std::unique_ptr(new i2p::transport::UPnP); - d.UPnP->Start (); - } - - bool nettime; i2p::config::GetOption("nettime.enabled", nettime); - if (nettime) - { - d.m_NTPSync = std::unique_ptr(new i2p::util::NTPTimeSync); - d.m_NTPSync->Start (); - } - - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); - LogPrint(eLogInfo, "Daemon: Starting Transports"); - if(!ssu2) LogPrint(eLogInfo, "Daemon: SSU2 disabled"); - if(!ntcp2) LogPrint(eLogInfo, "Daemon: NTCP2 disabled"); - - i2p::transport::transports.Start(ntcp2, ssu2); - if (i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2()) - LogPrint(eLogInfo, "Daemon: Transports started"); - else - { - LogPrint(eLogCritical, "Daemon: Failed to start Transports"); - /** shut down netdb right away */ - i2p::transport::transports.Stop(); - i2p::data::netdb.Stop(); - return false; - } - - bool http; i2p::config::GetOption("http.enabled", http); - if (http) { - std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); - uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); - LogPrint(eLogInfo, "Daemon: Starting Webconsole at ", httpAddr, ":", httpPort); - try - { - d.httpServer = std::unique_ptr(new i2p::http::HTTPServer(httpAddr, httpPort)); - d.httpServer->Start(); - } - catch (std::exception& ex) - { - LogPrint (eLogCritical, "Daemon: Failed to start Webconsole: ", ex.what ()); - ThrowFatal ("Unable to start webconsole at ", httpAddr, ":", httpPort, ": ", ex.what ()); - } - } - - LogPrint(eLogInfo, "Daemon: Starting Tunnels"); - i2p::tunnel::tunnels.Start(); - - LogPrint(eLogInfo, "Daemon: Starting Router context"); - i2p::context.Start(); - - LogPrint(eLogInfo, "Daemon: Starting Client"); - i2p::client::context.Start (); - - // I2P Control Protocol - bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); - if (i2pcontrol) { - std::string i2pcpAddr; i2p::config::GetOption("i2pcontrol.address", i2pcpAddr); - uint16_t i2pcpPort; i2p::config::GetOption("i2pcontrol.port", i2pcpPort); - LogPrint(eLogInfo, "Daemon: Starting I2PControl at ", i2pcpAddr, ":", i2pcpPort); - try - { - d.m_I2PControlService = std::unique_ptr(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort)); - d.m_I2PControlService->Start (); - } - catch (std::exception& ex) - { - LogPrint (eLogCritical, "Daemon: Failed to start I2PControl: ", ex.what ()); - ThrowFatal ("Unable to start I2PControl service at ", i2pcpAddr, ":", i2pcpPort, ": ", ex.what ()); - } - } - return true; - } - - bool Daemon_Singleton::stop() - { - LogPrint(eLogInfo, "Daemon: Shutting down"); - LogPrint(eLogInfo, "Daemon: Stopping Client"); - i2p::client::context.Stop(); - LogPrint(eLogInfo, "Daemon: Stopping Router context"); - i2p::context.Stop(); - LogPrint(eLogInfo, "Daemon: Stopping Tunnels"); - i2p::tunnel::tunnels.Stop(); - - if (d.UPnP) - { - d.UPnP->Stop (); - d.UPnP = nullptr; - } - - if (d.m_NTPSync) - { - d.m_NTPSync->Stop (); - d.m_NTPSync = nullptr; - } - - LogPrint(eLogInfo, "Daemon: Stopping Transports"); - i2p::transport::transports.Stop(); - LogPrint(eLogInfo, "Daemon: Stopping NetDB"); - i2p::data::netdb.Stop(); - if (d.httpServer) { - LogPrint(eLogInfo, "Daemon: Stopping HTTP Server"); - d.httpServer->Stop(); - d.httpServer = nullptr; - } - if (d.m_I2PControlService) - { - LogPrint(eLogInfo, "Daemon: Stopping I2PControl"); - d.m_I2PControlService->Stop (); - d.m_I2PControlService = nullptr; - } - i2p::crypto::TerminateCrypto (); - i2p::log::Logger().Stop(); - - return true; - } -} -} diff --git a/daemon/Daemon.h b/daemon/Daemon.h deleted file mode 100644 index 26d4a047..00000000 --- a/daemon/Daemon.h +++ /dev/null @@ -1,129 +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 DAEMON_H__ -#define DAEMON_H__ - -#include -#include -#include - -namespace i2p -{ -namespace util -{ - class Daemon_Singleton_Private; - class Daemon_Singleton - { - public: - - virtual bool init (int argc, char* argv[], std::shared_ptr logstream); - virtual bool init (int argc, char* argv[]); - virtual bool start (); - virtual bool stop (); - virtual void run () {}; - - virtual void setDataDir (std::string path); - - bool isDaemon; - bool running; - - protected: - - Daemon_Singleton (); - virtual ~Daemon_Singleton (); - - bool IsService () const; - - // d-pointer for httpServer, httpProxy, etc. - class Daemon_Singleton_Private; - Daemon_Singleton_Private &d; - - private: - - std::string DaemonDataDir; - }; - -#if defined(QT_GUI_LIB) // check if QT -#define Daemon i2p::util::DaemonQT::Instance() - // dummy, invoked from RunQT - class DaemonQT: public i2p::util::Daemon_Singleton - { - public: - - static DaemonQT& Instance() - { - static DaemonQT instance; - return instance; - } - }; - -#elif defined(_WIN32) -#define Daemon i2p::util::DaemonWin32::Instance() - class DaemonWin32 : public Daemon_Singleton - { - public: - - static DaemonWin32& Instance() - { - static DaemonWin32 instance; - return instance; - } - - bool init(int argc, char* argv[]); - bool start(); - bool stop(); - void run (); - - bool isGraceful; - - DaemonWin32 ():isGraceful(false) {} - }; -#elif (defined(ANDROID) && !defined(ANDROID_BINARY)) -#define Daemon i2p::util::DaemonAndroid::Instance() - // dummy, invoked from android/jni/DaemonAndroid.* - class DaemonAndroid: public i2p::util::Daemon_Singleton - { - public: - - static DaemonAndroid& Instance() - { - static DaemonAndroid instance; - return instance; - } - }; -#else -#define Daemon i2p::util::DaemonLinux::Instance() - class DaemonLinux : public Daemon_Singleton - { - public: - - static DaemonLinux& Instance() - { - static DaemonLinux instance; - return instance; - } - - bool start(); - bool stop(); - void run (); - - private: - - std::string pidfile; - int pidFH; - - public: - - int gracefulShutdownInterval; // in seconds - }; -#endif -} -} - -#endif // DAEMON_H__ diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp deleted file mode 100644 index dca545fe..00000000 --- a/daemon/HTTPServer.cpp +++ /dev/null @@ -1,1587 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include -#include - -#include -#include - -#include "Base.h" -#include "FS.h" -#include "Log.h" -#include "Config.h" -#include "Tunnel.h" -#include "Transports.h" -#include "NetDb.hpp" -#include "HTTP.h" -#include "LeaseSet.h" -#include "Destination.h" -#include "RouterContext.h" -#include "ClientContext.h" -#include "HTTPServer.h" -#include "Daemon.h" -#include "util.h" -#include "ECIESX25519AEADRatchetSession.h" -#include "I18N.h" - -#ifdef WIN32_APP -#include "Win32App.h" -#endif - -// For image, style and info -#include "version.h" -#include "HTTPServerResources.h" - -namespace i2p { -namespace http { - static void LoadExtCSS () - { - std::stringstream s; - std::string styleFile = i2p::fs::DataDirPath ("webconsole/style.css"); - if (i2p::fs::Exists(styleFile)) { - std::ifstream f(styleFile, std::ifstream::binary); - s << f.rdbuf(); - externalCSS = s.str(); - } else if (externalCSS.length() != 0) { // clean up external style if file was removed - externalCSS = ""; - } - } - - static void GetStyles (std::stringstream& s) - { - if (externalCSS.length() != 0) - s << "\r\n"; - else - s << internalCSS; - } - - const char HTTP_PAGE_TUNNELS[] = "tunnels"; - const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels"; - const char HTTP_PAGE_TRANSPORTS[] = "transports"; - const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations"; - const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination"; - const char HTTP_PAGE_I2CP_LOCAL_DESTINATION[] = "i2cp_local_destination"; - const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions"; - const char HTTP_PAGE_SAM_SESSION[] = "sam_session"; - const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels"; - const char HTTP_PAGE_COMMANDS[] = "commands"; - const char HTTP_PAGE_LEASESETS[] = "leasesets"; - const char HTTP_COMMAND_ENABLE_TRANSIT[] = "enable_transit"; - const char HTTP_COMMAND_DISABLE_TRANSIT[] = "disable_transit"; - const char HTTP_COMMAND_SHUTDOWN_START[] = "shutdown_start"; - const char HTTP_COMMAND_SHUTDOWN_CANCEL[] = "shutdown_cancel"; - const char HTTP_COMMAND_SHUTDOWN_NOW[] = "terminate"; - const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test"; - const char HTTP_COMMAND_RELOAD_TUNNELS_CONFIG[] = "reload_tunnels_config"; - const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel"; - const char HTTP_COMMAND_KILLSTREAM[] = "closestream"; - const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit"; - const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string"; - const char HTTP_COMMAND_SETLANGUAGE[] = "setlanguage"; - const char HTTP_COMMAND_RELOAD_CSS[] = "reload_css"; - const char HTTP_COMMAND_EXPIRELEASE[] = "expirelease"; - - static std::string ConvertTime (uint64_t time) - { - lldiv_t divTime = lldiv(time, 1000); - time_t t = divTime.quot; - struct tm *tm = localtime(&t); - char date[128]; - snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03lld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem); - return date; - } - - static void ShowUptime (std::stringstream& s, int seconds) - { - int num; - - if ((num = seconds / 86400) > 0) { - s << ntr("%d day", "%d days", num, num) << ", "; - seconds -= num * 86400; - } - if ((num = seconds / 3600) > 0) { - s << ntr("%d hour", "%d hours", num, num) << ", "; - seconds -= num * 3600; - } - if ((num = seconds / 60) > 0) { - s << ntr("%d minute", "%d minutes", num, num) << ", "; - seconds -= num * 60; - } - s << ntr("%d second", "%d seconds", seconds, seconds); - } - - static void ShowTraffic (std::stringstream& s, uint64_t bytes) - { - s << std::fixed << std::setprecision(2); - auto numKBytes = (double) bytes / 1024; - if (numKBytes < 1024) - s << tr(/* tr: Kibibyte */ "%.2f KiB", numKBytes); - else if (numKBytes < 1024 * 1024) - s << tr(/* tr: Mebibyte */ "%.2f MiB", numKBytes / 1024); - else - s << tr(/* tr: Gibibyte */ "%.2f GiB", numKBytes / 1024 / 1024); - } - - static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes) - { - std::string state; - std::string_view stateText; - switch (eState) - { - case i2p::tunnel::eTunnelStateBuildReplyReceived : - case i2p::tunnel::eTunnelStatePending : state = "building"; break; - case i2p::tunnel::eTunnelStateBuildFailed : state = "failed"; stateText = "declined"; break; - case i2p::tunnel::eTunnelStateTestFailed : state = "failed"; stateText = "test failed"; break; - case i2p::tunnel::eTunnelStateFailed : state = "failed"; break; - case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break; - case i2p::tunnel::eTunnelStateEstablished : state = "established"; break; - default: state = "unknown"; break; - } - if (stateText.empty ()) stateText = tr(state); - - s << " " << stateText << ((explr) ? " (" + std::string(tr("exploratory")) + ")" : "") << ", "; // TODO: - ShowTraffic(s, bytes); - s << "\r\n"; - } - - static void SetLogLevel (const std::string& level) - { - if (level == "none" || level == "critical" || level == "error" || level == "warn" || level == "info" || level == "debug") - i2p::log::Logger().SetLogLevel(level); - else { - LogPrint(eLogError, "HTTPServer: Unknown loglevel set attempted"); - return; - } - i2p::log::Logger().Reopen (); - } - - static void ShowPageHead (std::stringstream& s) - { - std::string webroot; i2p::config::GetOption("http.webroot", webroot); - - // Page language - std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language - auto it = i2p::i18n::languages.find(currLang); - std::string langCode = it->second.ShortCode; - - s << - "\r\n" - "\r\n" - " \r\n" /* TODO: Find something to parse html/template system. This is horrible. */ - " \r\n" - " \r\n" - " \r\n" - " " << tr(/* tr: Webconsole page title */ "Purple I2P Webconsole") << "\r\n"; - GetStyles(s); - s << - "\r\n" - "\r\n" - "
" << tr("i2pd webconsole") << "
\r\n" - "
\r\n" - "
\r\n" - " " << tr("Main page") << "

\r\n" - " " << tr("Router commands") << "
\r\n" - " " << tr("Local Destinations") << "
\r\n"; - if (i2p::context.IsFloodfill ()) - s << " " << tr("LeaseSets") << "
\r\n"; - s << - " " << tr("Tunnels") << "
\r\n"; - if (i2p::context.AcceptsTunnels () || i2p::tunnel::tunnels.CountTransitTunnels()) - s << " " << tr("Transit Tunnels") << "
\r\n"; - s << - " " << tr("Transports") << "
\r\n" - " " << tr("I2P tunnels") << "
\r\n"; - if (i2p::client::context.GetSAMBridge ()) - s << " " << tr("SAM sessions") << "
\r\n"; - s << - "
\r\n" - "
"; - } - - static void ShowPageTail (std::stringstream& s) - { - s << - "
\r\n
\r\n" - "\r\n" - "\r\n"; - } - - static void ShowError(std::stringstream& s, std::string_view string) - { - s << "" << tr("ERROR") << ": " << string << "
\r\n"; - } - - static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, bool testing, RouterError error) - { - switch (status) - { - case eRouterStatusOK: s << tr("OK"); break; - case eRouterStatusFirewalled: s << tr("Firewalled"); break; - case eRouterStatusUnknown: s << tr("Unknown"); break; - case eRouterStatusProxy: s << tr("Proxy"); break; - case eRouterStatusMesh: s << tr("Mesh"); break; - default: s << tr("Unknown"); - } - if (testing) - s << " (" << tr("Testing") << ")"; - if (error != eRouterErrorNone) - { - switch (error) - { - case eRouterErrorClockSkew: - s << " - " << tr("Clock skew"); - break; - case eRouterErrorOffline: - s << " - " << tr("Offline"); - break; - case eRouterErrorSymmetricNAT: - s << " - " << tr("Symmetric NAT"); - break; - case eRouterErrorFullConeNAT: - s << " - " << tr("Full cone NAT"); - break; - case eRouterErrorNoDescriptors: - s << " - " << tr("No Descriptors"); - break; - default: ; - } - } - } - - void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat) - { - s << "" << tr("Uptime") << ": "; - ShowUptime(s, i2p::context.GetUptime ()); - s << "
\r\n"; - s << "" << tr("Network status") << ": "; - ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetTesting(), i2p::context.GetError ()); - s << "
\r\n"; - if (i2p::context.SupportsV6 ()) - { - s << "" << tr("Network status v6") << ": "; - ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetTestingV6(), i2p::context.GetErrorV6 ()); - s << "
\r\n"; - } -#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) - if (auto remains = Daemon.gracefulShutdownInterval) { - s << "" << tr("Stopping in") << ": "; - ShowUptime(s, remains); - s << "
\r\n"; - } -#elif defined(WIN32_APP) - if (i2p::win32::g_GracefulShutdownEndtime != 0) { - uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000; - s << "" << tr("Stopping in") << ": "; - ShowUptime(s, remains); - s << "
\r\n"; - } -#endif - auto family = i2p::context.GetFamily (); - if (family.length () > 0) - s << ""<< tr("Family") << ": " << family << "
\r\n"; - s << "" << tr("Tunnel creation success rate") << ": " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%
\r\n"; - bool isTotalTCSR; - i2p::config::GetOption("http.showTotalTCSR", isTotalTCSR); - if (isTotalTCSR) { - s << "" << tr("Total tunnel creation success rate") << ": " << i2p::tunnel::tunnels.GetTotalTunnelCreationSuccessRate() << "%
\r\n"; - } - s << "" << tr("Received") << ": "; - ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ()); - s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetInBandwidth15s () / 1024) << ")
\r\n"; - s << "" << tr("Sent") << ": "; - ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ()); - s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetOutBandwidth15s () / 1024) << ")
\r\n"; - s << "" << tr("Transit") << ": "; - ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); - s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetTransitBandwidth15s () / 1024) << ")
\r\n"; - s << "" << tr("Data path") << ": " << i2p::fs::GetUTF8DataDir() << "
\r\n"; - s << "
"; - if ((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) { - s << "\r\n\r\n
\r\n"; - } - if (includeHiddenContent) - { - s << "" << tr("Router Ident") << ": " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; - if (!i2p::context.GetRouterInfo().GetProperty("family").empty()) - s << "" << tr("Router Family") << ": " << i2p::context.GetRouterInfo().GetProperty("family") << "
\r\n"; - s << "" << tr("Router Caps") << ": " << i2p::context.GetRouterInfo().GetProperty("caps") << "
\r\n"; - s << "" << tr("Version") << ": " VERSION "
\r\n"; - s << ""<< tr("Our external address") << ":" << "
\r\n\r\n"; - auto addresses = i2p::context.GetRouterInfo().GetAddresses (); - if (addresses) - { - for (const auto& address : *addresses) - { - if (!address) continue; - s << "\r\n\r\n"; - if (address->published) - s << "\r\n"; - else - { - s << "\r\n"; - } - s << "\r\n"; - } - } - s << "
"; - switch (address->transportStyle) - { - case i2p::data::RouterInfo::eTransportNTCP2: - s << "NTCP2"; - break; - case i2p::data::RouterInfo::eTransportSSU2: - s << "SSU2"; - break; - default: - s << tr("Unknown"); - } - bool v6 = address->IsV6 (); - if (v6) - { - if (address->IsV4 ()) s << "v4"; - s << "v6"; - } - s << "" << (v6 ? "[" : "") << address->host.to_string() << (v6 ? "]:" : ":") << address->port << "" << tr(/* tr: Shown when router doesn't publish itself and have "Firewalled" state */ "supported"); - if (address->port) - s << " :" << address->port; - s << "
\r\n"; - } - s << "
\r\n
\r\n"; - if (outputFormat == OutputFormatEnum::forQtUi) { - s << "
"; - } - s << "" << tr("Routers") << ": " << i2p::data::netdb.GetNumRouters () << " "; - s << "" << tr("Floodfills") << ": " << i2p::data::netdb.GetNumFloodfills () << " "; - s << "" << tr("LeaseSets") << ": " << i2p::data::netdb.GetNumLeaseSets () << "
\r\n"; - - size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels(); - clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels(); - size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels(); - - s << "" << tr("Client Tunnels") << ": " << std::to_string(clientTunnelCount) << " "; - s << "" << tr("Transit Tunnels") << ": " << std::to_string(transitTunnelCount) << "
\r\n
\r\n"; - - if (outputFormat==OutputFormatEnum::forWebConsole) { - bool httpproxy = i2p::client::context.GetHttpProxy () ? true : false; - bool socksproxy = i2p::client::context.GetSocksProxy () ? true : false; - bool bob = i2p::client::context.GetBOBCommandChannel () ? true : false; - bool sam = i2p::client::context.GetSAMBridge () ? true : false; - bool i2cp = i2p::client::context.GetI2CPServer () ? true : false; - bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "
" << tr("Services") << "
" << "HTTP " << tr("Proxy") << "" << (httpproxy ? tr("Enabled") : tr("Disabled")) << "
" << "SOCKS " << tr("Proxy") << "" << (socksproxy ? tr("Enabled") : tr("Disabled")) << "
" << "BOB" << "" << (bob ? tr("Enabled") : tr("Disabled")) << "
" << "SAM" << "" << (sam ? tr("Enabled") : tr("Disabled")) << "
" << "I2CP" << "" << (i2cp ? tr("Enabled") : tr("Disabled")) << "
" << "I2PControl" << "" << (i2pcontrol ? tr("Enabled") : tr("Disabled")) << "
\r\n"; - } - } - - void ShowLocalDestinations (std::stringstream& s) - { - std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "" << tr("Local Destinations") << ":
\r\n
\r\n"; - for (auto& it: i2p::client::context.GetDestinations ()) - { - auto ident = it.second->GetIdentHash (); - s << "\r\n" << std::endl; - } - s << "
\r\n"; - - auto i2cpServer = i2p::client::context.GetI2CPServer (); - if (i2cpServer && !(i2cpServer->GetSessions ().empty ())) - { - s << "
I2CP "<< tr("Local Destinations") << ":
\r\n
\r\n"; - for (auto& it: i2cpServer->GetSessions ()) - { - auto dest = it.second->GetDestination (); - if (dest) - { - auto ident = dest->GetIdentHash (); - auto& name = dest->GetNickname (); - s << "
[ "; - s << name << " ] ⇔ " << i2p::client::context.GetAddressBook ().ToAddress(ident) <<"
\r\n" << std::endl; - } - } - s << "
\r\n"; - } - } - - static void ShowHop(std::stringstream& s, const i2p::data::IdentityEx& ident) - { - auto identHash = ident.GetIdentHash(); - auto router = i2p::data::netdb.FindRouter(identHash); - s << i2p::data::GetIdentHashAbbreviation(identHash); - if (router) - s << " " << router->GetBandwidthCap() << ""; - } - - static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr dest, uint32_t token) - { - s << "Base32:
\r\n
\r\n
\r\n"; - - s << "Base64:
\r\n
\r\n
\r\n"; - - if (dest->IsEncryptedLeaseSet ()) - { - i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ()); - s << "
\r\n\r\n
\r\n"; - s << blinded.ToB33 () << ".b32.i2p
\r\n"; - s << "
\r\n
\r\n"; - } - - if (dest->IsPublic() && token && !dest->IsEncryptedLeaseSet ()) - { - std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "
\r\n\r\n
\r\n" - "
\r\n" - " \r\n" - " \r\n" - " GetIdentHash ().ToBase32 () << "\">\r\n" - " " << tr("Domain") << ":\r\n\r\n" - " \r\n" - "
\r\n" << tr("Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.") << "\r\n
\r\n
\r\n
\r\n"; - } - - if (dest->GetNumRemoteLeaseSets()) - { - s << "
\r\n\r\n
\r\n" - << "" - << "" - << "" // LeaseSet expiration button column - << "" - << "" - << ""; - for(auto& it: dest->GetLeaseSets ()) - { - s << "" - << "" - << "" - << "" - << "" - << "\r\n"; - } - s << "
" << tr("Address") << " " << tr("Type") << "" << tr("EncType") << "
" << it.first.ToBase32 () << "" << (int)it.second->GetStoreType () << "" << (int)it.second->GetEncryptionType () <<"
\r\n
\r\n
\r\n
\r\n"; - } else - s << "" << tr("LeaseSets") << ": 0
\r\n
\r\n"; - - auto pool = dest->GetTunnelPool (); - if (pool) - { - s << "" << tr("Inbound tunnels") << ":
\r\n
\r\n"; - for (auto & it : pool->GetInboundTunnels ()) { - s << "
"; - // for each tunnel hop if not zero-hop - if (it->GetNumHops ()) - { - it->VisitTunnelHops( - [&s](std::shared_ptr hopIdent) - { - s << "⇒ "; - ShowHop(s, *hopIdent); - s << " "; - } - ); - } - s << "⇒ " << it->GetTunnelID () << ":me"; - if (it->LatencyIsKnown()) - s << " ( " << tr(/* tr: Milliseconds */ "%dms", it->GetMeanLatency()) << " )"; - ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); - s << "
\r\n"; - } - s << "
\r\n"; - s << "" << tr("Outbound tunnels") << ":
\r\n
\r\n"; - for (auto & it : pool->GetOutboundTunnels ()) { - s << "
"; - s << it->GetTunnelID () << ":me ⇒"; - // for each tunnel hop if not zero-hop - if (it->GetNumHops ()) - { - it->VisitTunnelHops( - [&s](std::shared_ptr hopIdent) - { - s << " "; - ShowHop(s, *hopIdent); - s << " ⇒"; - } - ); - } - if (it->LatencyIsKnown()) - s << " ( " << tr("%dms", it->GetMeanLatency()) << " )"; - ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ()); - s << "
\r\n"; - } - } - s << "
\r\n"; - - s << "" << tr("Tags") << "
\r\n" - << tr("Incoming") << ": " << dest->GetNumIncomingTags () << "
\r\n"; - if (!dest->GetSessions ().empty ()) { - std::stringstream tmp_s; uint32_t out_tags = 0; - for (const auto& it: dest->GetSessions ()) { - tmp_s << "" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "" << it.second->GetNumOutgoingTags () << "\r\n"; - out_tags += it.second->GetNumOutgoingTags (); - } - s << "
\r\n" - << "\r\n" - << "
\r\n" - << "\r\n\r\n" - << "\r\n" << tmp_s.str () << "
" << tr("Destination") << "" << tr("Amount") << "
\r\n
\r\n
\r\n"; - } else - s << tr("Outgoing") << ": 0
\r\n"; - s << "
\r\n"; - - auto numECIESx25519Tags = dest->GetNumIncomingECIESx25519Tags (); - if (numECIESx25519Tags > 0) { - s << "ECIESx25519
\r\n" << tr("Incoming Tags") << ": " << numECIESx25519Tags << "
\r\n"; - if (!dest->GetECIESx25519Sessions ().empty ()) - { - std::stringstream tmp_s; uint32_t ecies_sessions = 0; - for (const auto& it: dest->GetECIESx25519Sessions ()) { - tmp_s << "" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) << "" << it.second->GetState () << "\r\n"; - ecies_sessions++; - } - s << "
\r\n" - << "\r\n" - << "
\r\n\r\n" - << "\r\n" - << "\r\n" << tmp_s.str () << "
" << tr("Destination") << "" << tr("Status") << "
\r\n
\r\n
\r\n"; - } else - s << tr("Tags sessions") << ": 0
\r\n"; - s << "
\r\n"; - } - } - - void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token) - { - s << "" << tr("Local Destination") << ":
\r\n
\r\n"; - i2p::data::IdentHash ident; - ident.FromBase32 (b32); - auto dest = i2p::client::context.FindLocalDestination (ident); - - if (dest) - { - ShowLeaseSetDestination (s, dest, token); - - // Print table with streams information - s << "\r\n\r\n\r\n" - << "" - << "" // Stream closing button column - << "" - << "" - << "" - << "" - << "" - << "" - << "" - << "" - << "" - << "\r\n\r\n\r\n"; - - for (const auto& it: dest->GetAllStreams ()) - { - auto streamDest = i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()); - std::string streamDestShort = streamDest.substr(0,12) + "….b32.i2p"; - s << ""; - s << ""; - if (it->GetRecvStreamID ()) { - s << ""; - } else { - s << ""; - s << ""; - s << ""; - s << ""; - s << ""; - s << ""; - s << ""; - s << ""; - s << ""; - s << "\r\n"; - } - s << "\r\n
" - << tr("Streams") - << "
StreamID DestinationSentReceivedOutInBufRTTWindowStatus
" << it->GetRecvStreamID () << ""; - } - s << "" << streamDestShort << "" << it->GetNumSentBytes () << "" << it->GetNumReceivedBytes () << "" << it->GetSendQueueSize () << "" << it->GetReceiveQueueSize () << "" << it->GetSendBufferSize () << "" << it->GetRTT () << "" << it->GetWindowSize () << "" << (int)it->GetStatus () << "
"; - } - else - ShowError(s, tr("Such destination is not found")); - } - - void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id) - { - auto i2cpServer = i2p::client::context.GetI2CPServer (); - if (i2cpServer) - { - s << "I2CP " << tr("Local Destination") << ":
\r\n
\r\n"; - auto it = i2cpServer->GetSessions ().find (std::stoi (id)); - if (it != i2cpServer->GetSessions ().end ()) - ShowLeaseSetDestination (s, it->second->GetDestination (), 0); - else - ShowError(s, tr("I2CP session not found")); - } - else - ShowError(s, tr("I2CP is not enabled")); - } - - void ShowLeasesSets(std::stringstream& s) - { - if (i2p::data::netdb.GetNumLeaseSets ()) - { - s << "" << tr("LeaseSets") << ":
\r\n
\r\n"; - int counter = 1; - // for each lease set - i2p::data::netdb.VisitLeaseSets( - [&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr leaseSet) - { - // create copy of lease set so we extract leases - auto storeType = leaseSet->GetStoreType (); - std::unique_ptr ls; - if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET) - ls.reset (new i2p::data::LeaseSet (leaseSet->GetBuffer(), leaseSet->GetBufferLen())); - else - { - ls.reset (new i2p::data::LeaseSet2 (storeType)); - ls->Update (leaseSet->GetBuffer(), leaseSet->GetBufferLen(), false); - } - if (!ls) return; - s << "
IsExpired()) - s << " expired"; // additional css class for expired - s << "\">\r\n"; - if (!ls->IsValid()) - s << "
!! " << tr("Invalid") << " !!
\r\n"; - s << "
\r\n"; - s << "\r\n
\r\n"; - s << "" << tr("Store type") << ": " << (int)storeType << "
\r\n"; - s << "" << tr("Expires") << ": " << ConvertTime(ls->GetExpirationTime()) << "
\r\n"; - if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2) - { - // leases information is available - auto leases = ls->GetNonExpiredLeases(); - s << "" << tr("Non Expired Leases") << ": " << leases.size() << "
\r\n"; - for ( auto & l : leases ) - { - s << "" << tr("Gateway") << ": " << l->tunnelGateway.ToBase64() << "
\r\n"; - s << "" << tr("TunnelID") << ": " << l->tunnelID << "
\r\n"; - s << "" << tr("EndDate") << ": " << ConvertTime(l->endDate) << "
\r\n"; - } - } - s << "
\r\n
\r\n
\r\n"; - } - ); - // end for each lease set - } - else if (!i2p::context.IsFloodfill ()) - { - s << "" << tr("LeaseSets") << ": " << tr(/* Message on LeaseSets page */ "floodfill mode is disabled") << ".
\r\n"; - } - else - { - s << "" << tr("LeaseSets") << ": 0
\r\n"; - } - } - - void ShowTunnels (std::stringstream& s) - { - s << "" << tr("Tunnels") << ":
\r\n"; - s << "" << tr("Queue size") << ": " << i2p::tunnel::tunnels.GetQueueSize () << "
\r\n
\r\n"; - s << "" << tr("TBM Queue size") << ": " << i2p::tunnel::tunnels.GetTBMQueueSize () << "
\r\n
\r\n"; - - auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool (); - - s << "" << tr("Inbound tunnels") << ":
\r\n
\r\n"; - for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) { - s << "
"; - if (it->GetNumHops ()) - { - it->VisitTunnelHops( - [&s](std::shared_ptr hopIdent) - { - s << "⇒ "; - ShowHop(s, *hopIdent); - s << " "; - } - ); - } - s << "⇒ " << it->GetTunnelID () << ":me"; - if (it->LatencyIsKnown()) - s << " ( " << tr("%dms", it->GetMeanLatency()) << " )"; - ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ()); - s << "
\r\n"; - } - s << "
\r\n
\r\n"; - s << "" << tr("Outbound tunnels") << ":
\r\n
\r\n"; - for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) { - s << "
"; - s << it->GetTunnelID () << ":me ⇒"; - // for each tunnel hop if not zero-hop - if (it->GetNumHops ()) - { - it->VisitTunnelHops( - [&s](std::shared_ptr hopIdent) - { - s << " "; - ShowHop(s, *hopIdent); - s << " ⇒"; - } - ); - } - if (it->LatencyIsKnown()) - s << " ( " << tr("%dms", it->GetMeanLatency()) << " )"; - ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ()); - s << "
\r\n"; - } - s << "
\r\n"; - } - - static void ShowCommands (std::stringstream& s, uint32_t token) - { - std::string webroot; i2p::config::GetOption("http.webroot", webroot); - - s << "" << tr("Router commands") << "
\r\n
\r\n
\r\n"; - s << " " << tr("Run peer test") << "
\r\n"; - s << " " << tr("Reload tunnels configuration") << "
\r\n"; - - if (i2p::context.AcceptsTunnels ()) - s << " " << tr("Decline transit tunnels") << "
\r\n"; - else - s << " " << tr("Accept transit tunnels") << "
\r\n"; - -#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) - if (Daemon.gracefulShutdownInterval) - s << " " << tr("Cancel graceful shutdown") << "
\r\n"; - else - s << " " << tr("Start graceful shutdown") << "
\r\n"; -#elif defined(WIN32_APP) - if (i2p::util::DaemonWin32::Instance().isGraceful) - s << " " << tr("Cancel graceful shutdown") << "
\r\n"; - else - s << " " << tr("Start graceful shutdown") << "
\r\n"; -#endif - - s << " " << tr("Force shutdown") << "

\r\n"; - s << " " << tr("Reload external CSS styles") << "\r\n"; - s << "
"; - - s << "
\r\n" << tr("Note: any action done here are not persistent and not changes your config files.") << "\r\n
\r\n"; - - auto loglevel = i2p::log::Logger().GetLogLevel(); - s << "" << tr("Logging level") << "
\r\n"; - s << " none \r\n"; - s << " critical \r\n"; - s << " error \r\n"; - s << " warn \r\n"; - s << " info \r\n"; - s << " debug
\r\n
\r\n"; - - uint32_t maxTunnels = i2p::tunnel::tunnels.GetMaxNumTransitTunnels (); - s << "" << tr("Transit tunnels limit") << "
\r\n"; - s << "
\r\n"; - s << " \r\n"; - s << " \r\n"; - s << " \r\n"; - s << " \r\n"; - s << "
\r\n
\r\n"; - - // get current used language - std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); - - s << "" - << tr("Change language") - << "
\r\n" - << "
\r\n" - << " \r\n" - << " \r\n" - << " \r\n" - << " \r\n" - << "
\r\n
\r\n"; - - } - - void ShowTransitTunnels (std::stringstream& s) - { - if (i2p::tunnel::tunnels.CountTransitTunnels()) - { - s << "" << tr("Transit Tunnels") << ":
\r\n"; - s << ""; - for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) - { - if (std::dynamic_pointer_cast(it)) - s << "\r\n"; - } - s << "
ID" << tr("Amount") << "" << tr("Next") << "
" << it->GetTunnelID () << ""; - else if (std::dynamic_pointer_cast(it)) - s << "
" << it->GetTunnelID () << ""; - else - s << "
" << it->GetTunnelID () << ""; - ShowTraffic(s, it->GetNumTransmittedBytes ()); - s << "" << it->GetNextPeerName () << "
\r\n"; - } - else - { - s << "" << tr("Transit Tunnels") << ": " << tr(/* Message on transit tunnels page */ "no transit tunnels currently built") << ".
\r\n"; - } - } - - template - static void ShowTransportSessions (std::stringstream& s, const Sessions& sessions, const std::string name) - { - auto comp = [](typename Sessions::mapped_type a, typename Sessions::mapped_type b) - { return a->GetRemoteEndpoint() < b->GetRemoteEndpoint(); }; - std::set sortedSessions(comp); - for (const auto& it : sessions) - { - auto ret = sortedSessions.insert(it.second); - if (!ret.second) - LogPrint(eLogError, "HTTPServer: Duplicate remote endpoint detected: ", (*ret.first)->GetRemoteEndpoint()); - } - std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0; - for (const auto& it: sortedSessions) - { - auto endpoint = it->GetRemoteEndpoint (); - if (it && it->IsEstablished () && endpoint.address ().is_v4 ()) - { - tmp_s << "
\r\n"; - if (it->IsOutgoing ()) tmp_s << " ⇒ "; - tmp_s << i2p::data::GetIdentHashAbbreviation (it->GetRemoteIdentity ()->GetIdentHash ()) << ": " - << endpoint.address ().to_string () << ":" << endpoint.port (); - if (!it->IsOutgoing ()) tmp_s << " ⇒ "; - tmp_s << " [" << it->GetNumSentBytes () << ":" << it->GetNumReceivedBytes () << "]"; - if (it->GetRelayTag ()) - tmp_s << " [itag:" << it->GetRelayTag () << "]"; - if (it->GetSendQueueSize () > 0) - tmp_s << " [queue:" << it->GetSendQueueSize () << "]"; - if (it->IsSlow ()) tmp_s << " [slow]"; - tmp_s << "
\r\n" << std::endl; - cnt++; - } - if (it && it->IsEstablished () && endpoint.address ().is_v6 ()) - { - tmp_s6 << "
\r\n"; - if (it->IsOutgoing ()) tmp_s6 << " ⇒ "; - tmp_s6 << i2p::data::GetIdentHashAbbreviation (it->GetRemoteIdentity ()->GetIdentHash ()) << ": " - << "[" << endpoint.address ().to_string () << "]:" << endpoint.port (); - if (!it->IsOutgoing ()) tmp_s6 << " ⇒ "; - tmp_s6 << " [" << it->GetNumSentBytes () << ":" << it->GetNumReceivedBytes () << "]"; - if (it->GetRelayTag ()) - tmp_s6 << " [itag:" << it->GetRelayTag () << "]"; - if (it->GetSendQueueSize () > 0) - tmp_s6 << " [queue:" << it->GetSendQueueSize () << "]"; - tmp_s6 << "
\r\n" << std::endl; - cnt6++; - } - } - if (!tmp_s.str ().empty ()) - { - s << "
\r\n\r\n
" - << tmp_s.str () << "
\r\n
\r\n"; - } - if (!tmp_s6.str ().empty ()) - { - s << "
\r\n\r\n
" - << tmp_s6.str () << "
\r\n
\r\n"; - } - } - - void ShowTransports (std::stringstream& s) - { - s << "" << tr("Transports") << ":
\r\n"; - auto ntcp2Server = i2p::transport::transports.GetNTCP2Server (); - if (ntcp2Server) - { - auto sessions = ntcp2Server->GetNTCP2Sessions (); - if (!sessions.empty ()) - ShowTransportSessions (s, sessions, "NTCP2"); - } - auto ssu2Server = i2p::transport::transports.GetSSU2Server (); - if (ssu2Server) - { - auto sessions = ssu2Server->GetSSU2Sessions (); - if (!sessions.empty ()) - ShowTransportSessions (s, sessions, "SSU2"); - } - } - - void ShowSAMSessions (std::stringstream& s) - { - std::string webroot; i2p::config::GetOption("http.webroot", webroot); - auto sam = i2p::client::context.GetSAMBridge (); - if (!sam) - { - ShowError(s, tr("SAM disabled")); - return; - } - - if (sam->GetSessions ().size ()) - { - s << "" << tr("SAM sessions") << ":
\r\n
\r\n"; - for (auto& it: sam->GetSessions ()) - { - auto& name = it.second->GetLocalDestination ()->GetNickname (); - s << "\r\n" << std::endl; - } - s << "
\r\n"; - } - else - s << "" << tr("SAM sessions") << ": " << tr(/* Message on SAM sessions page */ "no sessions currently running") << ".
\r\n"; - } - - void ShowSAMSession (std::stringstream& s, const std::string& id) - { - auto sam = i2p::client::context.GetSAMBridge (); - if (!sam) { - ShowError(s, tr("SAM disabled")); - return; - } - - auto session = sam->FindSession (id); - if (!session) { - ShowError(s, tr("SAM session not found")); - return; - } - - std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "" << tr("SAM Session") << ":
\r\n
\r\n"; - auto& ident = session->GetLocalDestination ()->GetIdentHash(); - s << "\r\n"; - s << "
\r\n"; - s << "" << tr("Streams") << ":
\r\n
\r\n"; - for (const auto& it: sam->ListSockets(id)) - { - s << "
"; - switch (it->GetSocketType ()) - { - case i2p::client::eSAMSocketTypeSession : s << "session"; break; - case i2p::client::eSAMSocketTypeStream : s << "stream"; break; - case i2p::client::eSAMSocketTypeAcceptor : s << "acceptor"; break; - case i2p::client::eSAMSocketTypeForward : s << "forward"; break; - default: s << "unknown"; break; - } - s << " [" << it->GetSocket ().remote_endpoint() << "]"; - s << "
\r\n"; - } - s << "
\r\n"; - } - - void ShowI2PTunnels (std::stringstream& s) - { - std::string webroot; i2p::config::GetOption("http.webroot", webroot); - - auto& clientTunnels = i2p::client::context.GetClientTunnels (); - auto httpProxy = i2p::client::context.GetHttpProxy (); - auto socksProxy = i2p::client::context.GetSocksProxy (); - if (!clientTunnels.empty () || httpProxy || socksProxy) - { - s << "" << tr("Client Tunnels") << ":
\r\n
\r\n"; - if (!clientTunnels.empty ()) - { - for (auto& it: clientTunnels) - { - auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - s << "
"; - s << it.second->GetName () << " ⇐ "; - s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; - } - } - if (httpProxy) - { - auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash(); - s << "
"; - s << "HTTP " << tr("Proxy") << " ⇐ "; - s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; - } - if (socksProxy) - { - auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash(); - s << "
"; - s << "SOCKS " << tr("Proxy") << " ⇐ "; - s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; - } - s << "
\r\n"; - } - - auto& serverTunnels = i2p::client::context.GetServerTunnels (); - if (!serverTunnels.empty ()) { - s << "
\r\n" << tr("Server Tunnels") << ":
\r\n
\r\n"; - for (auto& it: serverTunnels) - { - auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - s << "
"; - s << it.second->GetName () << " ⇒ "; - s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << ":" << it.second->GetLocalPort (); - s << "
\r\n"<< std::endl; - } - s << "
\r\n"; - } - - auto& clientForwards = i2p::client::context.GetClientForwards (); - if (!clientForwards.empty ()) - { - s << "
\r\n" << tr("Client Forwards") << ":
\r\n
\r\n"; - for (auto& it: clientForwards) - { - auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - s << "
"; - s << it.second->GetName () << " ⇐ "; - s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; - } - s << "
\r\n"; - } - auto& serverForwards = i2p::client::context.GetServerForwards (); - if (!serverForwards.empty ()) - { - s << "
\r\n" << tr("Server Forwards") << ":
\r\n
\r\n"; - for (auto& it: serverForwards) - { - auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - s << "
"; - s << it.second->GetName () << " ⇐ "; - s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; - } - s << "
\r\n"; - } - } - - HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr socket): - m_Socket (socket), m_BufferLen (0), expected_host(hostname) - { - /* cache options */ - i2p::config::GetOption("http.auth", needAuth); - i2p::config::GetOption("http.user", user); - i2p::config::GetOption("http.pass", pass); - } - - void HTTPConnection::Receive () - { - m_Socket->async_read_some (boost::asio::buffer (m_Buffer, HTTP_CONNECTION_BUFFER_SIZE), - std::bind(&HTTPConnection::HandleReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) { - if (ecode != boost::asio::error::operation_aborted) - Terminate (ecode); - return; - } - m_Buffer[bytes_transferred] = '\0'; - m_BufferLen = bytes_transferred; - RunRequest(); - Receive (); - } - - void HTTPConnection::RunRequest () - { - HTTPReq request; - int ret = request.parse(m_Buffer); - if (ret < 0) { - m_Buffer[0] = '\0'; - m_BufferLen = 0; - return; /* error */ - } - if (ret == 0) - return; /* need more data */ - - HandleRequest (request); - } - - void HTTPConnection::Terminate (const boost::system::error_code& ecode) - { - if (ecode == boost::asio::error::operation_aborted) - return; - boost::system::error_code ignored_ec; - m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); - m_Socket->close (); - } - - bool HTTPConnection::CheckAuth (const HTTPReq & req) - { - /* method #1: http://user:pass@127.0.0.1:7070/ */ - if (req.uri.find('@') != std::string::npos) { - URL url; - if (url.parse(req.uri) && url.user == user && url.pass == pass) - return true; - } - /* method #2: 'Authorization' header sent */ - auto provided = req.GetHeader ("Authorization"); - if (provided.length () > 0) - { - std::string expected = "Basic " + i2p::data::ToBase64Standard (user + ":" + pass); - if (expected == provided) return true; - } - - LogPrint(eLogWarning, "HTTPServer: Auth failure from ", m_Socket->remote_endpoint().address ()); - return false; - } - - void HTTPConnection::HandleRequest (const HTTPReq & req) - { - std::stringstream s; - std::string content; - HTTPRes res; - - LogPrint(eLogDebug, "HTTPServer: Request: ", req.uri); - - if (needAuth && !CheckAuth(req)) { - res.code = 401; - res.add_header("WWW-Authenticate", "Basic realm=\"WebAdmin\""); - SendReply(res, content); - return; - } - - bool strictheaders; - i2p::config::GetOption("http.strictheaders", strictheaders); - if (strictheaders) - { - std::string http_hostname; - i2p::config::GetOption("http.hostname", http_hostname); - std::string host = req.GetHeader("Host"); - auto idx = host.find(':'); - /* strip out port so it's just host */ - if (idx != std::string::npos && idx > 0) - { - host = host.substr(0, idx); - } - if (!(host == expected_host || host == http_hostname)) - { - /* deny request as it's from a non whitelisted hostname */ - res.code = 403; - content = "host mismatch"; - SendReply(res, content); - return; - } - } - - // HTML head start - ShowPageHead (s); - if (req.uri.find("page=") != std::string::npos) { - HandlePage (req, res, s); - } else if (req.uri.find("cmd=") != std::string::npos) { - HandleCommand (req, res, s); - } else { - ShowStatus (s, true, i2p::http::OutputFormatEnum::forWebConsole); - res.add_header("Refresh", "10"); - } - ShowPageTail (s); - - res.code = 200; - content = s.str (); - SendReply (res, content); - } - - std::map HTTPConnection::m_Tokens; - - uint32_t HTTPConnection::CreateToken () - { - uint32_t token; - RAND_bytes ((uint8_t *)&token, 4); - token &= 0x7FFFFFFF; // clear first bit - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_Tokens.begin (); it != m_Tokens.end (); ) - { - if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT) - it = m_Tokens.erase (it); - else - ++it; - } - m_Tokens[token] = ts; - return token; - } - - void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s) - { - std::map params; - std::string page(""); - URL url; - - url.parse(req.uri); - url.parse_query(params); - page = params["page"]; - - if (page == HTTP_PAGE_TRANSPORTS) - ShowTransports (s); - else if (page == HTTP_PAGE_TUNNELS) - ShowTunnels (s); - else if (page == HTTP_PAGE_COMMANDS) - { - uint32_t token = CreateToken (); - ShowCommands (s, token); - } - else if (page == HTTP_PAGE_TRANSIT_TUNNELS) - ShowTransitTunnels (s); - else if (page == HTTP_PAGE_LOCAL_DESTINATIONS) - ShowLocalDestinations (s); - else if (page == HTTP_PAGE_LOCAL_DESTINATION) - { - uint32_t token = CreateToken (); - ShowLocalDestination (s, params["b32"], token); - } - else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION) - ShowI2CPLocalDestination (s, params["i2cp_id"]); - else if (page == HTTP_PAGE_SAM_SESSIONS) - ShowSAMSessions (s); - else if (page == HTTP_PAGE_SAM_SESSION) - ShowSAMSession (s, params["sam_id"]); - else if (page == HTTP_PAGE_I2P_TUNNELS) - ShowI2PTunnels (s); - else if (page == HTTP_PAGE_LEASESETS) - ShowLeasesSets(s); - else { - res.code = 400; - ShowError(s, std::string (tr("Unknown page")) + ": " + page); // TODO - return; - } - } - - void HTTPConnection::HandleCommand (const HTTPReq& req, HTTPRes& res, std::stringstream& s) - { - std::map params; - URL url; - - url.parse(req.uri); - url.parse_query(params); - - std::string webroot; i2p::config::GetOption("http.webroot", webroot); - std::string redirect = std::to_string(COMMAND_REDIRECT_TIMEOUT) + "; url=" + webroot + "?page=commands"; - std::string token = params["token"]; - - if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ()) - { - ShowError(s, tr("Invalid token")); - return; - } - - std::string cmd = params["cmd"]; - if (cmd == HTTP_COMMAND_RUN_PEER_TEST) - i2p::transport::transports.PeerTest (); - else if (cmd == HTTP_COMMAND_RELOAD_TUNNELS_CONFIG) - i2p::client::context.ReloadConfig (); - else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT) - i2p::context.SetAcceptsTunnels (true); - else if (cmd == HTTP_COMMAND_DISABLE_TRANSIT) - i2p::context.SetAcceptsTunnels (false); - else if (cmd == HTTP_COMMAND_SHUTDOWN_START) - { - i2p::context.SetAcceptsTunnels (false); -#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) - Daemon.gracefulShutdownInterval = 10*60; -#elif defined(WIN32_APP) - i2p::win32::GracefulShutdown (); -#endif - } - else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) - { - i2p::context.SetAcceptsTunnels (true); -#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) - Daemon.gracefulShutdownInterval = 0; -#elif defined(WIN32_APP) - i2p::win32::StopGracefulShutdown (); -#endif - } - else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) - { -#ifndef WIN32_APP - Daemon.running = false; -#else - i2p::win32::StopWin32App (); -#endif - } - else if (cmd == HTTP_COMMAND_LOGLEVEL) - { - std::string level = params["level"]; - SetLogLevel (level); - } - else if (cmd == HTTP_COMMAND_KILLSTREAM) - { - std::string b32 = params["b32"]; - uint32_t streamID = std::stoul(params["streamID"], nullptr); - - i2p::data::IdentHash ident; - ident.FromBase32 (b32); - auto dest = i2p::client::context.FindLocalDestination (ident); - - if (streamID) - { - if (dest) - { - if (dest->DeleteStream (streamID)) - s << "" << tr("SUCCESS") << ": " << tr("Stream closed") << "
\r\n
\r\n"; - else - s << "" << tr("ERROR") << ": " << tr("Stream not found or already was closed") << "
\r\n
\r\n"; - } - else - s << "" << tr("ERROR") << ": " << tr("Destination not found") << "
\r\n
\r\n"; - } - else - s << "" << tr("ERROR") << ": " << tr("StreamID can't be null") << "
\r\n
\r\n"; - - s << "" << tr("Return to destination page") << "
\r\n"; - s << "

" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << ""; - redirect = std::to_string(COMMAND_REDIRECT_TIMEOUT) + "; url=" + webroot + "?page=local_destination&b32=" + b32; - res.add_header("Refresh", redirect.c_str()); - return; - } - else if (cmd == HTTP_COMMAND_EXPIRELEASE) - { - std::string b32 = params["b32"]; - std::string lease = params["lease"]; - - i2p::data::IdentHash ident, leaseident; - ident.FromBase32 (b32); - leaseident.FromBase32 (lease); - auto dest = i2p::client::context.FindLocalDestination (ident); - - if (dest) - { - auto leaseset = dest->FindLeaseSet (leaseident); - if (leaseset) - { - leaseset->ExpireLease (); - s << "" << tr("SUCCESS") << ": " << tr("LeaseSet expiration time updated") << "
\r\n
\r\n"; - } - else - s << "" << tr("ERROR") << ": " << tr("LeaseSet is not found or already expired") << "
\r\n
\r\n"; - } - else - s << "" << tr("ERROR") << ": " << tr("Destination not found") << "
\r\n
\r\n"; - - s << "" << tr("Return to destination page") << "
\r\n"; - s << "

" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << ""; - redirect = std::to_string(COMMAND_REDIRECT_TIMEOUT) + "; url=" + webroot + "?page=local_destination&b32=" + b32; - res.add_header("Refresh", redirect.c_str()); - return; - } - else if (cmd == HTTP_COMMAND_LIMITTRANSIT) - { - uint32_t limit = std::stoul(params["limit"], nullptr); - if (limit > 0 && limit <= TRANSIT_TUNNELS_LIMIT) - i2p::tunnel::tunnels.SetMaxNumTransitTunnels (limit); - else { - s << "" << tr("ERROR") << ": " << tr("Transit tunnels count must not exceed %d", TRANSIT_TUNNELS_LIMIT) << "\r\n
\r\n
\r\n"; - s << "" << tr("Back to commands list") << "\r\n
\r\n"; - s << "

" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << ""; - res.add_header("Refresh", redirect.c_str()); - return; - } - } - else if (cmd == HTTP_COMMAND_GET_REG_STRING) - { - std::string b32 = params["b32"]; - std::string name = i2p::http::UrlDecode(params["name"]); - - i2p::data::IdentHash ident; - ident.FromBase32 (b32); - auto dest = i2p::client::context.FindLocalDestination (ident); - - if (dest) - { - std::size_t pos; - pos = name.find (".i2p"); - if (pos == (name.length () - 4)) - { - pos = name.find (".b32.i2p"); - if (pos == std::string::npos) - { - auto signatureLen = dest->GetIdentity ()->GetSignatureLen (); - uint8_t * signature = new uint8_t[signatureLen]; - std::stringstream out; - - out << name << "=" << dest->GetIdentity ()->ToBase64 (); - dest->Sign ((uint8_t *)out.str ().c_str (), out.str ().length (), signature); - auto sig = i2p::data::ByteStreamToBase64 (signature, signatureLen); - out << "#!sig=" << sig; - s << "" << tr("SUCCESS") << ":
\r\n

\r\n" - "\r\n
\r\n
\r\n" - "" << tr("Register at reg.i2p") << ":\r\n
\r\n" - "" << tr("Description") << ":\r\n\r\n" - "\r\n" - "
\r\n
\r\n"; - delete[] signature; - } - else - s << "" << tr("ERROR") << ": " << tr("Domain can't end with .b32.i2p") << "\r\n
\r\n
\r\n"; - } - else - s << "" << tr("ERROR") << ": " << tr("Domain must end with .i2p") << "\r\n
\r\n
\r\n"; - } - else - s << "" << tr("ERROR") << ": " << tr("Such destination is not found") << "\r\n
\r\n
\r\n"; - - s << "" << tr("Return to destination page") << "\r\n"; - return; - } - else if (cmd == HTTP_COMMAND_SETLANGUAGE) - { - std::string lang = params["lang"]; - std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); - - if (currLang.compare(lang) != 0) - i2p::i18n::SetLanguage(lang); - } - else if (cmd == HTTP_COMMAND_RELOAD_CSS) - { - LoadExtCSS(); - } - else - { - res.code = 400; - ShowError(s, std::string (tr("Unknown command")) + ": " + cmd); // TODO - return; - } - - s << "" << tr("SUCCESS") << ": " << tr("Command accepted") << "

\r\n"; - s << "" << tr("Back to commands list") << "
\r\n"; - s << "

" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << ""; - res.add_header("Refresh", redirect.c_str()); - } - - void HTTPConnection::SendReply (HTTPRes& reply, std::string& content) - { - reply.add_header("X-Frame-Options", "SAMEORIGIN"); - reply.add_header("X-Content-Type-Options", "nosniff"); - reply.add_header("X-XSS-Protection", "1; mode=block"); - reply.add_header("Content-Type", "text/html"); - reply.body = content; - - m_SendBuffer = reply.to_string(); - boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer), boost::asio::transfer_all (), - std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1)); - } - - HTTPServer::HTTPServer (const std::string& address, int port): - m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service.get_executor ()), - m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::make_address(address), port)), - m_Hostname(address) - { - } - - HTTPServer::~HTTPServer () - { - Stop (); - } - - void HTTPServer::Start () - { - bool needAuth; i2p::config::GetOption("http.auth", needAuth); - std::string user; i2p::config::GetOption("http.user", user); - std::string pass; i2p::config::GetOption("http.pass", pass); - /* generate pass if needed */ - if (needAuth && pass == "") { - uint8_t random[16]; - char alnum[] = "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz"; - pass.resize(sizeof(random)); - RAND_bytes(random, sizeof(random)); - for (size_t i = 0; i < sizeof(random); i++) { - pass[i] = alnum[random[i] % (sizeof(alnum) - 1)]; - } - i2p::config::SetOption("http.pass", pass); - LogPrint(eLogInfo, "HTTPServer: Password set to ", pass); - } - - m_IsRunning = true; - m_Thread.reset (new std::thread (std::bind (&HTTPServer::Run, this))); - m_Acceptor.listen (); - Accept (); - - LoadExtCSS(); - } - - void HTTPServer::Stop () - { - m_IsRunning = false; - - boost::system::error_code ec; - m_Acceptor.cancel(ec); - if (ec) - LogPrint (eLogDebug, "HTTPServer: Error while cancelling operations on acceptor: ", ec.message ()); - m_Acceptor.close(); - - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - m_Thread = nullptr; - } - } - - void HTTPServer::Run () - { - i2p::util::SetThreadName("Webconsole"); - - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "HTTPServer: Runtime exception: ", ex.what ()); - } - } - } - - void HTTPServer::Accept () - { - auto newSocket = std::make_shared (m_Service); - m_Acceptor.async_accept (*newSocket, std::bind (&HTTPServer::HandleAccept, this, - std::placeholders::_1, newSocket)); - } - - void HTTPServer::HandleAccept(const boost::system::error_code& ecode, - std::shared_ptr newSocket) - { - if (!ecode) - CreateConnection(newSocket); - else - { - if (newSocket) newSocket->close(); - LogPrint(eLogError, "HTTP Server: Error handling accept: ", ecode.message()); - } - Accept (); - } - - void HTTPServer::CreateConnection(std::shared_ptr newSocket) - { - auto conn = std::make_shared (m_Hostname, newSocket); - conn->Receive (); - } -} // http -} // i2p diff --git a/daemon/HTTPServer.h b/daemon/HTTPServer.h deleted file mode 100644 index 38b790d4..00000000 --- a/daemon/HTTPServer.h +++ /dev/null @@ -1,108 +0,0 @@ -/* -* Copyright (c) 2013-2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef HTTP_SERVER_H__ -#define HTTP_SERVER_H__ - -#include -#include -#include -#include -#include -#include -#include -#include "HTTP.h" - -namespace i2p -{ -namespace http -{ - const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; - const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds - const int COMMAND_REDIRECT_TIMEOUT = 5; // in seconds - const int TRANSIT_TUNNELS_LIMIT = 1000000; - - class HTTPConnection: public std::enable_shared_from_this - { - public: - - HTTPConnection (std::string serverhost, std::shared_ptr socket); - void Receive (); - - private: - - void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void Terminate (const boost::system::error_code& ecode); - - void RunRequest (); - bool CheckAuth (const HTTPReq & req); - void HandleRequest (const HTTPReq & req); - void HandlePage (const HTTPReq & req, HTTPRes & res, std::stringstream& data); - void HandleCommand (const HTTPReq & req, HTTPRes & res, std::stringstream& data); - void SendReply (HTTPRes & res, std::string & content); - uint32_t CreateToken (); - - private: - - std::shared_ptr m_Socket; - char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1]; - size_t m_BufferLen; - std::string m_SendBuffer; - bool needAuth; - std::string user; - std::string pass; - std::string expected_host; - - static std::map m_Tokens; // token->timestamp in seconds - }; - - class HTTPServer - { - public: - - HTTPServer (const std::string& address, int port); - ~HTTPServer (); - - void Start (); - void Stop (); - - private: - - void Run (); - void Accept (); - void HandleAccept(const boost::system::error_code& ecode, - std::shared_ptr newSocket); - void CreateConnection(std::shared_ptr newSocket); - - private: - - bool m_IsRunning; - std::unique_ptr m_Thread; - boost::asio::io_context m_Service; - boost::asio::executor_work_guard m_Work; - boost::asio::ip::tcp::acceptor m_Acceptor; - std::string m_Hostname; - }; - - //all the below functions are also used by Qt GUI, see mainwindow.cpp -> getStatusPageHtml - enum OutputFormatEnum { forWebConsole, forQtUi }; - void ShowStatus (std::stringstream& s, bool includeHiddenContent, OutputFormatEnum outputFormat); - void ShowLocalDestinations (std::stringstream& s); - void ShowLeasesSets(std::stringstream& s); - void ShowTunnels (std::stringstream& s); - void ShowTransitTunnels (std::stringstream& s); - void ShowTransports (std::stringstream& s); - void ShowSAMSessions (std::stringstream& s); - void ShowI2PTunnels (std::stringstream& s); - void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token); - void ShowSAMSession (std::stringstream& s, const std::string& id); - void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id); -} // http -} // i2p - -#endif /* HTTP_SERVER_H__ */ diff --git a/daemon/HTTPServerResources.h b/daemon/HTTPServerResources.h deleted file mode 100644 index 1e5b6f75..00000000 --- a/daemon/HTTPServerResources.h +++ /dev/null @@ -1,97 +0,0 @@ -/* -* Copyright (c) 2013-2023, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef HTTP_SERVER_RESOURCES_H__ -#define HTTP_SERVER_RESOURCES_H__ - -namespace i2p -{ -namespace http -{ - const std::string itoopieFavicon = - "data:image/png;base64," - "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx" - "jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0" - "d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB" - "vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn" - "cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP" - "6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps" - "/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X" - "+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/" - "z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/" - "P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am" - "+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM" - "EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1" - "oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ" - "JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ" - "RU5ErkJggg=="; - - // bundled style sheet - const std::string internalCSS = - "\r\n"; - - // for external style sheet - std::string externalCSS; - -} // http -} // i2p - -#endif /* HTTP_SERVER_RESOURCES_H__ */ diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp deleted file mode 100644 index 6261a14c..00000000 --- a/daemon/I2PControl.cpp +++ /dev/null @@ -1,485 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include -#include -#include - -// Use global placeholders from boost introduced when local_time.hpp is loaded -#define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include - -#include "FS.h" -#include "Log.h" -#include "Config.h" -#include "NetDb.hpp" -#include "Tunnel.h" -#include "Daemon.h" -#include "I2PControl.h" - -namespace i2p -{ -namespace client -{ - I2PControlService::I2PControlService (const std::string& address, int port): - m_IsRunning (false), - m_SSLContext (boost::asio::ssl::context::sslv23), - m_ShutdownTimer (m_Service) - { - if (port) - m_Acceptor = std::make_unique(m_Service, - boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port)); - else -#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) - { - std::remove (address.c_str ()); // just in case - m_LocalAcceptor = std::make_unique(m_Service, - boost::asio::local::stream_protocol::endpoint(address)); - } -#else - LogPrint(eLogError, "I2PControl: Local sockets are not supported"); -#endif - - i2p::config::GetOption("i2pcontrol.password", m_Password); - - // certificate / keys - std::string i2pcp_crt; i2p::config::GetOption("i2pcontrol.cert", i2pcp_crt); - std::string i2pcp_key; i2p::config::GetOption("i2pcontrol.key", i2pcp_key); - - if (i2pcp_crt.at(0) != '/') - i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt); - if (i2pcp_key.at(0) != '/') - i2pcp_key = i2p::fs::DataDirPath(i2pcp_key); - if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) - { - LogPrint (eLogInfo, "I2PControl: Creating new certificate for control connection"); - CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str()); - } - else - LogPrint(eLogDebug, "I2PControl: Using cert from ", i2pcp_crt); - m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); - boost::system::error_code ec; - m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec); - if (!ec) - m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec); - if (ec) - { - LogPrint (eLogInfo, "I2PControl: Failed to load ceritifcate: ", ec.message (), ". Recreating"); - CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str()); - m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec); - if (!ec) - m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec); - if (ec) - // give up - LogPrint (eLogError, "I2PControl: Can't load certificates"); - } - - // handlers - m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler; - m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler; - m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler; - m_MethodHandlers["RouterInfo"] = &I2PControlHandlers::RouterInfoHandler; - m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler; - m_MethodHandlers["NetworkSetting"] = &I2PControlHandlers::NetworkSettingHandler; - m_MethodHandlers["ClientServicesInfo"] = &I2PControlHandlers::ClientServicesInfoHandler; - - // I2PControl - m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler; - - // RouterManager - m_RouterManagerHandlers["Reseed"] = &I2PControlService::ReseedHandler; - m_RouterManagerHandlers["Shutdown"] = &I2PControlService::ShutdownHandler; - m_RouterManagerHandlers["ShutdownGraceful"] = &I2PControlService::ShutdownGracefulHandler; - } - - I2PControlService::~I2PControlService () - { - Stop (); - } - - void I2PControlService::Start () - { - if (!m_IsRunning) - { - Accept (); - m_IsRunning = true; - m_Thread = std::make_unique(std::bind (&I2PControlService::Run, this)); - } - } - - void I2PControlService::Stop () - { - if (m_IsRunning) - { - m_IsRunning = false; - if (m_Acceptor) m_Acceptor->cancel (); -#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) - if (m_LocalAcceptor) - { - auto path = m_LocalAcceptor->local_endpoint().path(); - m_LocalAcceptor->cancel (); - std::remove (path.c_str ()); - } -#endif - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - m_Thread = nullptr; - } - } - } - - void I2PControlService::Run () - { - i2p::util::SetThreadName("I2PC"); - - while (m_IsRunning) - { - try { - m_Service.run (); - } catch (std::exception& ex) { - LogPrint (eLogError, "I2PControl: Runtime exception: ", ex.what ()); - } - } - } - - void I2PControlService::Accept () - { - if (m_Acceptor) - { - auto newSocket = std::make_shared > (m_Service, m_SSLContext); - m_Acceptor->async_accept (newSocket->lowest_layer(), - [this, newSocket](const boost::system::error_code& ecode) - { - HandleAccepted (ecode, newSocket); - }); - } -#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) - else if (m_LocalAcceptor) - { - auto newSocket = std::make_shared > (m_Service, m_SSLContext); - m_LocalAcceptor->async_accept (newSocket->lowest_layer(), - [this, newSocket](const boost::system::error_code& ecode) - { - HandleAccepted (ecode, newSocket); - }); - } -#endif - } - - template - void I2PControlService::HandleAccepted (const boost::system::error_code& ecode, - std::shared_ptr newSocket) - { - if (ecode != boost::asio::error::operation_aborted) - Accept (); - - if (ecode) - { - LogPrint (eLogError, "I2PControl: Accept error: ", ecode.message ()); - return; - } - LogPrint (eLogDebug, "I2PControl: New request from ", newSocket->lowest_layer ().remote_endpoint ()); - Handshake (newSocket); - } - - template - void I2PControlService::Handshake (std::shared_ptr socket) - { - socket->async_handshake(boost::asio::ssl::stream_base::server, - [this, socket](const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ()); - return; - } - ReadRequest (socket); - }); - } - - template - void I2PControlService::ReadRequest (std::shared_ptr socket) - { - auto request = std::make_shared(); - socket->async_read_some ( -#if defined(BOOST_ASIO_HAS_STD_ARRAY) - boost::asio::buffer (*request), -#else - boost::asio::buffer (request->data (), request->size ()), -#endif - [this, socket, request](const boost::system::error_code& ecode, size_t bytes_transferred) - { - HandleRequestReceived (ecode, bytes_transferred, socket, request); - }); - } - - template - void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode, - size_t bytes_transferred, std::shared_ptr socket, - std::shared_ptr buf) - { - if (ecode) - { - LogPrint (eLogError, "I2PControl: Read error: ", ecode.message ()); - return; - } - else - { - bool isHtml = !memcmp (buf->data (), "POST", 4); - try - { - std::stringstream ss; - ss.write (buf->data (), bytes_transferred); - if (isHtml) - { - std::string header; - size_t contentLength = 0; - while (!ss.eof () && header != "\r") - { - std::getline(ss, header); - auto colon = header.find (':'); - if (colon != std::string::npos && header.substr (0, colon) == "Content-Length") - contentLength = std::stoi (header.substr (colon + 1)); - } - if (ss.eof ()) - { - LogPrint (eLogError, "I2PControl: Malformed request, HTTP header expected"); - return; // TODO: - } - std::streamoff rem = contentLength + ss.tellg () - bytes_transferred; // more bytes to read - if (rem > 0) - { - bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), rem)); - ss.write (buf->data (), bytes_transferred); - } - } - std::ostringstream response; - boost::property_tree::ptree pt; - boost::property_tree::read_json (ss, pt); - - std::string id = pt.get("id"); - std::string method = pt.get("method"); - auto it = m_MethodHandlers.find (method); - if (it != m_MethodHandlers.end ()) - { - response << "{\"id\":" << id << ",\"result\":{"; - (this->*(it->second))(pt.get_child ("params"), response); - response << "},\"jsonrpc\":\"2.0\"}"; - } - else - { - LogPrint (eLogWarning, "I2PControl: Unknown method ", method); - response << "{\"id\":null,\"error\":"; - response << "{\"code\":-32601,\"message\":\"Method not found\"},"; - response << "\"jsonrpc\":\"2.0\"}"; - } - SendResponse (socket, buf, response, isHtml); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "I2PControl: Exception when handle request: ", ex.what ()); - std::ostringstream response; - response << "{\"id\":null,\"error\":"; - response << "{\"code\":-32700,\"message\":\"" << ex.what () << "\"},"; - response << "\"jsonrpc\":\"2.0\"}"; - SendResponse (socket, buf, response, isHtml); - } - catch (...) - { - LogPrint (eLogError, "I2PControl: Handle request unknown exception"); - } - } - } - - template - void I2PControlService::SendResponse (std::shared_ptr socket, - std::shared_ptr buf, std::ostringstream& response, bool isHtml) - { - size_t len = response.str ().length (), offset = 0; - if (isHtml) - { - std::ostringstream header; - header << "HTTP/1.1 200 OK\r\n"; - header << "Connection: close\r\n"; - header << "Content-Length: " << std::to_string(len) << "\r\n"; - header << "Content-Type: application/json\r\n"; - header << "Date: "; - std::time_t t = std::time (nullptr); - std::tm tm = *std::gmtime (&t); - header << std::put_time(&tm, "%a, %d %b %Y %T GMT") << "\r\n"; - header << "\r\n"; - offset = header.str ().size (); - memcpy (buf->data (), header.str ().c_str (), offset); - } - memcpy (buf->data () + offset, response.str ().c_str (), len); - boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len), - boost::asio::transfer_all (), - [socket, buf](const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - LogPrint (eLogError, "I2PControl: Write error: ", ecode.message ()); - }); - } - -// handlers - - void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - int api = params.get ("API"); - auto password = params.get ("Password"); - LogPrint (eLogDebug, "I2PControl: Authenticate API=", api, " Password=", password); - if (password != m_Password) { - LogPrint (eLogError, "I2PControl: Authenticate - Invalid password: ", password); - return; - } - InsertParam (results, "API", api); - results << ","; - std::string token = boost::lexical_cast(i2p::util::GetSecondsSinceEpoch ()); - m_Tokens.insert (token); - InsertParam (results, "Token", token); - } - - void I2PControlService::EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - auto echo = params.get ("Echo"); - LogPrint (eLogDebug, "I2PControl Echo Echo=", echo); - InsertParam (results, "Result", echo); - } - - -// I2PControl - - void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - for (auto& it: params) - { - LogPrint (eLogDebug, "I2PControl: I2PControl request: ", it.first); - auto it1 = m_I2PControlHandlers.find (it.first); - if (it1 != m_I2PControlHandlers.end ()) - { - (this->*(it1->second))(it.second.data ()); - InsertParam (results, it.first, ""); - } - else - LogPrint (eLogError, "I2PControl: I2PControl unknown request: ", it.first); - } - } - - void I2PControlService::PasswordHandler (const std::string& value) - { - LogPrint (eLogWarning, "I2PControl: New password=", value, ", to make it persistent you should update your config!"); - m_Password = value; - m_Tokens.clear (); - } - - -// RouterManager - - void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - for (auto it = params.begin (); it != params.end (); it++) - { - LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first); - auto it1 = m_RouterManagerHandlers.find (it->first); - if (it1 != m_RouterManagerHandlers.end ()) - { - if (it != params.begin ()) results << ","; - (this->*(it1->second))(results); - } else - LogPrint (eLogError, "I2PControl: RouterManager unknown request: ", it->first); - } - } - - - void I2PControlService::ShutdownHandler (std::ostringstream& results) - { - LogPrint (eLogInfo, "I2PControl: Shutdown requested"); - InsertParam (results, "Shutdown", ""); - m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent - m_ShutdownTimer.async_wait ( - [](const boost::system::error_code& ecode) - { - Daemon.running = 0; - }); - } - - void I2PControlService::ShutdownGracefulHandler (std::ostringstream& results) - { - i2p::context.SetAcceptsTunnels (false); - int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout (); - LogPrint (eLogInfo, "I2PControl: Graceful shutdown requested, ", timeout, " seconds remains"); - InsertParam (results, "ShutdownGraceful", ""); - m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second - m_ShutdownTimer.async_wait ( - [](const boost::system::error_code& ecode) - { - Daemon.running = 0; - }); - } - - void I2PControlService::ReseedHandler (std::ostringstream& results) - { - LogPrint (eLogInfo, "I2PControl: Reseed requested"); - InsertParam (results, "Reseed", ""); - i2p::data::netdb.Reseed (); - } - - // certificate - void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path) - { - FILE *f = NULL; - EVP_PKEY * pkey = EVP_PKEY_new (); - RSA * rsa = RSA_new (); - BIGNUM * e = BN_dup (i2p::crypto::GetRSAE ()); - RSA_generate_key_ex (rsa, 4096, e, NULL); - BN_free (e); - if (rsa) - { - EVP_PKEY_assign_RSA (pkey, rsa); - X509 * x509 = X509_new (); - ASN1_INTEGER_set (X509_get_serialNumber (x509), 1); - X509_gmtime_adj (X509_getm_notBefore (x509), 0); - X509_gmtime_adj (X509_getm_notAfter (x509), I2P_CONTROL_CERTIFICATE_VALIDITY*24*60*60); // expiration - X509_set_pubkey (x509, pkey); // public key - X509_NAME * name = X509_get_subject_name (x509); - X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *)"A1", -1, -1, 0); // country (Anonymous proxy) - X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization - X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name - X509_set_issuer_name (x509, name); // set issuer to ourselves - X509_sign (x509, pkey, EVP_sha1 ()); // sign, last param must be NULL for EdDSA - - // save cert - if ((f = fopen (crt_path, "wb")) != NULL) { - LogPrint (eLogInfo, "I2PControl: Saving new cert to ", crt_path); - PEM_write_X509 (f, x509); - fclose (f); - } else { - LogPrint (eLogError, "I2PControl: Can't write cert: ", strerror(errno)); - } - - // save key - if ((f = fopen (key_path, "wb")) != NULL) { - LogPrint (eLogInfo, "I2PControl: saving cert key to ", key_path); - PEM_write_PrivateKey (f, pkey, NULL, NULL, 0, NULL, NULL); - fclose (f); - } else { - LogPrint (eLogError, "I2PControl: Can't write key: ", strerror(errno)); - } - - X509_free (x509); - } else { - LogPrint (eLogError, "I2PControl: Can't create RSA key for certificate"); - } - EVP_PKEY_free (pkey); - } -} -} diff --git a/daemon/I2PControl.h b/daemon/I2PControl.h deleted file mode 100644 index 83dd6549..00000000 --- a/daemon/I2PControl.h +++ /dev/null @@ -1,107 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef I2P_CONTROL_H__ -#define I2P_CONTROL_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "I2PControlHandlers.h" - -namespace i2p -{ -namespace client -{ - const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024; - typedef std::array I2PControlBuffer; - - const long I2P_CONTROL_CERTIFICATE_VALIDITY = 365*10; // 10 years - const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol"; - const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P"; - - class I2PControlService: public I2PControlHandlers - { - public: - - I2PControlService (const std::string& address, int port); - ~I2PControlService (); - - void Start (); - void Stop (); - - private: - - void Run (); - void Accept (); - template - void HandleAccepted (const boost::system::error_code& ecode, std::shared_ptr newSocket); - template - void Handshake (std::shared_ptr socket); - template - void ReadRequest (std::shared_ptr socket); - template - void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred, - std::shared_ptr socket, std::shared_ptr buf); - template - void SendResponse (std::shared_ptr socket, - std::shared_ptr buf, std::ostringstream& response, bool isHtml); - - void CreateCertificate (const char *crt_path, const char *key_path); - - private: - - // methods - typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results); - - void AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - void EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - - // I2PControl - typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value); - void PasswordHandler (const std::string& value); - - // RouterManager - typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results); - void ShutdownHandler (std::ostringstream& results); - void ShutdownGracefulHandler (std::ostringstream& results); - void ReseedHandler (std::ostringstream& results); - - private: - - std::string m_Password; - bool m_IsRunning; - std::unique_ptr m_Thread; - - boost::asio::io_context m_Service; - std::unique_ptr m_Acceptor; -#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) - std::unique_ptr m_LocalAcceptor; -#endif - boost::asio::ssl::context m_SSLContext; - boost::asio::deadline_timer m_ShutdownTimer; - std::set m_Tokens; - - std::map m_MethodHandlers; - std::map m_I2PControlHandlers; - std::map m_RouterManagerHandlers; - }; -} -} - -#endif diff --git a/daemon/I2PControlHandlers.cpp b/daemon/I2PControlHandlers.cpp deleted file mode 100644 index f3ea7f61..00000000 --- a/daemon/I2PControlHandlers.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include -#include - -#include "Log.h" -#include "RouterContext.h" -#include "NetDb.hpp" -#include "Tunnel.h" -#include "Transports.h" -#include "version.h" -#include "ClientContext.h" -#include "I2PControlHandlers.h" - -namespace i2p -{ -namespace client -{ - I2PControlHandlers::I2PControlHandlers () - { - // RouterInfo - m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlHandlers::UptimeHandler; - m_RouterInfoHandlers["i2p.router.version"] = &I2PControlHandlers::VersionHandler; - m_RouterInfoHandlers["i2p.router.status"] = &I2PControlHandlers::StatusHandler; - m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlHandlers::NetDbKnownPeersHandler; - m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlHandlers::NetDbActivePeersHandler; - m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlHandlers::InboundBandwidth1S; - m_RouterInfoHandlers["i2p.router.net.bw.inbound.15s"] = &I2PControlHandlers::InboundBandwidth15S; - m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlHandlers::OutboundBandwidth1S; - m_RouterInfoHandlers["i2p.router.net.bw.outbound.15s"] = &I2PControlHandlers::OutboundBandwidth15S; - m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlHandlers::NetStatusHandler; - m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlHandlers::TunnelsParticipatingHandler; - m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = &I2PControlHandlers::TunnelsSuccessRateHandler; - m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlHandlers::NetTotalReceivedBytes; - m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlHandlers::NetTotalSentBytes; - - // NetworkSetting - m_NetworkSettingHandlers["i2p.router.net.bw.in"] = &I2PControlHandlers::InboundBandwidthLimit; - m_NetworkSettingHandlers["i2p.router.net.bw.out"] = &I2PControlHandlers::OutboundBandwidthLimit; - - // ClientServicesInfo - m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlHandlers::I2PTunnelInfoHandler; - m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlHandlers::HTTPProxyInfoHandler; - m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlHandlers::SOCKSInfoHandler; - m_ClientServicesInfoHandlers["SAM"] = &I2PControlHandlers::SAMInfoHandler; - m_ClientServicesInfoHandlers["BOB"] = &I2PControlHandlers::BOBInfoHandler; - m_ClientServicesInfoHandlers["I2CP"] = &I2PControlHandlers::I2CPInfoHandler; - } - - void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, int value) const - { - ss << "\"" << name << "\":" << value; - } - - void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value, bool quotes) const - { - ss << "\"" << name << "\":"; - if (value.length () > 0) - { - if (quotes) - ss << "\"" << value << "\""; - else - ss << value; - } - else - ss << "null"; - } - - void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, double value) const - { - ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value; - } - - void I2PControlHandlers::InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const - { - std::ostringstream buf; - boost::property_tree::write_json (buf, value, false); - ss << "\"" << name << "\":" << buf.str(); - } - -// RouterInfo - - void I2PControlHandlers::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - bool first = true; - for (auto it = params.begin (); it != params.end (); it++) - { - LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first); - auto it1 = m_RouterInfoHandlers.find (it->first); - if (it1 != m_RouterInfoHandlers.end ()) - { - if (!first) results << ","; - else first = false; - (this->*(it1->second))(results); - } - else - LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first); - } - } - - void I2PControlHandlers::UptimeHandler (std::ostringstream& results) - { - InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL), false); - } - - void I2PControlHandlers::VersionHandler (std::ostringstream& results) - { - InsertParam (results, "i2p.router.version", VERSION); - } - - void I2PControlHandlers::StatusHandler (std::ostringstream& results) - { - auto dest = i2p::client::context.GetSharedLocalDestination (); - InsertParam (results, "i2p.router.status", (dest && dest->IsReady ()) ? "1" : "0"); - } - - void I2PControlHandlers::NetDbKnownPeersHandler (std::ostringstream& results) - { - InsertParam (results, "i2p.router.netdb.knownpeers", i2p::data::netdb.GetNumRouters ()); - } - - void I2PControlHandlers::NetDbActivePeersHandler (std::ostringstream& results) - { - InsertParam (results, "i2p.router.netdb.activepeers", (int)i2p::transport::transports.GetPeers ().size ()); - } - - void I2PControlHandlers::NetStatusHandler (std::ostringstream& results) - { - InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ()); - } - - void I2PControlHandlers::TunnelsParticipatingHandler (std::ostringstream& results) - { - int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size (); - InsertParam (results, "i2p.router.net.tunnels.participating", transit); - } - - void I2PControlHandlers::TunnelsSuccessRateHandler (std::ostringstream& results) - { - int rate = i2p::tunnel::tunnels.GetTunnelCreationSuccessRate (); - InsertParam (results, "i2p.router.net.tunnels.successrate", rate); - } - - void I2PControlHandlers::InboundBandwidth1S (std::ostringstream& results) - { - double bw = i2p::transport::transports.GetInBandwidth (); - InsertParam (results, "i2p.router.net.bw.inbound.1s", bw); - } - - void I2PControlHandlers::InboundBandwidth15S (std::ostringstream& results) - { - double bw = i2p::transport::transports.GetInBandwidth15s (); - InsertParam (results, "i2p.router.net.bw.inbound.15s", bw); - } - - void I2PControlHandlers::OutboundBandwidth1S (std::ostringstream& results) - { - double bw = i2p::transport::transports.GetOutBandwidth (); - InsertParam (results, "i2p.router.net.bw.outbound.1s", bw); - } - - void I2PControlHandlers::OutboundBandwidth15S (std::ostringstream& results) - { - double bw = i2p::transport::transports.GetOutBandwidth15s (); - InsertParam (results, "i2p.router.net.bw.outbound.15s", bw); - } - - void I2PControlHandlers::NetTotalReceivedBytes (std::ostringstream& results) - { - InsertParam (results, "i2p.router.net.total.received.bytes", (double)i2p::transport::transports.GetTotalReceivedBytes ()); - } - - void I2PControlHandlers::NetTotalSentBytes (std::ostringstream& results) - { - InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ()); - } - -// network setting - void I2PControlHandlers::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - for (auto it = params.begin (); it != params.end (); it++) - { - LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first); - auto it1 = m_NetworkSettingHandlers.find (it->first); - if (it1 != m_NetworkSettingHandlers.end ()) { - if (it != params.begin ()) results << ","; - (this->*(it1->second))(it->second.data (), results); - } else - LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first); - } - } - - void I2PControlHandlers::InboundBandwidthLimit (const std::string& value, std::ostringstream& results) - { - if (value != "null") - i2p::context.SetBandwidth (std::atoi(value.c_str())); - int bw = i2p::context.GetBandwidthLimit(); - InsertParam (results, "i2p.router.net.bw.in", bw); - } - - void I2PControlHandlers::OutboundBandwidthLimit (const std::string& value, std::ostringstream& results) - { - if (value != "null") - i2p::context.SetBandwidth (std::atoi(value.c_str())); - int bw = i2p::context.GetBandwidthLimit(); - InsertParam (results, "i2p.router.net.bw.out", bw); - } - -// ClientServicesInfo - - void I2PControlHandlers::ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) - { - for (auto it = params.begin (); it != params.end (); it++) - { - LogPrint (eLogDebug, "I2PControl: ClientServicesInfo request: ", it->first); - auto it1 = m_ClientServicesInfoHandlers.find (it->first); - if (it1 != m_ClientServicesInfoHandlers.end ()) - { - if (it != params.begin ()) results << ","; - (this->*(it1->second))(results); - } - else - LogPrint (eLogError, "I2PControl: ClientServicesInfo unknown request ", it->first); - } - } - - void I2PControlHandlers::I2PTunnelInfoHandler (std::ostringstream& results) - { - boost::property_tree::ptree pt; - boost::property_tree::ptree client_tunnels, server_tunnels; - - for (auto& it: i2p::client::context.GetClientTunnels ()) - { - auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - boost::property_tree::ptree ct; - ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); - client_tunnels.add_child(it.second->GetName (), ct); - } - - auto& serverTunnels = i2p::client::context.GetServerTunnels (); - if (!serverTunnels.empty ()) { - for (auto& it: serverTunnels) - { - auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - boost::property_tree::ptree st; - st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); - st.put("port", it.second->GetLocalPort ()); - server_tunnels.add_child(it.second->GetName (), st); - } - } - - auto& clientForwards = i2p::client::context.GetClientForwards (); - if (!clientForwards.empty ()) - { - for (auto& it: clientForwards) - { - auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - boost::property_tree::ptree ct; - ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); - client_tunnels.add_child(it.second->GetName (), ct); - } - } - - auto& serverForwards = i2p::client::context.GetServerForwards (); - if (!serverForwards.empty ()) - { - for (auto& it: serverForwards) - { - auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - boost::property_tree::ptree st; - st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); - server_tunnels.add_child(it.second->GetName (), st); - } - } - - pt.add_child("client", client_tunnels); - pt.add_child("server", server_tunnels); - - InsertParam (results, "I2PTunnel", pt); - } - - void I2PControlHandlers::HTTPProxyInfoHandler (std::ostringstream& results) - { - boost::property_tree::ptree pt; - - auto httpProxy = i2p::client::context.GetHttpProxy (); - if (httpProxy) - { - auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash(); - pt.put("enabled", true); - pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); - } - else - pt.put("enabled", false); - - InsertParam (results, "HTTPProxy", pt); - } - - void I2PControlHandlers::SOCKSInfoHandler (std::ostringstream& results) - { - boost::property_tree::ptree pt; - - auto socksProxy = i2p::client::context.GetSocksProxy (); - if (socksProxy) - { - auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash(); - pt.put("enabled", true); - pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); - } - else - pt.put("enabled", false); - - InsertParam (results, "SOCKS", pt); - } - - void I2PControlHandlers::SAMInfoHandler (std::ostringstream& results) - { - boost::property_tree::ptree pt; - auto sam = i2p::client::context.GetSAMBridge (); - if (sam) - { - pt.put("enabled", true); - boost::property_tree::ptree sam_sessions; - for (auto& it: sam->GetSessions ()) - { - boost::property_tree::ptree sam_session, sam_session_sockets; - auto& name = it.second->GetLocalDestination ()->GetNickname (); - auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - sam_session.put("name", name); - sam_session.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); - - for (const auto& socket: sam->ListSockets(it.first)) - { - boost::property_tree::ptree stream; - stream.put("type", socket->GetSocketType ()); - stream.put("peer", socket->GetSocket ().remote_endpoint()); - - sam_session_sockets.push_back(std::make_pair("", stream)); - } - sam_session.add_child("sockets", sam_session_sockets); - sam_sessions.add_child(it.first, sam_session); - } - - pt.add_child("sessions", sam_sessions); - } - else - pt.put("enabled", false); - - InsertParam (results, "SAM", pt); - } - - void I2PControlHandlers::BOBInfoHandler (std::ostringstream& results) - { - boost::property_tree::ptree pt; - auto bob = i2p::client::context.GetBOBCommandChannel (); - if (bob) - { - /* TODO more info */ - pt.put("enabled", true); - } - else - pt.put("enabled", false); - - InsertParam (results, "BOB", pt); - } - - void I2PControlHandlers::I2CPInfoHandler (std::ostringstream& results) - { - boost::property_tree::ptree pt; - auto i2cp = i2p::client::context.GetI2CPServer (); - if (i2cp) - { - /* TODO more info */ - pt.put("enabled", true); - } - else - pt.put("enabled", false); - - InsertParam (results, "I2CP", pt); - } -} -} diff --git a/daemon/I2PControlHandlers.h b/daemon/I2PControlHandlers.h deleted file mode 100644 index d106f288..00000000 --- a/daemon/I2PControlHandlers.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -* Copyright (c) 2013-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef I2P_CONTROL_HANDLERS_H__ -#define I2P_CONTROL_HANDLERS_H__ - -#include -#include -#include -#include - -namespace i2p -{ -namespace client -{ - class I2PControlHandlers - { - public: - - I2PControlHandlers (); - - // methods - // TODO: make protected - void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - void ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results); - - protected: - - void InsertParam (std::ostringstream& ss, const std::string& name, int value) const; - void InsertParam (std::ostringstream& ss, const std::string& name, double value) const; - void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value, bool quotes = true) const; - void InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const; - - private: - - // RouterInfo - typedef void (I2PControlHandlers::*RouterInfoRequestHandler)(std::ostringstream& results); - void UptimeHandler (std::ostringstream& results); - void VersionHandler (std::ostringstream& results); - void StatusHandler (std::ostringstream& results); - void NetDbKnownPeersHandler (std::ostringstream& results); - void NetDbActivePeersHandler (std::ostringstream& results); - void NetStatusHandler (std::ostringstream& results); - void TunnelsParticipatingHandler (std::ostringstream& results); - void TunnelsSuccessRateHandler (std::ostringstream& results); - void InboundBandwidth1S (std::ostringstream& results); - void InboundBandwidth15S (std::ostringstream& results); - void OutboundBandwidth1S (std::ostringstream& results); - void OutboundBandwidth15S (std::ostringstream& results); - void NetTotalReceivedBytes (std::ostringstream& results); - void NetTotalSentBytes (std::ostringstream& results); - - // NetworkSetting - typedef void (I2PControlHandlers::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results); - void InboundBandwidthLimit (const std::string& value, std::ostringstream& results); - void OutboundBandwidthLimit (const std::string& value, std::ostringstream& results); - - // ClientServicesInfo - typedef void (I2PControlHandlers::*ClientServicesInfoRequestHandler)(std::ostringstream& results); - void I2PTunnelInfoHandler (std::ostringstream& results); - void HTTPProxyInfoHandler (std::ostringstream& results); - void SOCKSInfoHandler (std::ostringstream& results); - void SAMInfoHandler (std::ostringstream& results); - void BOBInfoHandler (std::ostringstream& results); - void I2CPInfoHandler (std::ostringstream& results); - - private: - - std::map m_RouterInfoHandlers; - std::map m_NetworkSettingHandlers; - std::map m_ClientServicesInfoHandlers; - }; -} -} - -#endif diff --git a/daemon/UPnP.cpp b/daemon/UPnP.cpp deleted file mode 100644 index 8e6dbcf6..00000000 --- a/daemon/UPnP.cpp +++ /dev/null @@ -1,280 +0,0 @@ -/* -* Copyright (c) 2013-2024, 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 -#include -#include - -#include "Log.h" - -#include "RouterContext.h" -#include "UPnP.h" -#include "NetDb.hpp" -#include "util.h" -#include "RouterInfo.h" -#include "Config.h" - -#include -#include - -namespace i2p -{ -namespace transport -{ - UPnP::UPnP () : m_IsRunning(false), m_Thread (nullptr), m_Timer (m_Service) - { - } - - void UPnP::Stop () - { - if (m_IsRunning) - { - LogPrint(eLogInfo, "UPnP: Stopping"); - m_IsRunning = false; - m_Timer.cancel (); - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - m_Thread.reset (nullptr); - } - CloseMapping (); - Close (); - } - } - - void UPnP::Start() - { - m_IsRunning = true; - LogPrint(eLogInfo, "UPnP: Starting"); - boost::asio::post (m_Service, std::bind (&UPnP::Discover, this)); - std::unique_lock l(m_StartedMutex); - m_Thread.reset (new std::thread (std::bind (&UPnP::Run, this))); - m_Started.wait_for (l, std::chrono::seconds (5)); // 5 seconds maximum - } - - UPnP::~UPnP () - { - Stop (); - } - - void UPnP::Run () - { - i2p::util::SetThreadName("UPnP"); - - while (m_IsRunning) - { - try - { - m_Service.run (); - // Discover failed - break; // terminate the thread - } - catch (std::exception& ex) - { - LogPrint (eLogError, "UPnP: Runtime exception: ", ex.what ()); - PortMapping (); - } - } - } - - void UPnP::Discover () - { - bool isError; - int err; - -#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS)) - err = UPNPDISCOVER_SUCCESS; - -#if (MINIUPNPC_API_VERSION >= 14) - m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, 2, &err); -#else - m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0, 0, &err); -#endif - - isError = err != UPNPDISCOVER_SUCCESS; -#else // MINIUPNPC_API_VERSION >= 8 - err = 0; - m_Devlist = upnpDiscover (UPNP_RESPONSE_TIMEOUT, NULL, NULL, 0); - isError = m_Devlist == NULL; -#endif // MINIUPNPC_API_VERSION >= 8 - { - // notify starting thread - std::unique_lock l(m_StartedMutex); - m_Started.notify_all (); - } - - if (isError) - { - LogPrint (eLogError, "UPnP: Unable to discover Internet Gateway Devices: error ", err); - return; - } - -#if (MINIUPNPC_API_VERSION >= 18) - err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr), - m_externalIPAddress, sizeof (m_externalIPAddress)); -#else - err = UPNP_GetValidIGD (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr)); -#endif - m_upnpUrlsInitialized=err!=0; - if (err == UPNP_IGD_VALID_CONNECTED) - { -#if (MINIUPNPC_API_VERSION < 18) - err = UPNP_GetExternalIPAddress (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress); - if(err != UPNPCOMMAND_SUCCESS) - { - LogPrint (eLogError, "UPnP: Unable to get external address: error ", err); - return; - } - else -#endif - { - LogPrint (eLogError, "UPnP: Found Internet Gateway Device ", m_upnpUrls.controlURL); - if (!m_externalIPAddress[0]) - { - LogPrint (eLogError, "UPnP: Found Internet Gateway Device doesn't know our external address"); - return; - } - } - } - else - { - LogPrint (eLogError, "UPnP: Unable to find valid Internet Gateway Device: error ", err); - return; - } - - // UPnP discovered - LogPrint (eLogDebug, "UPnP: ExternalIPAddress is ", m_externalIPAddress); - i2p::context.UpdateAddress (boost::asio::ip::make_address (m_externalIPAddress)); - // port mapping - PortMapping (); - } - - int UPnP::CheckMapping (const char* port, const char* type) - { - int err = UPNPCOMMAND_SUCCESS; - -#if (MINIUPNPC_API_VERSION >= 10) - err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL, NULL, NULL, NULL, NULL); -#elif ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS)) - err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL, NULL, NULL, NULL); -#else - err = UPNP_GetSpecificPortMappingEntry(m_upnpUrls.controlURL, m_upnpData.first.servicetype, port, type, NULL, NULL); -#endif - return err; - } - - void UPnP::PortMapping () - { - auto a = context.GetRouterInfo().GetAddresses(); - if (!a) return; - for (const auto& address : *a) - { - if (address && !address->host.is_v6 () && address->port) - TryPortMapping (address); - } - m_Timer.expires_from_now (boost::posix_time::minutes(UPNP_PORT_FORWARDING_INTERVAL)); // every 20 minutes - m_Timer.async_wait ([this](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - PortMapping (); - }); - } - - void UPnP::TryPortMapping (std::shared_ptr address) - { - std::string strType (GetProto (address)), strPort (std::to_string (address->port)); - std::string strDesc; i2p::config::GetOption("upnp.name", strDesc); - int err = UPNPCOMMAND_SUCCESS; - - // check for existing mapping - err = CheckMapping (strPort.c_str (), strType.c_str ()); - if (err != UPNPCOMMAND_SUCCESS) // if mapping not found - { - LogPrint (eLogDebug, "UPnP: Port ", strPort, " is possibly not forwarded: return code ", err); - -#if ((MINIUPNPC_API_VERSION >= 8) || defined (UPNPDISCOVER_SUCCESS)) - err = UPNP_AddPortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), NULL, NULL); -#else - 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); -#endif - if (err != UPNPCOMMAND_SUCCESS) - { - LogPrint (eLogError, "UPnP: Port forwarding to ", m_NetworkAddr, ":", strPort, " failed: return code ", err); - return; - } - else - { - LogPrint (eLogInfo, "UPnP: Port successfully forwarded (", m_externalIPAddress ,":", strPort, " type ", strType, " -> ", m_NetworkAddr ,":", strPort ,")"); - return; - } - } - else - { - LogPrint (eLogDebug, "UPnP: External forward from ", m_NetworkAddr, ":", strPort, " exists on current Internet Gateway Device"); - return; - } - } - - void UPnP::CloseMapping () - { - auto a = context.GetRouterInfo().GetAddresses(); - if (!a) return; - for (const auto& address : *a) - { - if (address && !address->host.is_v6 () && address->port) - CloseMapping (address); - } - } - - void UPnP::CloseMapping (std::shared_ptr address) - { - if(!m_upnpUrlsInitialized) { - return; - } - std::string strType (GetProto (address)), strPort (std::to_string (address->port)); - int err = UPNPCOMMAND_SUCCESS; - - err = CheckMapping (strPort.c_str (), strType.c_str ()); - if (err == UPNPCOMMAND_SUCCESS) - { - err = UPNP_DeletePortMapping (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), NULL); - LogPrint (eLogError, "UPnP: DeletePortMapping() returned : ", err); - } - } - - void UPnP::Close () - { - freeUPNPDevlist (m_Devlist); - m_Devlist = 0; - if(m_upnpUrlsInitialized){ - FreeUPNPUrls (&m_upnpUrls); - m_upnpUrlsInitialized=false; - } - } - - std::string UPnP::GetProto (std::shared_ptr address) - { - switch (address->transportStyle) - { - case i2p::data::RouterInfo::eTransportNTCP2: - return "TCP"; - break; - case i2p::data::RouterInfo::eTransportSSU2: - default: - return "UDP"; - } - } -} -} -#else /* USE_UPNP */ -namespace i2p { -namespace transport { -} -} -#endif /* USE_UPNP */ diff --git a/daemon/UPnP.h b/daemon/UPnP.h deleted file mode 100644 index 2a5fe9f3..00000000 --- a/daemon/UPnP.h +++ /dev/null @@ -1,99 +0,0 @@ -/* -* Copyright (c) 2013-2024, 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 __UPNP_H__ -#define __UPNP_H__ - -#ifdef USE_UPNP -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace i2p -{ -namespace transport -{ - const int UPNP_RESPONSE_TIMEOUT = 2000; // in milliseconds - const int UPNP_PORT_FORWARDING_INTERVAL = 20; // in minutes - - enum - { - UPNP_IGD_NONE = 0, - UPNP_IGD_VALID_CONNECTED = 1, - UPNP_IGD_VALID_NOT_CONNECTED = 2, - UPNP_IGD_INVALID = 3 - }; - - class UPnP - { - public: - - UPnP (); - ~UPnP (); - void Close (); - - void Start (); - void Stop (); - - private: - - void Discover (); - int CheckMapping (const char* port, const char* type); - void PortMapping (); - void TryPortMapping (std::shared_ptr address); - void CloseMapping (); - void CloseMapping (std::shared_ptr address); - - void Run (); - std::string GetProto (std::shared_ptr address); - - private: - - bool m_IsRunning; - std::unique_ptr m_Thread; - std::condition_variable m_Started; - std::mutex m_StartedMutex; - boost::asio::io_context m_Service; - boost::asio::deadline_timer m_Timer; - bool m_upnpUrlsInitialized = false; - struct UPNPUrls m_upnpUrls; - struct IGDdatas m_upnpData; - - // For miniupnpc - struct UPNPDev * m_Devlist = 0; - char m_NetworkAddr[64]; - char m_externalIPAddress[40]; - }; -} -} - -#else // USE_UPNP -namespace i2p { -namespace transport { - /* class stub */ - class UPnP { - public: - - UPnP () {}; - ~UPnP () {}; - void Start () { LogPrint(eLogWarning, "UPnP: this module was disabled at compile-time"); } - void Stop () {}; - }; -} -} -#endif // USE_UPNP -#endif // __UPNP_H__ diff --git a/daemon/UnixDaemon.cpp b/daemon/UnixDaemon.cpp deleted file mode 100644 index 66661e0f..00000000 --- a/daemon/UnixDaemon.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/* -* Copyright (c) 2013-2024, 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 "Daemon.h" - -#ifndef _WIN32 - -#include -#include -#include -#include -#include -#include -#include - -#include "Config.h" -#include "FS.h" -#include "Log.h" -#include "Tunnel.h" -#include "RouterContext.h" -#include "ClientContext.h" -#include "Transports.h" -#include "util.h" - -void handle_signal(int sig) -{ - switch (sig) - { - case SIGHUP: - LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening tunnel configuration..."); - i2p::client::context.ReloadConfig(); - break; - case SIGUSR1: - LogPrint(eLogInfo, "Daemon: Got SIGUSR1, reopening logs..."); - i2p::log::Logger().Reopen (); - break; - case SIGINT: - if (i2p::context.AcceptsTunnels () && !Daemon.gracefulShutdownInterval) - { - i2p::context.SetAcceptsTunnels (false); - Daemon.gracefulShutdownInterval = 10*60; // 10 minutes - LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefulShutdownInterval, " seconds"); - } - else - Daemon.running = 0; - break; - case SIGABRT: - case SIGTERM: - Daemon.running = 0; // Exit loop - break; - case SIGPIPE: - LogPrint(eLogInfo, "SIGPIPE received"); - break; - case SIGTSTP: - LogPrint(eLogInfo, "Daemon: Got SIGTSTP, disconnecting from network..."); - i2p::transport::transports.SetOnline(false); - break; - case SIGCONT: - LogPrint(eLogInfo, "Daemon: Got SIGCONT, restoring connection to network..."); - i2p::transport::transports.SetOnline(true); - break; - } -} - -namespace i2p -{ - namespace util - { - bool DaemonLinux::start() - { - if (isDaemon) - { - pid_t pid; - pid = fork(); - if (pid > 0) // parent - ::exit (EXIT_SUCCESS); - - if (pid < 0) // error - { - LogPrint(eLogError, "Daemon: Could not fork: ", strerror(errno)); - std::cerr << "i2pd: Could not fork: " << strerror(errno) << std::endl; - return false; - } - - // child - umask(S_IWGRP | S_IRWXO); // 0027 - int sid = setsid(); - if (sid < 0) - { - LogPrint(eLogError, "Daemon: Could not create process group."); - std::cerr << "i2pd: Could not create process group." << std::endl; - return false; - } - std::string d = i2p::fs::GetDataDir(); - if (chdir(d.c_str()) != 0) - { - LogPrint(eLogError, "Daemon: Could not chdir: ", strerror(errno)); - std::cerr << "i2pd: Could not chdir: " << strerror(errno) << std::endl; - return false; - } - - // point std{in,out,err} descriptors to /dev/null - freopen("/dev/null", "r", stdin); - freopen("/dev/null", "w", stdout); - freopen("/dev/null", "w", stderr); - } - - // set proc limits - struct rlimit limit; - uint16_t nfiles; i2p::config::GetOption("limits.openfiles", nfiles); - getrlimit(RLIMIT_NOFILE, &limit); - if (nfiles == 0) { - LogPrint(eLogInfo, "Daemon: Using system limit in ", limit.rlim_cur, " max open files"); - } else if (nfiles <= limit.rlim_max) { - limit.rlim_cur = nfiles; - if (setrlimit(RLIMIT_NOFILE, &limit) == 0) { - LogPrint(eLogInfo, "Daemon: Set max number of open files to ", - nfiles, " (system limit is ", limit.rlim_max, ")"); - } else { - LogPrint(eLogError, "Daemon: Can't set max number of open files: ", strerror(errno)); - } - } else { - LogPrint(eLogError, "Daemon: limits.openfiles exceeds system limit: ", limit.rlim_max); - } - uint32_t cfsize; i2p::config::GetOption("limits.coresize", cfsize); - if (cfsize) // core file size set - { - cfsize *= 1024; - getrlimit(RLIMIT_CORE, &limit); - if (cfsize <= limit.rlim_max) { - limit.rlim_cur = cfsize; - if (setrlimit(RLIMIT_CORE, &limit) != 0) { - LogPrint(eLogError, "Daemon: Can't set max size of coredump: ", strerror(errno)); - } else if (cfsize == 0) { - LogPrint(eLogInfo, "Daemon: coredumps disabled"); - } else { - LogPrint(eLogInfo, "Daemon: Set max size of core files to ", cfsize / 1024, "Kb"); - } - } else { - LogPrint(eLogError, "Daemon: limits.coresize exceeds system limit: ", limit.rlim_max); - } - } - - // Pidfile - // this code is c-styled and a bit ugly, but we need fd for locking pidfile - std::string pidfile; i2p::config::GetOption("pidfile", pidfile); - if (pidfile == "") { - pidfile = i2p::fs::DataDirPath("i2pd.pid"); - } - if (pidfile != "") { - pidFH = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600); - if (pidFH < 0) - { - LogPrint(eLogError, "Daemon: Could not create pid file ", pidfile, ": ", strerror(errno)); - std::cerr << "i2pd: Could not create pid file " << pidfile << ": " << strerror(errno) << std::endl; - return false; - } - -#ifndef ANDROID - if (lockf(pidFH, F_TLOCK, 0) != 0) -#else - struct flock fl; - fl.l_len = 0; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - - if (fcntl(pidFH, F_SETLK, &fl) != 0) -#endif - { - LogPrint(eLogError, "Daemon: Could not lock pid file ", pidfile, ": ", strerror(errno)); - std::cerr << "i2pd: Could not lock pid file " << pidfile << ": " << strerror(errno) << std::endl; - return false; - } - - char pid[10]; - sprintf(pid, "%d\n", getpid()); - ftruncate(pidFH, 0); - if (write(pidFH, pid, strlen(pid)) < 0) - { - LogPrint(eLogCritical, "Daemon: Could not write pidfile ", pidfile, ": ", strerror(errno)); - std::cerr << "i2pd: Could not write pidfile " << pidfile << ": " << strerror(errno) << std::endl; - return false; - } - } - gracefulShutdownInterval = 0; // not specified - - // handle signal TSTP - bool handleTSTP; i2p::config::GetOption("unix.handle_sigtstp", handleTSTP); - - // Signal handler - struct sigaction sa; - sa.sa_handler = handle_signal; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sigaction(SIGHUP, &sa, 0); - sigaction(SIGUSR1, &sa, 0); - sigaction(SIGABRT, &sa, 0); - sigaction(SIGTERM, &sa, 0); - sigaction(SIGINT, &sa, 0); - sigaction(SIGPIPE, &sa, 0); - if (handleTSTP) - { - sigaction(SIGTSTP, &sa, 0); - sigaction(SIGCONT, &sa, 0); - } - - return Daemon_Singleton::start(); - } - - bool DaemonLinux::stop() - { - i2p::fs::Remove(pidfile); - return Daemon_Singleton::stop(); - } - - void DaemonLinux::run () - { - i2p::util::SetThreadName ("i2pd-daemon"); - while (running) - { - std::this_thread::sleep_for (std::chrono::seconds(1)); - if (gracefulShutdownInterval) - { - gracefulShutdownInterval--; // - 1 second - if (gracefulShutdownInterval <= 0 || i2p::tunnel::tunnels.CountTransitTunnels() <= 0) - { - LogPrint(eLogInfo, "Graceful shutdown"); - return; - } - } - } - } - } -} -#endif diff --git a/daemon/i2pd.cpp b/daemon/i2pd.cpp deleted file mode 100644 index 028aa916..00000000 --- a/daemon/i2pd.cpp +++ /dev/null @@ -1,52 +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 -*/ - -#include -#include "Daemon.h" - -#if defined(QT_GUI_LIB) -namespace i2p -{ -namespace qt -{ - int RunQT (int argc, char* argv[]); -} -} - -int main( int argc, char* argv[] ) -{ - return i2p::qt::RunQT (argc, argv); -} -#else -int main( int argc, char* argv[] ) -{ - if (Daemon.init(argc, argv)) - { - if (Daemon.start()) - Daemon.run (); - else - return EXIT_FAILURE; - Daemon.stop(); - } - return EXIT_SUCCESS; -} -#endif - -#ifdef _WIN32 -#include - -int CALLBACK WinMain( - _In_ HINSTANCE hInstance, - _In_ HINSTANCE hPrevInstance, - _In_ LPSTR lpCmdLine, - _In_ int nCmdShow - ) -{ - return main(__argc, __argv); -} -#endif diff --git a/debian/.gitignore b/debian/.gitignore deleted file mode 100644 index b03f9e24..00000000 --- a/debian/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -debhelper-build-stamp -files -i2pd-dbg.substvars -i2pd.postinst.debhelper -i2pd.postrm.debhelper -i2pd.prerm.debhelper -i2pd.substvars -i2pd/ -i2pd-dbg/ diff --git a/debian/NEWS b/debian/NEWS deleted file mode 100644 index c0add110..00000000 --- a/debian/NEWS +++ /dev/null @@ -1,5 +0,0 @@ -i2pd (2.53.0-1) unstable; urgency=medium - - i2pd binary moved from /usr/sbin to /usr/bin. Please check your scripts if you used the old path. - - -- r4sas Fri, 19 Jul 2024 16:00:00 +0000 diff --git a/debian/changelog b/debian/changelog index d170f534..23e93bd4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,403 +1,11 @@ -i2pd (2.56.0-1) unstable; urgency=medium +i2pd (20140919-2) unstable; urgency=low - * updated to version 2.56.0/0.9.65 - - -- orignal Tue, 11 Feb 2025 16:00:00 +0000 - -i2pd (2.55.0-1) unstable; urgency=medium - - * updated to version 2.55.0 - - -- orignal Mon, 30 Dec 2024 16:00:00 +0000 - -i2pd (2.54.0-1) unstable; urgency=medium - - * updated to version 2.54.0/0.9.64 - - -- orignal Sun, 6 Oct 2024 16:00:00 +0000 - -i2pd (2.53.1-1) unstable; urgency=medium - - * updated to version 2.53.1 - - -- orignal Tue, 30 Jul 2024 16:00:00 +0000 - -i2pd (2.53.0-1) unstable; urgency=medium - - * updated to version 2.53.0/0.9.63 - * binary moved from /usr/sbin to /usr/bin - - -- r4sas Sat, 20 Jul 2024 15:10:00 +0000 - -i2pd (2.52.0-1) unstable; urgency=medium - - * updated to version 2.52.0 - - -- orignal Sun, 12 May 2024 16:00:00 +0000 - -i2pd (2.51.0-1) unstable; urgency=medium - - * updated to version 2.51.0/0.9.62 - - -- orignal Sat, 06 Apr 2024 16:00:00 +0000 - -i2pd (2.50.2-1) unstable; urgency=medium - - * updated to version 2.50.2/0.9.61 - - -- orignal Sat, 06 Jan 2024 16:00:00 +0000 - -i2pd (2.50.1-1) unstable; urgency=medium - - * updated to version 2.50.1/0.9.61 - - -- r4sas Sat, 23 Dec 2023 18:30:00 +0000 - -i2pd (2.50.0-1) unstable; urgency=medium - - * updated to version 2.50.0/0.9.61 - - -- orignal Mon, 18 Dec 2023 16:00:00 +0000 - -i2pd (2.49.0-1) unstable; urgency=medium - - * updated to version 2.49.0/0.9.60 - - -- orignal Mon, 18 Sep 2023 16:00:00 +0000 - -i2pd (2.48.0-1) unstable; urgency=high - - * updated to version 2.48.0/0.9.59 - - -- orignal Mon, 12 Jun 2023 16:00:00 +0000 - -i2pd (2.47.0-1) unstable; urgency=high - - * updated to version 2.47.0/0.9.58 - - -- orignal Sat, 11 Mar 2023 16:00:00 +0000 - -i2pd (2.46.1-2) unstable; urgency=critical - - * re-pushed release due to new critical bug - - -- r4sas Mon, 20 Feb 2023 23:40:00 +0000 - -i2pd (2.46.1-1) unstable; urgency=high - - * updated to version 2.46.1/0.9.57 - - -- r4sas Mon, 20 Feb 2023 02:45:00 +0000 - -i2pd (2.46.0-1) unstable; urgency=high - - * updated to version 2.46.0/0.9.57 - - -- orignal Wed, 15 Feb 2023 19:00:00 +0000 - -i2pd (2.45.1-1) unstable; urgency=medium - - * updated to version 2.45.1/0.9.57 - - -- orignal Wed, 11 Jan 2023 19:00:00 +0000 - -i2pd (2.45.0-1) unstable; urgency=high - - * updated to version 2.45.0/0.9.57 - * compat level 12 - * standards version 4.3.0 - * increased nofile limit in service and init.d to 8192 - * added conffiles - * removed #1210 patch - - -- r4sas Tue, 3 Jan 2023 18:00:00 +0000 - -i2pd (2.44.0-1) unstable; urgency=medium - - * updated to version 2.44.0/0.9.56 - - -- orignal Sun, 20 Nov 2022 19:00:00 +0000 - -i2pd (2.43.0-1) unstable; urgency=medium - - * updated to version 2.43.0/0.9.55 - - -- orignal Mon, 22 Aug 2022 16:00:00 +0000 - -i2pd (2.42.1-1) unstable; urgency=medium - - * updated to version 2.42.1/0.9.54 - * remove -O3 optimization flag - - -- r4sas Tue, 24 May 2022 12:00:00 +0000 - -i2pd (2.42.0-1) unstable; urgency=medium - - * updated to version 2.42.0/0.9.54 - - -- orignal Sun, 22 May 2022 16:00:00 +0000 - -i2pd (2.41.0-1) unstable; urgency=medium - - * updated to version 2.41.0/0.9.53 - - -- r4sas Sun, 20 Feb 2022 13:00:00 +0000 - -i2pd (2.40.0-1) unstable; urgency=medium - - * updated to version 2.40.0/0.9.52 - - -- orignal Mon, 29 Nov 2021 16:00:00 +0000 - -i2pd (2.39.0-1) unstable; urgency=medium - - * updated to version 2.39.0/0.9.51 - - -- orignal Mon, 23 Aug 2021 16:00:00 +0000 - -i2pd (2.38.0-1) unstable; urgency=medium - - * updated to version 2.38.0/0.9.50 - - -- orignal Mon, 17 May 2021 16:00:00 +0000 - -i2pd (2.37.0-1) unstable; urgency=medium - - * updated to version 2.37.0 - - -- orignal Mon, 15 Mar 2021 16:00:00 +0000 - -i2pd (2.36.0-1) unstable; urgency=high - - * updated to version 2.36.0/0.9.49 - - -- orignal Mon, 15 Feb 2021 16:00:00 +0000 - -i2pd (2.35.0-1) unstable; urgency=high - - * updated to version 2.35.0/0.9.48 - - -- orignal Mon, 30 Nov 2020 16:00:00 +0000 - -i2pd (2.34.0-1) unstable; urgency=medium - - * updated to version 2.34.0 - - -- orignal Tue, 27 Oct 2020 16:00:00 +0000 - -i2pd (2.33.0-1) unstable; urgency=medium - - * updated to version 2.33.0/0.9.47 - - -- orignal Mon, 24 Aug 2020 16:00:00 +0000 - -i2pd (2.32.1-1) unstable; urgency=high - - * updated to version 2.32.1 - - -- r4sas Tue, 02 Jun 2020 16:30:00 +0000 - -i2pd (2.32.0-1) unstable; urgency=high - - * updated to version 2.32.0/0.9.46 - * updated systemd service file (see #1394) - * updated apparmor profile (see 9318388007cff0495b4b360d0480f4fc1219a9dc) - * updated logrotate config and moved it to contrib - - -- r4sas Mon, 25 May 2020 12:45:00 +0000 - -i2pd (2.31.0-1) unstable; urgency=medium - - * updated to version 2.31.0 - - -- orignal Fri, 10 Apr 2020 16:00:00 +0000 - -i2pd (2.30.0-1) unstable; urgency=medium - - * updated to version 2.30.0/0.9.45 - - -- orignal Tue, 25 Feb 2020 16:00:00 +0000 - -i2pd (2.29.0-1) unstable; urgency=medium - - * updated to version 2.29.0/0.9.43 - - -- orignal Mon, 21 Oct 2019 16:00:00 +0000 - -i2pd (2.28.0-1) unstable; urgency=medium - - * updated to version 2.28.0/0.9.42 - - -- orignal Tue, 27 Aug 2019 16:00:00 +0000 - -i2pd (2.27.0-1) unstable; urgency=medium - - * updated to version 2.27.0/0.9.41 - - -- orignal Wed, 3 Jul 2019 16:00:00 +0000 - -i2pd (2.26.0-1) unstable; urgency=medium - - * updated to version 2.26.0 - - -- orignal Fri, 7 Jun 2019 16:00:00 +0000 - -i2pd (2.25.0-1) unstable; urgency=medium - - * updated to version 2.25.0/0.9.40 - - -- orignal Thu, 9 May 2019 16:00:00 +0000 - -i2pd (2.24.0-1) unstable; urgency=medium - - * updated to version 2.24.0/0.9.39 - - -- orignal Thu, 21 Mar 2019 16:00:00 +0000 - -i2pd (2.23.0-1) unstable; urgency=medium - - * updated to version 2.23.0/0.9.38 - * update docs, dirs, install, links files - - -- orignal Mon, 21 Jan 2019 16:00:00 +0000 - -i2pd (2.22.0-1) unstable; urgency=medium - - * updated to version 2.22.0/0.9.37 - * update manpage (1) - * update links, install files to support tunnelsdir option - * renamed and updated patch (#1210) - - -- r4sas Fri, 09 Nov 2018 02:00:00 +0000 - -i2pd (2.21.1-1) unstable; urgency=medium - - * updated to version 2.21.1 - - -- orignal Mon, 22 Oct 2018 16:00:00 +0000 - -i2pd (2.21.0-1) unstable; urgency=medium - - * updated to version 2.21.0/0.9.37 - - -- orignal Thu, 4 Oct 2018 16:00:00 +0000 - -i2pd (2.20.0-1) unstable; urgency=medium - - * updated to version 2.20.0/0.9.36 - - -- orignal Thu, 23 Aug 2018 16:00:00 +0000 - -i2pd (2.19.0-1) unstable; urgency=medium - - * updated to version 2.19.0/0.9.35 - * update manpage (1) - * update docfiles - * update build rules - * fixes in systemd unit (#1089, #1142, #1154, #1155) - * package now building with systemd support - - -- R4SAS Tue, 26 Jun 2018 16:27:45 +0000 - -i2pd (2.18.0-1) unstable; urgency=low - - * updated to version 2.18.0/0.9.33 - - -- orignal Tue, 30 Jan 2018 16:00:00 +0000 - -i2pd (2.17.0-1) unstable; urgency=low - - * updated to version 2.17.0/0.9.32 - - -- orignal Mon, 4 Dec 2017 18:00:00 +0000 - -i2pd (2.16.0-1) unstable; urgency=low - - * updated to version 2.16.0/0.9.32 - - -- orignal Mon, 13 Nov 2017 18:00:00 +0000 - -i2pd (2.15.0-1) unstable; urgency=low - - * updated to version 2.15.0/0.9.31 - - -- orignal Thu, 17 Aug 2017 18:00:00 +0000 - -i2pd (2.14.0-1) unstable; urgency=low - - * updated to version 2.14.0/0.9.30 - * updated debian/control - * renamed logrotate to i2pd.logrotate - * fixed init.d script - - -- orignal Thu, 1 Jun 2017 14:00:00 +0000 - -i2pd (2.13.0-1) unstable; urgency=low - - * updated to version 2.13.0/0.9.29 - * updated debian/control - * renamed logrotate to i2pd.logrotate - * fixed init.d script - - -- orignal Thu, 6 Apr 2017 14:00:00 +0000 - -i2pd (2.12.0-1) unstable; urgency=low - - * updated to version 2.12.0/0.9.28 - - -- orignal Tue, 14 Feb 2017 17:59:30 +0000 - -i2pd (2.11.0-1) unstable; urgency=low - - * updated to version 2.11.0/0.9.28 - - -- orignal Sun, 18 Dec 2016 21:01:30 +0000 - -i2pd (2.10.2-1) unstable; urgency=low - - * updated to version 2.10.2 - - -- orignal Sun, 4 Dec 2016 19:38:30 +0000 - -i2pd (2.10.1-1) unstable; urgency=low - - * updated to version 2.10.1 - - -- orignal Mon, 7 Nov 2016 14:18:30 +0000 - -i2pd (2.10.0-1) unstable; urgency=low - - * updated to version 2.10.0/0.9.27 - * reseed.verify set to true by default - - -- orignal Sun, 16 Oct 2016 13:55:40 +0000 - -i2pd (2.9.0-1) unstable; urgency=low - - * updated to version 2.9.0 - * updated tune-patch - * removed I2PD_PORT in i2pd.default - * removed all port assigments in services files - * fixed logrotate - * subscriptions.txt and tunnels.conf taken from docs folder - - -- orignal Fri, 12 Aug 2016 14:25:40 +0000 - -i2pd (2.7.0-1) unstable; urgency=low - - * updated to version 2.7.0/0.9.25 - - -- hagen Wed, 18 May 2016 01:11:04 +0000 - -i2pd (2.2.0-2) unstable; urgency=low - - * updated to version 2.2.0 - - -- hagen Wed, 23 Dec 2015 01:29:40 +0000 - -i2pd (2.1.0-1) unstable; urgency=low - - * updated to version 2.1.0/0.9.23 - * updated deps + * updated to latest sources -- hagen Fri, 19 Sep 2014 05:16:12 +0000 + +i2pd (20140919-1) unstable; urgency=low + + * Initial release (Closes: #nnnn) + + -- hagen Mon, 19 Sep 2014 00:00:00 +0000 diff --git a/debian/compat b/debian/compat index 48082f72..45a4fb75 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -12 +8 diff --git a/debian/control b/debian/control index 48f5e680..98312c87 100644 --- a/debian/control +++ b/debian/control @@ -1,18 +1,22 @@ Source: i2pd Section: net -Priority: optional -Maintainer: r4sas -Build-Depends: debhelper (>= 12~), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev -Standards-Version: 4.3.0 -Homepage: http://i2pd.website/ -Vcs-Git: git://github.com/PurpleI2P/i2pd.git -Vcs-Browser: https://github.com/PurpleI2P/i2pd +Priority: extra +Maintainer: hagen +Build-Depends: debhelper (>= 8.0.0), dpkg-dev (>= 1.16.1~), + cmake (>= 2.8), gcc (>= 4.6) | clang (>= 3.3), + libboost-regex-dev, + libboost-system-dev (>= 1.46), + libboost-date-time-dev, + libboost-filesystem-dev, + libboost-program-options-dev, + libcrypto++-dev +Standards-Version: 3.9.3 +Homepage: https://github.com/PrivacySolutions/i2pd +Vcs-Git: git://github.com/PrivacySolutions/i2pd.git +Vcs-Browser: https://github.com/PrivacySolutions/i2pd.git Package: i2pd Architecture: any -Pre-Depends: ${misc:Pre-Depends}, adduser -Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base, -Description: Full-featured C++ implementation of I2P client. - I2P (Invisible Internet Protocol) is a universal anonymous network layer. All - communications over I2P are anonymous and end-to-end encrypted, participants - don't reveal their real IP addresses. +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: i2p router written in C++ + Mainly runs on linux, but also supports OSX, BSD and Windows diff --git a/debian/copyright b/debian/copyright index 352e260b..ec52d972 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,48 +1,16 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: i2pd -Source: https://github.com/PurpleI2P +Source: https://github.com/PrivacySolutions Files: * -Copyright: 2013-2023 PurpleI2P -License: BSD-3-clause +Copyright: 2013-2014 PrivacySolutions +License: GPL-2.0+ Files: debian/* -Copyright: 2013-2015 Kill Your TV - 2014-2016 hagen - 2016-2023 R4SAS - 2017-2020 Yangfl -License: GPL-2+ +Copyright: 2014 hagen +License: GPL-2.0+ -License: BSD-3-clause - Copyright (c) 2013-2023, The PurpleI2P Project - . - All rights reserved. - . - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: - . - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. - . - 2. Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials - provided with the distribution. - . - 3. Neither the name of the copyright holder nor the names of its contributors may be used - to endorse or promote products derived from this software without specific prior written - permission. - . - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -License: GPL-2+ +License: GPL-2.0+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or diff --git a/debian/i2pd.1 b/debian/i2pd.1 deleted file mode 100644 index 42c282d9..00000000 --- a/debian/i2pd.1 +++ /dev/null @@ -1,130 +0,0 @@ -.TH "I2PD" "1" "June 20, 2018" - -.SH "NAME" -i2pd \- Full-featured C++ implementation of I2P client. -.SH "SYNOPSIS" -.B i2pd -[\fIOPTION1\fR] [\fIOPTION2\fR]... -.SH "DESCRIPTION" -i2pd -is a C++ implementation of the router for the I2P anonymizing network, offering -a simple layer that identity-sensitive applications can use to securely -communicate. All data is wrapped with several layers of encryption, and the -network is both distributed and dynamic, with no trusted parties. -.PP -Any of the configuration options below can be used in the \fBDAEMON_ARGS\fR variable in \fI/etc/default/i2pd\fR. -.SH "OPTIONS" -.TP -\fB\-\-help\fR -Show available options. -.TP -\fB\-\-conf=\fR -Config file (default: \fI~/.i2pd/i2pd.conf\fR or \fI/var/lib/i2pd/i2pd.conf\fR) -.BR -This parameter will be silently ignored if the specified config file does not exist. -Options specified on the command line take precedence over those in the config file. -.TP -\fB\-\-tunconf=\fR -Tunnels config file (default: \fI~/.i2pd/tunnels.conf\fR or \fI/var/lib/i2pd/tunnels.conf\fR) -.TP -\fB\-\-pidfile=\fR -Where to write pidfile (don\'t write by default) -.TP -\fB\-\-log=\fR -Logs destination: \fIstdout\fR, \fIfile\fR, \fIsyslog\fR (\fIstdout\fR if not set, \fIfile\fR - otherwise, for compatibility) -.TP -\fB\-\-logfile=\fR -Path to logfile (default - autodetect) -.TP -\fB\-\-loglevel=\fR -Log messages above this level (\fIdebug\fR, \fBinfo\fR, \fIwarn\fR, \fIerror\fR, \fInone\fR) -.TP -\fB\-\-logclftime\fR -Log messages with full CLF-formatted date and time (\fIdisabled\fR by default) -.TP -\fB\-\-datadir=\fR -Path to storage of i2pd data (RI, keys, peer profiles, ...) -.TP -\fB\-\-tunnelsdir=\fR -Path to tunnels configuration files (default: \fI~/.i2pd/tunnels.d\fR or \fI/var/lib/i2pd/tunnels.d\fR) -.TP -\fB\-\-host=\fR -The external IP address -.TP -\fB\-\-port=\fR -The port to listen on for incoming connections -.TP -\fB\-\-ifname=\fR -The network interface to bind to -.TP -\fB\-\-ifname4=\fR -The network interface to bind to for IPv4 connections -.TP -\fB\-\-ifname6=\fR -The network interface to bind to for IPv6 connections -.TP -\fB\-\-ipv4=\fR -Enable communication through ipv4 (\fIenabled\fR by default) -.TP -\fB\-\-ipv6\fR -Enable communication through ipv6 (\fIdisabled\fR by default) -.TP -\fB\-\-ntcp=\fR -Enable usage of NTCP transport (\fIenabled\fR by default) -.TP -\fB\-\-ntcpproxy=\fR -Set proxy URL for NTCP transport -.TP -\fB\-\-ssu=\fR -Enable usage of SSU transport (\fIenabled\fR by default) -.TP -\fB\-\-notransit\fR -Router will not accept transit tunnels at startup (\fIdisabled\fR by default) -.TP -\fB\-\-floodfill\fR -Router will be floodfill (\fIdisabled\fR by default) -.TP -\fB\-\-bandwidth=\fR -Bandwidth limit: integer in KBps or letter aliases: \fBL (32KBps)\fR, \fIO (256)\fR, \fIP (2048)\fR, \fIX (>9000)\fR -.TP -\fB\-\-share=\fR -Limit of transit traffic from max bandwidth in percents. (default: 100) -.TP -\fB\-\-daemon\fR -Router will go to background after start (\fIdisabled\fR by default) -.TP -\fB\-\-service\fR -Router will use system folders like \fI/var/lib/i2pd\fR (\fIdisabled\fR by default) -.TP -\fB\-\-family=\fR -Name of a family, router belongs to. -.PP -Switches, which enabled by default (like \fB\-\-ssu\fR, \fB\-\-ntcp\fR, etc.), can be disabled in config file. -.RE -See service-specific parameters in example config file \fI/usr/share/doc/i2pd/i2pd.conf.gz\fR -.SH "FILES" -/etc/i2pd/i2pd.conf, /etc/i2pd/tunnels.conf, /etc/default/i2pd -.RS 4 -i2pd configuration files (when running as a system service) -.RE -.PP -/var/lib/i2pd/ -.RS 4 -i2pd profile directory (when running as a system service, see \fB\-\-service\fR above) -.RE -.PP -$HOME/.i2pd/ -.RS 4 -i2pd profile directory (when running as a normal user) -.SH "SEE ALSO" -Documentation at Read the Docs: \m[blue]\fBhttps://i2pd\&.readthedocs\&.io/en/latest/\fR\m[] -.SH "AUTHOR" -This manual page was written by kytv <\m[blue]\fBkillyourtv@i2pmail\&.org\fR\m[]> for the Debian system (but may be used by others). -.RE -Updated by hagen <\m[blue]\fBhagen@i2pmail\&.org\fR\m[]> in 2016. -.RE -Updated by R4SAS <\m[blue]\fBr4sas@i2pmail\&.org\fR\m[]> in 2018. -.PP -Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 2 or any later version published by the Free Software Foundation. -.RE -On Debian systems, the complete text of the GNU General Public License can be found in \fI/usr/share/common-licenses/GPL\fR diff --git a/debian/i2pd.default b/debian/i2pd.default index 90392ede..b76000ab 100644 --- a/debian/i2pd.default +++ b/debian/i2pd.default @@ -1,11 +1,7 @@ # Defaults for i2pd initscript # sourced by /etc/init.d/i2pd # installed at /etc/default/i2pd by the maintainer scripts -I2PD_ENABLED="yes" # Additional options that are passed to the Daemon. -# see possible switches in /usr/share/doc/i2pd/configuration.md.gz -DAEMON_OPTS="" - -# If you have problems with hunging i2pd, you can try enable this -ulimit -n 8192 +DAEMON_OPTS="--host=1.2.3.4 --port=4567" +# change ip and port above to your external address and port diff --git a/debian/i2pd.init b/debian/i2pd.init index 9b5f7669..eda135c6 100644 --- a/debian/i2pd.init +++ b/debian/i2pd.init @@ -13,14 +13,10 @@ PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC=i2pd # Introduce a short description here NAME=i2pd # Introduce the short server's name here -DAEMON=/usr/bin/$NAME # Introduce the server's location here +DAEMON=/usr/sbin/i2pd # Introduce the server's location here DAEMON_OPTS="" # Arguments to run the daemon with -PIDFILE=/var/run/$NAME/$NAME.pid -I2PCONF=/etc/$NAME/i2pd.conf -TUNCONF=/etc/$NAME/tunnels.conf -TUNDIR=/etc/$NAME/tunnels.conf.d -LOGFILE=/var/log/$NAME/$NAME.log -USER="i2pd" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed [ -x $DAEMON ] || exit 0 @@ -36,27 +32,11 @@ do_start() # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started - - if [ "x$I2PD_ENABLED" != "xyes" ]; then - log_warning_msg "$NAME disabled in config" - return 2 - fi - - test -e /var/run/i2pd || install -m 755 -o i2pd -g i2pd -d /var/run/i2pd - touch "$PIDFILE" - chown -f $USER:adm "$PIDFILE" - - test -e /var/log/i2pd || install -m 755 -o i2pd -g i2pd -d /var/log/i2pd - touch "$LOGFILE" - chown -f $USER:adm "$LOGFILE" - - start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid "$USER" --test > /dev/null \ + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 - start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid "$USER" -- \ - --service --daemon --log=file --logfile=$LOGFILE --conf=$I2PCONF --tunconf=$TUNCONF \ - --tunnelsdir=$TUNDIR --pidfile=$PIDFILE $DAEMON_OPTS > /dev/null 2>&1 \ + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + --daemon=1 --log=1 $DAEMON_OPTS \ || return 2 - return $? } # Function that stops the daemon/service @@ -126,7 +106,7 @@ case "$1" in esac ;; *) - echo "Usage: $0 {start|stop|status|restart|reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac diff --git a/debian/i2pd.install b/debian/i2pd.install index bde52854..66af1abd 100644 --- a/debian/i2pd.install +++ b/debian/i2pd.install @@ -1,6 +1 @@ -i2pd usr/bin/ -contrib/i2pd.conf etc/i2pd/ -contrib/tunnels.conf etc/i2pd/ -contrib/certificates/ usr/share/i2pd/ -contrib/tunnels.d/README etc/i2pd/tunnels.conf.d/ -contrib/apparmor/usr.bin.i2pd etc/apparmor.d +i2pd usr/sbin/ diff --git a/debian/i2pd.links b/debian/i2pd.links deleted file mode 100644 index 16558791..00000000 --- a/debian/i2pd.links +++ /dev/null @@ -1,4 +0,0 @@ -etc/i2pd/i2pd.conf var/lib/i2pd/i2pd.conf -etc/i2pd/tunnels.conf var/lib/i2pd/tunnels.conf -etc/i2pd/tunnels.conf.d var/lib/i2pd/tunnels.d -usr/share/i2pd/certificates var/lib/i2pd/certificates diff --git a/debian/i2pd.logrotate b/debian/i2pd.logrotate deleted file mode 120000 index 40dea037..00000000 --- a/debian/i2pd.logrotate +++ /dev/null @@ -1 +0,0 @@ -../contrib/i2pd.logrotate \ No newline at end of file diff --git a/debian/i2pd.manpages b/debian/i2pd.manpages deleted file mode 100644 index 1de1d6a4..00000000 --- a/debian/i2pd.manpages +++ /dev/null @@ -1 +0,0 @@ -debian/i2pd.1 diff --git a/debian/i2pd.service b/debian/i2pd.service deleted file mode 120000 index 57d6b4da..00000000 --- a/debian/i2pd.service +++ /dev/null @@ -1 +0,0 @@ -../contrib/debian/i2pd.service \ No newline at end of file diff --git a/debian/i2pd.tmpfile b/debian/i2pd.tmpfile deleted file mode 120000 index 22dfb4cf..00000000 --- a/debian/i2pd.tmpfile +++ /dev/null @@ -1 +0,0 @@ -../contrib/debian/i2pd.tmpfile \ No newline at end of file diff --git a/contrib/upstart/i2pd.upstart b/debian/i2pd.upstart similarity index 57% rename from contrib/upstart/i2pd.upstart rename to debian/i2pd.upstart index d2cd4d5e..0527935d 100644 --- a/contrib/upstart/i2pd.upstart +++ b/debian/i2pd.upstart @@ -4,8 +4,7 @@ start on runlevel [2345] stop on runlevel [016] or unmounting-filesystem # these can be overridden in /etc/init/i2pd.override -env LOGFILE="/var/log/i2pd/i2pd.log" +env I2P_HOST="1.2.3.4" +env I2P_PORT="4567" -expect fork - -exec /usr/bin/i2pd --daemon --service --log=file --logfile=$LOGFILE +exec /usr/sbin/i2pd --daemon=0 --log=1 --host=$I2P_HOST --port=$I2P_PORT diff --git a/debian/lintian-overrides b/debian/lintian-overrides deleted file mode 100644 index af3d61c6..00000000 --- a/debian/lintian-overrides +++ /dev/null @@ -1,2 +0,0 @@ -# GPL come from debian/ -i2pd: possible-gpl-code-linked-with-openssl \ No newline at end of file diff --git a/debian/patches/01-upnp.patch b/debian/patches/01-upnp.patch deleted file mode 100644 index 74d36c06..00000000 --- a/debian/patches/01-upnp.patch +++ /dev/null @@ -1,17 +0,0 @@ -Description: Enable UPnP usage in package -Author: r4sas - -Reviewed-By: r4sas -Last-Update: 2024-12-30 - ---- i2pd.orig/Makefile -+++ i2pd/Makefile -@@ -31,7 +31,7 @@ # import source files lists - include filelist.mk - - USE_STATIC := $(or $(USE_STATIC),no) --USE_UPNP := $(or $(USE_UPNP),no) -+USE_UPNP := $(or $(USE_UPNP),yes) - DEBUG := $(or $(DEBUG),yes) - - # for debugging purposes only, when commit hash needed in trunk builds in i2pd version string diff --git a/debian/patches/rename-binary.patch b/debian/patches/rename-binary.patch new file mode 100644 index 00000000..70973dec --- /dev/null +++ b/debian/patches/rename-binary.patch @@ -0,0 +1,23 @@ +--- a/Makefile ++++ b/Makefile +@@ -10,9 +10,9 @@ + include Makefile.linux + endif + +-all: obj i2p ++all: obj i2pd + +-i2p: $(OBJECTS:obj/%=obj/%) ++i2pd: $(OBJECTS:obj/%=obj/%) + $(CXX) -o $@ $^ $(LDFLAGS) $(LIBS) + + .SUFFIXES: +@@ -25,7 +25,7 @@ + mkdir -p obj + + clean: +- rm -fr obj i2p ++ rm -fr obj i2pd + + .PHONY: all + .PHONY: clean diff --git a/debian/patches/series b/debian/patches/series index f97fdf65..714c2e62 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1 +1 @@ -01-upnp.patch +rename-binary.patch diff --git a/debian/postinst b/debian/postinst deleted file mode 100755 index 720753fd..00000000 --- a/debian/postinst +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh -set -e - -LOGFILE='/var/log/i2pd/i2pd.log' -I2PDHOME='/var/lib/i2pd' -I2PDUSER='i2pd' - -case "$1" in - configure|reconfigure) - # Older versions of adduser created the home directory. - # The version of adduser in Debian unstable does not. - # Create user and group as a system user. - if getent passwd $I2PDUSER > /dev/null 2>&1; then - groupadd -f $I2PDUSER || true - else - adduser --system --quiet --group --home $I2PDHOME $I2PDUSER - fi - - mkdir -p -m0750 /var/log/i2pd - chown -f ${I2PDUSER}:adm /var/log/i2pd - touch $LOGFILE - chmod 640 $LOGFILE - chown -f ${I2PDUSER}:adm $LOGFILE - mkdir -p -m0750 $I2PDHOME - chown -f -P ${I2PDUSER}:${I2PDUSER} ${I2PDHOME} - ;; - abort-upgrade|abort-remove|abort-deconfigure) - echo "Aborting upgrade" - exit 0 - ;; - *) - echo "postinst called with unknown argument '$1'" >&2 - exit 0 - ;; -esac - -#DEBHELPER# - -exit 0 diff --git a/debian/postrm b/debian/postrm deleted file mode 100755 index ba69785e..00000000 --- a/debian/postrm +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -set -e - -if [ "$1" = "purge" ]; then - rm -f /etc/default/i2pd - rm -rf /etc/i2pd - rm -rf /var/lib/i2pd - rm -rf /var/log/i2pd - rm -rf /run/i2pd -fi - -#DEBHELPER# - -exit 0 diff --git a/debian/rules b/debian/rules index fc769066..bf31e452 100755 --- a/debian/rules +++ b/debian/rules @@ -1,13 +1,13 @@ #!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 -export DEB_BUILD_MAINT_OPTIONS = hardening=+all -include /usr/share/dpkg/architecture.mk - -export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic -export DEB_LDFLAGS_MAINT_APPEND = +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/buildflags.mk +CXXFLAGS+=$(CPPFLAGS) +PREFIX=/usr %: - dh $@ - -override_dh_auto_install: + dh $@ diff --git a/debian/watch b/debian/watch index 2ec6c29b..f3faedd2 100644 --- a/debian/watch +++ b/debian/watch @@ -1,4 +1,3 @@ -version=4 -opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%i2pd-$1.tar.gz%" \ - https://github.com/PurpleI2P/i2pd/tags \ - (?:.*?/)?(\d[\d.]*)\.tar\.gz debian uupdate +version=3 +opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/i2pd-$1\.tar\.gz/ \ + https://github.com/PrivacySolutions/i2pd/tags .*/v?(\d\S*)\.tar\.gz diff --git a/docs/Doxyfile b/docs/Doxyfile deleted file mode 100644 index 275d668e..00000000 --- a/docs/Doxyfile +++ /dev/null @@ -1,2354 +0,0 @@ -# Doxyfile 1.8.8 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = "i2pd" - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = "load-balanced unspoofable packet switching network" - -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = docs/generated - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII -# characters to appear in the names of generated files. If set to NO, non-ASCII -# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode -# U+3044. -# The default value is: NO. - -ALLOW_UNICODE_NAMES = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = YES - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:\n" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. -# -# Note For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be -# included in the documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = YES - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. See also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = YES - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = NO - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. -# The default value is: NO. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. -# Note: If this tag is empty the current directory is searched. - -INPUT = - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. - -FILE_PATTERNS = *.cpp \ - *.h \ - *.hpp - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = NO - -# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. -# Note: The availability of this option depends on whether or not doxygen was -# compiled with the --with-libclang option. -# The default value is: NO. - -CLANG_ASSISTED_PARSING = NO - -# If clang assisted parsing is enabled you can provide the compiler with command -# line options that you would normally use when invoking the compiler. Note that -# the include paths will already be set by doxygen for the files and directories -# specified with INPUT and INCLUDE_PATH. -# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. - -CLANG_OPTIONS = - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined -# cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefore more robust against future updates. -# Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra stylesheet files is of importance (e.g. the last -# stylesheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it -# enables the Previous and Next buttons. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /

address, uint64_t ts) - { - if (!address || !address->ssu) return; - int numValid = 0; - for (auto& it: address->ssu->introducers) - { - if (it.iTag && ts < it.iExp && !it.iH.IsZero ()) - numValid++; - else - it.iTag = 0; - } - if (!numValid) - address->ssu->introducers.resize (0); - } - - bool RouterInfo::IsNewer (const uint8_t * buf, size_t len) const - { - if (!m_RouterIdentity) return false; - size_t size = m_RouterIdentity->GetFullLen (); - if (size + 8 > len) return false; - return bufbe64toh (buf + size) > m_Timestamp; - } - - const uint8_t * RouterInfo::LoadBuffer (const std::string& fullPath) - { - if (!m_Buffer) - { - if (LoadFile (fullPath)) - LogPrint (eLogDebug, "RouterInfo: Buffer for ", GetIdentHashAbbreviation (GetIdentHash ()), " loaded from file"); - else - return nullptr; - } - return m_Buffer->data (); - } - - bool RouterInfo::SaveToFile (const std::string& fullPath, std::shared_ptr buf) - { - if (!buf) return false; - std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); - if (!f.is_open ()) - { - LogPrint (eLogError, "RouterInfo: Can't save to ", fullPath); - return false; - } - f.write ((char *)buf->data (), buf->GetBufferLen ()); - return true; - } - - bool RouterInfo::SaveToFile (const std::string& fullPath) - { - if (m_IsUnreachable) return false; // don't save bad router - if (!m_Buffer) - { - LogPrint (eLogWarning, "RouterInfo: Can't save, m_Buffer == NULL"); - return false; - } - return SaveToFile (fullPath, m_Buffer); - } - - std::string_view RouterInfo::ExtractString (const uint8_t * buf, size_t len) const - { - uint8_t l = buf[0]; - if (l > len) - { - LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len); - l = len; - } - return { (const char *)(buf + 1), l }; - } - - std::tuple RouterInfo::ExtractParam (const uint8_t * buf, size_t len) const - { - auto key = ExtractString (buf, len); - size_t offset = key.length () + 1; - if (offset >= len) return { std::string_view(), std::string_view(), len }; - if (buf[offset] != '=') - { - LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead '=' after ", key); - key = std::string_view(); - } - offset++; - if (offset >= len) return { key, std::string_view(), len }; - auto value = ExtractString (buf + offset, len - offset); - offset += value.length () + 1; - if (offset >= len) return { key, std::string_view(), len }; - if (buf[offset] != ';') - { - LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead ';' after ", value); - value = std::string_view(); - } - offset++; - return { key, value, offset }; - } - - void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,int port, uint8_t caps) - { - auto addr = std::make_shared
(); - addr->port = port; - addr->transportStyle = eTransportNTCP2; - addr->caps = caps; - addr->date = 0; - addr->published = false; - memcpy (addr->s, staticKey, 32); - memcpy (addr->i, iv, 16); - if (addr->IsV4 ()) - { - m_SupportedTransports |= eNTCP2V4; - (*GetAddresses ())[eNTCP2V4Idx] = addr; - } - if (addr->IsV6 ()) - { - m_SupportedTransports |= eNTCP2V6; - (*GetAddresses ())[eNTCP2V6Idx] = addr; - } - } - - void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, - const boost::asio::ip::address& host, int port) - { - auto addr = std::make_shared
(); - addr->host = host; - addr->port = port; - addr->transportStyle = eTransportNTCP2; - addr->date = 0; - addr->published = true; - memcpy (addr->s, staticKey, 32); - memcpy (addr->i, iv, 16); - addr->caps = 0; - if (host.is_unspecified ()) - { - if (host.is_v4 ()) addr->caps |= eV4; - if (host.is_v6 ()) addr->caps |= eV6; - } - auto addresses = GetAddresses (); - if (addr->IsV4 ()) - { - m_SupportedTransports |= eNTCP2V4; - m_ReachableTransports |= eNTCP2V4; - (*addresses)[eNTCP2V4Idx] = addr; - } - if (addr->IsV6 ()) - { - if (i2p::util::net::IsYggdrasilAddress (addr->host)) - { - m_SupportedTransports |= eNTCP2V6Mesh; - m_ReachableTransports |= eNTCP2V6Mesh; - (*addresses)[eNTCP2V6MeshIdx] = addr; - } - else - { - m_SupportedTransports |= eNTCP2V6; - m_ReachableTransports |= eNTCP2V6; - (*addresses)[eNTCP2V6Idx] = addr; - } - } - } - - void RouterInfo::RemoveNTCP2Address (bool v4) - { - auto addresses = GetAddresses (); - if (v4) - { - if ((*addresses)[eNTCP2V6Idx]) - (*addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4; - (*addresses)[eNTCP2V4Idx].reset (); - } - else - { - if ((*addresses)[eNTCP2V4Idx]) - (*addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6; - (*addresses)[eNTCP2V6Idx].reset (); - } - UpdateSupportedTransports (); - } - - void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, int port, uint8_t caps) - { - auto addr = std::make_shared
(); - addr->transportStyle = eTransportSSU2; - addr->port = port; - addr->caps = caps; - addr->date = 0; - addr->ssu.reset (new SSUExt ()); - addr->ssu->mtu = 0; - memcpy (addr->s, staticKey, 32); - memcpy (addr->i, introKey, 32); - auto addresses = GetAddresses (); - if (addr->IsV4 ()) - { - m_SupportedTransports |= eSSU2V4; - (*addresses)[eSSU2V4Idx] = addr; - } - if (addr->IsV6 ()) - { - m_SupportedTransports |= eSSU2V6; - (*addresses)[eSSU2V6Idx] = addr; - } - } - - void RouterInfo::AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, - const boost::asio::ip::address& host, int port) - { - auto addr = std::make_shared
(); - addr->transportStyle = eTransportSSU2; - addr->host = host; - addr->port = port; - addr->published = true; - addr->date = 0; - addr->ssu.reset (new SSUExt ()); - addr->ssu->mtu = 0; - memcpy (addr->s, staticKey, 32); - memcpy (addr->i, introKey, 32); - if (!host.is_unspecified ()) - addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC; - else - { - addr->caps = 0; - if (host.is_v4 ()) addr->caps |= eV4; - if (host.is_v6 ()) addr->caps |= eV6; - } - auto addresses = GetAddresses (); - if (addr->IsV4 ()) - { - m_SupportedTransports |= eSSU2V4; - m_ReachableTransports |= eSSU2V4; - (*addresses)[eSSU2V4Idx] = addr; - } - if (addr->IsV6 ()) - { - m_SupportedTransports |= eSSU2V6; - m_ReachableTransports |= eSSU2V6; - (*addresses)[eSSU2V6Idx] = addr; - } - } - - void RouterInfo::RemoveSSU2Address (bool v4) - { - auto addresses = GetAddresses (); - if (v4) - { - if ((*addresses)[eSSU2V6Idx]) - (*addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4; - (*addresses)[eSSU2V4Idx].reset (); - } - else - { - if ((*addresses)[eSSU2V4Idx]) - (*addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6; - (*addresses)[eSSU2V6Idx].reset (); - } - UpdateSupportedTransports (); - } - - bool RouterInfo::IsNTCP2 (bool v4only) const - { - if (v4only) - return m_SupportedTransports & eNTCP2V4; - else - return m_SupportedTransports & (eNTCP2V4 | eNTCP2V6); - } - - - void RouterInfo::EnableV6 () - { - if (!IsV6 ()) - { - uint8_t addressCaps = AddressCaps::eV6; - if (IsV4 ()) addressCaps |= AddressCaps::eV4; - SetUnreachableAddressesTransportCaps (addressCaps); - UpdateSupportedTransports (); - } - } - - void RouterInfo::EnableV4 () - { - if (!IsV4 ()) - { - uint8_t addressCaps = AddressCaps::eV4; - if (IsV6 ()) addressCaps |= AddressCaps::eV6; - SetUnreachableAddressesTransportCaps (addressCaps); - UpdateSupportedTransports (); - } - } - - - void RouterInfo::DisableV6 () - { - if (IsV6 ()) - { - auto addresses = GetAddresses (); - if ((*addresses)[eNTCP2V6Idx]) - { - if ((*addresses)[eNTCP2V6Idx]->IsV4 () && (*addresses)[eNTCP2V4Idx]) - (*addresses)[eNTCP2V4Idx]->caps &= ~AddressCaps::eV6; - (*addresses)[eNTCP2V6Idx].reset (); - } - if ((*addresses)[eSSU2V6Idx]) - { - if ((*addresses)[eSSU2V6Idx]->IsV4 () && (*addresses)[eSSU2V4Idx]) - (*addresses)[eSSU2V4Idx]->caps &= ~AddressCaps::eV6; - (*addresses)[eSSU2V6Idx].reset (); - } - UpdateSupportedTransports (); - } - } - - void RouterInfo::DisableV4 () - { - if (IsV4 ()) - { - auto addresses = GetAddresses (); - if ((*addresses)[eNTCP2V4Idx]) - { - if ((*addresses)[eNTCP2V4Idx]->IsV6 () && (*addresses)[eNTCP2V6Idx]) - (*addresses)[eNTCP2V6Idx]->caps &= ~AddressCaps::eV4; - (*addresses)[eNTCP2V4Idx].reset (); - } - if ((*addresses)[eSSU2V4Idx]) - { - if ((*addresses)[eSSU2V4Idx]->IsV6 () && (*addresses)[eSSU2V6Idx]) - (*addresses)[eSSU2V6Idx]->caps &= ~AddressCaps::eV4; - (*addresses)[eSSU2V4Idx].reset (); - } - UpdateSupportedTransports (); - } - } - - void RouterInfo::EnableMesh () - { - if (!IsMesh ()) - { - m_SupportedTransports |= eNTCP2V6Mesh; - m_ReachableTransports |= eNTCP2V6Mesh; - } - } - - void RouterInfo::DisableMesh () - { - if (IsMesh ()) - { - m_SupportedTransports &= ~eNTCP2V6Mesh; - m_ReachableTransports &= ~eNTCP2V6Mesh; - (*GetAddresses ())[eNTCP2V6MeshIdx].reset (); - } - } - - std::shared_ptr RouterInfo::GetSSU2V4Address () const - { - return (*GetAddresses ())[eSSU2V4Idx]; - } - - std::shared_ptr RouterInfo::GetSSU2V6Address () const - { - return (*GetAddresses ())[eSSU2V6Idx]; - } - - std::shared_ptr RouterInfo::GetSSU2Address (bool v4) const - { - if (v4) - { - if (m_SupportedTransports & eSSU2V4) - return GetSSU2V4Address (); - } - else - { - if (m_SupportedTransports & eSSU2V6) - return GetSSU2V6Address (); - } - return nullptr; - } - - RouterInfo::AddressesPtr RouterInfo::GetAddresses () const - { -#ifdef __cpp_lib_atomic_shared_ptr - return m_Addresses; -#else - return boost::atomic_load (&m_Addresses); -#endif - } - - template - std::shared_ptr RouterInfo::GetAddress (Filter filter) const - { - // TODO: make it more generic using comparator -#ifdef __cpp_lib_atomic_shared_ptr - AddressesPtr addresses = m_Addresses; -#else - auto addresses = boost::atomic_load (&m_Addresses); -#endif - for (const auto& address : *addresses) - if (address && filter (address)) return address; - - return nullptr; - } - - std::shared_ptr RouterInfo::GetNTCP2V4Address () const - { - return (*GetAddresses ())[eNTCP2V4Idx]; - } - - std::shared_ptr RouterInfo::GetNTCP2V6Address () const - { - return (*GetAddresses ())[eNTCP2V6Idx]; - } - - std::shared_ptr RouterInfo::GetPublishedNTCP2V4Address () const - { - auto addr = (*GetAddresses ())[eNTCP2V4Idx]; - if (addr && addr->IsPublishedNTCP2 ()) return addr; - return nullptr; - } - - std::shared_ptr RouterInfo::GetPublishedNTCP2V6Address () const - { - auto addr = (*GetAddresses ())[eNTCP2V6Idx]; - if (addr && addr->IsPublishedNTCP2 ()) return addr; - return nullptr; - } - - std::shared_ptr RouterInfo::GetYggdrasilAddress () const - { - return (*GetAddresses ())[eNTCP2V6MeshIdx]; - } - - std::shared_ptr RouterInfo::GetProfile () const - { - auto profile = m_Profile; - if (!profile) - { - profile = GetRouterProfile (GetIdentHash ()); - m_Profile = profile; - } - return profile; - } - - void RouterInfo::Encrypt (const uint8_t * data, uint8_t * encrypted) const - { - auto encryptor = m_RouterIdentity->CreateEncryptor (nullptr); - if (encryptor) - encryptor->Encrypt (data, encrypted); - } - - bool RouterInfo::IsEligibleFloodfill () const - { - // floodfill must have published ipv4 or reachable ipv4 and published ipv6 - // >= 0.9.59 and not DSA - return m_Version >= NETDB_MIN_FLOODFILL_VERSION && (IsPublished (true) || - (IsReachableBy (eNTCP2V4 | eSSU2V4) && IsPublished (false))) && - GetIdentity ()->GetSigningKeyType () != SIGNING_KEY_TYPE_DSA_SHA1; - } - - bool RouterInfo::IsPublished (bool v4) const - { - if (m_Caps & (eUnreachable | eHidden)) return false; // if router sets U or H we assume that all addresses are not published - return IsPublishedOn (v4 ? (eNTCP2V4 | eSSU2V4) : (eNTCP2V6 | eSSU2V6)); - } - - bool RouterInfo::IsPublishedOn (CompatibleTransports transports) const - { - return m_PublishedTransports & transports; - } - - bool RouterInfo::IsNAT2NATOnly (const RouterInfo& other) const - { - return !(m_PublishedTransports & other.m_SupportedTransports) && - !(other.m_PublishedTransports & m_SupportedTransports); - } - - bool RouterInfo::IsSSU2PeerTesting (bool v4) const - { - if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; - auto addr = (*GetAddresses ())[v4 ? eSSU2V4Idx : eSSU2V6Idx]; - return addr && addr->IsPeerTesting () && addr->IsReachableSSU (); - } - - bool RouterInfo::IsSSU2Introducer (bool v4) const - { - if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false; - auto addr = (*GetAddresses ())[v4 ? eSSU2V4Idx : eSSU2V6Idx]; - return addr && addr->IsIntroducer () && !addr->host.is_unspecified () && addr->port; - } - - void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) - { - for (auto& addr: *GetAddresses ()) - { - if (addr && !addr->published) - { - addr->caps &= ~(eV4 | eV6); - addr->caps |= transports; - } - } - } - - void RouterInfo::UpdateSupportedTransports () - { - m_SupportedTransports = 0; - m_ReachableTransports = 0; - for (const auto& addr: *GetAddresses ()) - { - if (!addr) continue; - uint8_t transports = 0; - switch (addr->transportStyle) - { - case eTransportNTCP2: - if (addr->IsV4 ()) transports |= eNTCP2V4; - if (addr->IsV6 ()) - transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6); - if (addr->IsPublishedNTCP2 ()) - m_ReachableTransports |= transports; - break; - case eTransportSSU2: - if (addr->IsV4 ()) transports |= eSSU2V4; - if (addr->IsV6 ()) transports |= eSSU2V6; - if (addr->IsReachableSSU ()) - m_ReachableTransports |= transports; - break; - default: ; - } - m_SupportedTransports |= transports; - } - } - - void RouterInfo::UpdateIntroducers (uint64_t ts) - { - if (ts*1000 < m_Timestamp + INTRODUCER_UPDATE_INTERVAL) return; - if (m_ReachableTransports & eSSU2V4) - { - auto addr = (*GetAddresses ())[eSSU2V4Idx]; - if (addr && addr->UsesIntroducer ()) - { - UpdateIntroducers (addr, ts); - if (!addr->UsesIntroducer ()) // no more valid introducers - m_ReachableTransports &= ~eSSU2V4; - } - } - if (m_ReachableTransports & eSSU2V6) - { - auto addr = (*GetAddresses ())[eSSU2V6Idx]; - if (addr && addr->UsesIntroducer ()) - { - UpdateIntroducers (addr, ts); - if (!addr->UsesIntroducer ()) // no more valid introducers - m_ReachableTransports &= ~eSSU2V6; - } - } - } - - void RouterInfo::UpdateBuffer (const uint8_t * buf, size_t len) - { - m_IsBufferScheduledToDelete = false; - if (!m_Buffer) - m_Buffer = NewBuffer (); - if (len > m_Buffer->size ()) len = m_Buffer->size (); - memcpy (m_Buffer->data (), buf, len); - m_Buffer->SetBufferLen (len); - } - - std::shared_ptr RouterInfo::CopyBuffer () const - { - if (!m_Buffer) return nullptr; - return netdb.NewRouterInfoBuffer (*m_Buffer); - } - - std::shared_ptr RouterInfo::NewBuffer () const - { - return netdb.NewRouterInfoBuffer (); - } - - std::shared_ptr RouterInfo::NewAddress () const - { - return netdb.NewRouterInfoAddress (); - } - - RouterInfo::AddressesPtr RouterInfo::NewAddresses () const - { - return netdb.NewRouterInfoAddresses (); - } - - std::shared_ptr RouterInfo::NewIdentity (const uint8_t * buf, size_t len) const - { - return netdb.NewIdentity (buf, len); - } - - void RouterInfo::RefreshTimestamp () - { - m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); - } - - bool RouterInfo::IsHighCongestion (bool highBandwidth) const - { - switch (m_Congestion) - { - case eLowCongestion: - return false; - break; - case eMediumCongestion: - return highBandwidth; - break; - case eHighCongestion: - return i2p::util::GetMillisecondsSinceEpoch () < m_Timestamp + HIGH_CONGESTION_INTERVAL*1000LL; - break; - case eRejectAll: - return true; - break; - default: - return false; - } - } - - std::string RouterInfo::GetTransportName (SupportedTransports tr) - { - switch (tr) - { - case eNTCP2V4: return "NTCP2V4"; - case eNTCP2V6: return "NTCP2V6"; - case eSSU2V4: return "SSU2V4"; - case eSSU2V6: return "SSU2V6"; - case eNTCP2V6Mesh: return "Mesh"; - default: return ""; - } - } - - void LocalRouterInfo::CreateBuffer (const PrivateKeys& privateKeys) - { - RefreshTimestamp (); - std::stringstream s; - uint8_t ident[1024]; - auto identLen = privateKeys.GetPublic ()->ToBuffer (ident, 1024); - auto signatureLen = privateKeys.GetPublic ()->GetSignatureLen (); - s.write ((char *)ident, identLen); - WriteToStream (s); - size_t len = s.str ().size (); - if (len + signatureLen < MAX_RI_BUFFER_SIZE) - { - UpdateBuffer ((const uint8_t *)s.str ().c_str (), len); - // signature - privateKeys.Sign (GetBuffer (), len, GetBufferPointer (len)); - SetBufferLen (len + signatureLen); - } - else - LogPrint (eLogError, "RouterInfo: Our RouterInfo is too long ", len + signatureLen); - } - - void LocalRouterInfo::UpdateCaps (uint8_t caps) - { - SetCaps (caps); - UpdateCapsProperty (); - } - - void LocalRouterInfo::UpdateCapsProperty () - { - std::string caps; - uint8_t c = GetCaps (); - if (c & eFloodfill) - { - if (c & eExtraBandwidth) caps += (c & eHighBandwidth) ? - CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X' - CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' - else - caps += CAPS_FLAG_HIGH_BANDWIDTH; // 'O' - caps += CAPS_FLAG_FLOODFILL; // floodfill - } - else - { - if (c & eExtraBandwidth) - caps += (c & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */ - else - caps += (c & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth - } - if (c & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden - if (c & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable - if (c & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable - - switch (GetCongestion ()) - { - case eMediumCongestion: - caps += CAPS_FLAG_MEDIUM_CONGESTION; - break; - case eHighCongestion: - caps += CAPS_FLAG_HIGH_CONGESTION; - break; - case eRejectAll: - caps += CAPS_FLAG_REJECT_ALL_CONGESTION; - break; - default: ; - }; - - SetProperty ("caps", caps); - } - - bool LocalRouterInfo::UpdateCongestion (Congestion c) - { - if (c != GetCongestion ()) - { - SetCongestion (c); - UpdateCapsProperty (); - return true; - } - return false; - } - - void LocalRouterInfo::WriteToStream (std::ostream& s) const - { - auto addresses = GetAddresses (); - if (!addresses) return; - - uint64_t ts = htobe64 (GetTimestamp ()); - s.write ((const char *)&ts, sizeof (ts)); - // addresses - uint8_t numAddresses = 0; - for (size_t idx = 0; idx < addresses->size(); idx++) - { - auto addr_ptr = (*addresses)[idx]; - if (!addr_ptr) continue; - if (idx == eNTCP2V6Idx && addr_ptr == (*addresses)[eNTCP2V4Idx]) continue; - if (idx == eSSU2V6Idx && addr_ptr == (*addresses)[eSSU2V4Idx]) continue; - numAddresses++; - } - s.write ((char *)&numAddresses, sizeof (numAddresses)); - for (size_t idx = 0; idx < addresses->size(); idx++) - { - auto addr_ptr = (*addresses)[idx]; - if (!addr_ptr) continue; - if (idx == eNTCP2V6Idx && addr_ptr == (*addresses)[eNTCP2V4Idx]) continue; - if (idx == eSSU2V6Idx && addr_ptr == (*addresses)[eSSU2V4Idx]) continue; - const Address& address = *addr_ptr; - // calculate cost - uint8_t cost = 0x7f; - if (address.transportStyle == eTransportNTCP2) - cost = address.published ? COST_NTCP2_PUBLISHED : COST_NTCP2_NON_PUBLISHED; - else if (address.transportStyle == eTransportSSU2) - cost = address.published ? COST_SSU2_DIRECT : COST_SSU2_NON_PUBLISHED; - else - continue; // skip unknown address - s.write ((const char *)&cost, sizeof (cost)); - s.write ((const char *)&address.date, sizeof (address.date)); - std::stringstream properties; - bool isPublished = address.published && !address.host.is_unspecified () && address.port; - if (address.transportStyle == eTransportNTCP2) - { - WriteString ("NTCP2", s); - // caps - if (!isPublished) - { - WriteString ("caps", properties); - properties << '='; - std::string caps; - if (address.IsV4 ()) caps += CAPS_FLAG_V4; - if (address.IsV6 () || address.host.is_v6 ()) caps += CAPS_FLAG_V6; // we set 6 for unspecified ipv6 - if (caps.empty ()) caps += CAPS_FLAG_V4; - WriteString (caps, properties); - properties << ';'; - } - } - else if (address.transportStyle == eTransportSSU2) - { - WriteString ("SSU2", s); - // caps - std::string caps; - if (isPublished) - { - if (address.IsPeerTesting ()) caps += CAPS_FLAG_SSU2_TESTING; - if (address.IsIntroducer ()) caps += CAPS_FLAG_SSU2_INTRODUCER; - } - else - { - if (address.IsV4 ()) caps += CAPS_FLAG_V4; - if (address.IsV6 () || address.host.is_v6 ()) caps += CAPS_FLAG_V6; // we set 6 for unspecified ipv6 - if (caps.empty ()) caps += CAPS_FLAG_V4; - } - if (!caps.empty ()) - { - WriteString ("caps", properties); - properties << '='; - WriteString (caps, properties); - properties << ';'; - } - } - else - WriteString ("", s); - - if (isPublished && !address.host.is_unspecified ()) - { - WriteString ("host", properties); - properties << '='; - WriteString (address.host.to_string (), properties); - properties << ';'; - } - if ((address.IsNTCP2 () && isPublished) || address.IsSSU2 ()) - { - // publish i for NTCP2 or SSU2 - WriteString ("i", properties); properties << '='; - size_t len = address.IsSSU2 () ? 32 : 16; - WriteString (address.i.ToBase64 (len), properties); properties << ';'; - } - if (address.transportStyle == eTransportSSU2) - { - // write introducers if any - if (address.ssu && !address.ssu->introducers.empty()) - { - int i = 0; - for (const auto& introducer: address.ssu->introducers) - { - if (!introducer.iTag) continue; - if (introducer.iExp) // expiration is specified - { - WriteString ("iexp" + std::to_string(i), properties); - properties << '='; - WriteString (std::to_string(introducer.iExp), properties); - properties << ';'; - } - i++; - } - i = 0; - for (const auto& introducer: address.ssu->introducers) - { - if (!introducer.iTag) continue; - WriteString ("ih" + std::to_string(i), properties); - properties << '='; - auto value = ByteStreamToBase64 (introducer.iH, 32); - WriteString (value, properties); - properties << ';'; - i++; - } - i = 0; - for (const auto& introducer: address.ssu->introducers) - { - if (!introducer.iTag) continue; - WriteString ("itag" + std::to_string(i), properties); - properties << '='; - WriteString (std::to_string(introducer.iTag), properties); - properties << ';'; - i++; - } - } - } - - if (address.transportStyle == eTransportSSU2) - { - // write mtu - if (address.ssu && address.ssu->mtu) - { - WriteString ("mtu", properties); - properties << '='; - WriteString (std::to_string(address.ssu->mtu), properties); - properties << ';'; - } - } - if (isPublished && address.port) - { - WriteString ("port", properties); - properties << '='; - WriteString (std::to_string(address.port), properties); - properties << ';'; - } - if (address.IsNTCP2 () || address.IsSSU2 ()) - { - // publish s and v for NTCP2 or SSU2 - WriteString ("s", properties); properties << '='; - WriteString (address.s.ToBase64 (), properties); properties << ';'; - WriteString ("v", properties); properties << '='; - WriteString ("2", properties); properties << ';'; - } - - uint16_t size = htobe16 (properties.str ().size ()); - s.write ((char *)&size, sizeof (size)); - s.write (properties.str ().c_str (), properties.str ().size ()); - } - - // peers - uint8_t numPeers = 0; - s.write ((char *)&numPeers, sizeof (numPeers)); - - // properties - std::stringstream properties; - for (const auto& p : m_Properties) - { - WriteString (p.first, properties); - properties << '='; - WriteString (p.second, properties); - properties << ';'; - } - uint16_t size = htobe16 (properties.str ().size ()); - s.write ((char *)&size, sizeof (size)); - s.write (properties.str ().c_str (), properties.str ().size ()); - } - - void LocalRouterInfo::SetProperty (std::string_view key, std::string_view value) - { - auto [it, inserted] = m_Properties.emplace (key, value); - if (!inserted) - it->second = value; - } - - void LocalRouterInfo::DeleteProperty (const std::string& key) - { - m_Properties.erase (key); - } - - std::string LocalRouterInfo::GetProperty (const std::string& key) const - { - auto it = m_Properties.find (key); - if (it != m_Properties.end ()) - return it->second; - return ""; - } - - void LocalRouterInfo::UpdateFloodfillProperty (bool floodfill) - { - if (floodfill) - { - UpdateCaps (GetCaps () | i2p::data::RouterInfo::eFloodfill); - SetFloodfill (); - } - else - { - UpdateCaps (GetCaps () & ~i2p::data::RouterInfo::eFloodfill); - ResetFloodfill (); - } - } - - void LocalRouterInfo::WriteString (const std::string& str, std::ostream& s) const - { - uint8_t len = str.size (); - s.write ((char *)&len, 1); - s.write (str.c_str (), len); - } - - std::shared_ptr LocalRouterInfo::NewBuffer () const - { - return std::make_shared (); - } - - std::shared_ptr LocalRouterInfo::NewAddress () const - { - return std::make_shared
(); - } - - RouterInfo::AddressesPtr LocalRouterInfo::NewAddresses () const - { - return RouterInfo::AddressesPtr(new RouterInfo::Addresses ()); - } - - std::shared_ptr LocalRouterInfo::NewIdentity (const uint8_t * buf, size_t len) const - { - return std::make_shared (buf, len); - } - - bool LocalRouterInfo::AddSSU2Introducer (const Introducer& introducer, bool v4) - { - auto addresses = GetAddresses (); - if (!addresses) return false; - auto addr = (*addresses)[v4 ? eSSU2V4Idx : eSSU2V6Idx]; - if (addr) - { - for (auto& intro: addr->ssu->introducers) - if (intro.iTag == introducer.iTag) return false; // already presented - addr->ssu->introducers.push_back (introducer); - SetReachableTransports (GetReachableTransports () | ((addr->IsV4 () ? eSSU2V4 : eSSU2V6))); - return true; - } - return false; - } - - bool LocalRouterInfo::RemoveSSU2Introducer (const IdentHash& h, bool v4) - { - auto addresses = GetAddresses (); - if (!addresses) return false; - auto addr = (*addresses)[v4 ? eSSU2V4Idx : eSSU2V6Idx]; - if (addr) - { - for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it) - if (h == it->iH) - { - addr->ssu->introducers.erase (it); - if (addr->ssu->introducers.empty ()) - SetReachableTransports (GetReachableTransports () & ~(addr->IsV4 () ? eSSU2V4 : eSSU2V6)); - return true; - } - } - return false; - } - - bool LocalRouterInfo::UpdateSSU2Introducer (const IdentHash& h, bool v4, uint32_t iTag, uint32_t iExp) - { - auto addresses = GetAddresses (); - if (!addresses) return false; - auto addr = (*addresses)[v4 ? eSSU2V4Idx : eSSU2V6Idx]; - if (addr) - { - for (auto& it: addr->ssu->introducers) - if (h == it.iH) - { - it.iTag = iTag; - it.iExp = iExp; - return true; - } - } - return false; - } -} -} diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h deleted file mode 100644 index cb3ae499..00000000 --- a/libi2pd/RouterInfo.h +++ /dev/null @@ -1,412 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 ROUTER_INFO_H__ -#define ROUTER_INFO_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef __cpp_lib_atomic_shared_ptr -#include -#endif -#include "Identity.h" -#include "Profiling.h" -#include "Family.h" - -namespace i2p -{ -namespace data -{ - const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets"; - const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters"; - const char ROUTER_INFO_PROPERTY_NETID[] = "netId"; - const char ROUTER_INFO_PROPERTY_VERSION[] = "router.version"; - const char ROUTER_INFO_PROPERTY_FAMILY[] = "family"; - const char ROUTER_INFO_PROPERTY_FAMILY_SIG[] = "family.sig"; - - const char CAPS_FLAG_FLOODFILL = 'f'; - const char CAPS_FLAG_HIDDEN = 'H'; - const char CAPS_FLAG_REACHABLE = 'R'; - const char CAPS_FLAG_UNREACHABLE = 'U'; - /* bandwidth flags */ - const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */ - const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */ - const char CAPS_FLAG_LOW_BANDWIDTH3 = 'M'; /* 48-64 KBps */ - const char CAPS_FLAG_LOW_BANDWIDTH4 = 'N'; /* 64-128 KBps */ - const char CAPS_FLAG_HIGH_BANDWIDTH = 'O'; /* 128-256 KBps */ - const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2048 KBps */ - const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2048 KBps */ - // bandwidth limits in kBps - const uint32_t LOW_BANDWIDTH_LIMIT = 48; - const uint32_t HIGH_BANDWIDTH_LIMIT = 256; - const uint32_t EXTRA_BANDWIDTH_LIMIT = 2048; - // congesion flags - const char CAPS_FLAG_MEDIUM_CONGESTION = 'D'; - const char CAPS_FLAG_HIGH_CONGESTION = 'E'; - const char CAPS_FLAG_REJECT_ALL_CONGESTION = 'G'; - - const char CAPS_FLAG_V4 = '4'; - const char CAPS_FLAG_V6 = '6'; - const char CAPS_FLAG_SSU2_TESTING = 'B'; - const char CAPS_FLAG_SSU2_INTRODUCER = 'C'; - - const uint8_t COST_NTCP2_PUBLISHED = 3; - const uint8_t COST_NTCP2_NON_PUBLISHED = 14; - const uint8_t COST_SSU2_DIRECT = 8; - const uint8_t COST_SSU2_NON_PUBLISHED = 15; - - const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later - const int HIGH_CONGESTION_INTERVAL = 15*60; // in seconds, 15 minutes - const int INTRODUCER_UPDATE_INTERVAL = 20*60*1000; // in milliseconds, 20 minutes - - class RouterInfo: public RoutingDestination - { - public: - - enum SupportedTransportsIdx - { - eNTCP2V4Idx = 0, - eNTCP2V6Idx, - eSSU2V4Idx, - eSSU2V6Idx, - eNTCP2V6MeshIdx, - eNumTransports - }; - -#define TransportBit(tr) e##tr = (1 << e##tr##Idx) - - enum SupportedTransports - { - TransportBit(NTCP2V4), // 0x01 - TransportBit(NTCP2V6), // 0x02 - TransportBit(SSU2V4), // 0x04 - TransportBit(SSU2V6), // 0x08 - TransportBit(NTCP2V6Mesh), // 0x10 - eAllTransports = 0xFF - }; - typedef uint8_t CompatibleTransports; - - enum Caps - { - eFloodfill = 0x01, - eHighBandwidth = 0x02, - eExtraBandwidth = 0x04, - eReachable = 0x08, - eHidden = 0x10, - eUnreachable = 0x20 - }; - - enum Congestion - { - eLowCongestion = 0, - eMediumCongestion, - eHighCongestion, - eRejectAll - }; - - enum AddressCaps - { - eV4 = 0x01, - eV6 = 0x02, - eSSUTesting = 0x04, - eSSUIntroducer = 0x08 - }; - - enum TransportStyle - { - eTransportUnknown = 0, - eTransportNTCP2, - eTransportSSU2 - }; - - struct Introducer - { - Introducer (): iTag (0), iExp (0) { iH.Fill(0); }; - IdentHash iH; - uint32_t iTag; - uint32_t iExp; - }; - - struct SSUExt - { - int mtu; - std::vector introducers; - }; - - struct Address - { - TransportStyle transportStyle; - boost::asio::ip::address host; - Tag<32> s, i; // keys, i is first 16 bytes for NTCP2 and 32 bytes intro key for SSU - int port; - uint64_t date; - uint8_t caps; - bool published = false; - std::unique_ptr ssu; // not null for SSU - - bool IsCompatible (const boost::asio::ip::address& other) const - { - return (IsV4 () && other.is_v4 ()) || - (IsV6 () && other.is_v6 ()); - } - - bool operator==(const Address& other) const - { - return transportStyle == other.transportStyle && - host == other.host && port == other.port; - } - - bool operator!=(const Address& other) const - { - return !(*this == other); - } - - bool IsNTCP2 () const { return transportStyle == eTransportNTCP2; }; - bool IsSSU2 () const { return transportStyle == eTransportSSU2; }; - bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; }; - bool IsReachableSSU () const { return (bool)ssu && (published || UsesIntroducer ()); }; - bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); }; - - bool IsIntroducer () const { return caps & eSSUIntroducer; }; - bool IsPeerTesting () const { return caps & eSSUTesting; }; - - bool IsV4 () const { return (caps & AddressCaps::eV4) || (host.is_v4 () && !host.is_unspecified ()); }; - bool IsV6 () const { return (caps & AddressCaps::eV6) || (host.is_v6 () && !host.is_unspecified ()); }; - }; - - class Buffer: public std::array - { - public: - - Buffer () = default; - Buffer (const uint8_t * buf, size_t len); - Buffer (const Buffer& other): Buffer (other.data (), other.m_BufferLen) {}; - - size_t GetBufferLen () const { return m_BufferLen; }; - void SetBufferLen (size_t len) { m_BufferLen = len; }; - - private: - - size_t m_BufferLen = 0; - }; - - typedef std::array, eNumTransports> Addresses; -#ifdef __cpp_lib_atomic_shared_ptr - typedef std::shared_ptr AddressesPtr; -#else - typedef boost::shared_ptr AddressesPtr; -#endif - RouterInfo (const std::string& fullPath); - RouterInfo (const RouterInfo& ) = delete; - RouterInfo& operator=(const RouterInfo& ) = delete; - RouterInfo (std::shared_ptr&& buf, size_t len); - RouterInfo (const uint8_t * buf, size_t len); - virtual ~RouterInfo (); - - std::shared_ptr GetRouterIdentity () const { return m_RouterIdentity; }; - void SetRouterIdentity (std::shared_ptr identity); - std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; - uint64_t GetTimestamp () const { return m_Timestamp; }; - int GetVersion () const { return m_Version; }; - virtual void SetProperty (std::string_view key, std::string_view value) {}; - virtual void ClearProperties () {}; - AddressesPtr GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr - std::shared_ptr GetNTCP2V4Address () const; - std::shared_ptr GetNTCP2V6Address () const; - std::shared_ptr GetPublishedNTCP2V4Address () const; - std::shared_ptr GetPublishedNTCP2V6Address () const; - std::shared_ptr GetYggdrasilAddress () const; - std::shared_ptr GetSSU2V4Address () const; - std::shared_ptr GetSSU2V6Address () const; - std::shared_ptr GetSSU2Address (bool v4) const; - - void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,int port, uint8_t caps); // non published - void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, - const boost::asio::ip::address& host, int port); // published - void RemoveNTCP2Address (bool v4); - void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, int port, uint8_t caps); // non published - void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, - const boost::asio::ip::address& host, int port); // published - void RemoveSSU2Address (bool v4); - void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps - void UpdateSupportedTransports (); - void UpdateIntroducers (uint64_t ts); // ts in seconds - bool IsFloodfill () const { return m_IsFloodfill; }; - void SetFloodfill () { m_IsFloodfill = true; }; - void ResetFloodfill () { m_IsFloodfill = false; }; - bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; - bool IsNTCP2 (bool v4only = true) const; - bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; }; - bool IsSSU2V4 () const { return m_SupportedTransports & eSSU2V4; }; - bool IsSSU2V6 () const { return m_SupportedTransports & eSSU2V6; }; - bool IsV6 () const { return m_SupportedTransports & (eNTCP2V6 | eSSU2V6); }; - bool IsV4 () const { return m_SupportedTransports & (eNTCP2V4 | eSSU2V4); }; - bool IsMesh () const { return m_SupportedTransports & eNTCP2V6Mesh; }; - void EnableV6 (); - void DisableV6 (); - void EnableV4 (); - void DisableV4 (); - void EnableMesh (); - void DisableMesh (); - bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; - bool IsReachableFrom (const RouterInfo& other) const { return m_ReachableTransports & other.m_SupportedTransports; }; - bool IsReachableBy (CompatibleTransports transports) const { return m_ReachableTransports & transports; }; - CompatibleTransports GetCompatibleTransports (bool incoming) const { return incoming ? m_ReachableTransports : m_SupportedTransports; }; - CompatibleTransports GetPublishedTransports () const { return m_PublishedTransports; }; - bool HasValidAddresses () const { return m_SupportedTransports; }; - bool IsHidden () const { return m_Caps & eHidden; }; - bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; }; - bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; }; - bool IsEligibleFloodfill () const; - bool IsDeclaredFloodfill () const { return m_Caps & RouterInfo::eFloodfill; }; - bool IsPublished (bool v4) const; - bool IsPublishedOn (CompatibleTransports transports) const; - bool IsNAT2NATOnly (const RouterInfo& other) const; // only NAT-to-NAT connection is possible - bool IsSSU2PeerTesting (bool v4) const; - bool IsSSU2Introducer (bool v4) const; - bool IsHighCongestion (bool highBandwidth) const; - - uint8_t GetCaps () const { return m_Caps; }; - char GetBandwidthCap() const { return m_BandwidthCap; }; - void SetCaps (uint8_t caps) { m_Caps = caps; }; - - Congestion GetCongestion () const { return m_Congestion; }; - - void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; - bool IsUnreachable () const { return m_IsUnreachable; }; - void ExcludeReachableTransports (CompatibleTransports transports) { m_ReachableTransports &= ~transports; }; - - const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; }; - const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary - size_t GetBufferLen () const { return m_Buffer ? m_Buffer->GetBufferLen () : 0; }; - void DeleteBuffer () { m_Buffer = nullptr; m_IsBufferScheduledToDelete = false; }; - std::shared_ptr GetSharedBuffer () const { return m_Buffer; }; - std::shared_ptr CopyBuffer () const; - void ScheduleBufferToDelete () { m_IsBufferScheduledToDelete = true; }; - void CancelBufferToDelete () { m_IsBufferScheduledToDelete = false; }; - bool IsBufferScheduledToDelete () const { return m_IsBufferScheduledToDelete; }; - - bool IsUpdated () const { return m_IsUpdated; }; - void SetUpdated (bool updated) { m_IsUpdated = updated; }; - bool SaveToFile (const std::string& fullPath); - static bool SaveToFile (const std::string& fullPath, std::shared_ptr buf); - - std::shared_ptr GetProfile () const; - void DropProfile () { m_Profile = nullptr; }; - bool HasProfile () const { return (bool)m_Profile; }; - - bool Update (const uint8_t * buf, size_t len); - bool IsNewer (const uint8_t * buf, size_t len) const; - - /** return true if we are in a router family and the signature is valid */ - bool IsFamily (FamilyID famid) const; - - // implements RoutingDestination - std::shared_ptr GetIdentity () const { return m_RouterIdentity; }; - void Encrypt (const uint8_t * data, uint8_t * encrypted) const; - - bool IsDestination () const { return false; }; - - protected: - - RouterInfo (); - uint8_t * GetBufferPointer (size_t offset = 0 ) { return m_Buffer->data () + offset; }; - void UpdateBuffer (const uint8_t * buf, size_t len); - void SetBufferLen (size_t len) { if (m_Buffer) m_Buffer->SetBufferLen (len); }; - void RefreshTimestamp (); - CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; }; - void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; }; - void SetCongestion (Congestion c) { m_Congestion = c; }; - - private: - - bool LoadFile (const std::string& fullPath); - void ReadFromFile (const std::string& fullPath); - bool ReadFromBuffer (const uint8_t * buf, size_t len); // return false if malformed - void ReadFromBuffer (bool verifySignature); - std::string_view ExtractString (const uint8_t * buf, size_t len) const; - std::tuple ExtractParam (const uint8_t * buf, size_t len) const; - void ExtractCaps (std::string_view value); - uint8_t ExtractAddressCaps (std::string_view value) const; - void UpdateIntroducers (std::shared_ptr
address, uint64_t ts); - template - std::shared_ptr GetAddress (Filter filter) const; - virtual std::shared_ptr NewBuffer () const; - virtual std::shared_ptr
NewAddress () const; - virtual AddressesPtr NewAddresses () const; - virtual std::shared_ptr NewIdentity (const uint8_t * buf, size_t len) const; - - private: - - FamilyID m_FamilyID; - std::shared_ptr m_RouterIdentity; - std::shared_ptr m_Buffer; - uint64_t m_Timestamp; // in milliseconds -#ifdef __cpp_lib_atomic_shared_ptr - std::atomic m_Addresses; -#else - AddressesPtr m_Addresses; -#endif - bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill, m_IsBufferScheduledToDelete; - CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports; - uint8_t m_Caps; - char m_BandwidthCap; - int m_Version; - Congestion m_Congestion; - mutable std::shared_ptr m_Profile; - - public: - - static std::string GetTransportName (SupportedTransports tr); - }; - - class LocalRouterInfo: public RouterInfo - { - public: - - LocalRouterInfo () = default; - void CreateBuffer (const PrivateKeys& privateKeys); - void UpdateCaps (uint8_t caps); - bool UpdateCongestion (Congestion c); // returns true if updated - - void SetProperty (std::string_view key, std::string_view value) override; - void DeleteProperty (const std::string& key); - std::string GetProperty (const std::string& key) const; - void ClearProperties () override { m_Properties.clear (); }; - void UpdateFloodfillProperty (bool floodfill); - - bool AddSSU2Introducer (const Introducer& introducer, bool v4); - bool RemoveSSU2Introducer (const IdentHash& h, bool v4); - bool UpdateSSU2Introducer (const IdentHash& h, bool v4, uint32_t iTag, uint32_t iExp); - - private: - - void WriteToStream (std::ostream& s) const; - void UpdateCapsProperty (); - void WriteString (const std::string& str, std::ostream& s) const; - std::shared_ptr NewBuffer () const override; - std::shared_ptr
NewAddress () const override; - RouterInfo::AddressesPtr NewAddresses () const override; - std::shared_ptr NewIdentity (const uint8_t * buf, size_t len) const override; - - private: - - std::map m_Properties; - }; -} -} - -#endif diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp deleted file mode 100644 index fc2355a5..00000000 --- a/libi2pd/SSU2.cpp +++ /dev/null @@ -1,1802 +0,0 @@ -/* -* Copyright (c) 2022-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include "Log.h" -#include "RouterContext.h" -#include "Transports.h" -#include "NetDb.hpp" -#include "Config.h" -#include "SSU2.h" - -namespace i2p -{ -namespace transport -{ - SSU2Server::SSU2Server (): - RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"), - m_SocketV4 (m_ReceiveService.GetService ()), m_SocketV6 (m_ReceiveService.GetService ()), - m_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()), - m_TerminationTimer (GetService ()), m_CleanupTimer (GetService ()), m_ResendTimer (GetService ()), - m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()), - m_IsPublished (true), m_IsSyncClockFromPeers (true), m_PendingTimeOffset (0), - m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL), m_IsThroughProxy (false) - { - } - - void SSU2Server::Start () - { - if (!IsRunning ()) - { - StartIOService (); - i2p::config::GetOption ("ssu2.published", m_IsPublished); - i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers); - bool found = false; - auto addresses = i2p::context.GetRouterInfo ().GetAddresses (); - if (!addresses) return; - for (const auto& address: *addresses) - { - if (!address) continue; - if (address->transportStyle == i2p::data::RouterInfo::eTransportSSU2) - { - if (m_IsThroughProxy) - { - found = true; - if (address->IsV6 ()) - { - uint16_t mtu; i2p::config::GetOption ("ssu2.mtu6", mtu); - if (!mtu || mtu > SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE) - mtu = SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE; - i2p::context.SetMTU (mtu, false); - } - else - { - uint16_t mtu; i2p::config::GetOption ("ssu2.mtu4", mtu); - if (!mtu || mtu > SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE) - mtu = SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE; - i2p::context.SetMTU (mtu, true); - } - continue; // we don't need port for proxy - } - auto port = address->port; - if (!port) - { - uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); - if (ssu2Port) port = ssu2Port; - else - { - uint16_t p; i2p::config::GetOption ("port", p); - if (p) port = p; - } - } - if (port) - { - if (address->IsV4 ()) - { - found = true; - LogPrint (eLogDebug, "SSU2: Opening IPv4 socket at Start"); - OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV4, port)); - boost::asio::post (m_ReceiveService.GetService (), - [this]() - { - Receive (m_SocketV4); - }); - ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers - } - if (address->IsV6 ()) - { - found = true; - LogPrint (eLogDebug, "SSU2: Opening IPv6 socket at Start"); - OpenSocket (boost::asio::ip::udp::endpoint (m_AddressV6, port)); - boost::asio::post (m_ReceiveService.GetService (), - [this]() - { - Receive (m_SocketV6); - }); - ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers - } - } - else - LogPrint (eLogCritical, "SSU2: Can't start server because port not specified"); - } - } - if (found) - { - if (m_IsThroughProxy) - ConnectToProxy (); - m_ReceiveService.Start (); - } - ScheduleTermination (); - ScheduleCleanup (); - ScheduleResend (false); - } - } - - void SSU2Server::Stop () - { - if (IsRunning ()) - { - m_TerminationTimer.cancel (); - m_CleanupTimer.cancel (); - m_ResendTimer.cancel (); - m_IntroducersUpdateTimer.cancel (); - m_IntroducersUpdateTimerV6.cancel (); - } - - auto sessions = m_Sessions; - for (auto& it: sessions) - { - it.second->RequestTermination (eSSU2TerminationReasonRouterShutdown); - it.second->Done (); - } - - if (context.SupportsV4 () || context.SupportsV6 ()) - m_ReceiveService.Stop (); - m_SocketV4.close (); - m_SocketV6.close (); - - if (m_UDPAssociateSocket) - { - m_UDPAssociateSocket->close (); - m_UDPAssociateSocket.reset (nullptr); - } - - StopIOService (); - - m_Sessions.clear (); - m_SessionsByRouterHash.clear (); - m_PendingOutgoingSessions.clear (); - m_Relays.clear (); - m_PeerTests.clear (); - m_Introducers.clear (); - m_IntroducersV6.clear (); - m_ConnectedRecently.clear (); - m_RequestedPeerTests.clear (); - - m_PacketsPool.ReleaseMt (m_ReceivedPacketsQueue); - m_ReceivedPacketsQueue.clear (); - } - - void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) - { - if (localAddress.is_unspecified ()) return; - if (localAddress.is_v4 ()) - { - m_AddressV4 = localAddress; - uint16_t mtu; i2p::config::GetOption ("ssu2.mtu4", mtu); - if (!mtu) mtu = i2p::util::net::GetMTU (localAddress); - if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; - if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE; - i2p::context.SetMTU (mtu, true); - } - else if (localAddress.is_v6 ()) - { - m_AddressV6 = localAddress; - uint16_t mtu; i2p::config::GetOption ("ssu2.mtu6", mtu); - if (!mtu) - { - int maxMTU = i2p::util::net::GetMaxMTU (localAddress.to_v6 ()); - mtu = i2p::util::net::GetMTU (localAddress); - if (mtu > maxMTU) mtu = maxMTU; - } - else - if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE; - if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; - i2p::context.SetMTU (mtu, false); - } - } - - bool SSU2Server::IsSupported (const boost::asio::ip::address& addr) const - { - if (m_IsThroughProxy) - return m_SocketV4.is_open (); - if (addr.is_v4 ()) - { - if (m_SocketV4.is_open ()) - return true; - } - else if (addr.is_v6 ()) - { - if (m_SocketV6.is_open ()) - return true; - } - return false; - } - - uint16_t SSU2Server::GetPort (bool v4) const - { - boost::system::error_code ec; - boost::asio::ip::udp::endpoint ep = (v4 || m_IsThroughProxy) ? m_SocketV4.local_endpoint (ec) : m_SocketV6.local_endpoint (ec); - if (ec) return 0; - return ep.port (); - } - - bool SSU2Server::IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep, bool max) - { - if (!ep.port () || ep.address ().is_unspecified ()) return false; - std::lock_guard l(m_ConnectedRecentlyMutex); - auto it = m_ConnectedRecently.find (ep); - if (it != m_ConnectedRecently.end ()) - { - if (i2p::util::GetSecondsSinceEpoch () <= it->second + (max ? SSU2_MAX_HOLE_PUNCH_EXPIRATION : SSU2_MIN_HOLE_PUNCH_EXPIRATION)) - return true; - else if (max) - m_ConnectedRecently.erase (it); - } - return false; - } - - void SSU2Server::AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts) - { - if (!ep.port () || ep.address ().is_unspecified () || - i2p::util::GetSecondsSinceEpoch () > ts + SSU2_MAX_HOLE_PUNCH_EXPIRATION) return; - std::lock_guard l(m_ConnectedRecentlyMutex); - auto [it, added] = m_ConnectedRecently.try_emplace (ep, ts); - if (!added && ts > it->second) - it->second = ts; // renew timestamp of existing endpoint - } - - void SSU2Server::AdjustTimeOffset (int64_t offset, std::shared_ptr from) - { - if (offset) - { - if (m_PendingTimeOffset) // one more - { - if (m_PendingTimeOffsetFrom && from && - m_PendingTimeOffsetFrom->GetIdentHash ().GetLL()[0] != from->GetIdentHash ().GetLL()[0]) // from different routers - { - if (std::abs (m_PendingTimeOffset - offset) < SSU2_CLOCK_SKEW) - { - offset = (m_PendingTimeOffset + offset)/2; // average - LogPrint (eLogWarning, "SSU2: Clock adjusted by ", offset, " seconds"); - i2p::util::AdjustTimeOffset (offset); - } - else - LogPrint (eLogWarning, "SSU2: Time offsets are too different. Clock not adjusted"); - m_PendingTimeOffset = 0; - m_PendingTimeOffsetFrom = nullptr; - } - else - LogPrint (eLogWarning, "SSU2: Time offsets from same router. Clock not adjusted"); - } - else - { - m_PendingTimeOffset = offset; // first - m_PendingTimeOffsetFrom = from; - } - } - else - { - m_PendingTimeOffset = 0; // reset - m_PendingTimeOffsetFrom = nullptr; - } - } - - boost::asio::ip::udp::socket& SSU2Server::OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint) - { - boost::asio::ip::udp::socket& socket = localEndpoint.address ().is_v6 () ? m_SocketV6 : m_SocketV4; - try - { - if (socket.is_open ()) - socket.close (); - socket.open (localEndpoint.protocol ()); - if (localEndpoint.address ().is_v6 ()) - socket.set_option (boost::asio::ip::v6_only (true)); - - uint64_t bufferSize = i2p::context.GetBandwidthLimit() * 1024 / 5; // max lag = 200ms - bufferSize = std::max(SSU2_SOCKET_MIN_BUFFER_SIZE, std::min(bufferSize, SSU2_SOCKET_MAX_BUFFER_SIZE)); - - boost::asio::socket_base::receive_buffer_size receiveBufferSizeSet (bufferSize); - boost::asio::socket_base::send_buffer_size sendBufferSizeSet (bufferSize); - socket.set_option (receiveBufferSizeSet); - socket.set_option (sendBufferSizeSet); - boost::asio::socket_base::receive_buffer_size receiveBufferSizeGet; - boost::asio::socket_base::send_buffer_size sendBufferSizeGet; - socket.get_option (receiveBufferSizeGet); - socket.get_option (sendBufferSizeGet); - if (receiveBufferSizeGet.value () != receiveBufferSizeSet.value () || - sendBufferSizeGet.value () != sendBufferSizeSet.value ()) - { - LogPrint (eLogWarning, "SSU2: Socket receive buffer size: requested = ", - receiveBufferSizeSet.value (), ", got = ", receiveBufferSizeGet.value ()); - LogPrint (eLogWarning, "SSU2: Socket send buffer size: requested = ", - sendBufferSizeSet.value (), ", got = ", sendBufferSizeGet.value ()); - } - else - { - LogPrint (eLogInfo, "SSU2: Socket receive buffer size: ", receiveBufferSizeGet.value ()); - LogPrint (eLogInfo, "SSU2: Socket send buffer size: ", sendBufferSizeGet.value ()); - } - - socket.non_blocking (true); - } - catch (std::exception& ex ) - { - LogPrint (eLogCritical, "SSU2: Failed to open socket on ", localEndpoint.address (), ": ", ex.what()); - ThrowFatal ("Unable to start SSU2 transport on ", localEndpoint.address (), ": ", ex.what ()); - return socket; - } - try - { - socket.bind (localEndpoint); - LogPrint (eLogInfo, "SSU2: Start listening on ", localEndpoint); - } - catch (std::exception& ex ) - { - LogPrint (eLogWarning, "SSU2: Failed to bind to ", localEndpoint, ": ", ex.what(), ". Actual endpoint is ", socket.local_endpoint ()); - // we can continue without binding being firewalled - } - return socket; - } - - void SSU2Server::Receive (boost::asio::ip::udp::socket& socket) - { - Packet * packet = m_PacketsPool.AcquireMt (); - socket.async_receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, - std::bind (&SSU2Server::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet, std::ref (socket))); - } - - void SSU2Server::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, - Packet * packet, boost::asio::ip::udp::socket& socket) - { - if (!ecode - || ecode == boost::asio::error::connection_refused - || ecode == boost::asio::error::connection_reset - || ecode == boost::asio::error::network_reset - || ecode == boost::asio::error::network_unreachable - || ecode == boost::asio::error::host_unreachable -#ifdef _WIN32 // windows can throw WinAPI error, which is not handled by ASIO - || ecode.value() == boost::winapi::ERROR_CONNECTION_REFUSED_ - || ecode.value() == boost::winapi::WSAENETRESET_ // 10052 - || ecode.value() == boost::winapi::ERROR_NETWORK_UNREACHABLE_ - || ecode.value() == boost::winapi::ERROR_HOST_UNREACHABLE_ -#endif - ) - // just try continue reading when received ICMP response otherwise socket can crash, - // but better to find out which host were sent it and mark that router as unreachable - { - i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); - if (bytes_transferred < SSU2_MIN_RECEIVED_PACKET_SIZE) - { - // drop too short packets - m_PacketsPool.ReleaseMt (packet); - Receive (socket); - return; - } - packet->len = bytes_transferred; - - boost::system::error_code ec; - size_t moreBytes = socket.available (ec); - if (!ec && moreBytes) - { - std::list packets; - packets.push_back (packet); - while (moreBytes && packets.size () < SSU2_MAX_NUM_PACKETS_PER_BATCH) - { - packet = m_PacketsPool.AcquireMt (); - packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec); - if (!ec) - { - i2p::transport::transports.UpdateReceivedBytes (packet->len); - if (packet->len >= SSU2_MIN_RECEIVED_PACKET_SIZE) - packets.push_back (packet); - else // drop too short packets - m_PacketsPool.ReleaseMt (packet); - moreBytes = socket.available(ec); - if (ec) break; - } - else - { - LogPrint (eLogError, "SSU2: receive_from error: code ", ec.value(), ": ", ec.message ()); - m_PacketsPool.ReleaseMt (packet); - break; - } - } - InsertToReceivedPacketsQueue (packets); - } - else - InsertToReceivedPacketsQueue (packet); - Receive (socket); - } - else - { - m_PacketsPool.ReleaseMt (packet); - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogError, "SSU2: Receive error: code ", ecode.value(), ": ", ecode.message ()); - if (m_IsThroughProxy) - { - m_UDPAssociateSocket.reset (nullptr); - m_ProxyRelayEndpoint.reset (nullptr); - m_SocketV4.close (); - ConnectToProxy (); - } - else - { - auto ep = socket.local_endpoint (); - LogPrint (eLogCritical, "SSU2: Reopening socket in HandleReceivedFrom: code ", ecode.value(), ": ", ecode.message ()); - OpenSocket (ep); - Receive (socket); - } - } - } - } - - void SSU2Server::HandleReceivedPackets (std::list&& packets) - { - if (packets.empty ()) return; - if (m_IsThroughProxy) - for (auto it: packets) - ProcessNextPacketFromProxy (it->buf, it->len); - else - for (auto it: packets) - ProcessNextPacket (it->buf, it->len, it->from); - m_PacketsPool.ReleaseMt (packets); - if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) - m_LastSession->FlushData (); - } - - void SSU2Server::InsertToReceivedPacketsQueue (Packet * packet) - { - if (!packet) return; - bool empty = false; - { - std::lock_guard l(m_ReceivedPacketsQueueMutex); - empty = m_ReceivedPacketsQueue.empty (); - m_ReceivedPacketsQueue.push_back (packet); - } - if (empty) - boost::asio::post (GetService (), [this]() { HandleReceivedPacketsQueue (); }); - } - - void SSU2Server::InsertToReceivedPacketsQueue (std::list& packets) - { - if (packets.empty ()) return; - size_t queueSize = 0; - { - std::lock_guard l(m_ReceivedPacketsQueueMutex); - queueSize = m_ReceivedPacketsQueue.size (); - if (queueSize < SSU2_MAX_RECEIVED_QUEUE_SIZE) - m_ReceivedPacketsQueue.splice (m_ReceivedPacketsQueue.end (), packets); - else - { - LogPrint (eLogError, "SSU2: Received queue size ", queueSize, " exceeds max size", SSU2_MAX_RECEIVED_QUEUE_SIZE); - m_PacketsPool.ReleaseMt (packets); - queueSize = 0; // invoke processing just in case - } - } - if (!queueSize) - boost::asio::post (GetService (), [this]() { HandleReceivedPacketsQueue (); }); - } - - void SSU2Server::HandleReceivedPacketsQueue () - { - std::list receivedPackets; - { - std::lock_guard l(m_ReceivedPacketsQueueMutex); - m_ReceivedPacketsQueue.swap (receivedPackets); - } - HandleReceivedPackets (std::move (receivedPackets)); - } - - bool SSU2Server::AddSession (std::shared_ptr session) - { - if (session) - { - if (m_Sessions.emplace (session->GetConnID (), session).second) - { - if (session->GetState () != eSSU2SessionStatePeerTest) - AddSessionByRouterHash (session); - return true; - } - } - return false; - } - - void SSU2Server::RemoveSession (uint64_t connID) - { - auto it = m_Sessions.find (connID); - if (it != m_Sessions.end ()) - { - if (it->second->GetState () != eSSU2SessionStatePeerTest) - { - auto ident = it->second->GetRemoteIdentity (); - if (ident) - { - std::lock_guard l(m_SessionsByRouterHashMutex); - auto it1 = m_SessionsByRouterHash.find (ident->GetIdentHash ()); - if (it1 != m_SessionsByRouterHash.end () && it->second == it1->second.lock ()) - m_SessionsByRouterHash.erase (it1); - } - } - if (m_LastSession == it->second) - m_LastSession = nullptr; - m_Sessions.erase (it); - } - } - - void SSU2Server::RequestRemoveSession (uint64_t connID) - { - boost::asio::post (GetService (), [connID, this]() { RemoveSession (connID); }); - } - - void SSU2Server::AddSessionByRouterHash (std::shared_ptr session) - { - if (session) - { - auto ident = session->GetRemoteIdentity (); - if (ident) - { - std::shared_ptr oldSession; - { - std::lock_guard l(m_SessionsByRouterHashMutex); - auto ret = m_SessionsByRouterHash.emplace (ident->GetIdentHash (), session); - if (!ret.second) - { - oldSession = ret.first->second.lock (); - // update session - ret.first->second = session; - } - } - if (oldSession && oldSession != session) - { - // session already exists - LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " already exists"); - // move unsent msgs to new session - oldSession->MoveSendQueue (session); - // terminate existing - boost::asio::post (GetService (), std::bind (&SSU2Session::RequestTermination, oldSession, eSSU2TerminationReasonReplacedByNewSession)); - } - } - } - } - - bool SSU2Server::AddPendingOutgoingSession (std::shared_ptr session) - { - if (!session) return false; - std::lock_guard l(m_PendingOutgoingSessionsMutex); - return m_PendingOutgoingSessions.emplace (session->GetRemoteEndpoint (), session).second; - } - - std::shared_ptr SSU2Server::FindSession (const i2p::data::IdentHash& ident) - { - std::lock_guard l(m_SessionsByRouterHashMutex); - auto it = m_SessionsByRouterHash.find (ident); - if (it != m_SessionsByRouterHash.end ()) - { - if (!it->second.expired ()) - { - auto s = it->second.lock (); - if (s && s->GetState () != eSSU2SessionStateTerminated) - return s; - } - m_SessionsByRouterHash.erase (it); - } - return nullptr; - } - - std::shared_ptr SSU2Server::FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const - { - std::lock_guard l(m_PendingOutgoingSessionsMutex); - auto it = m_PendingOutgoingSessions.find (ep); - if (it != m_PendingOutgoingSessions.end ()) - return it->second; - return nullptr; - } - - void SSU2Server::RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) - { - std::lock_guard l(m_PendingOutgoingSessionsMutex); - m_PendingOutgoingSessions.erase (ep); - } - - std::shared_ptr SSU2Server::GetRandomPeerTestSession ( - i2p::data::RouterInfo::CompatibleTransports remoteTransports, const i2p::data::IdentHash& excluded) - { - if (m_Sessions.empty ()) return nullptr; - int ind = m_Rng () % m_Sessions.size (); - auto it = m_Sessions.begin (); - std::advance (it, ind); - while (it != m_Sessions.end ()) - { - if (it->second->IsEstablished () && (it->second->GetRemotePeerTestTransports () & remoteTransports) && - it->second->GetRemoteIdentity ()->GetIdentHash () != excluded) - return it->second; - it++; - } - // not found, try from beginning - it = m_Sessions.begin (); - while (it != m_Sessions.end () && ind) - { - if (it->second->IsEstablished () && (it->second->GetRemotePeerTestTransports () & remoteTransports) && - it->second->GetRemoteIdentity ()->GetIdentHash () != excluded) - return it->second; - it++; ind--; - } - return nullptr; - } - - void SSU2Server::AddRelay (uint32_t tag, std::shared_ptr relay) - { - m_Relays.emplace (tag, relay); - } - - void SSU2Server::RemoveRelay (uint32_t tag) - { - m_Relays.erase (tag); - } - - std::shared_ptr SSU2Server::FindRelaySession (uint32_t tag) - { - auto it = m_Relays.find (tag); - if (it != m_Relays.end ()) - { - if (!it->second.expired ()) - { - auto s = it->second.lock (); - if (s && s->IsEstablished ()) - return s; - } - m_Relays.erase (it); - } - return nullptr; - } - - bool SSU2Server::AddPeerTest (uint32_t nonce, std::shared_ptr aliceSession, uint64_t ts) - { - return m_PeerTests.emplace (nonce, std::pair{ aliceSession, ts }).second; - } - - std::shared_ptr SSU2Server::GetPeerTest (uint32_t nonce) - { - auto it = m_PeerTests.find (nonce); - if (it != m_PeerTests.end ()) - { - auto s = it->second.first.lock (); - m_PeerTests.erase (it); - return s; - } - return nullptr; - } - - bool SSU2Server::AddRequestedPeerTest (uint32_t nonce, std::shared_ptr session, uint64_t ts) - { - return m_RequestedPeerTests.emplace (nonce, std::pair{ session, ts }).second; - } - - std::shared_ptr SSU2Server::GetRequestedPeerTest (uint32_t nonce) - { - auto it = m_RequestedPeerTests.find (nonce); - if (it != m_RequestedPeerTests.end ()) - { - auto s = it->second.first.lock (); - m_RequestedPeerTests.erase (it); - return s; - } - return nullptr; - } - - void SSU2Server::ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) - { - if (len < 24) return; - uint64_t connID; - memcpy (&connID, buf, 8); - connID ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); - if (!m_LastSession || m_LastSession->GetConnID () != connID) - { - if (m_LastSession) m_LastSession->FlushData (); - auto it = m_Sessions.find (connID); - if (it != m_Sessions.end ()) - m_LastSession = it->second; - else - m_LastSession = nullptr; - } - if (m_LastSession) - { - switch (m_LastSession->GetState ()) - { - case eSSU2SessionStateEstablished: - case eSSU2SessionStateSessionConfirmedSent: - m_LastSession->ProcessData (buf, len, senderEndpoint); - break; - case eSSU2SessionStateSessionCreatedSent: - if (!m_LastSession->ProcessSessionConfirmed (buf, len)) - { - m_LastSession->Done (); - m_LastSession = nullptr; - } - break; - case eSSU2SessionStateIntroduced: - if (m_LastSession->GetRemoteEndpoint ().address ().is_unspecified ()) - m_LastSession->SetRemoteEndpoint (senderEndpoint); - if (m_LastSession->GetRemoteEndpoint ().address () == senderEndpoint.address ()) // port might be different - m_LastSession->ProcessHolePunch (buf, len); - else - { - LogPrint (eLogWarning, "SSU2: HolePunch address ", senderEndpoint.address (), - " doesn't match RelayResponse ", m_LastSession->GetRemoteEndpoint ().address ()); - m_LastSession->Done (); - m_LastSession = nullptr; - } - break; - case eSSU2SessionStatePeerTest: - m_LastSession->SetRemoteEndpoint (senderEndpoint); - m_LastSession->ProcessPeerTest (buf, len); - break; - case eSSU2SessionStateHolePunch: - m_LastSession->ProcessFirstIncomingMessage (connID, buf, len); // SessionRequest - break; - case eSSU2SessionStateClosing: - m_LastSession->ProcessData (buf, len, senderEndpoint); // we might receive termintaion block - if (m_LastSession && m_LastSession->GetState () == eSSU2SessionStateClosing) - m_LastSession->RequestTermination (eSSU2TerminationReasonIdleTimeout); // send termination again - break; - case eSSU2SessionStateClosingConfirmed: - case eSSU2SessionStateTerminated: - m_LastSession = nullptr; - break; - default: - LogPrint (eLogWarning, "SSU2: Invalid session state ", (int)m_LastSession->GetState ()); - } - } - else - { - // check pending sessions if it's SessionCreated or Retry - auto it1 = m_PendingOutgoingSessions.find (senderEndpoint); - if (it1 != m_PendingOutgoingSessions.end ()) - { - if (it1->second->GetState () == eSSU2SessionStateSessionRequestSent && - it1->second->ProcessSessionCreated (buf, len)) - { - std::lock_guard l(m_PendingOutgoingSessionsMutex); - m_PendingOutgoingSessions.erase (it1); // we are done with that endpoint - } - else - it1->second->ProcessRetry (buf, len); - } - else if (!i2p::transport::transports.IsInReservedRange(senderEndpoint.address ()) && senderEndpoint.port ()) - { - // assume new incoming session - auto session = std::make_shared (*this); - session->SetRemoteEndpoint (senderEndpoint); - session->ProcessFirstIncomingMessage (connID, buf, len); - } - else - LogPrint (eLogError, "SSU2: Incoming packet received from invalid endpoint ", senderEndpoint); - } - } - - void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen, - const boost::asio::ip::udp::endpoint& to) - { - if (m_IsThroughProxy) - { - SendThroughProxy (header, headerLen, nullptr, 0, payload, payloadLen, to); - return; - } - - std::vector bufs - { - boost::asio::buffer (header, headerLen), - boost::asio::buffer (payload, payloadLen) - }; - - boost::system::error_code ec; - if (to.address ().is_v6 ()) - { - if (!m_SocketV6.is_open ()) return; - m_SocketV6.send_to (bufs, to, 0, ec); - } - else - { - if (!m_SocketV4.is_open ()) return; - m_SocketV4.send_to (bufs, to, 0, ec); - } - - if (!ec) - i2p::transport::transports.UpdateSentBytes (headerLen + payloadLen); - else - { - LogPrint (ec == boost::asio::error::would_block ? eLogInfo : eLogError, - "SSU2: Send exception: ", ec.message (), " to ", to); - } - } - - void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, - const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to) - { - if (m_IsThroughProxy) - { - SendThroughProxy (header, headerLen, headerX, headerXLen, payload, payloadLen, to); - return; - } - - std::vector bufs - { - boost::asio::buffer (header, headerLen), - boost::asio::buffer (headerX, headerXLen), - boost::asio::buffer (payload, payloadLen) - }; - - boost::system::error_code ec; - if (to.address ().is_v6 ()) - { - if (!m_SocketV6.is_open ()) return; - m_SocketV6.send_to (bufs, to, 0, ec); - } - else - { - if (!m_SocketV4.is_open ()) return; - m_SocketV4.send_to (bufs, to, 0, ec); - } - - if (!ec) - i2p::transport::transports.UpdateSentBytes (headerLen + headerXLen + payloadLen); - else - { - LogPrint (ec == boost::asio::error::would_block ? eLogInfo : eLogError, - "SSU2: Send exception: ", ec.message (), " to ", to); - } - } - - bool SSU2Server::CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest) - { - auto s = FindPendingOutgoingSession (ep); - if (s) - { - if (peerTest) - { - // if peer test requested add it to the list for pending session - auto onEstablished = s->GetOnEstablished (); - if (onEstablished) - s->SetOnEstablished ([s, onEstablished]() - { - onEstablished (); - s->SendPeerTest (); - }); - else - s->SetOnEstablished ([s]() { s->SendPeerTest (); }); - } - return true; - } - return false; - } - - bool SSU2Server::CreateSession (std::shared_ptr router, - std::shared_ptr address, bool peerTest) - { - if (router && address) - { - // check if no session - auto existingSession = FindSession (router->GetIdentHash ()); - if (existingSession) - { - // session with router found, trying to send peer test if requested - if (peerTest && existingSession->IsEstablished ()) - boost::asio::post (GetService (), [existingSession]() { existingSession->SendPeerTest (); }); - return false; - } - // check is no pending session - bool isValidEndpoint = !address->host.is_unspecified () && address->port; - if (isValidEndpoint) - { - if (i2p::transport::transports.IsInReservedRange(address->host)) return false; - if (CheckPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port), peerTest)) return false; - } - - auto session = std::make_shared (*this, router, address); - if (!isValidEndpoint && router->HasProfile () && router->GetProfile ()->HasLastEndpoint (address->IsV4 ())) - { - // router doesn't publish endpoint, but we connected before and hole punch might be alive - auto ep = router->GetProfile ()->GetLastEndpoint (); - if (IsConnectedRecently (ep, false)) - { - if (CheckPendingOutgoingSession (ep, peerTest)) return false; - session->SetRemoteEndpoint (ep); - isValidEndpoint = true; - } - } - if (peerTest) - session->SetOnEstablished ([session]() {session->SendPeerTest (); }); - - if (isValidEndpoint) // we know endpoint - boost::asio::post (GetService (), [session]() { session->Connect (); }); - else if (address->UsesIntroducer ()) // we don't know endpoint yet - boost::asio::post (GetService (), std::bind (&SSU2Server::ConnectThroughIntroducer, this, session)); - else - return false; - } - else - return false; - return true; - } - - void SSU2Server::ConnectThroughIntroducer (std::shared_ptr session) - { - if (!session) return; - auto address = session->GetAddress (); - if (!address) return; - session->WaitForIntroduction (); - auto ts = i2p::util::GetSecondsSinceEpoch (); - std::vector indices; int i = 0; - // try to find existing session first - for (auto& it: address->ssu->introducers) - { - if (it.iTag && ts < it.iExp) - { - auto s = FindSession (it.iH); - if (s) - { - auto addr = s->GetAddress (); - if (addr && addr->IsIntroducer ()) - { - s->Introduce (session, it.iTag); - return; - } - } - else - indices.push_back(i); - } - i++; - } - // we have to start a new session to an introducer - std::vector newRouters; - std::shared_ptr r; - std::shared_ptr addr; - uint32_t relayTag = 0; - if (!indices.empty ()) - { - if (indices.size () > 1) - std::shuffle (indices.begin(), indices.end(), m_Rng); - - for (auto ind: indices) - { - const auto& introducer = address->ssu->introducers[ind]; - // introducer is not expired, because in indices - r = i2p::data::netdb.FindRouter (introducer.iH); - if (r) - { - if (r->IsPublishedOn (i2p::context.GetRouterInfo ().GetCompatibleTransports (false) & // outgoing - (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6))) - { - relayTag = introducer.iTag; - addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); - if (addr && addr->IsIntroducer () && !addr->host.is_unspecified () && addr->port && - !i2p::transport::transports.IsInReservedRange(addr->host)) - break; - else - { - // address is invalid or not intrudcer, try another SSU2 address if exists - if (address->IsV4 ()) - { - if (i2p::context.SupportsV6 ()) - addr = r->GetSSU2V6Address (); - } - else - { - if (i2p::context.SupportsV4 ()) - addr = r->GetSSU2V4Address (); - } - if (addr && addr->IsIntroducer () && !addr->host.is_unspecified () && addr->port && - !i2p::transport::transports.IsInReservedRange(addr->host)) - break; - else - { - // all addresses are invalid, try next introducer - relayTag = 0; - addr = nullptr; - r = nullptr; - } - } - } - else - r = nullptr; - } - else if (!i2p::data::IsRouterBanned (introducer.iH)) - newRouters.push_back (introducer.iH); - } - } - if (r) - { - if (relayTag && addr) - { - // introducer and tag found connect to it through SSU2 - auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port)); - if (!s) - { - s = std::make_shared (*this, r, addr); - s->SetOnEstablished ([session, s, relayTag]() { s->Introduce (session, relayTag); }); - s->Connect (); - } - else - { - auto onEstablished = s->GetOnEstablished (); - if (onEstablished) - s->SetOnEstablished ([session, s, relayTag, onEstablished]() - { - onEstablished (); - s->Introduce (session, relayTag); - }); - else - s->SetOnEstablished ([session, s, relayTag]() {s->Introduce (session, relayTag); }); - } - } - else - session->Done (); - } - else - { - // introducers not found, try to request them - for (auto& it: newRouters) - i2p::data::netdb.RequestDestination (it); - session->Done (); // don't wait for connect timeout - } - } - - bool SSU2Server::StartPeerTest (std::shared_ptr router, bool v4) - { - if (!router) return false; - auto addr = v4 ? router->GetSSU2V4Address () : router->GetSSU2V6Address (); - if (!addr) return false; - auto session = FindSession (router->GetIdentHash ()); - if (session) - { - auto remoteAddr = session->GetAddress (); - if (!remoteAddr || !remoteAddr->IsPeerTesting () || - (v4 && !remoteAddr->IsV4 ()) || (!v4 && !remoteAddr->IsV6 ())) return false; - if (session->IsEstablished ()) - boost::asio::post (GetService (), [session]() { session->SendPeerTest (); }); - else - session->SetOnEstablished ([session]() { session->SendPeerTest (); }); - return true; - } - else - CreateSession (router, addr, true); - return true; - } - - void SSU2Server::ScheduleTermination () - { - m_TerminationTimer.expires_from_now (boost::posix_time::seconds( - SSU2_TERMINATION_CHECK_TIMEOUT + m_Rng () % SSU2_TERMINATION_CHECK_TIMEOUT_VARIANCE)); - m_TerminationTimer.async_wait (std::bind (&SSU2Server::HandleTerminationTimer, - this, std::placeholders::_1)); - } - - void SSU2Server::HandleTerminationTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - - { - std::lock_guard l(m_PendingOutgoingSessionsMutex); - for (auto it = m_PendingOutgoingSessions.begin (); it != m_PendingOutgoingSessions.end ();) - { - if (it->second->IsTerminationTimeoutExpired (ts)) - { - //it->second->Terminate (); - it = m_PendingOutgoingSessions.erase (it); - } - else - it++; - } - } - - for (auto it: m_Sessions) - { - auto state = it.second->GetState (); - if (state == eSSU2SessionStateTerminated || state == eSSU2SessionStateClosing) - it.second->Done (); - else if (it.second->IsTerminationTimeoutExpired (ts)) - { - if (it.second->IsEstablished ()) - it.second->RequestTermination (eSSU2TerminationReasonIdleTimeout); - else - it.second->Done (); - } - else - it.second->CleanUp (ts); - } - - ScheduleTermination (); - } - } - - void SSU2Server::ScheduleCleanup () - { - m_CleanupTimer.expires_from_now (boost::posix_time::seconds(SSU2_CLEANUP_INTERVAL)); - m_CleanupTimer.async_wait (std::bind (&SSU2Server::HandleCleanupTimer, - this, std::placeholders::_1)); - } - - void SSU2Server::HandleCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_Relays.begin (); it != m_Relays.begin ();) - { - if (it->second.expired ()) - it = m_Relays.erase (it); - else - it++; - } - - for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();) - { - if (ts > it->second.second + SSU2_PEER_TEST_EXPIRATION_TIMEOUT || it->second.first.expired ()) - { - LogPrint (eLogInfo, "SSU2: Peer test nonce ", it->first, " was not responded in ", SSU2_PEER_TEST_EXPIRATION_TIMEOUT, " seconds or session invalid. Deleted"); - it = m_PeerTests.erase (it); - } - else - it++; - } - - for (auto it = m_IncomingTokens.begin (); it != m_IncomingTokens.end (); ) - { - if (ts > it->second.second) - it = m_IncomingTokens.erase (it); - else - it++; - } - - for (auto it = m_OutgoingTokens.begin (); it != m_OutgoingTokens.end (); ) - { - if (ts > it->second.second) - it = m_OutgoingTokens.erase (it); - else - it++; - } - - for (auto it = m_ConnectedRecently.begin (); it != m_ConnectedRecently.end (); ) - { - if (ts > it->second + SSU2_MAX_HOLE_PUNCH_EXPIRATION) - it = m_ConnectedRecently.erase (it); - else - it++; - } - - for (auto it = m_RequestedPeerTests.begin (); it != m_RequestedPeerTests.end ();) - { - if (ts > it->second.second + SSU2_PEER_TEST_EXPIRATION_TIMEOUT) - it = m_RequestedPeerTests.erase (it); - else - it++; - } - - { - std::lock_guard l(m_SessionsByRouterHashMutex); - for (auto it = m_SessionsByRouterHash.begin (); it != m_SessionsByRouterHash.begin ();) - { - if (it->second.expired ()) - it = m_SessionsByRouterHash.erase (it); - else - it++; - } - } - - m_PacketsPool.CleanUpMt (); - m_SentPacketsPool.CleanUp (); - m_IncompleteMessagesPool.CleanUp (); - m_FragmentsPool.CleanUp (); - ScheduleCleanup (); - } - } - - void SSU2Server::ScheduleResend (bool more) - { - m_ResendTimer.expires_from_now (boost::posix_time::milliseconds (more ? - (SSU2_RESEND_CHECK_MORE_TIMEOUT + m_Rng () % SSU2_RESEND_CHECK_MORE_TIMEOUT_VARIANCE): - (SSU2_RESEND_CHECK_TIMEOUT + m_Rng () % SSU2_RESEND_CHECK_TIMEOUT_VARIANCE))); - m_ResendTimer.async_wait (std::bind (&SSU2Server::HandleResendTimer, - this, std::placeholders::_1)); - } - - void SSU2Server::HandleResendTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - size_t resentPacketsNum = 0; - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto it: m_Sessions) - { - if (ts >= it.second->GetLastResendTime () + SSU2_RESEND_CHECK_TIMEOUT) - resentPacketsNum += it.second->Resend (ts); - if (resentPacketsNum > SSU2_MAX_RESEND_PACKETS) break; - } - for (auto it: m_PendingOutgoingSessions) - it.second->Resend (ts); - ScheduleResend (resentPacketsNum > SSU2_MAX_RESEND_PACKETS); - } - } - - void SSU2Server::UpdateOutgoingToken (const boost::asio::ip::udp::endpoint& ep, uint64_t token, uint32_t exp) - { - m_OutgoingTokens[ep] = {token, exp}; - } - - uint64_t SSU2Server::FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) - { - auto it = m_OutgoingTokens.find (ep); - if (it != m_OutgoingTokens.end ()) - { - if (i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_THRESHOLD > it->second.second) - { - // token expired - m_OutgoingTokens.erase (it); - return 0; - } - return it->second.first; - } - return 0; - } - - uint64_t SSU2Server::GetIncomingToken (const boost::asio::ip::udp::endpoint& ep) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - auto it = m_IncomingTokens.find (ep); - if (it != m_IncomingTokens.end ()) - { - if (ts + SSU2_TOKEN_EXPIRATION_THRESHOLD <= it->second.second) - return it->second.first; - else // token expired - m_IncomingTokens.erase (it); - } - uint64_t token; - RAND_bytes ((uint8_t *)&token, 8); - if (!token) token = 1; // token can't be zero - m_IncomingTokens.try_emplace (ep, token, uint32_t(ts + SSU2_TOKEN_EXPIRATION_TIMEOUT)); - return token; - } - - std::pair SSU2Server::NewIncomingToken (const boost::asio::ip::udp::endpoint& ep) - { - uint64_t token; - RAND_bytes ((uint8_t *)&token, 8); - if (!token) token = 1; // token can't be zero - uint32_t expires = i2p::util::GetSecondsSinceEpoch () + SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT; - auto [it, inserted] = m_IncomingTokens.try_emplace (ep, token, expires); - if (!inserted) - it->second = { token, expires }; // override - return it->second; - } - - std::vector > SSU2Server::FindIntroducers (int maxNumIntroducers, - bool v4, const std::unordered_set& excluded) - { - std::vector > ret; - if (maxNumIntroducers <= 0 || m_Sessions.empty ()) return ret; - - std::vector > eligible; - eligible.reserve (m_Sessions.size ()/2); - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (const auto& s : m_Sessions) - { - if (s.second->IsEstablished () && (s.second->GetRelayTag () && s.second->IsOutgoing ()) && - ts < s.second->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION/2 && - !excluded.count (s.second->GetRemoteIdentity ()->GetIdentHash ()) && - ((v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V4)) || - (!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6)))) - eligible.push_back (s.second); - } - - if (eligible.size () <= (size_t)maxNumIntroducers) - return eligible; - else - std::sample (eligible.begin(), eligible.end(), std::back_inserter(ret), maxNumIntroducers, m_Rng); - return ret; - } - - void SSU2Server::UpdateIntroducers (bool v4) - { - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - std::list > newList, impliedList; - auto& introducers = v4 ? m_Introducers : m_IntroducersV6; - std::unordered_set excluded; - for (const auto& [ident, tag] : introducers) - { - std::shared_ptr session = FindSession (ident); - if (session) - excluded.insert (ident); - if (session) - { - if (session->IsEstablished () && session->GetRelayTag () && session->IsOutgoing () && // still session with introducer? - ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION) - { - session->SendKeepAlive (); - if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION) - { - newList.push_back ({ident, session->GetRelayTag ()}); - if (tag != session->GetRelayTag ()) - { - LogPrint (eLogDebug, "SSU2: Introducer session to ", session->GetIdentHashBase64() , " was replaced. iTag ", tag, "->", session->GetRelayTag ()); - i2p::context.UpdateSSU2Introducer (ident, v4, session->GetRelayTag (), - session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION); - } - } - else - { - impliedList.push_back ({ident, session->GetRelayTag ()}); // keep in introducers list, but not publish - session = nullptr; - } - } - else - session = nullptr; - } - - if (!session) - i2p::context.RemoveSSU2Introducer (ident, v4); - } - int numOldSessions = 0; - if (newList.size () < SSU2_MAX_NUM_INTRODUCERS) - { - auto sessions = FindIntroducers (SSU2_MAX_NUM_INTRODUCERS - newList.size (), v4, excluded); - if (sessions.empty () && !impliedList.empty ()) - { - LogPrint (eLogDebug, "SSU2: No new introducers found. Trying to reuse existing"); - for (const auto& it : impliedList) - { - auto session = FindSession (it.first); - if (session) - { - if (std::find_if (newList.begin (), newList.end (), - [&ident = it.first](const auto& s){ return ident == s.first; }) == newList.end ()) - { - sessions.push_back (session); - numOldSessions++; - } - } - } - impliedList.clear (); - } - - for (const auto& it : sessions) - { - uint32_t tag = it->GetRelayTag (); - uint32_t exp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION; - if (!tag && ts >= exp) - continue; // don't publish expired introducer - i2p::data::RouterInfo::Introducer introducer; - introducer.iTag = tag; - introducer.iH = it->GetRemoteIdentity ()->GetIdentHash (); - introducer.iExp = exp; - excluded.insert (it->GetRemoteIdentity ()->GetIdentHash ()); - if (i2p::context.AddSSU2Introducer (introducer, v4)) - { - LogPrint (eLogDebug, "SSU2: Introducer added ", it->GetRelayTag (), " at ", - i2p::data::GetIdentHashAbbreviation (it->GetRemoteIdentity ()->GetIdentHash ())); - newList.push_back ({ it->GetRemoteIdentity ()->GetIdentHash (), tag }); - it->SendKeepAlive (); - if (newList.size () >= SSU2_MAX_NUM_INTRODUCERS) break; - } - } - } - introducers = newList; - - if (introducers.size () < SSU2_MAX_NUM_INTRODUCERS || numOldSessions) - { - // we need to create more sessions with relay tag - - // exclude all existing sessions - excluded.clear (); - { - std::lock_guard l(m_SessionsByRouterHashMutex); - for (const auto& [ident, s] : m_SessionsByRouterHash) - excluded.insert (ident); - } - - // session about to expire are not counted - for (auto i = introducers.size (); i < SSU2_MAX_NUM_INTRODUCERS + numOldSessions; i++) - { - auto introducer = i2p::data::netdb.GetRandomSSU2Introducer (v4, excluded); - if (introducer) - { - auto address = v4 ? introducer->GetSSU2V4Address () : introducer->GetSSU2V6Address (); - if (address) - { - CreateSession (introducer, address); - excluded.insert (introducer->GetIdentHash ()); - } - } - else - { - LogPrint (eLogDebug, "SSU2: Can't find more introducers"); - break; - } - } - } - introducers.splice (introducers.end (), impliedList); // insert non-published, but non-expired introducers back - } - - void SSU2Server::ScheduleIntroducersUpdateTimer () - { - if (m_IsPublished) - { - m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds( - SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)); - m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, true)); - } - } - - void SSU2Server::RescheduleIntroducersUpdateTimer () - { - if (m_IsPublished) - { - m_IntroducersUpdateTimer.cancel (); - i2p::context.ClearSSU2Introducers (true); - m_Introducers.clear (); - m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds( - (SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2)); - m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, true)); - } - } - - void SSU2Server::ScheduleIntroducersUpdateTimerV6 () - { - if (m_IsPublished) - { - m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds( - SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)); - m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, false)); - } - } - - void SSU2Server::RescheduleIntroducersUpdateTimerV6 () - { - if (m_IsPublished) - { - m_IntroducersUpdateTimerV6.cancel (); - i2p::context.ClearSSU2Introducers (false); - m_IntroducersV6.clear (); - m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds( - (SSU2_KEEP_ALIVE_INTERVAL + m_Rng () % SSU2_KEEP_ALIVE_INTERVAL_VARIANCE)/2)); - m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer, - this, std::placeholders::_1, false)); - } - } - - void SSU2Server::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4) - { - if (ecode != boost::asio::error::operation_aborted) - { - // timeout expired - if (v4) - { - if (i2p::context.GetTesting ()) - { - // we still don't know if we need introducers - ScheduleIntroducersUpdateTimer (); - return; - } - if (i2p::context.GetStatus () != eRouterStatusFirewalled) - { - // we don't need introducers - i2p::context.ClearSSU2Introducers (true); - m_Introducers.clear (); - return; - } - // we are firewalled - auto addr = i2p::context.GetRouterInfo ().GetSSU2V4Address (); - if (addr && addr->ssu && addr->ssu->introducers.empty ()) - i2p::context.SetUnreachable (true, false); // v4 - - UpdateIntroducers (true); - ScheduleIntroducersUpdateTimer (); - } - else - { - if (i2p::context.GetTestingV6 ()) - { - // we still don't know if we need introducers - ScheduleIntroducersUpdateTimerV6 (); - return; - } - if (i2p::context.GetStatusV6 () != eRouterStatusFirewalled) - { - // we don't need introducers - i2p::context.ClearSSU2Introducers (false); - m_IntroducersV6.clear (); - return; - } - // we are firewalled - auto addr = i2p::context.GetRouterInfo ().GetSSU2V6Address (); - if (addr && addr->ssu && addr->ssu->introducers.empty ()) - i2p::context.SetUnreachable (false, true); // v6 - - UpdateIntroducers (false); - ScheduleIntroducersUpdateTimerV6 (); - } - } - } - - bool SSU2Server::AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, - const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) - { - return m_Encryptor.Encrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); - } - - bool SSU2Server::AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, - const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) - { - return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); - } - - void SSU2Server::ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out) - { - m_ChaCha20 (msg, msgLen, key, nonce, out); - } - - void SSU2Server::SendThroughProxy (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, - const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to) - { - if (!m_ProxyRelayEndpoint) return; - size_t requestHeaderSize = 0; - memset (m_UDPRequestHeader, 0, 3); - if (to.address ().is_v6 ()) - { - m_UDPRequestHeader[3] = SOCKS5_ATYP_IPV6; - memcpy (m_UDPRequestHeader + 4, to.address ().to_v6().to_bytes().data(), 16); - requestHeaderSize = SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE; - } - else - { - m_UDPRequestHeader[3] = SOCKS5_ATYP_IPV4; - memcpy (m_UDPRequestHeader + 4, to.address ().to_v4().to_bytes().data(), 4); - requestHeaderSize = SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE; - } - htobe16buf (m_UDPRequestHeader + requestHeaderSize - 2, to.port ()); - - std::vector bufs; - bufs.push_back (boost::asio::buffer (m_UDPRequestHeader, requestHeaderSize)); - bufs.push_back (boost::asio::buffer (header, headerLen)); - if (headerX) bufs.push_back (boost::asio::buffer (headerX, headerXLen)); - bufs.push_back (boost::asio::buffer (payload, payloadLen)); - - boost::system::error_code ec; - m_SocketV4.send_to (bufs, *m_ProxyRelayEndpoint, 0, ec); // TODO: implement ipv6 proxy - if (!ec) - i2p::transport::transports.UpdateSentBytes (headerLen + payloadLen); - else - LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to); - } - - void SSU2Server::ProcessNextPacketFromProxy (uint8_t * buf, size_t len) - { - if (buf[2]) // FRAG - { - LogPrint (eLogWarning, "SSU2: Proxy packet fragmentation is not supported"); - return; - } - size_t offset = 0; - boost::asio::ip::udp::endpoint ep; - switch (buf[3]) // ATYP - { - case SOCKS5_ATYP_IPV4: - { - offset = SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE; - if (offset > len) return; - boost::asio::ip::address_v4::bytes_type bytes; - memcpy (bytes.data (), buf + 4, 4); - uint16_t port = bufbe16toh (buf + 8); - ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v4 (bytes), port); - break; - } - case SOCKS5_ATYP_IPV6: - { - offset = SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE; - if (offset > len) return; - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), buf + 4, 16); - uint16_t port = bufbe16toh (buf + 20); - ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v6 (bytes), port); - break; - } - default: - { - LogPrint (eLogWarning, "SSU2: Unknown ATYP ", (int)buf[3], " from proxy relay"); - return; - } - } - ProcessNextPacket (buf + offset, len - offset, ep); - } - - void SSU2Server::ConnectToProxy () - { - if (!m_ProxyEndpoint) return; - m_UDPAssociateSocket.reset (new boost::asio::ip::tcp::socket (m_ReceiveService.GetService ())); - m_UDPAssociateSocket->async_connect (*m_ProxyEndpoint, - [this] (const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint (eLogError, "SSU2: Can't connect to proxy ", *m_ProxyEndpoint, " ", ecode.message ()); - m_UDPAssociateSocket.reset (nullptr); - ReconnectToProxy (); - } - else - HandshakeWithProxy (); - }); - } - - void SSU2Server::HandshakeWithProxy () - { - if (!m_UDPAssociateSocket) return; - m_UDPRequestHeader[0] = SOCKS5_VER; - m_UDPRequestHeader[1] = 1; // 1 method - m_UDPRequestHeader[2] = 0; // no authentication - boost::asio::async_write (*m_UDPAssociateSocket, boost::asio::buffer (m_UDPRequestHeader, 3), boost::asio::transfer_all(), - [this] (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint(eLogError, "SSU2: Proxy write error ", ecode.message()); - m_UDPAssociateSocket.reset (nullptr); - ReconnectToProxy (); - } - else - ReadHandshakeWithProxyReply (); - }); - } - - void SSU2Server::ReadHandshakeWithProxyReply () - { - if (!m_UDPAssociateSocket) return; - boost::asio::async_read (*m_UDPAssociateSocket, boost::asio::buffer (m_UDPRequestHeader, 2), boost::asio::transfer_all(), - [this] (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint(eLogError, "SSU2: Proxy read error ", ecode.message()); - m_UDPAssociateSocket.reset (nullptr); - ReconnectToProxy (); - } - else - { - if (m_UDPRequestHeader[0] == SOCKS5_VER && !m_UDPRequestHeader[1]) - SendUDPAssociateRequest (); - else - { - LogPrint(eLogError, "SSU2: Invalid proxy reply"); - m_UDPAssociateSocket.reset (nullptr); - } - } - }); - } - - void SSU2Server::SendUDPAssociateRequest () - { - if (!m_UDPAssociateSocket) return; - m_UDPRequestHeader[0] = SOCKS5_VER; - m_UDPRequestHeader[1] = SOCKS5_CMD_UDP_ASSOCIATE; - m_UDPRequestHeader[2] = 0; // RSV - m_UDPRequestHeader[3] = SOCKS5_ATYP_IPV4; // TODO: implement ipv6 proxy - memset (m_UDPRequestHeader + 4, 0, 6); // address and port all zeros - boost::asio::async_write (*m_UDPAssociateSocket, boost::asio::buffer (m_UDPRequestHeader, SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE), boost::asio::transfer_all(), - [this] (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint(eLogError, "SSU2: Proxy write error ", ecode.message()); - m_UDPAssociateSocket.reset (nullptr); - ReconnectToProxy (); - } - else - ReadUDPAssociateReply (); - }); - } - - void SSU2Server::ReadUDPAssociateReply () - { - if (!m_UDPAssociateSocket) return; - boost::asio::async_read (*m_UDPAssociateSocket, boost::asio::buffer (m_UDPRequestHeader, SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE), boost::asio::transfer_all(), - [this] (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint(eLogError, "SSU2: Proxy read error ", ecode.message()); - m_UDPAssociateSocket.reset (nullptr); - ReconnectToProxy (); - } - else - { - if (m_UDPRequestHeader[0] == SOCKS5_VER && !m_UDPRequestHeader[1]) - { - if (m_UDPRequestHeader[3] == SOCKS5_ATYP_IPV4) - { - boost::asio::ip::address_v4::bytes_type bytes; - memcpy (bytes.data (), m_UDPRequestHeader + 4, 4); - uint16_t port = bufbe16toh (m_UDPRequestHeader + 8); - m_ProxyRelayEndpoint.reset (new boost::asio::ip::udp::endpoint (boost::asio::ip::address_v4 (bytes), port)); - m_SocketV4.open (boost::asio::ip::udp::v4 ()); - Receive (m_SocketV4); - ReadUDPAssociateSocket (); - } - else - { - LogPrint(eLogError, "SSU2: Proxy UDP associate unsupported ATYP ", (int)m_UDPRequestHeader[3]); - m_UDPAssociateSocket.reset (nullptr); - } - } - else - { - LogPrint(eLogError, "SSU2: Proxy UDP associate error ", (int)m_UDPRequestHeader[1]); - m_UDPAssociateSocket.reset (nullptr); - } - } - }); - } - - void SSU2Server::ReadUDPAssociateSocket () - { - if (!m_UDPAssociateSocket) return; - m_UDPAssociateSocket->async_read_some (boost::asio::buffer (m_UDPRequestHeader, 1), - [this] (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint(eLogWarning, "SSU2: Proxy UDP Associate socket error ", ecode.message()); - m_UDPAssociateSocket.reset (nullptr); - m_ProxyRelayEndpoint.reset (nullptr); - m_SocketV4.close (); - ConnectToProxy (); // try to reconnect immediately - } - else - ReadUDPAssociateSocket (); - }); - } - - void SSU2Server::ReconnectToProxy () - { - LogPrint(eLogInfo, "SSU2: Reconnect to proxy after ", SSU2_PROXY_CONNECT_RETRY_TIMEOUT, " seconds"); - if (m_ProxyConnectRetryTimer) - m_ProxyConnectRetryTimer->cancel (); - else - m_ProxyConnectRetryTimer.reset (new boost::asio::deadline_timer (m_ReceiveService.GetService ())); - m_ProxyConnectRetryTimer->expires_from_now (boost::posix_time::seconds (SSU2_PROXY_CONNECT_RETRY_TIMEOUT)); - m_ProxyConnectRetryTimer->async_wait ( - [this](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - m_UDPAssociateSocket.reset (nullptr); - m_ProxyRelayEndpoint.reset (nullptr); - LogPrint(eLogInfo, "SSU2: Reconnecting to proxy"); - ConnectToProxy (); - } - }); - } - - bool SSU2Server::SetProxy (const std::string& address, uint16_t port) - { - boost::system::error_code ecode; - auto addr = boost::asio::ip::make_address (address, ecode); - if (!ecode && !addr.is_unspecified () && port) - { - m_IsThroughProxy = true; - m_ProxyEndpoint.reset (new boost::asio::ip::tcp::endpoint (addr, port)); - } - else - { - if (ecode) - LogPrint (eLogError, "SSU2: Invalid proxy address ", address, " ", ecode.message()); - return false; - } - return true; - } -} -} diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h deleted file mode 100644 index a8598ce3..00000000 --- a/libi2pd/SSU2.h +++ /dev/null @@ -1,228 +0,0 @@ -/* -* Copyright (c) 2022-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef SSU2_H__ -#define SSU2_H__ - -#include -#include -#include -#include -#include -#include -#include -#include "util.h" -#include "SSU2Session.h" -#include "SSU2OutOfSession.h" -#include "Socks5.h" - -namespace i2p -{ -namespace transport -{ - const int SSU2_TERMINATION_CHECK_TIMEOUT = 23; // in seconds - const int SSU2_TERMINATION_CHECK_TIMEOUT_VARIANCE = 5; // in seconds - const int SSU2_CLEANUP_INTERVAL = 72; // in seconds - const int SSU2_RESEND_CHECK_TIMEOUT = 40; // in milliseconds - const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 10; // in milliseconds - const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 4; // in milliseconds - const int SSU2_RESEND_CHECK_MORE_TIMEOUT_VARIANCE = 9; // in milliseconds - const size_t SSU2_MAX_RESEND_PACKETS = 128; // packets to resend at the time - const uint64_t SSU2_SOCKET_MIN_BUFFER_SIZE = 128 * 1024; - const uint64_t SSU2_SOCKET_MAX_BUFFER_SIZE = 4 * 1024 * 1024; - const size_t SSU2_MAX_NUM_INTRODUCERS = 3; - const size_t SSU2_MIN_RECEIVED_PACKET_SIZE = 40; // 16 byte short header + 8 byte minimum payload + 16 byte MAC - const size_t SSU2_MAX_RECEIVED_QUEUE_SIZE = 2500; // in packets - const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour - const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes - const int SSU2_KEEP_ALIVE_INTERVAL = 15; // in seconds - const int SSU2_KEEP_ALIVE_INTERVAL_VARIANCE = 4; // in seconds - const int SSU2_PROXY_CONNECT_RETRY_TIMEOUT = 30; // in seconds - const int SSU2_MIN_HOLE_PUNCH_EXPIRATION = 30; // in seconds - const int SSU2_MAX_HOLE_PUNCH_EXPIRATION = 160; // in seconds - const size_t SSU2_MAX_NUM_PACKETS_PER_BATCH = 64; - - class SSU2Server: private i2p::util::RunnableServiceWithWork - { - struct Packet - { - uint8_t buf[SSU2_MAX_PACKET_SIZE]; - size_t len; - boost::asio::ip::udp::endpoint from; - }; - - class ReceiveService: public i2p::util::RunnableService - { - public: - - ReceiveService (const std::string& name): RunnableService (name) {}; - auto& GetService () { return GetIOService (); }; - void Start () { StartIOService (); }; - void Stop () { StopIOService (); }; - }; - - public: - - SSU2Server (); - ~SSU2Server () {}; - - void Start (); - void Stop (); - auto& GetService () { return GetIOService (); }; - void SetLocalAddress (const boost::asio::ip::address& localAddress); - bool SetProxy (const std::string& address, uint16_t port); - bool UsesProxy () const { return m_IsThroughProxy; }; - bool IsSupported (const boost::asio::ip::address& addr) const; - uint16_t GetPort (bool v4) const; - bool IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep, bool max = true); - void AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts); - std::mt19937& GetRng () { return m_Rng; } - bool AEADChaCha20Poly1305Encrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); - bool AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, - const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len); - void ChaCha20 (const uint8_t * msg, size_t msgLen, const uint8_t * key, const uint8_t * nonce, uint8_t * out); - bool IsMaxNumIntroducers (bool v4) const { return (v4 ? m_Introducers.size () : m_IntroducersV6.size ()) >= SSU2_MAX_NUM_INTRODUCERS; } - bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; }; - void AdjustTimeOffset (int64_t offset, std::shared_ptr from); - - bool AddSession (std::shared_ptr session); - void RemoveSession (uint64_t connID); - void RequestRemoveSession (uint64_t connID); - void AddSessionByRouterHash (std::shared_ptr session); - bool AddPendingOutgoingSession (std::shared_ptr session); - void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep); - std::shared_ptr FindSession (const i2p::data::IdentHash& ident); - std::shared_ptr FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const; - std::shared_ptr GetRandomPeerTestSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports, - const i2p::data::IdentHash& excluded); - - void AddRelay (uint32_t tag, std::shared_ptr relay); - void RemoveRelay (uint32_t tag); - std::shared_ptr FindRelaySession (uint32_t tag); - - bool AddPeerTest (uint32_t nonce, std::shared_ptr aliceSession, uint64_t ts); - std::shared_ptr GetPeerTest (uint32_t nonce); - - bool AddRequestedPeerTest (uint32_t nonce, std::shared_ptr session, uint64_t ts); - std::shared_ptr GetRequestedPeerTest (uint32_t nonce); - - void Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen, - const boost::asio::ip::udp::endpoint& to); - void Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, - const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to); - - bool CreateSession (std::shared_ptr router, - std::shared_ptr address, bool peerTest = false); - bool StartPeerTest (std::shared_ptr router, bool v4); - - void UpdateOutgoingToken (const boost::asio::ip::udp::endpoint& ep, uint64_t token, uint32_t exp); - uint64_t FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep); - uint64_t GetIncomingToken (const boost::asio::ip::udp::endpoint& ep); - std::pair NewIncomingToken (const boost::asio::ip::udp::endpoint& ep); - - void RescheduleIntroducersUpdateTimer (); - void RescheduleIntroducersUpdateTimerV6 (); - - i2p::util::MemoryPool& GetSentPacketsPool () { return m_SentPacketsPool; }; - i2p::util::MemoryPool& GetIncompleteMessagesPool () { return m_IncompleteMessagesPool; }; - i2p::util::MemoryPool& GetFragmentsPool () { return m_FragmentsPool; }; - - private: - - boost::asio::ip::udp::socket& OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint); - void Receive (boost::asio::ip::udp::socket& socket); - void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, - Packet * packet, boost::asio::ip::udp::socket& socket); - void HandleReceivedPackets (std::list&& packets); - void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); - void InsertToReceivedPacketsQueue (Packet * packet); - void InsertToReceivedPacketsQueue (std::list& packets); - void HandleReceivedPacketsQueue (); - - void ScheduleTermination (); - void HandleTerminationTimer (const boost::system::error_code& ecode); - - void ScheduleCleanup (); - void HandleCleanupTimer (const boost::system::error_code& ecode); - - void ScheduleResend (bool more); - void HandleResendTimer (const boost::system::error_code& ecode); - - bool CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest); - void ConnectThroughIntroducer (std::shared_ptr session); - std::vector > FindIntroducers (int maxNumIntroducers, - bool v4, const std::unordered_set& excluded); - void UpdateIntroducers (bool v4); - void ScheduleIntroducersUpdateTimer (); - void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4); - void ScheduleIntroducersUpdateTimerV6 (); - - void SendThroughProxy (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, - const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to); - void ProcessNextPacketFromProxy (uint8_t * buf, size_t len); - void ConnectToProxy (); - void ReconnectToProxy (); - void HandshakeWithProxy (); - void ReadHandshakeWithProxyReply (); - void SendUDPAssociateRequest (); - void ReadUDPAssociateReply (); - void ReadUDPAssociateSocket (); // handle if closed by peer - - private: - - ReceiveService m_ReceiveService; - boost::asio::ip::udp::socket m_SocketV4, m_SocketV6; - boost::asio::ip::address m_AddressV4, m_AddressV6; - std::unordered_map > m_Sessions; - std::unordered_map > m_SessionsByRouterHash; - mutable std::mutex m_SessionsByRouterHashMutex; - std::map > m_PendingOutgoingSessions; - mutable std::mutex m_PendingOutgoingSessionsMutex; - std::map > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds) - std::unordered_map > m_Relays; // we are introducer, relay tag -> session - std::unordered_map, uint64_t > > m_PeerTests; // nonce->(Alice, timestamp). We are Bob - std::list > m_Introducers, m_IntroducersV6; // introducers we are connected to - i2p::util::MemoryPoolMt m_PacketsPool; - i2p::util::MemoryPool m_SentPacketsPool; - i2p::util::MemoryPool m_IncompleteMessagesPool; - i2p::util::MemoryPool m_FragmentsPool; - boost::asio::deadline_timer m_TerminationTimer, m_CleanupTimer, m_ResendTimer, - m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6; - std::shared_ptr m_LastSession; - bool m_IsPublished; // if we maintain introducers - bool m_IsSyncClockFromPeers; - int64_t m_PendingTimeOffset; // during peer test - std::shared_ptr m_PendingTimeOffsetFrom; - std::mt19937 m_Rng; - std::map m_ConnectedRecently; // endpoint -> last activity time in seconds - mutable std::mutex m_ConnectedRecentlyMutex; - std::unordered_map, uint64_t > > m_RequestedPeerTests; // nonce->(Alice, timestamp) - std::list m_ReceivedPacketsQueue; - mutable std::mutex m_ReceivedPacketsQueueMutex; - i2p::crypto::AEADChaCha20Poly1305Encryptor m_Encryptor; - i2p::crypto::AEADChaCha20Poly1305Decryptor m_Decryptor; - i2p::crypto::ChaCha20Context m_ChaCha20; - - // proxy - bool m_IsThroughProxy; - uint8_t m_UDPRequestHeader[SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE]; - std::unique_ptr m_ProxyEndpoint; - std::unique_ptr m_UDPAssociateSocket; - std::unique_ptr m_ProxyRelayEndpoint; - std::unique_ptr m_ProxyConnectRetryTimer; - - public: - - // for HTTP/I2PControl - const decltype(m_Sessions)& GetSSU2Sessions () const { return m_Sessions; }; - }; -} -} - -#endif diff --git a/libi2pd/SSU2OutOfSession.cpp b/libi2pd/SSU2OutOfSession.cpp deleted file mode 100644 index 3760e329..00000000 --- a/libi2pd/SSU2OutOfSession.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* -* Copyright (c) 2024-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include "Log.h" -#include "SSU2.h" -#include "SSU2OutOfSession.h" - -namespace i2p -{ -namespace transport -{ - SSU2PeerTestSession::SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID): - SSU2Session (server, nullptr, nullptr, false), - m_MsgNumReceived (0), m_NumResends (0),m_IsConnectedRecently (false), m_IsStatusChanged (false), - m_PeerTestResendTimer (server.GetService ()) - { - if (!sourceConnID) sourceConnID = ~destConnID; - if (!destConnID) destConnID = ~sourceConnID; - SetSourceConnID (sourceConnID); - SetDestConnID (destConnID); - SetState (eSSU2SessionStatePeerTest); - SetTerminationTimeout (SSU2_PEER_TEST_EXPIRATION_TIMEOUT); - } - - bool SSU2PeerTestSession::ProcessPeerTest (uint8_t * buf, size_t len) - { - // we are Alice or Charlie, msgs 5,6,7 - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); - header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); - if (header.h.type != eSSU2PeerTest) - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest); - return false; - } - if (len < 48) - { - LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len); - return false; - } - uint8_t nonce[12] = {0}; - uint64_t headerX[2]; // sourceConnID, token - GetServer ().ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); - SetDestConnID (headerX[0]); - // decrypt and handle payload - uint8_t * payload = buf + 32; - CreateNonce (be32toh (header.h.packetNum), nonce); - uint8_t h[32]; - memcpy (h, header.buf, 16); - memcpy (h + 16, &headerX, 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, - i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) - { - LogPrint (eLogWarning, "SSU2: PeerTest AEAD verification failed "); - return false; - } - HandlePayload (payload, len - 48); - SetIsDataReceived (false); - return true; - } - - void SSU2PeerTestSession::HandleAddress (const uint8_t * buf, size_t len) - { - if (!ExtractEndpoint (buf, len, m_OurEndpoint)) - LogPrint (eLogWarning, "SSU2: Can't handle address block from peer test message"); - } - - void SSU2PeerTestSession::HandlePeerTest (const uint8_t * buf, size_t len) - { - // msgs 5-7 - if (len < 8) return; - uint8_t msg = buf[0]; - if (msg <= m_MsgNumReceived) - { - LogPrint (eLogDebug, "SSU2: PeerTest msg num ", msg, " received after ", m_MsgNumReceived, ". Ignored"); - return; - } - size_t offset = 3; // points to signed data after msg + code + flag - uint32_t nonce = bufbe32toh (buf + offset + 1); // 1 - ver - switch (msg) // msg - { - case 5: // Alice from Charlie 1 - { - if (htobe64 (((uint64_t)nonce << 32) | nonce) == GetSourceConnID ()) - { - m_PeerTestResendTimer.cancel (); // cancel delayed msg 6 if any - m_IsConnectedRecently = GetServer ().IsConnectedRecently (GetRemoteEndpoint ()); - if (GetAddress ()) - { - if (!m_IsConnectedRecently) - SetRouterStatus (eRouterStatusOK); - else if (m_IsStatusChanged && GetRouterStatus () == eRouterStatusFirewalled) - SetRouterStatus (eRouterStatusUnknown); - SendPeerTest (6, buf + offset, len - offset); - } - } - else - LogPrint (eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", GetSourceConnID ()); - break; - } - case 6: // Charlie from Alice - { - m_PeerTestResendTimer.cancel (); // no more msg 5 resends - if (GetAddress ()) - SendPeerTest (7, buf + offset, len - offset); - else - LogPrint (eLogWarning, "SSU2: Unknown address for peer test 6"); - GetServer ().RequestRemoveSession (GetConnID ()); - break; - } - case 7: // Alice from Charlie 2 - { - m_PeerTestResendTimer.cancel (); // no more msg 6 resends - if (m_MsgNumReceived < 5 && m_OurEndpoint.port ()) // msg 5 was not received - { - if (m_OurEndpoint.address ().is_v4 ()) // ipv4 - { - if (i2p::context.GetStatus () == eRouterStatusFirewalled) - { - if (m_OurEndpoint.port () != GetServer ().GetPort (true)) - i2p::context.SetError (eRouterErrorSymmetricNAT); - else if (i2p::context.GetError () == eRouterErrorSymmetricNAT) - i2p::context.SetError (eRouterErrorNone); - } - } - else - { - if (i2p::context.GetStatusV6 () == eRouterStatusFirewalled) - { - if (m_OurEndpoint.port () != GetServer ().GetPort (false)) - i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT); - else if (i2p::context.GetErrorV6 () == eRouterErrorSymmetricNAT) - i2p::context.SetErrorV6 (eRouterErrorNone); - } - } - } - GetServer ().RequestRemoveSession (GetConnID ()); - break; - } - default: - LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", msg); - return; - } - m_MsgNumReceived = msg; - } - - void SSU2PeerTestSession::SendPeerTest (uint8_t msg) - { - auto addr = GetAddress (); - if (!addr) return; - Header header; - uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; - // fill packet - header.h.connID = GetDestConnID (); // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2PeerTest; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - htobuf64 (h + 16, GetSourceConnID ()); // source id - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - size_t payloadSize = 7; - if (msg == 6 || msg == 7) - payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, GetRemoteEndpoint ()); - payloadSize += CreatePeerTestBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, - msg, eSSU2PeerTestCodeAccept, nullptr, m_SignedData.data (), m_SignedData.size ()); - payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize); - // encrypt - uint8_t n[12]; - CreateNonce (be32toh (header.h.packetNum), n); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12)); - memset (n, 0, 12); - GetServer ().ChaCha20 (h + 16, 16, addr->i, n, h + 16); - // send - GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, GetRemoteEndpoint ()); - UpdateNumSentBytes (payloadSize + 32); - } - - void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed) - { - m_SignedData.assign (signedData, signedData + signedDataLen); - if (!delayed) - SendPeerTest (msg); - // schedule resend for msgs 5 or 6 - if (msg == 5 || msg == 6) - ScheduleResend (msg); - } - - void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, - std::shared_ptr addr, bool delayed) - { - if (!addr) return; - SetAddress (addr); - SendPeerTest (msg, signedData, signedDataLen, delayed); - } - - void SSU2PeerTestSession::Connect () - { - LogPrint (eLogError, "SSU2: Can't connect peer test session"); - } - - bool SSU2PeerTestSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) - { - LogPrint (eLogError, "SSU2: Can't handle incoming message in peer test session"); - return false; - } - - void SSU2PeerTestSession::ScheduleResend (uint8_t msg) - { - if (m_NumResends < SSU2_PEER_TEST_MAX_NUM_RESENDS) - { - m_PeerTestResendTimer.expires_from_now (boost::posix_time::milliseconds( - SSU2_PEER_TEST_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE)); - std::weak_ptr s(std::static_pointer_cast(shared_from_this ())); - m_PeerTestResendTimer.async_wait ([s, msg](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto s1 = s.lock (); - if (s1) - { - if (msg > s1->m_MsgNumReceived) - { - s1->SendPeerTest (msg); - s1->m_NumResends++; - s1->ScheduleResend (msg); - } - } - } - }); - } - } - - SSU2HolePunchSession::SSU2HolePunchSession (SSU2Server& server, uint32_t nonce, - const boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr addr): - SSU2Session (server), // we create full incoming session - m_NumResends (0), m_HolePunchResendTimer (server.GetService ()) - { - // we are Charlie - uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id - uint64_t sourceConnID = ~destConnID; - SetSourceConnID (sourceConnID); - SetDestConnID (destConnID); - SetState (eSSU2SessionStateHolePunch); - SetRemoteEndpoint (remoteEndpoint); - SetAddress (addr); - SetTerminationTimeout (SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT); - } - - void SSU2HolePunchSession::SendHolePunch () - { - auto addr = GetAddress (); - if (!addr) return; - auto& ep = GetRemoteEndpoint (); - LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep); - Header header; - uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; - // fill packet - header.h.connID = GetDestConnID (); // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2HolePunch; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - htobuf64 (h + 16, GetSourceConnID ()); // source id - RAND_bytes (h + 24, 8); // header token, to be ignored by Alice - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - size_t payloadSize = 7; - payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, ep); - // relay response block - if (payloadSize + m_RelayResponseBlock.size () < GetMaxPayloadSize ()) - { - memcpy (payload + payloadSize, m_RelayResponseBlock.data (), m_RelayResponseBlock.size ()); - payloadSize += m_RelayResponseBlock.size (); - } - payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize); - // encrypt - uint8_t n[12]; - CreateNonce (be32toh (header.h.packetNum), n); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12)); - memset (n, 0, 12); - GetServer ().ChaCha20 (h + 16, 16, addr->i, n, h + 16); - // send - GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep); - UpdateNumSentBytes (payloadSize + 32); - } - - void SSU2HolePunchSession::SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen) - { - m_RelayResponseBlock.assign (relayResponseBlock, relayResponseBlock + relayResponseBlockLen); - SendHolePunch (); - ScheduleResend (); - } - - void SSU2HolePunchSession::ScheduleResend () - { - if (m_NumResends < SSU2_HOLE_PUNCH_MAX_NUM_RESENDS) - { - m_HolePunchResendTimer.expires_from_now (boost::posix_time::milliseconds( - SSU2_HOLE_PUNCH_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_HOLE_PUNCH_RESEND_INTERVAL_VARIANCE)); - std::weak_ptr s(std::static_pointer_cast(shared_from_this ())); - m_HolePunchResendTimer.async_wait ([s](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto s1 = s.lock (); - if (s1 && s1->GetState () == eSSU2SessionStateHolePunch) - { - s1->SendHolePunch (); - s1->m_NumResends++; - s1->ScheduleResend (); - } - } - }); - } - } - - bool SSU2HolePunchSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) - { - m_HolePunchResendTimer.cancel (); - return SSU2Session::ProcessFirstIncomingMessage (connID, buf, len); - } -} -} diff --git a/libi2pd/SSU2OutOfSession.h b/libi2pd/SSU2OutOfSession.h deleted file mode 100644 index e8c55c3c..00000000 --- a/libi2pd/SSU2OutOfSession.h +++ /dev/null @@ -1,86 +0,0 @@ -/* -* Copyright (c) 2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef SSU2_OUT_OF_SESSION_H__ -#define SSU2_OUT_OF_SESSION_H__ - -#include -#include "SSU2Session.h" - -namespace i2p -{ -namespace transport -{ - const int SSU2_PEER_TEST_RESEND_INTERVAL = 3000; // in milliseconds - const int SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE = 2000; // in milliseconds - const int SSU2_PEER_TEST_MAX_NUM_RESENDS = 3; - - class SSU2PeerTestSession: public SSU2Session // for PeerTest msgs 5,6,7 - { - public: - - SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID); - - uint8_t GetMsgNumReceived () const { return m_MsgNumReceived; } - bool IsConnectedRecently () const { return m_IsConnectedRecently; } - void SetStatusChanged () { m_IsStatusChanged = true; } - - void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, - std::shared_ptr addr, bool delayed = false); - bool ProcessPeerTest (uint8_t * buf, size_t len) override; - void Connect () override; // outgoing - bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // incoming - - private: - - void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed = false); // PeerTest message - void SendPeerTest (uint8_t msg); // send or resend m_SignedData - void HandlePeerTest (const uint8_t * buf, size_t len) override; - void HandleAddress (const uint8_t * buf, size_t len) override; - - void ScheduleResend (uint8_t msg); - - private: - - uint8_t m_MsgNumReceived, m_NumResends; - bool m_IsConnectedRecently, m_IsStatusChanged; - std::vector m_SignedData; // for resends - boost::asio::deadline_timer m_PeerTestResendTimer; - boost::asio::ip::udp::endpoint m_OurEndpoint; // as seen by peer - }; - - const int SSU2_HOLE_PUNCH_RESEND_INTERVAL = 1000; // in milliseconds - const int SSU2_HOLE_PUNCH_RESEND_INTERVAL_VARIANCE = 500; // in milliseconds - const int SSU2_HOLE_PUNCH_MAX_NUM_RESENDS = 3; - - class SSU2HolePunchSession: public SSU2Session // Charlie - { - public: - - SSU2HolePunchSession (SSU2Server& server, uint32_t nonce, const boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr addr); - - void SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen); - - bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // SessionRequest - - private: - - void SendHolePunch (); - void ScheduleResend (); - - private: - - int m_NumResends; - std::vector m_RelayResponseBlock; - boost::asio::deadline_timer m_HolePunchResendTimer; - }; -} -} - -#endif diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp deleted file mode 100644 index cb10f848..00000000 --- a/libi2pd/SSU2Session.cpp +++ /dev/null @@ -1,3214 +0,0 @@ -/* -* Copyright (c) 2022-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include "Log.h" -#include "Transports.h" -#include "Gzip.h" -#include "NetDb.hpp" -#include "SSU2.h" -#include "SSU2Session.h" - -namespace i2p -{ -namespace transport -{ - void SSU2IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize) - { - if (msg->len + fragmentSize > msg->maxLen) - { - LogPrint (eLogInfo, "SSU2: I2NP message size ", msg->maxLen, " is not enough"); - auto newMsg = NewI2NPMessage (msg->len + fragmentSize); - *newMsg = *msg; - msg = newMsg; - } - if (msg->Concat (fragment, fragmentSize) < fragmentSize) - LogPrint (eLogError, "SSU2: I2NP buffer overflow ", msg->maxLen); - nextFragmentNum++; - } - - bool SSU2IncompleteMessage::ConcatOutOfSequenceFragments () - { - bool isLast = false; - while (outOfSequenceFragments) - { - if (outOfSequenceFragments->fragmentNum == nextFragmentNum) - { - AttachNextFragment (outOfSequenceFragments->buf, outOfSequenceFragments->len); - isLast = outOfSequenceFragments->isLast; - if (isLast) - outOfSequenceFragments = nullptr; - else - outOfSequenceFragments = outOfSequenceFragments->next; - } - else - break; - } - return isLast; - } - - void SSU2IncompleteMessage::AddOutOfSequenceFragment (std::shared_ptr fragment) - { - if (!fragment || !fragment->fragmentNum) return; // fragment 0 not allowed - if (fragment->fragmentNum < nextFragmentNum) return; // already processed - if (!outOfSequenceFragments) - outOfSequenceFragments = fragment; - else - { - auto frag = outOfSequenceFragments; - std::shared_ptr prev; - do - { - if (fragment->fragmentNum < frag->fragmentNum) break; // found - if (fragment->fragmentNum == frag->fragmentNum) return; // duplicate - prev = frag; frag = frag->next; - } - while (frag); - fragment->next = frag; - if (prev) - prev->next = fragment; - else - outOfSequenceFragments = fragment; - } - lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); - } - - SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr in_RemoteRouter, - std::shared_ptr addr, bool noise): - TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT), - m_Server (server), m_Address (addr), m_RemoteTransports (0), m_RemotePeerTestTransports (0), - m_RemoteVersion (0), m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown), - m_SendPacketNum (0), m_ReceivePacketNum (0), m_LastDatetimeSentPacketNum (0), - m_IsDataReceived (false), m_RTT (SSU2_UNKNOWN_RTT), - m_MsgLocalExpirationTimeout (I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MAX), - m_MsgLocalSemiExpirationTimeout (I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MAX / 2), - m_WindowSize (SSU2_MIN_WINDOW_SIZE), - m_RTO (SSU2_INITIAL_RTO), m_RelayTag (0),m_ConnectTimer (server.GetService ()), - m_TerminationReason (eSSU2TerminationReasonNormalClose), - m_MaxPayloadSize (SSU2_MIN_PACKET_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - 32), // min size - m_LastResendTime (0), m_LastResendAttemptTime (0), m_NumRanges (0) - { - if (noise) - m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState); - if (in_RemoteRouter && m_Address) - { - // outgoing - if (noise) - InitNoiseXKState1 (*m_NoiseState, m_Address->s); - m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port); - m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false); - m_RemoteVersion = in_RemoteRouter->GetVersion (); - if (in_RemoteRouter->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4; - if (in_RemoteRouter->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6; - RAND_bytes ((uint8_t *)&m_DestConnID, 8); - RAND_bytes ((uint8_t *)&m_SourceConnID, 8); - } - else - { - // incoming - if (noise) - InitNoiseXKState1 (*m_NoiseState, i2p::context.GetSSU2StaticPublicKey ()); - } - } - - SSU2Session::~SSU2Session () - { - } - - void SSU2Session::Connect () - { - if (m_State == eSSU2SessionStateUnknown || m_State == eSSU2SessionStateTokenReceived) - { - LogPrint(eLogDebug, "SSU2: Connecting to ", GetRemoteEndpoint (), - " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ")"); - ScheduleConnectTimer (); - auto token = m_Server.FindOutgoingToken (m_RemoteEndpoint); - if (token) - SendSessionRequest (token); - else - { - m_State = eSSU2SessionStateUnknown; - SendTokenRequest (); - } - } - } - - void SSU2Session::ScheduleConnectTimer () - { - m_ConnectTimer.cancel (); - m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU2_CONNECT_TIMEOUT)); - m_ConnectTimer.async_wait (std::bind (&SSU2Session::HandleConnectTimer, - shared_from_this (), std::placeholders::_1)); - } - - void SSU2Session::HandleConnectTimer (const boost::system::error_code& ecode) - { - if (!ecode && m_State != eSSU2SessionStateTerminated) - { - // timeout expired - if (m_State == eSSU2SessionStateIntroduced) // WaitForIntroducer - LogPrint (eLogWarning, "SSU2: Session was not introduced after ", SSU2_CONNECT_TIMEOUT, " seconds"); - else - LogPrint (eLogWarning, "SSU2: Session with ", m_RemoteEndpoint, " was not established after ", SSU2_CONNECT_TIMEOUT, " seconds"); - Terminate (); - } - } - - bool SSU2Session::Introduce (std::shared_ptr session, uint32_t relayTag) - { - // we are Alice - if (!session || !relayTag) return false; - // find local address to introduce - auto localAddress = session->FindLocalAddress (); - if (!localAddress || localAddress->host.is_unspecified () || !localAddress->port) - { - // can't introduce invalid endpoint - LogPrint (eLogWarning, "SSU2: Can't find local address to introduce"); - return false; - } - // create nonce - uint32_t nonce; - RAND_bytes ((uint8_t *)&nonce, 4); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - // payload - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - uint8_t * payload = packet->payload; - payload[0] = eSSU2BlkRelayRequest; - payload[3] = 0; // flag - htobe32buf (payload + 4, nonce); - htobe32buf (payload + 8, relayTag); - htobe32buf (payload + 12, ts/1000); - payload[16] = 2; // ver - size_t asz = CreateEndpoint (payload + 18, m_MaxPayloadSize - 18, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port)); - if (!asz) return false; - payload[17] = asz; - packet->payloadSize = asz + 18; - SignedData<128> s; - s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (session->GetRemoteIdentity ()->GetIdentHash (), 32); // chash - s.Insert (payload + 4, 14 + asz); // nonce, relay tag, timestamp, ver, asz and Alice's endpoint - s.Sign (i2p::context.GetPrivateKeys (), payload + packet->payloadSize); - packet->payloadSize += i2p::context.GetIdentity ()->GetSignatureLen (); - htobe16buf (payload + 1, packet->payloadSize - 3); // size - packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - // send - m_RelaySessions.emplace (nonce, std::make_pair (session, ts/1000)); - session->m_SourceConnID = htobe64 (((uint64_t)nonce << 32) | nonce); - session->m_DestConnID = ~session->m_SourceConnID; - m_Server.AddSession (session); - int32_t packetNum = SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - m_SentPackets.emplace (packetNum, packet); - - return true; - } - - void SSU2Session::WaitForIntroduction () - { - m_State = eSSU2SessionStateIntroduced; - ScheduleConnectTimer (); - } - - void SSU2Session::ConnectAfterIntroduction () - { - if (m_State == eSSU2SessionStateIntroduced) - { - // we are Alice - // keep ConnIDs used for introduction, because Charlie waits for SessionRequest from us - m_State = eSSU2SessionStateTokenReceived; - // move session to pending outgoing - if (m_Server.AddPendingOutgoingSession (shared_from_this ())) - { - m_Server.RemoveSession (GetConnID ()); - // update endpoint in profile because we know it now - auto identity = GetRemoteIdentity (); - if (identity) - { - auto profile = i2p::data::GetRouterProfile (identity->GetIdentHash ()); - if (profile) profile->SetLastEndpoint (m_RemoteEndpoint); - } - // connect - LogPrint (eLogDebug, "SSU2: Connecting after introduction to ", GetIdentHashBase64()); - Connect (); - } - else - { - LogPrint (eLogError, "SSU2: Session ", GetConnID (), " is already pending"); - m_Server.RequestRemoveSession (GetConnID ()); - } - } - } - - void SSU2Session::SendPeerTest () - { - // we are Alice - uint32_t nonce; - RAND_bytes ((uint8_t *)&nonce, 4); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - // session for message 5 - auto session = std::make_shared (m_Server, - htobe64 (((uint64_t)nonce << 32) | nonce), 0); - m_Server.AddRequestedPeerTest (nonce, session, ts/1000); - m_Server.AddSession (session); - // peer test block - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - packet->payloadSize = CreatePeerTestBlock (packet->payload, m_MaxPayloadSize, nonce); - if (packet->payloadSize > 0) - { - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED); - packet->sendTime = ts; - m_SentPackets.emplace (packetNum, packet); - LogPrint (eLogDebug, "SSU2: PeerTest msg=1 sent to ", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ())); - } - } - - void SSU2Session::SendKeepAlive () - { - if (IsEstablished ()) - { - uint8_t payload[20]; - size_t payloadSize = CreatePaddingBlock (payload, 20, 8); - SendData (payload, payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED); - } - } - - void SSU2Session::Terminate () - { - if (m_State != eSSU2SessionStateTerminated) - { - m_State = eSSU2SessionStateTerminated; - m_ConnectTimer.cancel (); - m_OnEstablished = nullptr; - if (m_RelayTag) - m_Server.RemoveRelay (m_RelayTag); - m_Server.AddConnectedRecently (m_RemoteEndpoint, GetLastActivityTimestamp ()); - m_SentHandshakePacket.reset (nullptr); - m_SessionConfirmedFragment.reset (nullptr); - m_PathChallenge.reset (nullptr); - if (!m_IntermediateQueue.empty ()) - m_SendQueue.splice (m_SendQueue.end (), m_IntermediateQueue); - for (auto& it: m_SendQueue) - it->Drop (); - m_SendQueue.clear (); - SetSendQueueSize (0); - m_SentPackets.clear (); - m_IncompleteMessages.clear (); - m_RelaySessions.clear (); - m_ReceivedI2NPMsgIDs.clear (); - m_Server.RemoveSession (m_SourceConnID); - transports.PeerDisconnected (shared_from_this ()); - auto remoteIdentity = GetRemoteIdentity (); - if (remoteIdentity) - LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), - " (", i2p::data::GetIdentHashAbbreviation (remoteIdentity->GetIdentHash ()), ") terminated"); - else - LogPrint (eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), " terminated"); - } - } - - void SSU2Session::RequestTermination (SSU2TerminationReason reason) - { - if (m_State == eSSU2SessionStateEstablished || m_State == eSSU2SessionStateClosing) - { - m_TerminationReason = reason; - SendTermination (); - m_State = eSSU2SessionStateClosing; - } - else - Done (); - } - - void SSU2Session::Established () - { - m_State = eSSU2SessionStateEstablished; - m_EphemeralKeys = nullptr; - m_NoiseState.reset (nullptr); - m_SessionConfirmedFragment.reset (nullptr); - m_SentHandshakePacket.reset (nullptr); - m_ConnectTimer.cancel (); - SetTerminationTimeout (SSU2_TERMINATION_TIMEOUT); - SendQueue (); - transports.PeerConnected (shared_from_this ()); - - LogPrint(eLogDebug, "SSU2: Session with ", GetRemoteEndpoint (), - " (", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), ") established"); - if (m_OnEstablished) - { - m_OnEstablished (); - m_OnEstablished = nullptr; - } - } - - void SSU2Session::Done () - { - boost::asio::post (m_Server.GetService (), std::bind (&SSU2Session::Terminate, shared_from_this ())); - } - - void SSU2Session::SendLocalRouterInfo (bool update) - { - if (update || !IsOutgoing ()) - { - auto s = shared_from_this (); - boost::asio::post (m_Server.GetService (), [s]() - { - if (!s->IsEstablished ()) return; - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = s->CreateRouterInfoBlock (payload, s->m_MaxPayloadSize - 32, i2p::context.CopyRouterInfoBuffer ()); - if (payloadSize) - { - if (payloadSize < s->m_MaxPayloadSize) - payloadSize += s->CreatePaddingBlock (payload + payloadSize, s->m_MaxPayloadSize - payloadSize); - s->SendData (payload, payloadSize); - } - else - s->SendFragmentedMessage (CreateDatabaseStoreMsg ()); - }); - } - - } - - void SSU2Session::SendI2NPMessages (std::list >& msgs) - { - if (m_State == eSSU2SessionStateTerminated || msgs.empty ()) - { - msgs.clear (); - return; - } - bool empty = false; - { - std::lock_guard l(m_IntermediateQueueMutex); - empty = m_IntermediateQueue.empty (); - m_IntermediateQueue.splice (m_IntermediateQueue.end (), msgs); - } - if (empty) - boost::asio::post (m_Server.GetService (), std::bind (&SSU2Session::PostI2NPMessages, shared_from_this ())); - } - - void SSU2Session::PostI2NPMessages () - { - if (m_State == eSSU2SessionStateTerminated) return; - std::list > msgs; - { - std::lock_guard l(m_IntermediateQueueMutex); - m_IntermediateQueue.swap (msgs); - } - uint64_t mts = i2p::util::GetMonotonicMicroseconds (); - bool isSemiFull = false; - if (m_SendQueue.size ()) - { - int64_t queueLag = (int64_t)mts - (int64_t)m_SendQueue.front ()->GetEnqueueTime (); - isSemiFull = queueLag > m_MsgLocalSemiExpirationTimeout; - if (isSemiFull) - { - LogPrint (eLogWarning, "SSU2: Outgoing messages queue to ", - i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), - " is semi-full (size = ", m_SendQueue.size (), ", lag = ", queueLag / 1000, ", rtt = ", (int)m_RTT, ")"); - } - } - if (isSemiFull) - { - for (auto it: msgs) - { - if (it->onDrop) - it->Drop (); // drop earlier because we can handle it - else - { - it->SetEnqueueTime (mts); - m_SendQueue.push_back (std::move (it)); - } - } - } - else - { - for (auto& it: msgs) it->SetEnqueueTime (mts); - m_SendQueue.splice (m_SendQueue.end (), msgs); - } - if (IsEstablished ()) - { - SendQueue (); - if (m_SendQueue.size () > 0) // windows is full - Resend (i2p::util::GetMillisecondsSinceEpoch ()); - } - SetSendQueueSize (m_SendQueue.size ()); - } - - void SSU2Session::MoveSendQueue (std::shared_ptr other) - { - if (!other || m_SendQueue.empty ()) return; - std::list > msgs; - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto it: m_SendQueue) - if (!it->IsExpired (ts)) - msgs.push_back (it); - else - it->Drop (); - m_SendQueue.clear (); - if (!msgs.empty ()) - other->SendI2NPMessages (msgs); - } - - bool SSU2Session::SendQueue () - { - if (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize && IsEstablished ()) - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - uint64_t mts = i2p::util::GetMonotonicMicroseconds (); - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - size_t ackBlockSize = CreateAckBlock (packet->payload, m_MaxPayloadSize); - bool ackBlockSent = false; - packet->payloadSize += ackBlockSize; - while (!m_SendQueue.empty () && m_SentPackets.size () <= m_WindowSize) - { - auto msg = m_SendQueue.front (); - if (!msg || msg->IsExpired (ts) || msg->GetEnqueueTime() + m_MsgLocalExpirationTimeout < mts) - { - // drop null or expired message - if (msg) msg->Drop (); - m_SendQueue.pop_front (); - continue; - } - size_t len = msg->GetNTCP2Length () + 3; - if (len > m_MaxPayloadSize) // message too long - { - m_SendQueue.pop_front (); - if (SendFragmentedMessage (msg)) - ackBlockSent = true; - } - else if (packet->payloadSize + len <= m_MaxPayloadSize) - { - m_SendQueue.pop_front (); - packet->payloadSize += CreateI2NPBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, std::move (msg)); - } - else - { - // create new packet and copy ack block - auto newPacket = m_Server.GetSentPacketsPool ().AcquireShared (); - memcpy (newPacket->payload, packet->payload, ackBlockSize); - newPacket->payloadSize = ackBlockSize; - // complete current packet - if (packet->payloadSize > ackBlockSize) // more than just ack block - { - ackBlockSent = true; - // try to add padding - if (packet->payloadSize + 16 < m_MaxPayloadSize) - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - } - else - { - // reduce ack block - if (len + 8 < m_MaxPayloadSize) - { - // keep Ack block and drop some ranges - ackBlockSent = true; - packet->payloadSize = m_MaxPayloadSize - len; - if (packet->payloadSize & 0x01) packet->payloadSize--; // make it even - htobe16buf (packet->payload + 1, packet->payloadSize - 3); // new block size - } - else // drop Ack block completely - packet->payloadSize = 0; - // msg fits single packet - m_SendQueue.pop_front (); - packet->payloadSize += CreateI2NPBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, std::move (msg)); - } - // send right a way - uint32_t packetNum = SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - m_SentPackets.emplace (packetNum, packet); - packet = newPacket; // just ack block - } - }; - if (packet->payloadSize > ackBlockSize) - { - // last - ackBlockSent = true; - if (packet->payloadSize + 16 < m_MaxPayloadSize) - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED); - packet->sendTime = ts; - m_SentPackets.emplace (packetNum, packet); - } - return ackBlockSent; - } - return false; - } - - bool SSU2Session::SendFragmentedMessage (std::shared_ptr msg) - { - if (!msg) return false; - size_t lastFragmentSize = (msg->GetNTCP2Length () + 3 - m_MaxPayloadSize) % (m_MaxPayloadSize - 8); - size_t extraSize = m_MaxPayloadSize - lastFragmentSize; - bool ackBlockSent = false; - uint32_t msgID; - memcpy (&msgID, msg->GetHeader () + I2NP_HEADER_MSGID_OFFSET, 4); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - if (extraSize >= 8) - { - packet->payloadSize = CreateAckBlock (packet->payload, extraSize); - ackBlockSent = true; - if (packet->payloadSize + 12 < m_MaxPayloadSize) - { - uint32_t packetNum = SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - m_SentPackets.emplace (packetNum, packet); - packet = m_Server.GetSentPacketsPool ().AcquireShared (); - } - else - extraSize -= packet->payloadSize; - } - size_t offset = extraSize > 0 ? (m_Server.GetRng ()() % extraSize) : 0; - if (offset + packet->payloadSize >= m_MaxPayloadSize) offset = 0; - auto size = CreateFirstFragmentBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - offset - packet->payloadSize, msg); - if (!size) return false; - extraSize -= offset; - packet->payloadSize += size; - uint32_t firstPacketNum = SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - m_SentPackets.emplace (firstPacketNum, packet); - uint8_t fragmentNum = 0; - while (msg->offset < msg->len) - { - offset = extraSize > 0 ? (m_Server.GetRng ()() % extraSize) : 0; - packet = m_Server.GetSentPacketsPool ().AcquireShared (); - packet->payloadSize = CreateFollowOnFragmentBlock (packet->payload, m_MaxPayloadSize - offset, msg, fragmentNum, msgID); - extraSize -= offset; - uint8_t flags = 0; - if (msg->offset >= msg->len && packet->payloadSize + 16 < m_MaxPayloadSize) // last fragment - { - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - if (fragmentNum > 2) // 3 or more fragments - flags |= SSU2_FLAG_IMMEDIATE_ACK_REQUESTED; - } - uint32_t followonPacketNum = SendData (packet->payload, packet->payloadSize, flags); - packet->sendTime = ts; - m_SentPackets.emplace (followonPacketNum, packet); - } - return ackBlockSent; - } - - size_t SSU2Session::Resend (uint64_t ts) - { - if (ts + SSU2_RESEND_ATTEMPT_MIN_INTERVAL < m_LastResendAttemptTime) return 0; - m_LastResendAttemptTime = ts; - // resend handshake packet - if (m_SentHandshakePacket && ts >= m_SentHandshakePacket->sendTime + SSU2_HANDSHAKE_RESEND_INTERVAL) - { - LogPrint (eLogDebug, "SSU2: Resending ", (int)m_State); - ResendHandshakePacket (); - m_SentHandshakePacket->sendTime = ts; - return 0; - } - // resend data packets - if (m_SentPackets.empty ()) return 0; - std::map > resentPackets; - for (auto it = m_SentPackets.begin (); it != m_SentPackets.end (); ) - if (ts >= it->second->sendTime + (it->second->numResends + 1) * m_RTO) - { - if (it->second->numResends > SSU2_MAX_NUM_RESENDS) - { - LogPrint (eLogInfo, "SSU2: Packet was not Acked after ", it->second->numResends, " attempts. Terminate session"); - m_SentPackets.clear (); - m_SendQueue.clear (); - SetSendQueueSize (0); - RequestTermination (eSSU2TerminationReasonTimeout); - return resentPackets.size (); - } - else - { - uint32_t packetNum = SendData (it->second->payload, it->second->payloadSize, - it->second->numResends > 1 ? SSU2_FLAG_IMMEDIATE_ACK_REQUESTED : 0); - it->second->numResends++; - it->second->sendTime = ts; - resentPackets.emplace (packetNum, it->second); - it = m_SentPackets.erase (it); - } - } - else - it++; - if (!resentPackets.empty ()) - { - m_LastResendTime = ts; - m_SentPackets.merge (resentPackets); - m_WindowSize >>= 1; // /2 - if (m_WindowSize < SSU2_MIN_WINDOW_SIZE) m_WindowSize = SSU2_MIN_WINDOW_SIZE; - return resentPackets.size (); - } - return 0; - } - - void SSU2Session::ResendHandshakePacket () - { - if (m_SentHandshakePacket) - { - m_Server.Send (m_SentHandshakePacket->header.buf, 16, m_SentHandshakePacket->headerX, 48, - m_SentHandshakePacket->payload, m_SentHandshakePacket->payloadSize, m_RemoteEndpoint); - if (m_SessionConfirmedFragment && m_State == eSSU2SessionStateSessionConfirmedSent) - // resend second fragment of SessionConfirmed - m_Server.Send (m_SessionConfirmedFragment->header.buf, 16, - m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize, m_RemoteEndpoint); - } - } - - bool SSU2Session::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) - { - // we are Bob - m_SourceConnID = connID; - Header header; - header.h.connID = connID; - memcpy (header.buf + 8, buf + 8, 8); - header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); - switch (header.h.type) - { - case eSSU2SessionRequest: - ProcessSessionRequest (header, buf, len); - break; - case eSSU2TokenRequest: - ProcessTokenRequest (header, buf, len); - break; - case eSSU2PeerTest: - { - // TODO: remove later - if (len < 32) - { - LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len); - break; - } - const uint8_t nonce[12] = {0}; - uint64_t headerX[2]; - m_Server.ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); - LogPrint (eLogWarning, "SSU2: Unexpected PeerTest message SourceConnID=", connID, " DestConnID=", headerX[0]); - break; - } - case eSSU2HolePunch: - LogPrint (eLogDebug, "SSU2: Late HolePunch for ", connID); - break; - default: - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " from ", m_RemoteEndpoint, " of ", len, " bytes"); - return false; - } - } - return true; - } - - void SSU2Session::SendSessionRequest (uint64_t token) - { - // we are Alice - m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); - m_SentHandshakePacket.reset (new HandshakePacket); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - m_SentHandshakePacket->sendTime = ts; - - Header& header = m_SentHandshakePacket->header; - uint8_t * headerX = m_SentHandshakePacket->headerX, - * payload = m_SentHandshakePacket->payload; - // fill packet - header.h.connID = m_DestConnID; // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2SessionRequest; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (headerX, &m_SourceConnID, 8); // source id - memcpy (headerX + 8, &token, 8); // token - memcpy (headerX + 16, m_EphemeralKeys->GetPublicKey (), 32); // X - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (ts + 500)/1000); - size_t payloadSize = 7; - if (GetRouterStatus () == eRouterStatusFirewalled && m_Address->IsIntroducer ()) - { - if (!m_Server.IsMaxNumIntroducers (m_RemoteEndpoint.address ().is_v4 ()) || - m_Server.GetRng ()() & 0x01) // request tag with probability 1/2 if we have enough introducers - { - // relay tag request - payload[payloadSize] = eSSU2BlkRelayTagRequest; - memset (payload + payloadSize + 1, 0, 2); // size = 0 - payloadSize += 3; - } - } - payloadSize += CreatePaddingBlock (payload + payloadSize, 40 - payloadSize, 1); - // KDF for session request - m_NoiseState->MixHash ({ {header.buf, 16}, {headerX, 16} }); // h = SHA256(h || header) - m_NoiseState->MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk); - uint8_t sharedSecret[32]; - m_EphemeralKeys->Agree (m_Address->s, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - // encrypt - const uint8_t nonce[12] = {0}; // always 0 - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 12)); - m_Server.ChaCha20 (headerX, 48, m_Address->i, nonce, headerX); - m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || encrypted payload from Session Request) for SessionCreated - m_SentHandshakePacket->payloadSize = payloadSize; - // send - if (m_State == eSSU2SessionStateTokenReceived || m_Server.AddPendingOutgoingSession (shared_from_this ())) - { - m_State = eSSU2SessionStateSessionRequestSent; - m_HandshakeInterval = ts; - m_Server.Send (header.buf, 16, headerX, 48, payload, payloadSize, m_RemoteEndpoint); - } - else - { - LogPrint (eLogWarning, "SSU2: SessionRequest request to ", m_RemoteEndpoint, " already pending"); - Terminate (); - } - } - - void SSU2Session::ProcessSessionRequest (Header& header, uint8_t * buf, size_t len) - { - // we are Bob - if (len < 88) - { - LogPrint (eLogWarning, "SSU2: SessionRequest message too short ", len); - return; - } - const uint8_t nonce[12] = {0}; - uint8_t headerX[48]; - m_Server.ChaCha20 (buf + 16, 48, i2p::context.GetSSU2IntroKey (), nonce, headerX); - memcpy (&m_DestConnID, headerX, 8); - uint64_t token; - memcpy (&token, headerX + 8, 8); - if (!token || token != m_Server.GetIncomingToken (m_RemoteEndpoint)) - { - LogPrint (eLogDebug, "SSU2: SessionRequest token mismatch. Retry"); - SendRetry (); - return; - } - // KDF for session request - m_NoiseState->MixHash ( { {header.buf, 16}, {headerX, 16} } ); // h = SHA256(h || header) - m_NoiseState->MixHash (headerX + 16, 32); // h = SHA256(h || aepk); - uint8_t sharedSecret[32]; - i2p::context.GetSSU2StaticKeys ().Agree (headerX + 16, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - // decrypt - uint8_t * payload = buf + 64; - std::vector decryptedPayload(len - 80); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 80, m_NoiseState->m_H, 32, - m_NoiseState->m_CK + 32, nonce, decryptedPayload.data (), decryptedPayload.size (), false)) - { - LogPrint (eLogWarning, "SSU2: SessionRequest AEAD verification failed "); - return; - } - m_NoiseState->MixHash (payload, len - 64); // h = SHA256(h || encrypted payload from Session Request) for SessionCreated - // payload - m_State = eSSU2SessionStateSessionRequestReceived; - HandlePayload (decryptedPayload.data (), decryptedPayload.size ()); - - if (m_TerminationReason == eSSU2TerminationReasonNormalClose) - { - m_Server.AddSession (shared_from_this ()); - SendSessionCreated (headerX + 16); - } - else - SendRetry (); - } - - void SSU2Session::SendSessionCreated (const uint8_t * X) - { - // we are Bob - m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); - m_SentHandshakePacket.reset (new HandshakePacket); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - m_SentHandshakePacket->sendTime = ts; - - uint8_t kh2[32]; - i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessCreateHeader", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessCreateHeader", 32) - // fill packet - Header& header = m_SentHandshakePacket->header; - uint8_t * headerX = m_SentHandshakePacket->headerX, - * payload = m_SentHandshakePacket->payload; - header.h.connID = m_DestConnID; // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2SessionCreated; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (headerX, &m_SourceConnID, 8); // source id - memset (headerX + 8, 0, 8); // token = 0 - memcpy (headerX + 16, m_EphemeralKeys->GetPublicKey (), 32); // Y - // payload - size_t maxPayloadSize = m_MaxPayloadSize - 48; - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (ts + 500)/1000); - size_t payloadSize = 7; - payloadSize += CreateAddressBlock (payload + payloadSize, maxPayloadSize - payloadSize, m_RemoteEndpoint); - if (m_RelayTag) - { - payload[payloadSize] = eSSU2BlkRelayTag; - htobe16buf (payload + payloadSize + 1, 4); - htobe32buf (payload + payloadSize + 3, m_RelayTag); - payloadSize += 7; - } - auto token = m_Server.NewIncomingToken (m_RemoteEndpoint); - if (ts + SSU2_TOKEN_EXPIRATION_THRESHOLD > token.second) // not expired? - { - payload[payloadSize] = eSSU2BlkNewToken; - htobe16buf (payload + payloadSize + 1, 12); - htobe32buf (payload + payloadSize + 3, token.second - SSU2_TOKEN_EXPIRATION_THRESHOLD); // expires - memcpy (payload + payloadSize + 7, &token.first, 8); // token - payloadSize += 15; - } - payloadSize += CreatePaddingBlock (payload + payloadSize, maxPayloadSize - payloadSize); - // KDF for SessionCreated - m_NoiseState->MixHash ( { {header.buf, 16}, {headerX, 16} } ); // h = SHA256(h || header) - m_NoiseState->MixHash (headerX + 16, 32); // h = SHA256(h || bepk); - uint8_t sharedSecret[32]; - m_EphemeralKeys->Agree (X, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - // encrypt - const uint8_t nonce[12] = {0}; // always zero - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true); - payloadSize += 16; - m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || encrypted Noise payload from Session Created) - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (kh2, payload + (payloadSize - 12)); - m_Server.ChaCha20 (headerX, 48, kh2, nonce, headerX); - m_State = eSSU2SessionStateSessionCreatedSent; - m_SentHandshakePacket->payloadSize = payloadSize; - // send - m_HandshakeInterval = ts; - m_Server.Send (header.buf, 16, headerX, 48, payload, payloadSize, m_RemoteEndpoint); - } - - bool SSU2Session::ProcessSessionCreated (uint8_t * buf, size_t len) - { - // we are Alice - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (m_Address->i, buf + (len - 24)); - uint8_t kh2[32]; - i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessCreateHeader", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessCreateHeader", 32) - header.ll[1] ^= CreateHeaderMask (kh2, buf + (len - 12)); - if (header.h.type != eSSU2SessionCreated) - // this situation is valid, because it might be Retry with different encryption - return false; - if (len < 80) - { - LogPrint (eLogWarning, "SSU2: SessionCreated message too short ", len); - return false; - } - m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval; - const uint8_t nonce[12] = {0}; - uint8_t headerX[48]; - m_Server.ChaCha20 (buf + 16, 48, kh2, nonce, headerX); - // KDF for SessionCreated - m_NoiseState->MixHash ( { {header.buf, 16}, {headerX, 16} } ); // h = SHA256(h || header) - m_NoiseState->MixHash (headerX + 16, 32); // h = SHA256(h || bepk); - uint8_t sharedSecret[32]; - m_EphemeralKeys->Agree (headerX + 16, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - // decrypt - uint8_t * payload = buf + 64; - std::vector decryptedPayload(len - 80); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 80, m_NoiseState->m_H, 32, - m_NoiseState->m_CK + 32, nonce, decryptedPayload.data (), decryptedPayload.size (), false)) - { - LogPrint (eLogWarning, "SSU2: SessionCreated AEAD verification failed "); - if (GetRemoteIdentity ()) - i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); // assume wrong s key - return false; - } - m_NoiseState->MixHash (payload, len - 64); // h = SHA256(h || encrypted payload from SessionCreated) for SessionConfirmed - // payload - m_State = eSSU2SessionStateSessionCreatedReceived; - HandlePayload (decryptedPayload.data (), decryptedPayload.size ()); - - m_Server.AddSession (shared_from_this ()); - AdjustMaxPayloadSize (); - SendSessionConfirmed (headerX + 16); - KDFDataPhase (m_KeyDataSend, m_KeyDataReceive); - - return true; - } - - void SSU2Session::SendSessionConfirmed (const uint8_t * Y) - { - // we are Alice - m_SentHandshakePacket.reset (new HandshakePacket); - m_SentHandshakePacket->sendTime = i2p::util::GetMillisecondsSinceEpoch (); - - uint8_t kh2[32]; - i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessionConfirmed", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessionConfirmed", 32) - // fill packet - Header& header = m_SentHandshakePacket->header; - header.h.connID = m_DestConnID; // dest id - header.h.packetNum = 0; // always zero - header.h.type = eSSU2SessionConfirmed; - memset (header.h.flags, 0, 3); - header.h.flags[0] = 1; // frag, total fragments always 1 - // payload - size_t maxPayloadSize = m_MaxPayloadSize - 48; // for part 2, 48 is part1 - uint8_t * payload = m_SentHandshakePacket->payload; - size_t payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.CopyRouterInfoBuffer ()); - if (!payloadSize) - { - // split by two fragments - maxPayloadSize += m_MaxPayloadSize; - payloadSize = CreateRouterInfoBlock (payload, maxPayloadSize, i2p::context.CopyRouterInfoBuffer ()); - header.h.flags[0] = 0x02; // frag 0, total fragments 2 - // TODO: check if we need more fragments - } - if (payloadSize < maxPayloadSize) - payloadSize += CreatePaddingBlock (payload + payloadSize, maxPayloadSize - payloadSize); - // KDF for Session Confirmed part 1 - m_NoiseState->MixHash (header.buf, 16); // h = SHA256(h || header) - // Encrypt part 1 - uint8_t * part1 = m_SentHandshakePacket->headerX; - uint8_t nonce[12]; - CreateNonce (1, nonce); // always one - i2p::crypto::AEADChaCha20Poly1305 (i2p::context.GetSSU2StaticPublicKey (), 32, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, part1, 48, true); - m_NoiseState->MixHash (part1, 48); // h = SHA256(h || ciphertext); - // KDF for Session Confirmed part 2 - uint8_t sharedSecret[32]; - i2p::context.GetSSU2StaticKeys ().Agree (Y, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - // Encrypt part2 - memset (nonce, 0, 12); // always zero - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true); - payloadSize += 16; - m_NoiseState->MixHash (payload, payloadSize); // h = SHA256(h || ciphertext); - m_SentHandshakePacket->payloadSize = payloadSize; - if (header.h.flags[0] > 1) - { - if (payloadSize > m_MaxPayloadSize - 48) - { - payloadSize = m_MaxPayloadSize - 48 - (m_Server.GetRng ()() % 16); - if (m_SentHandshakePacket->payloadSize - payloadSize < 24) - payloadSize -= 24; - } - else - header.h.flags[0] = 1; - } - // Encrypt header - header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (kh2, payload + (payloadSize - 12)); - m_State = eSSU2SessionStateSessionConfirmedSent; - // send - m_Server.Send (header.buf, 16, part1, 48, payload, payloadSize, m_RemoteEndpoint); - m_SendPacketNum++; - if (m_SentHandshakePacket->payloadSize > payloadSize) - { - // send second fragment - m_SessionConfirmedFragment.reset (new HandshakePacket); - Header& header = m_SessionConfirmedFragment->header; - header.h.connID = m_DestConnID; // dest id - header.h.packetNum = 0; - header.h.type = eSSU2SessionConfirmed; - memset (header.h.flags, 0, 3); - header.h.flags[0] = 0x12; // frag 1, total fragments 2 - m_SessionConfirmedFragment->payloadSize = m_SentHandshakePacket->payloadSize - payloadSize; - memcpy (m_SessionConfirmedFragment->payload, m_SentHandshakePacket->payload + payloadSize, m_SessionConfirmedFragment->payloadSize); - m_SentHandshakePacket->payloadSize = payloadSize; - header.ll[0] ^= CreateHeaderMask (m_Address->i, m_SessionConfirmedFragment->payload + (m_SessionConfirmedFragment->payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (kh2, m_SessionConfirmedFragment->payload + (m_SessionConfirmedFragment->payloadSize - 12)); - m_Server.Send (header.buf, 16, m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize, m_RemoteEndpoint); - } - } - - bool SSU2Session::ProcessSessionConfirmed (uint8_t * buf, size_t len) - { - // we are Bob - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); - uint8_t kh2[32]; - i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "SessionConfirmed", kh2, 32); // k_header_2 = HKDF(chainKey, ZEROLEN, "SessionConfirmed", 32) - header.ll[1] ^= CreateHeaderMask (kh2, buf + (len - 12)); - if (header.h.type != eSSU2SessionConfirmed) - { - LogPrint (eLogInfo, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2SessionConfirmed); - // TODO: queue up - return true; - } - // packet num must be always zero - if (header.h.packetNum) - { - LogPrint (eLogError, "SSU2: Non zero packet number in SessionConfirmed"); - return false; - } - // check if fragmented - uint8_t numFragments = header.h.flags[0] & 0x0F; - if (numFragments > 1) - { - // fragmented - if (numFragments > 2) - { - LogPrint (eLogError, "SSU2: Too many fragments ", (int)numFragments, " in SessionConfirmed from ", m_RemoteEndpoint); - return false; - } - if (len < 32) - { - LogPrint (eLogWarning, "SSU2: SessionConfirmed fragment too short ", len); - if (m_SessionConfirmedFragment) m_SessionConfirmedFragment.reset (nullptr); - return false; - } - if (!(header.h.flags[0] & 0xF0)) - { - // first fragment - if (!m_SessionConfirmedFragment) - { - m_SessionConfirmedFragment.reset (new HandshakePacket); - m_SessionConfirmedFragment->header = header; - memcpy (m_SessionConfirmedFragment->payload, buf + 16, len - 16); - m_SessionConfirmedFragment->payloadSize = len - 16; - return true; // wait for second fragment - } - else if (m_SessionConfirmedFragment->isSecondFragment) - { - // we have second fragment - m_SessionConfirmedFragment->header = header; - memmove (m_SessionConfirmedFragment->payload + (len - 16), m_SessionConfirmedFragment->payload, m_SessionConfirmedFragment->payloadSize); - memcpy (m_SessionConfirmedFragment->payload, buf + 16, len - 16); - m_SessionConfirmedFragment->payloadSize += (len - 16); - m_SessionConfirmedFragment->isSecondFragment = false; - buf = m_SessionConfirmedFragment->payload - 16; - len = m_SessionConfirmedFragment->payloadSize + 16; - } - else - return true; - } - else - { - // second fragment - if (!m_SessionConfirmedFragment) - { - // out of sequence, save it - m_SessionConfirmedFragment.reset (new HandshakePacket); - memcpy (m_SessionConfirmedFragment->payload, buf + 16, len - 16); - m_SessionConfirmedFragment->payloadSize = len - 16; - m_SessionConfirmedFragment->isSecondFragment = true; - return true; - } - header = m_SessionConfirmedFragment->header; - if (m_SessionConfirmedFragment->payloadSize + (len - 16) <= SSU2_MAX_PACKET_SIZE*2) - { - memcpy (m_SessionConfirmedFragment->payload + m_SessionConfirmedFragment->payloadSize, buf + 16, len - 16); - m_SessionConfirmedFragment->payloadSize += (len - 16); - } - buf = m_SessionConfirmedFragment->payload - 16; - len = m_SessionConfirmedFragment->payloadSize + 16; - } - } - if (len < 80) - { - LogPrint (eLogWarning, "SSU2: SessionConfirmed message too short ", len); - if (m_SessionConfirmedFragment) m_SessionConfirmedFragment.reset (nullptr); - return false; - } - m_HandshakeInterval = i2p::util::GetMillisecondsSinceEpoch () - m_HandshakeInterval; - // KDF for Session Confirmed part 1 - m_NoiseState->MixHash (header.buf, 16); // h = SHA256(h || header) - // decrypt part1 - uint8_t nonce[12]; - CreateNonce (1, nonce); - uint8_t S[32]; - if (!i2p::crypto::AEADChaCha20Poly1305 (buf + 16, 32, m_NoiseState->m_H, 32, - m_NoiseState->m_CK + 32, nonce, S, 32, false)) - { - LogPrint (eLogWarning, "SSU2: SessionConfirmed part 1 AEAD verification failed "); - if (m_SessionConfirmedFragment) m_SessionConfirmedFragment.reset (nullptr); - return false; - } - m_NoiseState->MixHash (buf + 16, 48); // h = SHA256(h || ciphertext); - // KDF for Session Confirmed part 2 and data phase - uint8_t sharedSecret[32]; - m_EphemeralKeys->Agree (S, sharedSecret); - m_NoiseState->MixKey (sharedSecret); - KDFDataPhase (m_KeyDataReceive, m_KeyDataSend); - // decrypt part2 - memset (nonce, 0, 12); - uint8_t * payload = buf + 64; - std::vector decryptedPayload(len - 80); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 80, m_NoiseState->m_H, 32, - m_NoiseState->m_CK + 32, nonce, decryptedPayload.data (), decryptedPayload.size (), false)) - { - LogPrint (eLogWarning, "SSU2: SessionConfirmed part 2 AEAD verification failed "); - if (m_SessionConfirmedFragment) m_SessionConfirmedFragment.reset (nullptr); - return false; - } - m_NoiseState->MixHash (payload, len - 64); // h = SHA256(h || ciphertext); - if (m_SessionConfirmedFragment) m_SessionConfirmedFragment.reset (nullptr); - // payload - // handle RouterInfo block that must be first - if (decryptedPayload[0] != eSSU2BlkRouterInfo) - { - LogPrint (eLogError, "SSU2: SessionConfirmed unexpected first block type ", (int)decryptedPayload[0]); - return false; - } - size_t riSize = bufbe16toh (decryptedPayload.data () + 1); - if (riSize + 3 > decryptedPayload.size ()) - { - LogPrint (eLogError, "SSU2: SessionConfirmed RouterInfo block is too long ", riSize); - return false; - } - LogPrint (eLogDebug, "SSU2: RouterInfo in SessionConfirmed"); - auto ri = ExtractRouterInfo (decryptedPayload.data () + 3, riSize); - if (!ri) - { - LogPrint (eLogError, "SSU2: SessionConfirmed malformed RouterInfo block"); - return false; - } - auto ts = i2p::util::GetMillisecondsSinceEpoch(); - if (ts > ri->GetTimestamp () + i2p::data::NETDB_MIN_EXPIRATION_TIMEOUT*1000LL) // 90 minutes - { - LogPrint (eLogError, "SSU2: RouterInfo in SessionConfirmed is too old for ", (ts - ri->GetTimestamp ())/1000LL, " seconds"); - return false; - } - if (ts + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri->GetTimestamp ()) // 2 minutes - { - LogPrint (eLogError, "SSU2: RouterInfo in SessionConfirmed is from future for ", (ri->GetTimestamp () - ts)/1000LL, " seconds"); - return false; - } - // update RouterInfo in netdb - auto ri1 = i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); // ri points to one from netdb now - if (!ri1) - { - LogPrint (eLogError, "SSU2: Couldn't update RouterInfo from SessionConfirmed in netdb"); - return false; - } - - bool isOlder = false; - if (ri->GetTimestamp () + i2p::data::NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < ri1->GetTimestamp ()) - { - // received RouterInfo is older than one in netdb - isOlder = true; - if (ri->HasProfile ()) - { - auto profile = i2p::data::GetRouterProfile (ri->GetIdentHash ()); // retrieve profile - if (profile && profile->IsDuplicated ()) - return false; - } - } - ri = ri1; - - m_Address = m_RemoteEndpoint.address ().is_v6 () ? ri->GetSSU2V6Address () : ri->GetSSU2V4Address (); - if (!m_Address || memcmp (S, m_Address->s, 32)) - { - LogPrint (eLogError, "SSU2: Wrong static key in SessionConfirmed from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ())); - return false; - } - if (m_Address->published && m_RemoteEndpoint.address () != m_Address->host && - (!m_RemoteEndpoint.address ().is_v6 () || - memcmp (m_RemoteEndpoint.address ().to_v6 ().to_bytes ().data (), m_Address->host.to_v6 ().to_bytes ().data (), 8))) // temporary address - { - if (isOlder) // older router? - i2p::data::UpdateRouterProfile (ri->GetIdentHash (), - [](std::shared_ptr profile) - { - if (profile) profile->Duplicated (); // mark router as duplicated in profile - }); - else - LogPrint (eLogInfo, "SSU2: Host mismatch between published address ", m_Address->host, - " and actual endpoint ", m_RemoteEndpoint.address (), " from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ())); - return false; - } - if (!m_Address->published) - { - if (ri->HasProfile ()) - ri->GetProfile ()->SetLastEndpoint (m_RemoteEndpoint); - else - i2p::data::UpdateRouterProfile (ri->GetIdentHash (), - [ep = m_RemoteEndpoint](std::shared_ptr profile) - { - if (profile) profile->SetLastEndpoint (ep); - }); - } - SetRemoteIdentity (ri->GetRouterIdentity ()); - AdjustMaxPayloadSize (); - m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now - m_RemoteTransports = ri->GetCompatibleTransports (false); - m_RemotePeerTestTransports = 0; - if (ri->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4; - if (ri->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6; - m_RemoteVersion = ri->GetVersion (); - - // handle other blocks - HandlePayload (decryptedPayload.data () + riSize + 3, decryptedPayload.size () - riSize - 3); - Established (); - - SendQuickAck (); - - return true; - } - - void SSU2Session::KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba) - { - uint8_t keydata[64]; - i2p::crypto::HKDF (m_NoiseState->m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) - // ab - i2p::crypto::HKDF (keydata, nullptr, 0, "HKDFSSU2DataKeys", keydata_ab); // keydata_ab = HKDF(keydata, ZEROLEN, "HKDFSSU2DataKeys", 64) - // ba - i2p::crypto::HKDF (keydata + 32, nullptr, 0, "HKDFSSU2DataKeys", keydata_ba); // keydata_ba = HKDF(keydata + 32, ZEROLEN, "HKDFSSU2DataKeys", 64) - } - - void SSU2Session::SendTokenRequest () - { - // we are Alice - Header header; - uint8_t h[32], payload[41]; - // fill packet - header.h.connID = m_DestConnID; // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2TokenRequest; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - memcpy (h + 16, &m_SourceConnID, 8); // source id - memset (h + 24, 0, 8); // zero token - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - size_t payloadSize = 7; - payloadSize += CreatePaddingBlock (payload + payloadSize, 25 - payloadSize, 1); - // encrypt - uint8_t nonce[12]; - CreateNonce (be32toh (header.h.packetNum), nonce); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, m_Address->i, nonce, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (m_Address->i, payload + (payloadSize - 12)); - memset (nonce, 0, 12); - m_Server.ChaCha20 (h + 16, 16, m_Address->i, nonce, h + 16); - // send - if (m_Server.AddPendingOutgoingSession (shared_from_this ())) - m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); - else - { - LogPrint (eLogWarning, "SSU2: TokenRequest request to ", m_RemoteEndpoint, " already pending"); - Terminate (); - } - } - - void SSU2Session::ProcessTokenRequest (Header& header, uint8_t * buf, size_t len) - { - // we are Bob - if (len < 48) - { - LogPrint (eLogWarning, "SSU2: Incorrect TokenRequest len ", len); - return; - } - uint8_t nonce[12] = {0}; - uint8_t h[32]; - memcpy (h, header.buf, 16); - m_Server.ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); - memcpy (&m_DestConnID, h + 16, 8); - // decrypt - CreateNonce (be32toh (header.h.packetNum), nonce); - uint8_t * payload = buf + 32; - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, - i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) - { - LogPrint (eLogWarning, "SSU2: TokenRequest AEAD verification failed "); - return; - } - // payload - m_State = eSSU2SessionStateTokenRequestReceived; - HandlePayload (payload, len - 48); - SendRetry (); - } - - void SSU2Session::SendRetry () - { - // we are Bob - Header header; - uint8_t h[32], payload[72]; - // fill packet - header.h.connID = m_DestConnID; // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2Retry; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - memcpy (h + 16, &m_SourceConnID, 8); // source id - uint64_t token = 0; - if (m_TerminationReason == eSSU2TerminationReasonNormalClose) - token = m_Server.GetIncomingToken (m_RemoteEndpoint); - memcpy (h + 24, &token, 8); // token - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - size_t payloadSize = 7; - payloadSize += CreateAddressBlock (payload + payloadSize, 56 - payloadSize, m_RemoteEndpoint); - if (m_TerminationReason != eSSU2TerminationReasonNormalClose) - payloadSize += CreateTerminationBlock (payload + payloadSize, 56 - payloadSize); - payloadSize += CreatePaddingBlock (payload + payloadSize, 56 - payloadSize); - // encrypt - uint8_t nonce[12]; - CreateNonce (be32toh (header.h.packetNum), nonce); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, i2p::context.GetSSU2IntroKey (), nonce, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), payload + (payloadSize - 12)); - memset (nonce, 0, 12); - m_Server.ChaCha20 (h + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, h + 16); - // send - m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, m_RemoteEndpoint); - } - - bool SSU2Session::ProcessRetry (uint8_t * buf, size_t len) - { - // we are Alice - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (m_Address->i, buf + (len - 24)); - header.ll[1] ^= CreateHeaderMask (m_Address->i, buf + (len - 12)); - if (header.h.type != eSSU2Retry) - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2Retry); - return false; - } - if (len < 48) - { - LogPrint (eLogWarning, "SSU2: Retry message too short ", len); - return false; - } - uint8_t nonce[12] = {0}; - uint64_t headerX[2]; // sourceConnID, token - m_Server.ChaCha20 (buf + 16, 16, m_Address->i, nonce, (uint8_t *)headerX); - uint64_t token = headerX[1]; - if (token) - m_Server.UpdateOutgoingToken (m_RemoteEndpoint, token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); - // decrypt and handle payload - uint8_t * payload = buf + 32; - CreateNonce (be32toh (header.h.packetNum), nonce); - uint8_t h[32]; - memcpy (h, header.buf, 16); - memcpy (h + 16, &headerX, 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, - m_Address->i, nonce, payload, len - 48, false)) - { - LogPrint (eLogWarning, "SSU2: Retry AEAD verification failed"); - return false; - } - m_State = eSSU2SessionStateTokenReceived; - HandlePayload (payload, len - 48); - if (!token) - { - // we should handle payload even for zero token to handle Datetime block and adjust clock in case of clock skew - LogPrint (eLogWarning, "SSU2: Retry token is zero"); - return false; - } - InitNoiseXKState1 (*m_NoiseState, m_Address->s); // reset Noise TODO: check state - SendSessionRequest (token); - return true; - } - - bool SSU2Session::ProcessHolePunch (uint8_t * buf, size_t len) - { - // we are Alice - LogPrint (eLogDebug, "SSU2: HolePunch"); - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); - header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); - if (header.h.type != eSSU2HolePunch) - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2HolePunch); - return false; - } - if (len < 48) - { - LogPrint (eLogWarning, "SSU2: HolePunch message too short ", len); - return false; - } - uint8_t nonce[12] = {0}; - uint64_t headerX[2]; // sourceConnID, token - m_Server.ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); - m_DestConnID = headerX[0]; - // decrypt and handle payload - uint8_t * payload = buf + 32; - CreateNonce (be32toh (header.h.packetNum), nonce); - uint8_t h[32]; - memcpy (h, header.buf, 16); - memcpy (h + 16, &headerX, 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, - i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) - { - LogPrint (eLogWarning, "SSU2: HolePunch AEAD verification failed "); - return false; - } - HandlePayload (payload, len - 48); - m_IsDataReceived = false; - // connect to Charlie - ConnectAfterIntroduction (); - - return true; - } - - bool SSU2Session::ProcessPeerTest (uint8_t * buf, size_t len) - { - LogPrint (eLogWarning, "SSU2: Unexpected peer test message for this session type"); - return false; - } - - uint32_t SSU2Session::SendData (const uint8_t * buf, size_t len, uint8_t flags) - { - if (len < 8) - { - LogPrint (eLogWarning, "SSU2: Data message payload is too short ", (int)len); - return 0; - } - Header header; - header.h.connID = m_DestConnID; - header.h.packetNum = htobe32 (m_SendPacketNum); - header.h.type = eSSU2Data; - memset (header.h.flags, 0, 3); - if (flags) header.h.flags[0] = flags; - uint8_t nonce[12]; - CreateNonce (m_SendPacketNum, nonce); - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - m_Server.AEADChaCha20Poly1305Encrypt (buf, len, header.buf, 16, m_KeyDataSend, nonce, payload, SSU2_MAX_PACKET_SIZE); - header.ll[0] ^= CreateHeaderMask (m_Address->i, payload + (len - 8)); - header.ll[1] ^= CreateHeaderMask (m_KeyDataSend + 32, payload + (len + 4)); - m_Server.Send (header.buf, 16, payload, len + 16, m_RemoteEndpoint); - m_SendPacketNum++; - UpdateNumSentBytes (len + 32); - return m_SendPacketNum - 1; - } - - void SSU2Session::ProcessData (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from) - { - Header header; - header.ll[0] = m_SourceConnID; - memcpy (header.buf + 8, buf + 8, 8); - header.ll[1] ^= CreateHeaderMask (m_KeyDataReceive + 32, buf + (len - 12)); - if (header.h.type != eSSU2Data) - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2Data); - if (IsEstablished ()) - SendQuickAck (); // in case it was SessionConfirmed - else - ResendHandshakePacket (); // assume we receive - return; - } - if (from != m_RemoteEndpoint && !i2p::transport::transports.IsInReservedRange (from.address ()) && - (!m_PathChallenge || from != m_PathChallenge->second)) // path challenge was not sent to this endpoint yet - { - LogPrint (eLogInfo, "SSU2: Remote endpoint update ", m_RemoteEndpoint, "->", from); - SendPathChallenge (from); - } - if (len < 32) - { - LogPrint (eLogWarning, "SSU2: Data message too short ", len); - return; - } - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = len - 32; - uint32_t packetNum = be32toh (header.h.packetNum); - uint8_t nonce[12]; - CreateNonce (packetNum, nonce); - if (!m_Server.AEADChaCha20Poly1305Decrypt (buf + 16, payloadSize, header.buf, 16, - m_KeyDataReceive, nonce, payload, payloadSize)) - { - LogPrint (eLogWarning, "SSU2: Data AEAD verification failed "); - return; - } - UpdateNumReceivedBytes (len); - if (header.h.flags[0] & SSU2_FLAG_IMMEDIATE_ACK_REQUESTED) m_IsDataReceived = true; - if (!packetNum || UpdateReceivePacketNum (packetNum)) - HandlePayload (payload, payloadSize); - } - - void SSU2Session::HandlePayload (const uint8_t * buf, size_t len) - { - size_t offset = 0; - while (offset < len) - { - uint8_t blk = buf[offset]; - offset++; - auto size = bufbe16toh (buf + offset); - offset += 2; - LogPrint (eLogDebug, "SSU2: Block type ", (int)blk, " of size ", size); - if (offset + size > len) - { - LogPrint (eLogError, "SSU2: Unexpected block length ", size); - break; - } - switch (blk) - { - case eSSU2BlkDateTime: - LogPrint (eLogDebug, "SSU2: Datetime"); - HandleDateTime (buf + offset, size); - break; - case eSSU2BlkOptions: - LogPrint (eLogDebug, "SSU2: Options"); - break; - case eSSU2BlkRouterInfo: - LogPrint (eLogDebug, "SSU2: RouterInfo"); - HandleRouterInfo (buf + offset, size); - break; - case eSSU2BlkI2NPMessage: - { - LogPrint (eLogDebug, "SSU2: I2NP message"); - auto nextMsg = (buf[offset] == eI2NPTunnelData) ? NewI2NPTunnelMessage (true) : NewI2NPShortMessage (); - nextMsg->len = nextMsg->offset + size + 7; // 7 more bytes for full I2NP header - memcpy (nextMsg->GetNTCP2Header (), buf + offset, size); - nextMsg->FromNTCP2 (); // SSU2 has the same format as NTCP2 - HandleI2NPMsg (std::move (nextMsg)); - m_IsDataReceived = true; - break; - } - case eSSU2BlkFirstFragment: - LogPrint (eLogDebug, "SSU2: First fragment"); - HandleFirstFragment (buf + offset, size); - m_IsDataReceived = true; - break; - case eSSU2BlkFollowOnFragment: - LogPrint (eLogDebug, "SSU2: Follow-on fragment"); - HandleFollowOnFragment (buf + offset, size); - m_IsDataReceived = true; - break; - case eSSU2BlkTermination: - { - if (size >= 9) - { - uint8_t rsn = buf[offset + 8]; // reason - LogPrint (eLogDebug, "SSU2: Termination reason=", (int)rsn); - if (IsEstablished () && rsn != eSSU2TerminationReasonTerminationReceived) - RequestTermination (eSSU2TerminationReasonTerminationReceived); - else if (m_State != eSSU2SessionStateTerminated) - { - if (m_State == eSSU2SessionStateClosing && rsn == eSSU2TerminationReasonTerminationReceived) - m_State = eSSU2SessionStateClosingConfirmed; - Done (); - } - } - else - LogPrint(eLogWarning, "SSU2: Unexpected termination block size ", size); - break; - } - case eSSU2BlkRelayRequest: - LogPrint (eLogDebug, "SSU2: RelayRequest"); - HandleRelayRequest (buf + offset, size); - m_IsDataReceived = true; - break; - case eSSU2BlkRelayResponse: - LogPrint (eLogDebug, "SSU2: RelayResponse"); - HandleRelayResponse (buf + offset, size); - m_IsDataReceived = true; - break; - case eSSU2BlkRelayIntro: - LogPrint (eLogDebug, "SSU2: RelayIntro"); - HandleRelayIntro (buf + offset, size); - m_IsDataReceived = true; - break; - case eSSU2BlkPeerTest: - LogPrint (eLogDebug, "SSU2: PeerTest msg=", (int)buf[offset], " code=", (int)buf[offset+1]); - HandlePeerTest (buf + offset, size); - if (buf[offset] < 5) - m_IsDataReceived = true; - break; - case eSSU2BlkNextNonce: - break; - case eSSU2BlkAck: - LogPrint (eLogDebug, "SSU2: Ack"); - HandleAck (buf + offset, size); - break; - case eSSU2BlkAddress: - LogPrint (eLogDebug, "SSU2: Address"); - HandleAddress (buf + offset, size); - break; - case eSSU2BlkIntroKey: - break; - case eSSU2BlkRelayTagRequest: - LogPrint (eLogDebug, "SSU2: RelayTagRequest"); - if (!m_RelayTag) - { - auto addr = FindLocalAddress (); - if (addr && addr->IsIntroducer ()) - { - RAND_bytes ((uint8_t *)&m_RelayTag, 4); - m_Server.AddRelay (m_RelayTag, shared_from_this ()); - } - } - break; - case eSSU2BlkRelayTag: - LogPrint (eLogDebug, "SSU2: RelayTag"); - m_RelayTag = bufbe32toh (buf + offset); - break; - case eSSU2BlkNewToken: - { - LogPrint (eLogDebug, "SSU2: New token"); - uint64_t token; - memcpy (&token, buf + offset + 4, 8); - m_Server.UpdateOutgoingToken (m_RemoteEndpoint, token, bufbe32toh (buf + offset)); - break; - } - case eSSU2BlkPathChallenge: - LogPrint (eLogDebug, "SSU2: Path challenge"); - SendPathResponse (buf + offset, size); - break; - case eSSU2BlkPathResponse: - { - LogPrint (eLogDebug, "SSU2: Path response"); - if (m_PathChallenge) - { - if (buf64toh (buf + offset) == m_PathChallenge->first) - { - m_RemoteEndpoint = m_PathChallenge->second; - m_PathChallenge.reset (nullptr); - } - } - break; - } - case eSSU2BlkFirstPacketNumber: - break; - case eSSU2BlkPadding: - LogPrint (eLogDebug, "SSU2: Padding"); - break; - default: - LogPrint (eLogWarning, "SSU2: Unknown block type ", (int)blk); - } - offset += size; - } - } - - void SSU2Session::HandleDateTime (const uint8_t * buf, size_t len) - { - int64_t offset = (int64_t)i2p::util::GetSecondsSinceEpoch () - (int64_t)bufbe32toh (buf); - switch (m_State) - { - case eSSU2SessionStateSessionRequestReceived: - case eSSU2SessionStateTokenRequestReceived: - case eSSU2SessionStateEstablished: - if (std::abs (offset) > SSU2_CLOCK_SKEW) - m_TerminationReason = eSSU2TerminationReasonClockSkew; - break; - case eSSU2SessionStateSessionCreatedReceived: - case eSSU2SessionStateTokenReceived: - if ((m_RemoteEndpoint.address ().is_v4 () && i2p::context.GetTesting ()) || - (m_RemoteEndpoint.address ().is_v6 () && i2p::context.GetTestingV6 ())) - { - if (m_Server.IsSyncClockFromPeers ()) - { - if (std::abs (offset) > SSU2_CLOCK_THRESHOLD) - { - LogPrint (eLogWarning, "SSU2: Time offset ", offset, " from ", m_RemoteEndpoint); - m_Server.AdjustTimeOffset (-offset, GetRemoteIdentity ()); - } - else - m_Server.AdjustTimeOffset (0, nullptr); - } - else if (std::abs (offset) > SSU2_CLOCK_SKEW) - { - LogPrint (eLogError, "SSU2: Clock skew detected ", offset, ". Check your clock"); - i2p::context.SetError (eRouterErrorClockSkew); - } - } - break; - default: ; - }; - } - - void SSU2Session::HandleRouterInfo (const uint8_t * buf, size_t len) - { - if (len < 2) return; - // not from SessionConfirmed, we must add it instantly to use in next block - std::shared_ptr newRi; - if (buf[0] & SSU2_ROUTER_INFO_FLAG_GZIP) // compressed? - { - auto ri = ExtractRouterInfo (buf, len); - if (ri) - newRi = i2p::data::netdb.AddRouterInfo (ri->GetBuffer (), ri->GetBufferLen ()); - } - else // use buffer directly. TODO: handle frag - newRi = i2p::data::netdb.AddRouterInfo (buf + 2, len - 2); - - if (newRi) - { - auto remoteIdentity = GetRemoteIdentity (); - if (remoteIdentity && remoteIdentity->GetIdentHash () == newRi->GetIdentHash ()) - { - // peer's RouterInfo update - SetRemoteIdentity (newRi->GetIdentity ()); - auto address = m_RemoteEndpoint.address ().is_v6 () ? newRi->GetSSU2V6Address () : newRi->GetSSU2V4Address (); - if (address) - { - m_Address = address; - if (IsOutgoing () && m_RelayTag && !address->IsIntroducer ()) - m_RelayTag = 0; // not longer introducer - } - } - } - } - - void SSU2Session::HandleAck (const uint8_t * buf, size_t len) - { - if (m_State == eSSU2SessionStateSessionConfirmedSent) - { - Established (); - return; - } - if (m_SentPackets.empty ()) return; - if (len < 5) return; - // acnt - uint32_t ackThrough = bufbe32toh (buf); - uint32_t firstPacketNum = ackThrough > buf[4] ? ackThrough - buf[4] : 0; - HandleAckRange (firstPacketNum, ackThrough, i2p::util::GetMillisecondsSinceEpoch ()); // acnt - // ranges - len -= 5; - if (!len || m_SentPackets.empty ()) return; // don't handle ranges if nothing to acknowledge - const uint8_t * ranges = buf + 5; - while (len > 0 && firstPacketNum && ackThrough - firstPacketNum < SSU2_MAX_NUM_ACK_PACKETS) - { - uint32_t lastPacketNum = firstPacketNum - 1; - if (*ranges > lastPacketNum) break; - lastPacketNum -= *ranges; ranges++; // nacks - if (*ranges > lastPacketNum + 1) break; - firstPacketNum = lastPacketNum - *ranges + 1; ranges++; // acks - len -= 2; - HandleAckRange (firstPacketNum, lastPacketNum, 0); - } - } - - void SSU2Session::HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts) - { - if (firstPacketNum > lastPacketNum) return; - auto it = m_SentPackets.begin (); - while (it != m_SentPackets.end () && it->first < firstPacketNum) it++; // find first acked packet - if (it == m_SentPackets.end () || it->first > lastPacketNum) return; // not found - auto it1 = it; - int numPackets = 0; - while (it1 != m_SentPackets.end () && it1->first <= lastPacketNum) - { - if (ts && !it1->second->numResends) - { - if (ts > it1->second->sendTime) - { - auto rtt = ts - it1->second->sendTime; - if (m_RTT != SSU2_UNKNOWN_RTT) - m_RTT = SSU2_RTT_EWMA_ALPHA * rtt + (1.0 - SSU2_RTT_EWMA_ALPHA) * m_RTT; - else - m_RTT = rtt; - m_RTO = m_RTT*SSU2_kAPPA; - m_MsgLocalExpirationTimeout = std::max (I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MIN, - std::min (I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MAX, - (unsigned int)(m_RTT * 1000 * I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_FACTOR))); - m_MsgLocalSemiExpirationTimeout = m_MsgLocalExpirationTimeout / 2; - if (m_RTO < SSU2_MIN_RTO) m_RTO = SSU2_MIN_RTO; - if (m_RTO > SSU2_MAX_RTO) m_RTO = SSU2_MAX_RTO; - } - ts = 0; // update RTT one time per range - } - it1++; - numPackets++; - } - m_SentPackets.erase (it, it1); - if (numPackets > 0) - { - m_WindowSize += numPackets; - if (m_WindowSize > SSU2_MAX_WINDOW_SIZE) m_WindowSize = SSU2_MAX_WINDOW_SIZE; - } - } - - void SSU2Session::HandleAddress (const uint8_t * buf, size_t len) - { - boost::asio::ip::udp::endpoint ep; - if (ExtractEndpoint (buf, len, ep)) - { - LogPrint (eLogInfo, "SSU2: Our external address is ", ep); - if (!i2p::transport::transports.IsInReservedRange (ep.address ())) - { - i2p::context.UpdateAddress (ep.address ()); - // check our port - bool isV4 = ep.address ().is_v4 (); - if (ep.port () != m_Server.GetPort (isV4)) - { - LogPrint (eLogInfo, "SSU2: Our port ", ep.port (), " received from ", m_RemoteEndpoint, " is different from ", m_Server.GetPort (isV4)); - if (isV4) - { - if (i2p::context.GetTesting ()) - i2p::context.SetError (eRouterErrorSymmetricNAT); - else if (m_State == eSSU2SessionStatePeerTest) - i2p::context.SetError (eRouterErrorFullConeNAT); - } - else - { - if (i2p::context.GetTestingV6 ()) - i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT); - else if (m_State == eSSU2SessionStatePeerTest) - i2p::context.SetErrorV6 (eRouterErrorFullConeNAT); - } - } - else - { - if (isV4) - { - if (i2p::context.GetError () == eRouterErrorSymmetricNAT) - { - if (m_State == eSSU2SessionStatePeerTest) - i2p::context.SetStatus (eRouterStatusOK); - i2p::context.SetError (eRouterErrorNone); - } - else if (i2p::context.GetError () == eRouterErrorFullConeNAT) - i2p::context.SetError (eRouterErrorNone); - } - else - { - if (i2p::context.GetErrorV6 () == eRouterErrorSymmetricNAT) - { - if (m_State == eSSU2SessionStatePeerTest) - i2p::context.SetStatusV6 (eRouterStatusOK); - i2p::context.SetErrorV6 (eRouterErrorNone); - } - else if (i2p::context.GetErrorV6 () == eRouterErrorFullConeNAT) - i2p::context.SetErrorV6 (eRouterErrorNone); - } - } - } - } - } - - void SSU2Session::HandleFirstFragment (const uint8_t * buf, size_t len) - { - auto msg = (buf[0] == eI2NPTunnelData) ? NewI2NPTunnelMessage (true) : NewI2NPShortMessage (); - uint32_t msgID; memcpy (&msgID, buf + 1, 4); - // same format as I2NP message block - msg->len = msg->offset + len + 7; - memcpy (msg->GetNTCP2Header (), buf, len); - std::shared_ptr m; - bool found = false; - auto it = m_IncompleteMessages.find (msgID); - if (it != m_IncompleteMessages.end ()) - { - found = true; - m = it->second; - } - else - { - m = m_Server.GetIncompleteMessagesPool ().AcquireShared (); - m_IncompleteMessages.emplace (msgID, m); - } - m->msg = msg; - m->nextFragmentNum = 1; - m->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); - if (found && m->ConcatOutOfSequenceFragments ()) - { - // we have all follow-on fragments already - m->msg->FromNTCP2 (); - HandleI2NPMsg (std::move (m->msg)); - m_IncompleteMessages.erase (it); - } - } - - void SSU2Session::HandleFollowOnFragment (const uint8_t * buf, size_t len) - { - if (len < 5) return; - uint8_t fragmentNum = buf[0] >> 1; - if (!fragmentNum || fragmentNum >= SSU2_MAX_NUM_FRAGMENTS) - { - LogPrint (eLogWarning, "SSU2: Invalid follow-on fragment num ", fragmentNum); - return; - } - bool isLast = buf[0] & 0x01; - uint32_t msgID; memcpy (&msgID, buf + 1, 4); - auto it = m_IncompleteMessages.find (msgID); - if (it != m_IncompleteMessages.end ()) - { - if (fragmentNum < it->second->nextFragmentNum) return; // duplicate - if (it->second->nextFragmentNum == fragmentNum && fragmentNum < SSU2_MAX_NUM_FRAGMENTS && - it->second->msg) - { - // in sequence - it->second->AttachNextFragment (buf + 5, len - 5); - if (isLast) - { - it->second->msg->FromNTCP2 (); - HandleI2NPMsg (std::move (it->second->msg)); - m_IncompleteMessages.erase (it); - } - else - { - if (it->second->ConcatOutOfSequenceFragments ()) - { - HandleI2NPMsg (std::move (it->second->msg)); - m_IncompleteMessages.erase (it); - } - else - it->second->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); - } - return; - } - } - else - { - // follow-on fragment before first fragment - auto msg = m_Server.GetIncompleteMessagesPool ().AcquireShared (); - msg->nextFragmentNum = 0; - it = m_IncompleteMessages.emplace (msgID, msg).first; - } - // insert out of sequence fragment - auto fragment = m_Server.GetFragmentsPool ().AcquireShared (); - memcpy (fragment->buf, buf + 5, len -5); - fragment->len = len - 5; - fragment->fragmentNum = fragmentNum; - fragment->isLast = isLast; - it->second->AddOutOfSequenceFragment (fragment); - } - - void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len) - { - // we are Bob - if (len < 9) return; - auto mts = i2p::util::GetMillisecondsSinceEpoch (); - uint32_t nonce = bufbe32toh (buf + 1); // nonce - uint32_t relayTag = bufbe32toh (buf + 5); // relay tag - auto session = m_Server.FindRelaySession (relayTag); - if (!session) - { - LogPrint (eLogWarning, "SSU2: RelayRequest session with relay tag ", relayTag, " not found"); - // send relay response back to Alice - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - packet->payloadSize = CreateAckBlock (packet->payload, m_MaxPayloadSize); - packet->payloadSize += CreateRelayResponseBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, - eSSU2RelayResponseCodeBobRelayTagNotFound, nonce, 0, false); - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = SendData (packet->payload, packet->payloadSize); - if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION) - { - // sometimes Alice doesn't ack this RelayResponse in older versions - packet->sendTime = mts; - m_SentPackets.emplace (packetNum, packet); - } - return; - } - if (session->m_RelaySessions.emplace (nonce, std::make_pair (shared_from_this (), mts/1000)).second) - { - // send relay intro to Charlie - auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI - if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr; - if (!r) LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found"); - - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0; - if (!packet->payloadSize && r) - session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); - packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len - 1); - if (packet->payloadSize < m_MaxPayloadSize) - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize); - packet->sendTime = mts; - // Charlie always responds with RelayResponse - session->m_SentPackets.emplace (packetNum, packet); - } - else - LogPrint (eLogInfo, "SSU2: Relay request nonce ", nonce, " already exists. Ignore"); - } - - void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts) - { - // we are Charlie - if (len < 47) return; - SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept; - boost::asio::ip::udp::endpoint ep; - std::shared_ptr addr; - auto r = i2p::data::netdb.FindRouter (buf + 1); // Alice - if (r) - { - SignedData<128> s; - s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (i2p::context.GetIdentHash (), 32); // chash - s.Insert (buf + 33, 14); // nonce, relay tag, timestamp, ver, asz - uint8_t asz = buf[46]; - if (asz + 47 + r->GetIdentity ()->GetSignatureLen () > len) - { - LogPrint (eLogWarning, "SSU2: Malformed RelayIntro len=", len); - return; - } - s.Insert (buf + 47, asz); // Alice Port, Alice IP - if (s.Verify (r->GetIdentity (), buf + 47 + asz)) - { - // obtain and check endpoint and address for HolePunch - if (ExtractEndpoint (buf + 47, asz, ep)) - { - if (!ep.address ().is_unspecified () && ep.port ()) - { - if (m_Server.IsSupported (ep.address ())) - { - addr = ep.address ().is_v6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); - if (!addr) - { - LogPrint (eLogWarning, "SSU2: RelayIntro address for endpoint not found"); - code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; - } - } - else - { - LogPrint (eLogWarning, "SSU2: RelayIntro unsupported address"); - code = eSSU2RelayResponseCodeCharlieUnsupportedAddress; - } - } - else - { - LogPrint (eLogWarning, "SSU2: RelayIntro invalid endpoint"); - code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; - } - } - else - { - LogPrint (eLogWarning, "SSU2: RelayIntro can't extract endpoint"); - code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; - } - } - else - { - LogPrint (eLogWarning, "SSU2: RelayIntro signature verification failed"); - code = eSSU2RelayResponseCodeCharlieSignatureFailure; - } - } - else if (!attempts) - { - // RouterInfo might come in the next packet, try again - auto vec = std::make_shared >(len); - memcpy (vec->data (), buf, len); - auto s = shared_from_this (); - boost::asio::post (m_Server.GetService (), [s, vec, attempts]() - { - LogPrint (eLogDebug, "SSU2: RelayIntro attempt ", attempts + 1); - s->HandleRelayIntro (vec->data (), vec->size (), attempts + 1); - }); - return; - } - else - { - LogPrint (eLogWarning, "SSU2: RelayIntro unknown router to introduce"); - code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; - } - // send relay response to Bob - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - uint32_t nonce = bufbe32toh (buf + 33); - packet->payloadSize = CreateRelayResponseBlock (packet->payload, m_MaxPayloadSize, - code, nonce, m_Server.GetIncomingToken (ep), ep.address ().is_v4 ()); - if (code == eSSU2RelayResponseCodeAccept && addr) - { - // send HolePunch - auto holePunchSession = std::make_shared(m_Server, nonce, ep, addr); - if (m_Server.AddSession (holePunchSession)) - holePunchSession->SendHolePunch (packet->payload, packet->payloadSize); // relay response block - else - { - LogPrint (eLogInfo, "SSU2: Relay intro nonce ", nonce, " already exists. Ignore"); - return; - } - } - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = SendData (packet->payload, packet->payloadSize); - if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION) - { - // sometimes Bob doesn't ack this RelayResponse in older versions - packet->sendTime = i2p::util::GetMillisecondsSinceEpoch (); - m_SentPackets.emplace (packetNum, packet); - } - } - - void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len) - { - if (len < 6) return; - uint32_t nonce = bufbe32toh (buf + 2); - if (m_State == eSSU2SessionStateIntroduced) - { - // HolePunch from Charlie - // TODO: verify address and signature - // verify nonce - if (~htobe64 (((uint64_t)nonce << 32) | nonce) != m_DestConnID) - LogPrint (eLogWarning, "SSU2: Relay response nonce mismatch ", nonce, " connID=", m_DestConnID); - if (len >= 8) - { - // new token - uint64_t token; - memcpy (&token, buf + len - 8, 8); - m_Server.UpdateOutgoingToken (m_RemoteEndpoint, token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); - } - return; - } - auto it = m_RelaySessions.find (nonce); - if (it != m_RelaySessions.end ()) - { - auto relaySession = it->second.first; - m_RelaySessions.erase (it); - if (relaySession && relaySession->IsEstablished ()) - { - // we are Bob, message from Charlie - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - uint8_t * payload = packet->payload; - payload[0] = eSSU2BlkRelayResponse; - htobe16buf (payload + 1, len); - memcpy (payload + 3, buf, len); // forward to Alice as is - packet->payloadSize = len + 3; - packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = relaySession->SendData (packet->payload, packet->payloadSize); - if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION) - { - // sometimes Alice doesn't ack this RelayResponse in older versions - packet->sendTime = i2p::util::GetMillisecondsSinceEpoch (); - relaySession->m_SentPackets.emplace (packetNum, packet); - } - } - else - { - // we are Alice, message from Bob - if (!buf[1]) // status code accepted? - { - // verify signature - uint8_t csz = (len >= 12) ? buf[11] : 0; - if (csz + 12 + relaySession->GetRemoteIdentity ()->GetSignatureLen () > len) - { - LogPrint (eLogWarning, "SSU2: Malformed RelayResponse len=", len); - relaySession->Done (); - return; - } - SignedData<128> s; - s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (buf + 2, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint - if (s.Verify (relaySession->GetRemoteIdentity (), buf + 12 + csz)) - { - if (relaySession->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet - { - // update Charlie's endpoint - if (ExtractEndpoint (buf + 12, csz, relaySession->m_RemoteEndpoint)) - { - // update token - uint64_t token; - memcpy (&token, buf + len - 8, 8); - m_Server.UpdateOutgoingToken (relaySession->m_RemoteEndpoint, - token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); - // connect to Charlie, HolePunch will be ignored - relaySession->ConnectAfterIntroduction (); - } - else - LogPrint (eLogWarning, "SSU2: RelayResponse can't extract endpoint"); - } - } - else - { - LogPrint (eLogWarning, "SSU2: RelayResponse signature verification failed"); - relaySession->Done (); - } - } - else - { - LogPrint (eLogInfo, "SSU2: RelayResponse status code=", (int)buf[1], " nonce=", bufbe32toh (buf + 2)); - relaySession->Done (); - } - } - } - else - LogPrint (eLogDebug, "SSU2: RelayResponse unknown nonce ", bufbe32toh (buf + 2)); - } - - void SSU2Session::HandlePeerTest (const uint8_t * buf, size_t len) - { - // msgs 1-4 - if (len < 3) return; - uint8_t msg = buf[0]; - size_t offset = 3; // points to signed data - if (msg == 2 || msg == 4) offset += 32; // hash is presented for msg 2 and 4 only - if (len < offset + 5) return; - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - uint32_t nonce = bufbe32toh (buf + offset + 1); - switch (msg) // msg - { - case 1: // Bob from Alice - { - auto session = m_Server.GetRandomPeerTestSession ((buf[12] == 6) ? i2p::data::RouterInfo::eSSU2V4 : i2p::data::RouterInfo::eSSU2V6, - GetRemoteIdentity ()->GetIdentHash ()); - if (session) // session with Charlie - { - if (m_Server.AddPeerTest (nonce, shared_from_this (), ts/1000)) - { - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - // Alice's RouterInfo - auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); - if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr; - packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0; - if (!packet->payloadSize && r) - session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); - if (packet->payloadSize + len + 48 > m_MaxPayloadSize) - { - // doesn't fit one message, send RouterInfo in separate message - uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED); - packet->sendTime = ts; - session->m_SentPackets.emplace (packetNum, packet); - packet = m_Server.GetSentPacketsPool ().AcquireShared (); // new packet - } - // PeerTest to Charlie - packet->payloadSize += CreatePeerTestBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, 2, - eSSU2PeerTestCodeAccept, GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset); - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED); - packet->sendTime = ts; - session->m_SentPackets.emplace (packetNum, packet); - } - else - LogPrint (eLogInfo, "SSU2: Peer test 1 nonce ", nonce, " already exists. Ignored"); - } - else - { - // Charlie not found, send error back to Alice - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - uint8_t zeroHash[32] = {0}; - packet->payloadSize = CreatePeerTestBlock (packet->payload, m_MaxPayloadSize, 4, - eSSU2PeerTestCodeBobNoCharlieAvailable, zeroHash, buf + offset, len - offset); - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - m_SentPackets.emplace (packetNum, packet); - } - break; - } - case 2: // Charlie from Bob - { - // sign with Charlie's key - if (len < offset + 9) return; - uint8_t asz = buf[offset + 9]; - size_t l = asz + 10 + i2p::context.GetIdentity ()->GetSignatureLen (); - if (len < offset + l) return; - std::vector newSignedData (l); - memcpy (newSignedData.data (), buf + offset, asz + 10); - SignedData<128> s; - s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (buf + 3, 32); // ahash - s.Insert (newSignedData.data (), asz + 10); // ver, nonce, ts, asz, Alice's endpoint - s.Sign (i2p::context.GetPrivateKeys (), newSignedData.data () + 10 + asz); - // send response (msg 3) back and msg 5 if accepted - SSU2PeerTestCode code = eSSU2PeerTestCodeAccept; - auto r = i2p::data::netdb.FindRouter (buf + 3); // find Alice - if (r) - { - size_t signatureLen = r->GetIdentity ()->GetSignatureLen (); - if (len >= offset + asz + 10 + signatureLen) - { - s.Reset (); - s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (buf + offset, asz + 10); // signed data - if (s.Verify (r->GetIdentity (), buf + offset + asz + 10)) - { - if (!m_Server.FindSession (r->GetIdentity ()->GetIdentHash ())) - { - boost::asio::ip::udp::endpoint ep; - std::shared_ptr addr; - if (ExtractEndpoint (buf + offset + 10, asz, ep) && !ep.address ().is_unspecified () && ep.port ()) - addr = r->GetSSU2Address (ep.address ().is_v4 ()); - if (addr && m_Server.IsSupported (ep.address ()) && - i2p::context.GetRouterInfo ().IsSSU2PeerTesting (ep.address ().is_v4 ())) - { - if (!m_Server.IsConnectedRecently (ep)) // no alive hole punch - { - // send msg 5 to Alice - auto session = std::make_shared (m_Server, - 0, htobe64 (((uint64_t)nonce << 32) | nonce)); - session->m_RemoteEndpoint = ep; // might be different - m_Server.AddSession (session); - session->SendPeerTest (5, newSignedData.data (), newSignedData.size (), addr); - } - else - code = eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected; - } - else - code = eSSU2PeerTestCodeCharlieUnsupportedAddress; - } - else - code = eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected; - } - else - code = eSSU2PeerTestCodeCharlieSignatureFailure; - } - else // maformed message - code = eSSU2PeerTestCodeCharlieReasonUnspecified; - } - else - code = eSSU2PeerTestCodeCharlieAliceIsUnknown; - // send msg 3 back to Bob - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - packet->payloadSize = CreatePeerTestBlock (packet->payload, m_MaxPayloadSize, 3, - code, nullptr, newSignedData.data (), newSignedData.size ()); - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - m_SentPackets.emplace (packetNum, packet); - break; - } - case 3: // Bob from Charlie - { - auto aliceSession = m_Server.GetPeerTest (nonce); - if (aliceSession && aliceSession->IsEstablished ()) - { - auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); - // Charlie's RouterInfo - auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); - if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr; - packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0; - if (!packet->payloadSize && r) - aliceSession->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); - if (packet->payloadSize + len + 16 > m_MaxPayloadSize) - { - // doesn't fit one message, send RouterInfo in separate message - uint32_t packetNum = aliceSession->SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - aliceSession->m_SentPackets.emplace (packetNum, packet); - packet = m_Server.GetSentPacketsPool ().AcquireShared (); - } - // PeerTest to Alice - packet->payloadSize += CreatePeerTestBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize, 4, - (SSU2PeerTestCode)buf[1], GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset); - if (packet->payloadSize < m_MaxPayloadSize) - packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); - uint32_t packetNum = aliceSession->SendData (packet->payload, packet->payloadSize); - packet->sendTime = ts; - aliceSession->m_SentPackets.emplace (packetNum, packet); - } - else - LogPrint (eLogDebug, "SSU2: Unknown peer test 3 nonce ", nonce); - break; - } - case 4: // Alice from Bob - { - auto session = m_Server.GetRequestedPeerTest (nonce); - if (session) - { - if (buf[1] == eSSU2PeerTestCodeAccept) - { - if (GetRouterStatus () == eRouterStatusUnknown) - SetTestingState (true); - auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie - if (r && len >= offset + 9) - { - uint8_t asz = buf[offset + 9]; - if (len < offset + asz + 10 + r->GetIdentity ()->GetSignatureLen ()) - { - LogPrint (eLogWarning, "Malformed PeerTest 4 len=", len); - session->Done (); - return; - } - SignedData<128> s; - s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (i2p::context.GetIdentity ()->GetIdentHash (), 32); // ahash - s.Insert (buf + offset, asz + 10); // ver, nonce, ts, asz, Alice's endpoint - if (s.Verify (r->GetIdentity (), buf + offset + asz + 10)) - { - session->SetRemoteIdentity (r->GetIdentity ()); - auto addr = r->GetSSU2Address (m_Address->IsV4 ()); - if (addr && addr->IsPeerTesting ()) - { - if (session->GetMsgNumReceived () >= 5) - { - // msg 5 already received and we know remote endpoint - if (session->GetMsgNumReceived () == 5) - { - if (!session->IsConnectedRecently ()) - SetRouterStatus (eRouterStatusOK); - // send msg 6 immeditely - session->SendPeerTest (6, buf + offset, len - offset, addr); - } - else - LogPrint (eLogWarning, "SSU2: PeerTest 4 received, but msg ", session->GetMsgNumReceived (), " already received"); - } - else - { - session->m_Address = addr; - if (GetTestingState ()) - { - // schedule msg 6 with delay - if (!addr->host.is_unspecified () && addr->port) - { - session->SetRemoteEndpoint (boost::asio::ip::udp::endpoint (addr->host, addr->port)); - session->SendPeerTest (6, buf + offset, len - offset, addr, true); - } - SetTestingState (false); - if (GetRouterStatus () != eRouterStatusFirewalled && addr->IsPeerTesting ()) - { - SetRouterStatus (eRouterStatusFirewalled); - session->SetStatusChanged (); - if (m_Address->IsV4 ()) - m_Server.RescheduleIntroducersUpdateTimer (); - else - m_Server.RescheduleIntroducersUpdateTimerV6 (); - } - } - } - LogPrint (eLogDebug, "SSU2: Peer test 4 received from ", i2p::data::GetIdentHashAbbreviation (GetRemoteIdentity ()->GetIdentHash ()), - " with information about ", i2p::data::GetIdentHashAbbreviation (i2p::data::IdentHash (buf + 3))); - } - else - { - LogPrint (eLogWarning, "SSU2: Peer test 4 address not found or not supported"); - session->Done (); - } - } - else - { - LogPrint (eLogWarning, "SSU2: Peer test 4 signature verification failed"); - session->Done (); - } - } - else - { - LogPrint (eLogWarning, "SSU2: Peer test 4 router not found"); - session->Done (); - } - } - else - { - LogPrint (eLogInfo, "SSU2: Peer test 4 error code ", (int)buf[1], " from ", - i2p::data::GetIdentHashAbbreviation (buf[1] < 64 ? GetRemoteIdentity ()->GetIdentHash () : i2p::data::IdentHash (buf + 3))); - if (GetTestingState () && GetRouterStatus () != eRouterStatusFirewalled) - SetRouterStatus (eRouterStatusUnknown); - session->Done (); - } - } - else - LogPrint (eLogDebug, "SSU2: Unknown peer test 4 nonce ", nonce); - break; - } - default: - LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", buf[0]); - } - } - - void SSU2Session::HandleI2NPMsg (std::shared_ptr&& msg) - { - if (!msg) return; - uint32_t msgID = msg->GetMsgID (); - if (!msg->IsExpired ()) - { - // m_LastActivityTimestamp is updated in ProcessData before - if (m_ReceivedI2NPMsgIDs.emplace (msgID, (uint32_t)GetLastActivityTimestamp ()).second) - m_Handler.PutNextMessage (std::move (msg)); - else - LogPrint (eLogDebug, "SSU2: Message ", msgID, " already received"); - } - else - LogPrint (eLogDebug, "SSU2: Message ", msgID, " expired"); - } - - bool SSU2Session::ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep) - { - if (size < 2) return false; - int port = bufbe16toh (buf); - if (size == 6) - { - boost::asio::ip::address_v4::bytes_type bytes; - memcpy (bytes.data (), buf + 2, 4); - ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v4 (bytes), port); - } - else if (size == 18) - { - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), buf + 2, 16); - ep = boost::asio::ip::udp::endpoint (boost::asio::ip::address_v6 (bytes), port); - } - else - { - LogPrint (eLogWarning, "SSU2: Address size ", int(size), " is not supported"); - return false; - } - return true; - } - - size_t SSU2Session::CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep) - { - if (len < 6) return 0; - htobe16buf (buf, ep.port ()); - size_t size = 0; - if (ep.address ().is_v4 ()) - { - memcpy (buf + 2, ep.address ().to_v4 ().to_bytes ().data (), 4); - size = 6; - } - else if (ep.address ().is_v6 ()) - { - if (len < 18) return 0; - memcpy (buf + 2, ep.address ().to_v6 ().to_bytes ().data (), 16); - size = 18; - } - else - { - LogPrint (eLogWarning, "SSU2: Wrong address type ", ep.address ().to_string ()); - return 0; - } - return size; - } - - std::shared_ptr SSU2Session::FindLocalAddress () const - { - if (m_Address) - return i2p::context.GetRouterInfo ().GetSSU2Address (m_Address->IsV4 ()); - else if (!m_RemoteEndpoint.address ().is_unspecified ()) - return i2p::context.GetRouterInfo ().GetSSU2Address (m_RemoteEndpoint.address ().is_v4 ()); - return nullptr; - } - - void SSU2Session::AdjustMaxPayloadSize (size_t maxMtu) - { - auto addr = FindLocalAddress (); - if (addr && addr->ssu) - { - int mtu = addr->ssu->mtu; - if (!mtu && addr->IsV4 ()) mtu = SSU2_MAX_PACKET_SIZE; - if (mtu > (int)maxMtu) mtu = maxMtu; - if (m_Address && m_Address->ssu && (!mtu || m_Address->ssu->mtu < mtu)) - mtu = m_Address->ssu->mtu; - if (mtu) - { - if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE; - if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; - m_MaxPayloadSize = mtu - (addr->IsV6 () ? IPV6_HEADER_SIZE: IPV4_HEADER_SIZE) - UDP_HEADER_SIZE - 32; - LogPrint (eLogDebug, "SSU2: Session MTU=", mtu, ", max payload size=", m_MaxPayloadSize); - } - } - } - - RouterStatus SSU2Session::GetRouterStatus () const - { - if (m_Address) - { - if (m_Address->IsV4 ()) - return i2p::context.GetStatus (); - if (m_Address->IsV6 ()) - return i2p::context.GetStatusV6 (); - } - return eRouterStatusUnknown; - } - - void SSU2Session::SetRouterStatus (RouterStatus status) const - { - if (m_Address) - { - if (m_Address->IsV4 ()) - i2p::context.SetStatus (status); - else if (m_Address->IsV6 ()) - i2p::context.SetStatusV6 (status); - } - } - - bool SSU2Session::GetTestingState () const - { - if (m_Address) - { - if (m_Address->IsV4 ()) - return i2p::context.GetTesting (); - if (m_Address->IsV6 ()) - return i2p::context.GetTestingV6 (); - } - return false; - } - - void SSU2Session::SetTestingState (bool testing) const - { - if (m_Address) - { - if (m_Address->IsV4 ()) - i2p::context.SetTesting (testing); - else if (m_Address->IsV6 ()) - i2p::context.SetTestingV6 (testing); - } - if (!testing) - m_Server.AdjustTimeOffset (0, nullptr); // reset time offset when testing is over - } - - size_t SSU2Session::CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep) - { - if (len < 9) return 0; - buf[0] = eSSU2BlkAddress; - size_t size = CreateEndpoint (buf + 3, len - 3, ep); - if (!size) return 0; - htobe16buf (buf + 1, size); - return size + 3; - } - - size_t SSU2Session::CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr r) - { - if (!r || len < 5) return 0; - return CreateRouterInfoBlock (buf, len, r->GetSharedBuffer ()); - } - - size_t SSU2Session::CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr riBuffer) - { - if (!riBuffer || len < 5) return 0; - buf[0] = eSSU2BlkRouterInfo; - size_t size = riBuffer->GetBufferLen (); - if (size + 5 < len) - { - memcpy (buf + 5, riBuffer->data (), size); - buf[3] = 0; // flag - } - else - { - i2p::data::GzipDeflator deflator; - deflator.SetCompressionLevel (9); - size = deflator.Deflate (riBuffer->data (), riBuffer->GetBufferLen (), buf + 5, len - 5); - if (!size) return 0; // doesn't fit - buf[3] = SSU2_ROUTER_INFO_FLAG_GZIP; // flag - } - htobe16buf (buf + 1, size + 2); // size - buf[4] = 1; // frag - return size + 5; - } - - - size_t SSU2Session::CreateAckBlock (uint8_t * buf, size_t len) - { - if (len < 8) return 0; - buf[0] = eSSU2BlkAck; - uint32_t ackThrough = m_OutOfSequencePackets.empty () ? m_ReceivePacketNum : *m_OutOfSequencePackets.rbegin (); - htobe32buf (buf + 3, ackThrough); // Ack Through - uint16_t acnt = 0; - if (ackThrough) - { - if (m_OutOfSequencePackets.empty ()) - { - acnt = std::min ((int)ackThrough, SSU2_MAX_NUM_ACNT); // no gaps - m_NumRanges = 0; - } - else - { - auto it = m_OutOfSequencePackets.rbegin (); it++; // prev packet num - while (it != m_OutOfSequencePackets.rend () && *it == ackThrough - acnt - 1) - { - acnt++; - if (acnt >= SSU2_MAX_NUM_ACK_PACKETS) - break; - else - it++; - } - // ranges - if (!m_NumRanges) - { - int maxNumRanges = (len - 8) >> 1; - if (maxNumRanges > SSU2_MAX_NUM_ACK_RANGES) maxNumRanges = SSU2_MAX_NUM_ACK_RANGES; - int numRanges = 0; - uint32_t lastNum = ackThrough - acnt; - if (acnt > SSU2_MAX_NUM_ACNT) - { - auto d = std::div (acnt - SSU2_MAX_NUM_ACNT, SSU2_MAX_NUM_ACNT); - acnt = SSU2_MAX_NUM_ACNT; - if (d.quot > maxNumRanges) - { - d.quot = maxNumRanges; - d.rem = 0; - } - // Acks only ranges for acnt - for (int i = 0; i < d.quot; i++) - { - m_Ranges[numRanges*2] = 0; m_Ranges[numRanges*2 + 1] = SSU2_MAX_NUM_ACNT; // NACKs 0, Acks 255 - numRanges++; - } - if (d.rem > 0) - { - m_Ranges[numRanges*2] = 0; m_Ranges[numRanges*2 + 1] = d.rem; - numRanges++; - } - } - int numPackets = acnt + numRanges*SSU2_MAX_NUM_ACNT; - while (it != m_OutOfSequencePackets.rend () && - numRanges < maxNumRanges && numPackets < SSU2_MAX_NUM_ACK_PACKETS) - { - if (lastNum - (*it) > SSU2_MAX_NUM_ACNT) - { - // NACKs only ranges - if (lastNum > (*it) + SSU2_MAX_NUM_ACNT*(maxNumRanges - numRanges)) break; // too many NACKs - while (lastNum - (*it) > SSU2_MAX_NUM_ACNT) - { - m_Ranges[numRanges*2] = SSU2_MAX_NUM_ACNT; m_Ranges[numRanges*2 + 1] = 0; // NACKs 255, Acks 0 - lastNum -= SSU2_MAX_NUM_ACNT; - numRanges++; - numPackets += SSU2_MAX_NUM_ACNT; - } - } - // NACKs and Acks ranges - m_Ranges[numRanges*2] = lastNum - (*it) - 1; // NACKs - numPackets += m_Ranges[numRanges*2]; - lastNum = *it; it++; - int numAcks = 1; - while (it != m_OutOfSequencePackets.rend () && lastNum > 0 && *it == lastNum - 1) - { - numAcks++; lastNum--; - it++; - } - while (numAcks > SSU2_MAX_NUM_ACNT) - { - // Acks only ranges - m_Ranges[numRanges*2 + 1] = SSU2_MAX_NUM_ACNT; // Acks 255 - numAcks -= SSU2_MAX_NUM_ACNT; - numRanges++; - numPackets += SSU2_MAX_NUM_ACNT; - m_Ranges[numRanges*2] = 0; // NACKs 0 - if (numRanges >= maxNumRanges || numPackets >= SSU2_MAX_NUM_ACK_PACKETS) break; - } - if (numAcks > SSU2_MAX_NUM_ACNT) numAcks = SSU2_MAX_NUM_ACNT; - m_Ranges[numRanges*2 + 1] = (uint8_t)numAcks; // Acks - numPackets += numAcks; - numRanges++; - } - if (it == m_OutOfSequencePackets.rend () && - numRanges < maxNumRanges && numPackets < SSU2_MAX_NUM_ACK_PACKETS) - { - // add range between out-of-sequence and received - int nacks = *m_OutOfSequencePackets.begin () - m_ReceivePacketNum - 1; - if (nacks > 0) - { - if (nacks > SSU2_MAX_NUM_ACNT) nacks = SSU2_MAX_NUM_ACNT; - m_Ranges[numRanges*2] = nacks; - m_Ranges[numRanges*2 + 1] = std::min ((int)m_ReceivePacketNum + 1, SSU2_MAX_NUM_ACNT); - numRanges++; - } - } - m_NumRanges = numRanges; - } - if (m_NumRanges) - memcpy (buf + 8, m_Ranges, m_NumRanges*2); - } - } - buf[7] = (uint8_t)acnt; // acnt - htobe16buf (buf + 1, 5 + m_NumRanges*2); - return 8 + m_NumRanges*2; - } - - size_t SSU2Session::CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize) - { - if (len < 3 || len < minSize) return 0; - size_t paddingSize = m_Server.GetRng ()() & 0x1F; // 0 - 31 - if (paddingSize + 3 > len) paddingSize = len - 3; - else if (paddingSize + 3 < minSize) paddingSize = minSize - 3; - buf[0] = eSSU2BlkPadding; - htobe16buf (buf + 1, paddingSize); - memset (buf + 3, 0, paddingSize); - return paddingSize + 3; - } - - size_t SSU2Session::CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr&& msg) - { - msg->ToNTCP2 (); - auto msgBuf = msg->GetNTCP2Header (); - auto msgLen = msg->GetNTCP2Length (); - if (msgLen + 3 > len) msgLen = len - 3; - buf[0] = eSSU2BlkI2NPMessage; - htobe16buf (buf + 1, msgLen); // size - memcpy (buf + 3, msgBuf, msgLen); - return msgLen + 3; - } - - size_t SSU2Session::CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg) - { - if (len < 12) return 0; - msg->ToNTCP2 (); - auto msgBuf = msg->GetNTCP2Header (); - auto msgLen = msg->GetNTCP2Length (); - if (msgLen + 3 <= len) return 0; - msgLen = len - 3; - buf[0] = eSSU2BlkFirstFragment; - htobe16buf (buf + 1, msgLen); // size - memcpy (buf + 3, msgBuf, msgLen); - msg->offset = (msgBuf - msg->buf) + msgLen; - return msgLen + 3; - } - - size_t SSU2Session::CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg, uint8_t& fragmentNum, uint32_t msgID) - { - if (len < 8) return 0; - bool isLast = true; - auto msgLen = msg->len - msg->offset; - if (msgLen + 8 > len) - { - msgLen = len - 8; - isLast = false; - } - buf[0] = eSSU2BlkFollowOnFragment; - htobe16buf (buf + 1, msgLen + 5); // size - fragmentNum++; - buf[3] = fragmentNum << 1; - if (isLast) buf[3] |= 0x01; - memcpy (buf + 4, &msgID, 4); - memcpy (buf + 8, msg->buf + msg->offset, msgLen); - msg->offset += msgLen; - return msgLen + 8; - } - - size_t SSU2Session::CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen) - { - buf[0] = eSSU2BlkRelayIntro; - size_t payloadSize = 1/* flag */ + 32/* Alice router hash */ + introDataLen; - if (payloadSize + 3 > len) return 0; - htobe16buf (buf + 1, payloadSize); // size - buf[3] = 0; // flag - memcpy (buf + 4, GetRemoteIdentity ()->GetIdentHash (), 32); // Alice router hash - memcpy (buf + 36, introData, introDataLen); - return payloadSize + 3; - } - - size_t SSU2Session::CreateRelayResponseBlock (uint8_t * buf, size_t len, - SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4) - { - buf[0] = eSSU2BlkRelayResponse; - buf[3] = 0; // flag - buf[4] = code; // code - htobe32buf (buf + 5, nonce); // nonce - htobe32buf (buf + 9, i2p::util::GetSecondsSinceEpoch ()); // timestamp - buf[13] = 2; // ver - size_t csz = 0; - if (code == eSSU2RelayResponseCodeAccept) - { - auto addr = i2p::context.GetRouterInfo ().GetSSU2Address (v4); - if (!addr) - { - LogPrint (eLogError, "SSU2: Can't find local address for RelayResponse"); - return 0; - } - csz = CreateEndpoint (buf + 15, len - 15, boost::asio::ip::udp::endpoint (addr->host, addr->port)); - if (!csz) - { - LogPrint (eLogError, "SSU2: Can't create local endpoint for RelayResponse"); - return 0; - } - } - buf[14] = csz; // csz - // signature - size_t signatureLen = i2p::context.GetIdentity ()->GetSignatureLen (); - if (15 + csz + signatureLen > len) - { - LogPrint (eLogError, "SSU2: Buffer for RelayResponse signature is too small ", len); - return 0; - } - SignedData<128> s; - s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue - if (code == eSSU2RelayResponseCodeAccept || code >= 64) // Charlie - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - else // Bob's reject - s.Insert (i2p::context.GetIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (buf + 5, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint - s.Sign (i2p::context.GetPrivateKeys (), buf + 15 + csz); - size_t payloadSize = 12 + csz + signatureLen; - if (!code) - { - if (payloadSize + 11 > len) - { - LogPrint (eLogError, "SSU2: Buffer for RelayResponse token is too small ", len); - return 0; - } - memcpy (buf + 3 + payloadSize, &token, 8); - payloadSize += 8; - } - htobe16buf (buf + 1, payloadSize); // size - return payloadSize + 3; - } - - size_t SSU2Session::CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, - const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen) - { - buf[0] = eSSU2BlkPeerTest; - size_t payloadSize = 3/* msg, code, flag */ + signedDataLen; - if (routerHash) payloadSize += 32; // router hash - if (payloadSize + 3 > len) return 0; - htobe16buf (buf + 1, payloadSize); // size - buf[3] = msg; // msg - buf[4] = (uint8_t)code; // code - buf[5] = 0; //flag - size_t offset = 6; - if (routerHash) - { - memcpy (buf + offset, routerHash, 32); // router hash - offset += 32; - } - memcpy (buf + offset, signedData, signedDataLen); - return payloadSize + 3; - } - - size_t SSU2Session::CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce) - { - auto localAddress = FindLocalAddress (); - if (!localAddress || !localAddress->port || localAddress->host.is_unspecified () || - localAddress->host.is_v4 () != m_RemoteEndpoint.address ().is_v4 ()) - { - LogPrint (eLogWarning, "SSU2: Can't find local address for peer test"); - return 0; - } - // signed data - auto ts = i2p::util::GetSecondsSinceEpoch (); - uint8_t signedData[96]; - signedData[0] = 2; // ver - htobe32buf (signedData + 1, nonce); - htobe32buf (signedData + 5, ts); - size_t asz = CreateEndpoint (signedData + 10, 86, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port)); - signedData[9] = asz; - // signature - SignedData<128> s; - s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue - s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash - s.Insert (signedData, 10 + asz); // ver, nonce, ts, asz, Alice's endpoint - s.Sign (i2p::context.GetPrivateKeys (), signedData + 10 + asz); - return CreatePeerTestBlock (buf, len, 1, eSSU2PeerTestCodeAccept, nullptr, - signedData, 10 + asz + i2p::context.GetIdentity ()->GetSignatureLen ()); - } - - size_t SSU2Session::CreateTerminationBlock (uint8_t * buf, size_t len) - { - buf[0] = eSSU2BlkTermination; - htobe16buf (buf + 1, 9); - htobe64buf (buf + 3, m_ReceivePacketNum); - buf[11] = (uint8_t)m_TerminationReason; - return 12; - } - - std::shared_ptr SSU2Session::ExtractRouterInfo (const uint8_t * buf, size_t size) - { - if (size < 2) return nullptr; - // TODO: handle frag - std::shared_ptr ri; - if (buf[0] & SSU2_ROUTER_INFO_FLAG_GZIP) - { - i2p::data::GzipInflator inflator; - uint8_t uncompressed[i2p::data::MAX_RI_BUFFER_SIZE]; - size_t uncompressedSize = inflator.Inflate (buf + 2, size - 2, uncompressed, i2p::data::MAX_RI_BUFFER_SIZE); - if (uncompressedSize && uncompressedSize <= i2p::data::MAX_RI_BUFFER_SIZE) - ri = std::make_shared(uncompressed, uncompressedSize); - else - LogPrint (eLogInfo, "SSU2: RouterInfo decompression failed ", uncompressedSize); - } - else if (size <= i2p::data::MAX_RI_BUFFER_SIZE + 2) - ri = std::make_shared(buf + 2, size - 2); - else - LogPrint (eLogInfo, "SSU2: RouterInfo is too long ", size); - return ri; - } - - bool SSU2Session::UpdateReceivePacketNum (uint32_t packetNum) - { - if (packetNum <= m_ReceivePacketNum) return false; // duplicate - if (packetNum == m_ReceivePacketNum + 1) - { - if (!m_OutOfSequencePackets.empty ()) - { - auto it = m_OutOfSequencePackets.begin (); - if (*it == packetNum + 1) - { - // first out of sequence packet is in sequence now - packetNum++; it++; - while (it != m_OutOfSequencePackets.end ()) - { - if (*it == packetNum + 1) - { - packetNum++; - it++; - } - else // next out of sequence - break; - } - m_OutOfSequencePackets.erase (m_OutOfSequencePackets.begin (), it); - } - m_NumRanges = 0; // recalculate ranges when create next Ack - } - m_ReceivePacketNum = packetNum; - } - else - { - if (m_NumRanges && (m_OutOfSequencePackets.empty () || - packetNum != (*m_OutOfSequencePackets.rbegin ()) + 1)) - m_NumRanges = 0; // reset ranges if received packet is not next - m_OutOfSequencePackets.insert (packetNum); - } - return true; - } - - void SSU2Session::SendQuickAck () - { - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = 0; - if (m_SendPacketNum > m_LastDatetimeSentPacketNum + SSU2_SEND_DATETIME_NUM_PACKETS) - { - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - payloadSize += 7; - m_LastDatetimeSentPacketNum = m_SendPacketNum; - } - payloadSize += CreateAckBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - SendData (payload, payloadSize); - } - - void SSU2Session::SendTermination () - { - uint8_t payload[32]; - size_t payloadSize = CreateTerminationBlock (payload, 32); - payloadSize += CreatePaddingBlock (payload + payloadSize, 32 - payloadSize); - SendData (payload, payloadSize); - } - - void SSU2Session::SendPathResponse (const uint8_t * data, size_t len) - { - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = 0; - // datetime block - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - payloadSize += 7; - // address block - payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, m_RemoteEndpoint); - // path response - if (payloadSize + len > m_MaxPayloadSize) - { - LogPrint (eLogWarning, "SSU2: Incorrect data size for path response ", len); - return; - } - payload[payloadSize] = eSSU2BlkPathResponse; - htobe16buf (payload + payloadSize + 1, len); - memcpy (payload + payloadSize + 3, data, len); - payloadSize += len + 3; - // ack block - if (payloadSize < m_MaxPayloadSize) - payloadSize += CreateAckBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - // padding - if (payloadSize < m_MaxPayloadSize) - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - SendData (payload, payloadSize); - } - - void SSU2Session::SendPathChallenge (const boost::asio::ip::udp::endpoint& to) - { - AdjustMaxPayloadSize (SSU2_MIN_PACKET_SIZE); // reduce to minimum - m_WindowSize = SSU2_MIN_WINDOW_SIZE; // reduce window to minimum - - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = 0; - // datetime block - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - payloadSize += 7; - // address block with new address - payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, to); - // path challenge block - payload[payloadSize] = eSSU2BlkPathChallenge; - uint64_t challenge; - RAND_bytes ((uint8_t *)&challenge, 8); - htobe16buf (payload + payloadSize + 1, 8); // always 8 bytes - htobuf64 (payload + payloadSize + 3, challenge); - payloadSize += 11; - m_PathChallenge = std::make_unique >(challenge, to); - // ack block - if (payloadSize < m_MaxPayloadSize) - payloadSize += CreateAckBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - // padding block - if (payloadSize < m_MaxPayloadSize) - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - // send to new endpoint - auto existing = m_RemoteEndpoint; - m_RemoteEndpoint = to; // send path challenge to new endpoint - SendData (payload, payloadSize); - m_RemoteEndpoint = existing; // restore endpoint back until path response received - } - - void SSU2Session::CleanUp (uint64_t ts) - { - for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) - { - if (ts > it->second->lastFragmentInsertTime + SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT) - { - LogPrint (eLogWarning, "SSU2: message ", it->first, " was not completed in ", SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds, deleted"); - it = m_IncompleteMessages.erase (it); - } - else - ++it; - } - if (m_ReceivedI2NPMsgIDs.size () > SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS || ts > GetLastActivityTimestamp () + SSU2_DECAY_INTERVAL) - // decay - m_ReceivedI2NPMsgIDs.clear (); - else - { - // delete old received msgIDs - for (auto it = m_ReceivedI2NPMsgIDs.begin (); it != m_ReceivedI2NPMsgIDs.end ();) - { - if (ts > it->second + SSU2_RECEIVED_I2NP_MSGIDS_CLEANUP_TIMEOUT) - it = m_ReceivedI2NPMsgIDs.erase (it); - else - ++it; - } - } - if (!m_OutOfSequencePackets.empty ()) - { - int ranges = 0; - while (ranges < 8 && !m_OutOfSequencePackets.empty () && - (m_OutOfSequencePackets.size () > 2*SSU2_MAX_NUM_ACK_RANGES || - *m_OutOfSequencePackets.rbegin () > m_ReceivePacketNum + SSU2_MAX_NUM_ACK_PACKETS)) - { - uint32_t packet = *m_OutOfSequencePackets.begin (); - if (packet > m_ReceivePacketNum + 1) - { - // like we've just received all packets before first - packet--; - m_ReceivePacketNum = packet - 1; - UpdateReceivePacketNum (packet); - ranges++; - } - else - { - LogPrint (eLogError, "SSU2: Out of sequence packet ", packet, " is less than last received ", m_ReceivePacketNum); - break; - } - } - if (m_OutOfSequencePackets.size () > 255*4) - { - // seems we have a serious network issue - m_ReceivePacketNum = *m_OutOfSequencePackets.rbegin (); - m_OutOfSequencePackets.clear (); - } - } - - for (auto it = m_RelaySessions.begin (); it != m_RelaySessions.end ();) - { - if (ts > it->second.second + SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT) - { - LogPrint (eLogInfo, "SSU2: Relay nonce ", it->first, " was not responded in ", SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT, " seconds, deleted"); - it = m_RelaySessions.erase (it); - } - else - ++it; - } - if (m_PathChallenge) - RequestTermination (eSSU2TerminationReasonNormalClose); - } - - void SSU2Session::FlushData () - { - bool sent = SendQueue (); // if we have something to send - if (sent) - SetSendQueueSize (m_SendQueue.size ()); - if (m_IsDataReceived) - { - if (!sent) SendQuickAck (); - m_Handler.Flush (); - m_IsDataReceived = false; - } - else if (!sent && !m_SentPackets.empty ()) // if only acks received, nothing sent and we still have something to resend - Resend (i2p::util::GetMillisecondsSinceEpoch ()); // than right time to resend - } - - i2p::data::RouterInfo::SupportedTransports SSU2Session::GetTransportType () const - { - return m_RemoteEndpoint.address ().is_v4 () ? i2p::data::RouterInfo::eSSU2V4 : i2p::data::RouterInfo::eSSU2V6; - } -} -} diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h deleted file mode 100644 index ee26255f..00000000 --- a/libi2pd/SSU2Session.h +++ /dev/null @@ -1,419 +0,0 @@ -/* -* Copyright (c) 2022-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef SSU2_SESSION_H__ -#define SSU2_SESSION_H__ - -#include -#include -#include -#include -#include -#include -#include "version.h" -#include "Crypto.h" -#include "RouterInfo.h" -#include "RouterContext.h" -#include "TransportSession.h" - -namespace i2p -{ -namespace transport -{ - const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds - const int SSU2_TERMINATION_TIMEOUT = 165; // in seconds - const int SSU2_CLOCK_SKEW = 60; // in seconds - const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust - const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds - const int SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52*60; // for next token block, in seconds - const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds - const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds - const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds - const size_t SSU2_MAX_PACKET_SIZE = 1500; - const size_t SSU2_MIN_PACKET_SIZE = 1280; - const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in milliseconds - const int SSU2_MAX_NUM_RESENDS = 5; - const int SSU2_RESEND_ATTEMPT_MIN_INTERVAL = 3; // in milliseconds - const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds - const int SSU2_MAX_NUM_RECEIVED_I2NP_MSGIDS = 5000; // how many msgID we store for duplicates check - const int SSU2_RECEIVED_I2NP_MSGIDS_CLEANUP_TIMEOUT = 10; // in seconds - const int SSU2_DECAY_INTERVAL = 20; // in seconds - const size_t SSU2_MIN_WINDOW_SIZE = 16; // in packets - const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets - const size_t SSU2_MIN_RTO = 100; // in milliseconds - const size_t SSU2_INITIAL_RTO = 540; // in milliseconds - const size_t SSU2_MAX_RTO = 2500; // in milliseconds - const double SSU2_UNKNOWN_RTT = -1; - const double SSU2_RTT_EWMA_ALPHA = 0.125; - const float SSU2_kAPPA = 1.8; - const int SSU2_MAX_NUM_ACNT = 255; // acnt, acks or nacks - const int SSU2_MAX_NUM_ACK_PACKETS = 511; // ackthrough + acnt + 1 range - const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send - const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64; - const int SSU2_SEND_DATETIME_NUM_PACKETS = 256; - const int SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION = MAKE_VERSION_NUMBER(0, 9, 64); // 0.9.64 - - // flags - const uint8_t SSU2_FLAG_IMMEDIATE_ACK_REQUESTED = 0x01; - - enum SSU2MessageType - { - eSSU2SessionRequest = 0, - eSSU2SessionCreated = 1, - eSSU2SessionConfirmed = 2, - eSSU2Data = 6, - eSSU2PeerTest = 7, - eSSU2Retry = 9, - eSSU2TokenRequest = 10, - eSSU2HolePunch = 11 - }; - - enum SSU2BlockType - { - eSSU2BlkDateTime = 0, - eSSU2BlkOptions, // 1 - eSSU2BlkRouterInfo, // 2 - eSSU2BlkI2NPMessage, // 3 - eSSU2BlkFirstFragment, // 4 - eSSU2BlkFollowOnFragment, // 5 - eSSU2BlkTermination, // 6 - eSSU2BlkRelayRequest, // 7 - eSSU2BlkRelayResponse, // 8 - eSSU2BlkRelayIntro, // 9 - eSSU2BlkPeerTest, // 10 - eSSU2BlkNextNonce, // 11 - eSSU2BlkAck, // 12 - eSSU2BlkAddress, // 13 - eSSU2BlkIntroKey, // 14 - eSSU2BlkRelayTagRequest, // 15 - eSSU2BlkRelayTag, // 16 - eSSU2BlkNewToken, // 17 - eSSU2BlkPathChallenge, // 18 - eSSU2BlkPathResponse, // 19 - eSSU2BlkFirstPacketNumber, // 20 - eSSU2BlkPadding = 254 - }; - - enum SSU2SessionState - { - eSSU2SessionStateUnknown, - eSSU2SessionStateTokenReceived, - eSSU2SessionStateSessionRequestSent, - eSSU2SessionStateSessionRequestReceived, - eSSU2SessionStateSessionCreatedSent, - eSSU2SessionStateSessionCreatedReceived, - eSSU2SessionStateSessionConfirmedSent, - eSSU2SessionStateEstablished, - eSSU2SessionStateClosing, - eSSU2SessionStateClosingConfirmed, - eSSU2SessionStateTerminated, - eSSU2SessionStateFailed, - eSSU2SessionStateIntroduced, - eSSU2SessionStateHolePunch, - eSSU2SessionStatePeerTest, - eSSU2SessionStateTokenRequestReceived - }; - - enum SSU2PeerTestCode - { - eSSU2PeerTestCodeAccept = 0, - eSSU2PeerTestCodeBobReasonUnspecified = 1, - eSSU2PeerTestCodeBobNoCharlieAvailable = 2, - eSSU2PeerTestCodeBobLimitExceeded = 3, - eSSU2PeerTestCodeBobSignatureFailure = 4, - eSSU2PeerTestCodeCharlieReasonUnspecified = 64, - eSSU2PeerTestCodeCharlieUnsupportedAddress = 65, - eSSU2PeerTestCodeCharlieLimitExceeded = 66, - eSSU2PeerTestCodeCharlieSignatureFailure = 67, - eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected = 68, - eSSU2PeerTestCodeCharlieAliceIsBanned = 69, - eSSU2PeerTestCodeCharlieAliceIsUnknown = 70, - eSSU2PeerTestCodeUnspecified = 128 - }; - - enum SSU2RelayResponseCode - { - eSSU2RelayResponseCodeAccept = 0, - eSSU2RelayResponseCodeBobRelayTagNotFound = 5, - eSSU2RelayResponseCodeCharlieUnsupportedAddress = 65, - eSSU2RelayResponseCodeCharlieSignatureFailure = 67, - eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70 - }; - - enum SSU2TerminationReason - { - eSSU2TerminationReasonNormalClose = 0, - eSSU2TerminationReasonTerminationReceived = 1, - eSSU2TerminationReasonIdleTimeout = 2, - eSSU2TerminationReasonRouterShutdown = 3, - eSSU2TerminationReasonDataPhaseAEADFailure= 4, - eSSU2TerminationReasonIncompatibleOptions = 5, - eSSU2TerminationReasonTncompatibleSignatureType = 6, - eSSU2TerminationReasonClockSkew = 7, - eSSU2TerminationPaddingViolation = 8, - eSSU2TerminationReasonAEADFramingError = 9, - eSSU2TerminationReasonPayloadFormatError = 10, - eSSU2TerminationReasonSessionRequestError = 11, - eSSU2TerminationReasonSessionCreatedError = 12, - eSSU2TerminationReasonSessionConfirmedError = 13, - eSSU2TerminationReasonTimeout = 14, - eSSU2TerminationReasonRouterInfoSignatureVerificationFail = 15, - eSSU2TerminationReasonInvalidS = 16, - eSSU2TerminationReasonBanned = 17, - eSSU2TerminationReasonBadToken = 18, - eSSU2TerminationReasonConnectionLimits = 19, - eSSU2TerminationReasonIncompatibleVersion = 20, - eSSU2TerminationReasonWrongNetID = 21, - eSSU2TerminationReasonReplacedByNewSession = 22 - }; - - struct SSU2IncompleteMessage - { - struct Fragment - { - uint8_t buf[SSU2_MAX_PACKET_SIZE]; - size_t len; - int fragmentNum; - bool isLast; - std::shared_ptr next; - }; - - std::shared_ptr msg; - int nextFragmentNum; - uint32_t lastFragmentInsertTime; // in seconds - std::shared_ptr outOfSequenceFragments; // #1 and more - - void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); - bool ConcatOutOfSequenceFragments (); // true if message complete - void AddOutOfSequenceFragment (std::shared_ptr fragment); - }; - - struct SSU2SentPacket - { - uint8_t payload[SSU2_MAX_PACKET_SIZE]; - size_t payloadSize = 0; - uint64_t sendTime; // in milliseconds - int numResends = 0; - }; - - // RouterInfo flags - const uint8_t SSU2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01; - const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02; - - class SSU2Server; - class SSU2Session: public TransportSession, public std::enable_shared_from_this - { - protected: - - union Header - { - uint64_t ll[2]; - uint8_t buf[16]; - struct - { - uint64_t connID; - uint32_t packetNum; - uint8_t type; - uint8_t flags[3]; - } h; - }; - - private: - - struct HandshakePacket - { - Header header; - uint8_t headerX[48]; // part1 for SessionConfirmed - uint8_t payload[SSU2_MAX_PACKET_SIZE*2]; - size_t payloadSize = 0; - uint64_t sendTime = 0; // in milliseconds - bool isSecondFragment = false; // for SessionConfirmed - }; - - typedef std::function OnEstablished; - - public: - - SSU2Session (SSU2Server& server, std::shared_ptr in_RemoteRouter = nullptr, - std::shared_ptr addr = nullptr, bool noise = true); - virtual ~SSU2Session (); - - void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; }; - const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; }; - i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports () const { return m_RemoteTransports; }; - i2p::data::RouterInfo::CompatibleTransports GetRemotePeerTestTransports () const { return m_RemotePeerTestTransports; }; - std::shared_ptr GetAddress () const { return m_Address; }; - void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; }; - OnEstablished GetOnEstablished () const { return m_OnEstablished; }; - - virtual void Connect (); - bool Introduce (std::shared_ptr session, uint32_t relayTag); - void WaitForIntroduction (); - void SendPeerTest (); // Alice, Data message - void SendKeepAlive (); - void RequestTermination (SSU2TerminationReason reason); - void CleanUp (uint64_t ts); - void FlushData (); - void Done () override; - void SendLocalRouterInfo (bool update) override; - void SendI2NPMessages (std::list >& msgs) override; - void MoveSendQueue (std::shared_ptr other); - uint32_t GetRelayTag () const override { return m_RelayTag; }; - size_t Resend (uint64_t ts); // return number of resent packets - uint64_t GetLastResendTime () const { return m_LastResendTime; }; - bool IsEstablished () const override { return m_State == eSSU2SessionStateEstablished; }; - i2p::data::RouterInfo::SupportedTransports GetTransportType () const override; - uint64_t GetConnID () const { return m_SourceConnID; }; - SSU2SessionState GetState () const { return m_State; }; - void SetState (SSU2SessionState state) { m_State = state; }; - - virtual bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len); - bool ProcessSessionCreated (uint8_t * buf, size_t len); - bool ProcessSessionConfirmed (uint8_t * buf, size_t len); - bool ProcessRetry (uint8_t * buf, size_t len); - bool ProcessHolePunch (uint8_t * buf, size_t len); - virtual bool ProcessPeerTest (uint8_t * buf, size_t len); - void ProcessData (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from); - - protected: - - SSU2Server& GetServer () { return m_Server; } - RouterStatus GetRouterStatus () const; - void SetRouterStatus (RouterStatus status) const; - size_t GetMaxPayloadSize () const { return m_MaxPayloadSize; } - void SetIsDataReceived (bool dataReceived) { m_IsDataReceived = dataReceived; }; - - uint64_t GetSourceConnID () const { return m_SourceConnID; } - void SetSourceConnID (uint64_t sourceConnID) { m_SourceConnID = sourceConnID; } - uint64_t GetDestConnID () const { return m_DestConnID; } - void SetDestConnID (uint64_t destConnID) { m_DestConnID = destConnID; } - - void SetAddress (std::shared_ptr addr) { m_Address = addr; } - void HandlePayload (const uint8_t * buf, size_t len); - - size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); - size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0); - size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen); - - bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep); - - private: - - void Terminate (); - void Established (); - void ScheduleConnectTimer (); - void HandleConnectTimer (const boost::system::error_code& ecode); - void PostI2NPMessages (); - bool SendQueue (); // returns true if ack block was sent - bool SendFragmentedMessage (std::shared_ptr msg); - void ResendHandshakePacket (); - void ConnectAfterIntroduction (); - - void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len); - void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len); - - void SendSessionRequest (uint64_t token = 0); - void SendSessionCreated (const uint8_t * X); - void SendSessionConfirmed (const uint8_t * Y); - void KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba); - void SendTokenRequest (); - void SendRetry (); - uint32_t SendData (const uint8_t * buf, size_t len, uint8_t flags = 0); // returns packet num - void SendQuickAck (); - void SendTermination (); - void SendPathResponse (const uint8_t * data, size_t len); - void SendPathChallenge (const boost::asio::ip::udp::endpoint& to); - - void HandleDateTime (const uint8_t * buf, size_t len); - void HandleRouterInfo (const uint8_t * buf, size_t len); - void HandleAck (const uint8_t * buf, size_t len); - void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts); - virtual void HandleAddress (const uint8_t * buf, size_t len); - size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); - std::shared_ptr FindLocalAddress () const; - void AdjustMaxPayloadSize (size_t maxMtu = SSU2_MAX_PACKET_SIZE); - bool GetTestingState () const; - void SetTestingState(bool testing) const; - std::shared_ptr ExtractRouterInfo (const uint8_t * buf, size_t size); - bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate - void HandleFirstFragment (const uint8_t * buf, size_t len); - void HandleFollowOnFragment (const uint8_t * buf, size_t len); - void HandleRelayRequest (const uint8_t * buf, size_t len); - void HandleRelayIntro (const uint8_t * buf, size_t len, int attempts = 0); - void HandleRelayResponse (const uint8_t * buf, size_t len); - virtual void HandlePeerTest (const uint8_t * buf, size_t len); - void HandleI2NPMsg (std::shared_ptr&& msg); - - size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr r); - size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr riBuffer); - size_t CreateAckBlock (uint8_t * buf, size_t len); - size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr&& msg); - size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg); - size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg, uint8_t& fragmentNum, uint32_t msgID); - size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen); - size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4); - - size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice - size_t CreateTerminationBlock (uint8_t * buf, size_t len); - - private: - - SSU2Server& m_Server; - std::shared_ptr m_EphemeralKeys; - std::unique_ptr m_NoiseState; - std::unique_ptr m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice - std::unique_ptr m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed - std::shared_ptr m_Address; - boost::asio::ip::udp::endpoint m_RemoteEndpoint; - i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports, m_RemotePeerTestTransports; - int m_RemoteVersion; - uint64_t m_DestConnID, m_SourceConnID; - SSU2SessionState m_State; - uint8_t m_KeyDataSend[64], m_KeyDataReceive[64]; - uint32_t m_SendPacketNum, m_ReceivePacketNum, m_LastDatetimeSentPacketNum; - std::set m_OutOfSequencePackets; // packet nums > receive packet num - std::map > m_SentPackets; // packetNum -> packet - std::unordered_map > m_IncompleteMessages; // msgID -> I2NP - std::unordered_map, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice - std::list > m_SendQueue; - i2p::I2NPMessagesHandler m_Handler; - std::list > m_IntermediateQueue; // from transports - mutable std::mutex m_IntermediateQueueMutex; - bool m_IsDataReceived; - double m_RTT; - int m_MsgLocalExpirationTimeout; - int m_MsgLocalSemiExpirationTimeout; - size_t m_WindowSize, m_RTO; - uint32_t m_RelayTag; // between Bob and Charlie - OnEstablished m_OnEstablished; // callback from Established - boost::asio::deadline_timer m_ConnectTimer; - SSU2TerminationReason m_TerminationReason; - size_t m_MaxPayloadSize; - std::unique_ptr > m_PathChallenge; - std::unordered_map m_ReceivedI2NPMsgIDs; // msgID -> timestamp in seconds - uint64_t m_LastResendTime, m_LastResendAttemptTime; // in milliseconds - int m_NumRanges; - uint8_t m_Ranges[SSU2_MAX_NUM_ACK_RANGES*2]; // ranges sent with previous Ack if any - }; - - inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce) - { - uint64_t data = 0; - i2p::crypto::ChaCha20 ((uint8_t *)&data, 8, kh, nonce, (uint8_t *)&data); - return data; - } - - inline void CreateNonce (uint64_t seqn, uint8_t * nonce) - { - memset (nonce, 0, 4); - htole64buf (nonce + 4, seqn); - } -} -} - -#endif diff --git a/libi2pd/Signature.cpp b/libi2pd/Signature.cpp deleted file mode 100644 index 3e4b451b..00000000 --- a/libi2pd/Signature.cpp +++ /dev/null @@ -1,487 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 -#include -#endif -#include "Log.h" -#include "Signature.h" - -namespace i2p -{ -namespace crypto -{ -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - DSAVerifier::DSAVerifier (): - m_PublicKey (nullptr) - { - } - - DSAVerifier::~DSAVerifier () - { - if (m_PublicKey) - EVP_PKEY_free (m_PublicKey); - } - - void DSAVerifier::SetPublicKey (const uint8_t * signingKey) - { - if (m_PublicKey) - EVP_PKEY_free (m_PublicKey); - BIGNUM * pub = BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL); - m_PublicKey = CreateDSA (pub); - BN_free (pub); - } - - bool DSAVerifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - // calculate SHA1 digest - uint8_t digest[20], sign[48]; - SHA1 (buf, len, digest); - // signature - DSA_SIG * sig = DSA_SIG_new(); - DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL)); - // to DER format - uint8_t * s = sign; - auto l = i2d_DSA_SIG (sig, &s); - DSA_SIG_free(sig); - // verify - auto ctx = EVP_PKEY_CTX_new (m_PublicKey, NULL); - EVP_PKEY_verify_init(ctx); - EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha1()); - bool ret = EVP_PKEY_verify(ctx, sign, l, digest, 20); - EVP_PKEY_CTX_free(ctx); - return ret; - } - - DSASigner::DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) - { - BIGNUM * priv = BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL); - m_PrivateKey = CreateDSA (nullptr, priv); - BN_free (priv); - } - - DSASigner::~DSASigner () - { - if (m_PrivateKey) - EVP_PKEY_free (m_PrivateKey); - } - - void DSASigner::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - uint8_t digest[20], sign[48]; - SHA1 (buf, len, digest); - auto ctx = EVP_PKEY_CTX_new (m_PrivateKey, NULL); - EVP_PKEY_sign_init(ctx); - EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha1()); - size_t l = 48; - EVP_PKEY_sign(ctx, sign, &l, digest, 20); - const uint8_t * s1 = sign; - DSA_SIG * sig = d2i_DSA_SIG (NULL, &s1, l); - const BIGNUM * r, * s; - DSA_SIG_get0 (sig, &r, &s); - bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2); - bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2); - DSA_SIG_free(sig); - EVP_PKEY_CTX_free(ctx); - } - - void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - EVP_PKEY * paramskey = CreateDSA(); - EVP_PKEY_CTX * ctx = EVP_PKEY_CTX_new_from_pkey(NULL, paramskey, NULL); - EVP_PKEY_keygen_init(ctx); - EVP_PKEY * pkey = nullptr; - EVP_PKEY_keygen(ctx, &pkey); - BIGNUM * pub = NULL, * priv = NULL; - EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, &pub); - bn2buf (pub, signingPublicKey, DSA_PUBLIC_KEY_LENGTH); - EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &priv); - bn2buf (priv, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH); - BN_free (pub); BN_free (priv); - EVP_PKEY_free (pkey); - EVP_PKEY_free (paramskey); - EVP_PKEY_CTX_free (ctx); - } -#else - - DSAVerifier::DSAVerifier () - { - m_PublicKey = CreateDSA (); - } - - DSAVerifier::~DSAVerifier () - { - DSA_free (m_PublicKey); - } - - void DSAVerifier::SetPublicKey (const uint8_t * signingKey) - { - DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL); - } - - bool DSAVerifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - // calculate SHA1 digest - uint8_t digest[20]; - SHA1 (buf, len, digest); - // signature - DSA_SIG * sig = DSA_SIG_new(); - DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL)); - // DSA verification - int ret = DSA_do_verify (digest, 20, sig, m_PublicKey); - DSA_SIG_free(sig); - return ret; - } - - DSASigner::DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) - { - m_PrivateKey = CreateDSA (); - DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL)); - } - - DSASigner::~DSASigner () - { - DSA_free (m_PrivateKey); - } - - void DSASigner::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - uint8_t digest[20]; - SHA1 (buf, len, digest); - DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey); - const BIGNUM * r, * s; - DSA_SIG_get0 (sig, &r, &s); - bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2); - bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2); - DSA_SIG_free(sig); - } - - void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - DSA * dsa = CreateDSA (); - DSA_generate_key (dsa); - const BIGNUM * pub_key, * priv_key; - DSA_get0_key(dsa, &pub_key, &priv_key); - bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH); - bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH); - DSA_free (dsa); - } -#endif - -#if OPENSSL_EDDSA - EDDSA25519Verifier::EDDSA25519Verifier (): - m_Pkey (nullptr) - { - } - - EDDSA25519Verifier::~EDDSA25519Verifier () - { - EVP_PKEY_free (m_Pkey); - } - - void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) - { - if (m_Pkey) EVP_PKEY_free (m_Pkey); - m_Pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, signingKey, 32); - } - - bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - if (m_Pkey) - { - EVP_MD_CTX * ctx = EVP_MD_CTX_create (); - EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, m_Pkey); - auto ret = EVP_DigestVerify (ctx, signature, 64, buf, len); - EVP_MD_CTX_destroy (ctx); - return ret; - } - else - LogPrint (eLogError, "EdDSA verification key is not set"); - return false; - } - -#else - EDDSA25519Verifier::EDDSA25519Verifier () - { - } - - EDDSA25519Verifier::~EDDSA25519Verifier () - { - } - - void EDDSA25519Verifier::SetPublicKey (const uint8_t * signingKey) - { - memcpy (m_PublicKeyEncoded, signingKey, EDDSA25519_PUBLIC_KEY_LENGTH); - BN_CTX * ctx = BN_CTX_new (); - m_PublicKey = GetEd25519 ()->DecodePublicKey (m_PublicKeyEncoded, ctx); - BN_CTX_free (ctx); - } - - bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - uint8_t digest[64]; - SHA512_CTX ctx; - SHA512_Init (&ctx); - SHA512_Update (&ctx, signature, EDDSA25519_SIGNATURE_LENGTH/2); // R - SHA512_Update (&ctx, m_PublicKeyEncoded, EDDSA25519_PUBLIC_KEY_LENGTH); // public key - SHA512_Update (&ctx, buf, len); // data - SHA512_Final (digest, &ctx); - - return GetEd25519 ()->Verify (m_PublicKey, digest, signature); - } -#endif - - EDDSA25519SignerCompat::EDDSA25519SignerCompat (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey) - { - // expand key - Ed25519::ExpandPrivateKey (signingPrivateKey, m_ExpandedPrivateKey); - // generate and encode public key - BN_CTX * ctx = BN_CTX_new (); - auto publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx); - GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); - - if (signingPublicKey && memcmp (m_PublicKeyEncoded, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) - { - // keys don't match, it means older key with 0x1F - LogPrint (eLogWarning, "Older EdDSA key detected"); - m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0xDF; // drop third bit - publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx); - GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); - } - BN_CTX_free (ctx); - } - - EDDSA25519SignerCompat::~EDDSA25519SignerCompat () - { - } - - void EDDSA25519SignerCompat::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - GetEd25519 ()->Sign (m_ExpandedPrivateKey, m_PublicKeyEncoded, buf, len, signature); - } - -#if OPENSSL_EDDSA - EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey): - m_Pkey (nullptr), m_Fallback (nullptr) - { - m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_ED25519, NULL, signingPrivateKey, 32); - uint8_t publicKey[EDDSA25519_PUBLIC_KEY_LENGTH]; - size_t len = EDDSA25519_PUBLIC_KEY_LENGTH; - EVP_PKEY_get_raw_public_key (m_Pkey, publicKey, &len); - if (signingPublicKey && memcmp (publicKey, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH)) - { - LogPrint (eLogWarning, "EdDSA public key mismatch. Fallback"); - m_Fallback = new EDDSA25519SignerCompat (signingPrivateKey, signingPublicKey); - EVP_PKEY_free (m_Pkey); - m_Pkey = nullptr; - } - } - - EDDSA25519Signer::~EDDSA25519Signer () - { - if (m_Fallback) delete m_Fallback; - if (m_Pkey) EVP_PKEY_free (m_Pkey); - } - - void EDDSA25519Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - if (m_Fallback) - return m_Fallback->Sign (buf, len, signature); - else if (m_Pkey) - { - - EVP_MD_CTX * ctx = EVP_MD_CTX_create (); - size_t l = 64; - uint8_t sig[64]; // temporary buffer for signature. openssl issue #7232 - EVP_DigestSignInit (ctx, NULL, NULL, NULL, m_Pkey); - if (!EVP_DigestSign (ctx, sig, &l, buf, len)) - LogPrint (eLogError, "EdDSA signing failed"); - memcpy (signature, sig, 64); - EVP_MD_CTX_destroy (ctx); - } - else - LogPrint (eLogError, "EdDSA signing key is not set"); - } -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) - static const OSSL_PARAM EDDSA25519phParams[] = - { - OSSL_PARAM_utf8_string ("instance", (char *)"Ed25519ph", 9), - OSSL_PARAM_END - }; - - bool EDDSA25519phVerifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - auto pkey = GetPkey (); - if (pkey) - { - uint8_t digest[64]; - SHA512 (buf, len, digest); - EVP_MD_CTX * ctx = EVP_MD_CTX_create (); - EVP_DigestVerifyInit_ex (ctx, NULL, NULL, NULL, NULL, pkey, EDDSA25519phParams); - auto ret = EVP_DigestVerify (ctx, signature, 64, digest, 64); - EVP_MD_CTX_destroy (ctx); - return ret; - } - else - LogPrint (eLogError, "EdDSA verification key is not set"); - return false; - } - - EDDSA25519phSigner::EDDSA25519phSigner (const uint8_t * signingPrivateKey): - EDDSA25519Signer (signingPrivateKey) - { - } - - void EDDSA25519phSigner::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - auto pkey = GetPkey (); - if (pkey) - { - uint8_t digest[64]; - SHA512 (buf, len, digest); - EVP_MD_CTX * ctx = EVP_MD_CTX_create (); - size_t l = 64; - uint8_t sig[64]; - EVP_DigestSignInit_ex (ctx, NULL, NULL, NULL, NULL, pkey, EDDSA25519phParams); - if (!EVP_DigestSign (ctx, sig, &l, digest, 64)) - LogPrint (eLogError, "EdDSA signing failed"); - memcpy (signature, sig, 64); - EVP_MD_CTX_destroy (ctx); - } - else - LogPrint (eLogError, "EdDSA signing key is not set"); - } -#endif - -#if OPENSSL_PQ - - MLDSA44Verifier::MLDSA44Verifier (): - m_Pkey (nullptr) - { - } - - MLDSA44Verifier::~MLDSA44Verifier () - { - EVP_PKEY_free (m_Pkey); - } - - void MLDSA44Verifier::SetPublicKey (const uint8_t * signingKey) - { - if (m_Pkey) - { - EVP_PKEY_free (m_Pkey); - m_Pkey = nullptr; - } - OSSL_PARAM params[] = - { - OSSL_PARAM_octet_string (OSSL_PKEY_PARAM_PUB_KEY, (uint8_t *)signingKey, GetPublicKeyLen ()), - OSSL_PARAM_END - }; - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, "ML-DSA-44", NULL); - if (ctx) - { - EVP_PKEY_fromdata_init (ctx); - EVP_PKEY_fromdata (ctx, &m_Pkey, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, params); - EVP_PKEY_CTX_free (ctx); - } - else - LogPrint (eLogError, "MLDSA44 can't create PKEY context"); - } - - bool MLDSA44Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - bool ret = false; - if (m_Pkey) - { - EVP_PKEY_CTX * vctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL); - if (vctx) - { - EVP_SIGNATURE * sig = EVP_SIGNATURE_fetch (NULL, "ML-DSA-44", NULL); - if (sig) - { - int encode = 1; - OSSL_PARAM params[] = - { - OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, &encode), - OSSL_PARAM_END - }; - EVP_PKEY_verify_message_init (vctx, sig, params); - ret = EVP_PKEY_verify (vctx, signature, GetSignatureLen (), buf, len); - EVP_SIGNATURE_free (sig); - } - EVP_PKEY_CTX_free (vctx); - } - else - LogPrint (eLogError, "MLDSA44 can't obtain context from PKEY"); - } - else - LogPrint (eLogError, "MLDSA44 verification key is not set"); - return ret; - } - - MLDSA44Signer::MLDSA44Signer (const uint8_t * signingPrivateKey): - m_Pkey (nullptr) - { - OSSL_PARAM params[] = - { - OSSL_PARAM_octet_string (OSSL_PKEY_PARAM_PRIV_KEY, (uint8_t *)signingPrivateKey, MLDSA44_PRIVATE_KEY_LENGTH), - OSSL_PARAM_END - }; - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name (NULL, "ML-DSA-44", NULL); - if (ctx) - { - EVP_PKEY_fromdata_init (ctx); - EVP_PKEY_fromdata (ctx, &m_Pkey, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, params); - EVP_PKEY_CTX_free (ctx); - } - else - LogPrint (eLogError, "MLDSA44 can't create PKEY context"); - } - - MLDSA44Signer::~MLDSA44Signer () - { - if (m_Pkey) EVP_PKEY_free (m_Pkey); - } - - void MLDSA44Signer::Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - if (m_Pkey) - { - EVP_PKEY_CTX * sctx = EVP_PKEY_CTX_new_from_pkey (NULL, m_Pkey, NULL); - if (sctx) - { - EVP_SIGNATURE * sig = EVP_SIGNATURE_fetch (NULL, "ML-DSA-44", NULL); - if (sig) - { - int encode = 1; - OSSL_PARAM params[] = - { - OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, &encode), - OSSL_PARAM_END - }; - EVP_PKEY_sign_message_init (sctx, sig, params); - size_t siglen = MLDSA44_SIGNATURE_LENGTH; - EVP_PKEY_sign (sctx, signature, &siglen, buf, len); - EVP_SIGNATURE_free (sig); - } - EVP_PKEY_CTX_free (sctx); - } - else - LogPrint (eLogError, "MLDSA44 can't obtain context from PKEY"); - } - else - LogPrint (eLogError, "MLDSA44 signing key is not set"); - } - -#endif -} -} diff --git a/libi2pd/Signature.h b/libi2pd/Signature.h deleted file mode 100644 index 20c7e11b..00000000 --- a/libi2pd/Signature.h +++ /dev/null @@ -1,577 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 SIGNATURE_H__ -#define SIGNATURE_H__ - -#include -#include -#include -#include -#include -#include -#include "Crypto.h" -#include "Ed25519.h" -#include "Gost.h" - -namespace i2p -{ -namespace crypto -{ - class Verifier - { - public: - - virtual ~Verifier () {}; - virtual bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const = 0; - virtual size_t GetPublicKeyLen () const = 0; - virtual size_t GetSignatureLen () const = 0; - virtual size_t GetPrivateKeyLen () const { return GetSignatureLen ()/2; }; - virtual void SetPublicKey (const uint8_t * signingKey) = 0; - }; - - class Signer - { - public: - - virtual ~Signer () {}; - virtual void Sign (const uint8_t * buf, int len, uint8_t * signature) const = 0; - }; - - // DSA - const size_t DSA_PUBLIC_KEY_LENGTH = 128; - const size_t DSA_SIGNATURE_LENGTH = 40; - const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH/2; - class DSAVerifier: public Verifier - { - public: - - DSAVerifier (); - ~DSAVerifier (); - - // implements Verifier - void SetPublicKey (const uint8_t * signingKey) override; - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const override; - size_t GetPublicKeyLen () const override { return DSA_PUBLIC_KEY_LENGTH; }; - size_t GetSignatureLen () const override { return DSA_SIGNATURE_LENGTH; }; - - private: - -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - EVP_PKEY * m_PublicKey; -#else - DSA * m_PublicKey; -#endif - }; - - class DSASigner: public Signer - { - public: - - DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey); - // openssl 1.1 always requires DSA public key even for signing - ~DSASigner (); - - // implements Signer - void Sign (const uint8_t * buf, int len, uint8_t * signature) const override; - - private: - -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - EVP_PKEY * m_PrivateKey; -#else - DSA * m_PrivateKey; -#endif - }; - - void CreateDSARandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey); - - // ECDSA - struct SHA256Hash - { - static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) - { - SHA256 (buf, len, digest); - } - - enum { hashLen = 32 }; - }; - - struct SHA384Hash - { - static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) - { - SHA384 (buf, len, digest); - } - - enum { hashLen = 48 }; - }; - - struct SHA512Hash - { - static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) - { - SHA512 (buf, len, digest); - } - - enum { hashLen = 64 }; - }; - - template - class ECDSAVerifier: public Verifier - { - public: - - ECDSAVerifier () - { - m_PublicKey = EC_KEY_new_by_curve_name (curve); - } - - void SetPublicKey (const uint8_t * signingKey) - { - BIGNUM * x = BN_bin2bn (signingKey, keyLen/2, NULL); - BIGNUM * y = BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL); - EC_KEY_set_public_key_affine_coordinates (m_PublicKey, x, y); - BN_free (x); BN_free (y); - } - - ~ECDSAVerifier () - { - EC_KEY_free (m_PublicKey); - } - - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - uint8_t digest[Hash::hashLen]; - Hash::CalculateHash (buf, len, digest); - ECDSA_SIG * sig = ECDSA_SIG_new(); - auto r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL); - auto s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL); - ECDSA_SIG_set0(sig, r, s); - // ECDSA verification - int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey); - ECDSA_SIG_free(sig); - return ret; - } - - size_t GetPublicKeyLen () const { return keyLen; }; - size_t GetSignatureLen () const { return keyLen; }; // signature length = key length - - - private: - - EC_KEY * m_PublicKey; - }; - - template - class ECDSASigner: public Signer - { - public: - - ECDSASigner (const uint8_t * signingPrivateKey) - { - m_PrivateKey = EC_KEY_new_by_curve_name (curve); - EC_KEY_set_private_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen/2, NULL)); - } - - ~ECDSASigner () - { - EC_KEY_free (m_PrivateKey); - } - - void Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - uint8_t digest[Hash::hashLen]; - Hash::CalculateHash (buf, len, digest); - ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey); - const BIGNUM * r, * s; - ECDSA_SIG_get0 (sig, &r, &s); - // signatureLen = keyLen - bn2buf (r, signature, keyLen/2); - bn2buf (s, signature + keyLen/2, keyLen/2); - ECDSA_SIG_free(sig); - } - - private: - - EC_KEY * m_PrivateKey; - }; - - inline void CreateECDSARandomKeys (int curve, size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - EC_KEY * signingKey = EC_KEY_new_by_curve_name (curve); - EC_KEY_generate_key (signingKey); - bn2buf (EC_KEY_get0_private_key (signingKey), signingPrivateKey, keyLen/2); - BIGNUM * x = BN_new(), * y = BN_new(); - EC_POINT_get_affine_coordinates_GFp (EC_KEY_get0_group(signingKey), - EC_KEY_get0_public_key (signingKey), x, y, NULL); - bn2buf (x, signingPublicKey, keyLen/2); - bn2buf (y, signingPublicKey + keyLen/2, keyLen/2); - BN_free (x); BN_free (y); - EC_KEY_free (signingKey); - } - -// ECDSA_SHA256_P256 - const size_t ECDSAP256_KEY_LENGTH = 64; - typedef ECDSAVerifier ECDSAP256Verifier; - typedef ECDSASigner ECDSAP256Signer; - - inline void CreateECDSAP256RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - CreateECDSARandomKeys (NID_X9_62_prime256v1, ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey); - } - -// ECDSA_SHA384_P384 - const size_t ECDSAP384_KEY_LENGTH = 96; - typedef ECDSAVerifier ECDSAP384Verifier; - typedef ECDSASigner ECDSAP384Signer; - - inline void CreateECDSAP384RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - CreateECDSARandomKeys (NID_secp384r1, ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey); - } - -// ECDSA_SHA512_P521 - const size_t ECDSAP521_KEY_LENGTH = 132; - typedef ECDSAVerifier ECDSAP521Verifier; - typedef ECDSASigner ECDSAP521Signer; - - inline void CreateECDSAP521RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - CreateECDSARandomKeys (NID_secp521r1, ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey); - } - - - // EdDSA - class EDDSA25519Verifier: public Verifier - { - public: - - EDDSA25519Verifier (); - void SetPublicKey (const uint8_t * signingKey); - ~EDDSA25519Verifier (); - - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; - - size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; }; - size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; }; - - private: - -#if OPENSSL_EDDSA - - EVP_PKEY * m_Pkey; - - protected: - - EVP_PKEY * GetPkey () const { return m_Pkey; }; -#else - EDDSAPoint m_PublicKey; - uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; -#endif - }; - -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - class EDDSA25519phVerifier: public EDDSA25519Verifier - { - public: - - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; - }; -#endif - - class EDDSA25519SignerCompat: public Signer - { - public: - - EDDSA25519SignerCompat (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr); - // we pass signingPublicKey to check if it matches private key - ~EDDSA25519SignerCompat (); - - void Sign (const uint8_t * buf, int len, uint8_t * signature) const; - const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation - - private: - - uint8_t m_ExpandedPrivateKey[64]; - uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; - }; - -#if OPENSSL_EDDSA - class EDDSA25519Signer: public Signer - { - public: - - EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr); - // we pass signingPublicKey to check if it matches private key - ~EDDSA25519Signer (); - - void Sign (const uint8_t * buf, int len, uint8_t * signature) const; - - protected: - - EVP_PKEY * GetPkey () const { return m_Pkey; }; - - private: - - EVP_PKEY * m_Pkey; - EDDSA25519SignerCompat * m_Fallback; - }; -#else - - typedef EDDSA25519SignerCompat EDDSA25519Signer; - -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0 - class EDDSA25519phSigner: public EDDSA25519Signer - { - public: - - EDDSA25519phSigner (const uint8_t * signingPrivateKey); - - void Sign (const uint8_t * buf, int len, uint8_t * signature) const; - }; - -#endif - - inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { -#if OPENSSL_EDDSA - EVP_PKEY *pkey = NULL; - EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_ED25519, NULL); - EVP_PKEY_keygen_init (pctx); - EVP_PKEY_keygen (pctx, &pkey); - EVP_PKEY_CTX_free (pctx); - size_t len = EDDSA25519_PUBLIC_KEY_LENGTH; - EVP_PKEY_get_raw_public_key (pkey, signingPublicKey, &len); - len = EDDSA25519_PRIVATE_KEY_LENGTH; - EVP_PKEY_get_raw_private_key (pkey, signingPrivateKey, &len); - EVP_PKEY_free (pkey); -#else - RAND_bytes (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH); - EDDSA25519Signer signer (signingPrivateKey); - memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); -#endif - } - - - // ГОСТ Р 34.11 - struct GOSTR3411_256_Hash - { - static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) - { - GOSTR3411_2012_256 (buf, len, digest); - } - - enum { hashLen = 32 }; - }; - - struct GOSTR3411_512_Hash - { - static void CalculateHash (const uint8_t * buf, size_t len, uint8_t * digest) - { - GOSTR3411_2012_512 (buf, len, digest); - } - - enum { hashLen = 64 }; - }; - - // ГОСТ Р 34.10 - const size_t GOSTR3410_256_PUBLIC_KEY_LENGTH = 64; - const size_t GOSTR3410_512_PUBLIC_KEY_LENGTH = 128; - - template - class GOSTR3410Verifier: public Verifier - { - public: - - enum { keyLen = Hash::hashLen }; - - GOSTR3410Verifier (GOSTR3410ParamSet paramSet): - m_ParamSet (paramSet), m_PublicKey (nullptr) - { - } - - void SetPublicKey (const uint8_t * signingKey) - { - BIGNUM * x = BN_bin2bn (signingKey, GetPublicKeyLen ()/2, NULL); - BIGNUM * y = BN_bin2bn (signingKey + GetPublicKeyLen ()/2, GetPublicKeyLen ()/2, NULL); - m_PublicKey = GetGOSTR3410Curve (m_ParamSet)->CreatePoint (x, y); - BN_free (x); BN_free (y); - } - ~GOSTR3410Verifier () - { - if (m_PublicKey) EC_POINT_free (m_PublicKey); - } - - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const - { - uint8_t digest[Hash::hashLen]; - Hash::CalculateHash (buf, len, digest); - BIGNUM * d = BN_bin2bn (digest, Hash::hashLen, nullptr); - BIGNUM * r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL); - BIGNUM * s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL); - bool ret = GetGOSTR3410Curve (m_ParamSet)->Verify (m_PublicKey, d, r, s); - BN_free (d); BN_free (r); BN_free (s); - return ret; - } - - size_t GetPublicKeyLen () const { return keyLen*2; } - size_t GetSignatureLen () const { return keyLen*2; } - - private: - - GOSTR3410ParamSet m_ParamSet; - EC_POINT * m_PublicKey; - }; - - template - class GOSTR3410Signer: public Signer - { - public: - - enum { keyLen = Hash::hashLen }; - - GOSTR3410Signer (GOSTR3410ParamSet paramSet, const uint8_t * signingPrivateKey): - m_ParamSet (paramSet) - { - m_PrivateKey = BN_bin2bn (signingPrivateKey, keyLen, nullptr); - } - ~GOSTR3410Signer () { BN_free (m_PrivateKey); } - - void Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - uint8_t digest[Hash::hashLen]; - Hash::CalculateHash (buf, len, digest); - BIGNUM * d = BN_bin2bn (digest, Hash::hashLen, nullptr); - BIGNUM * r = BN_new (), * s = BN_new (); - GetGOSTR3410Curve (m_ParamSet)->Sign (m_PrivateKey, d, r, s); - bn2buf (r, signature, keyLen); - bn2buf (s, signature + keyLen, keyLen); - BN_free (d); BN_free (r); BN_free (s); - } - - private: - - GOSTR3410ParamSet m_ParamSet; - BIGNUM * m_PrivateKey; - }; - - inline void CreateGOSTR3410RandomKeys (GOSTR3410ParamSet paramSet, uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - const auto& curve = GetGOSTR3410Curve (paramSet); - auto keyLen = curve->GetKeyLen (); - RAND_bytes (signingPrivateKey, keyLen); - BIGNUM * priv = BN_bin2bn (signingPrivateKey, keyLen, nullptr); - - auto pub = curve->MulP (priv); - BN_free (priv); - BIGNUM * x = BN_new (), * y = BN_new (); - curve->GetXY (pub, x, y); - EC_POINT_free (pub); - bn2buf (x, signingPublicKey, keyLen); - bn2buf (y, signingPublicKey + keyLen, keyLen); - BN_free (x); BN_free (y); - } - - typedef GOSTR3410Verifier GOSTR3410_256_Verifier; - typedef GOSTR3410Signer GOSTR3410_256_Signer; - typedef GOSTR3410Verifier GOSTR3410_512_Verifier; - typedef GOSTR3410Signer GOSTR3410_512_Signer; - - // RedDSA - typedef EDDSA25519Verifier RedDSA25519Verifier; - class RedDSA25519Signer: public Signer - { - public: - - RedDSA25519Signer (const uint8_t * signingPrivateKey) - { - memcpy (m_PrivateKey, signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH); - BN_CTX * ctx = BN_CTX_new (); - auto publicKey = GetEd25519 ()->GeneratePublicKey (m_PrivateKey, ctx); - GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx); - BN_CTX_free (ctx); - } - ~RedDSA25519Signer () {}; - - void Sign (const uint8_t * buf, int len, uint8_t * signature) const - { - GetEd25519 ()->SignRedDSA (m_PrivateKey, m_PublicKeyEncoded, buf, len, signature); - } - - const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; }; // for keys creation - - private: - - uint8_t m_PrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH]; - uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; - }; - - inline void CreateRedDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - GetEd25519 ()->CreateRedDSAPrivateKey (signingPrivateKey); - RedDSA25519Signer signer (signingPrivateKey); - memcpy (signingPublicKey, signer.GetPublicKey (), EDDSA25519_PUBLIC_KEY_LENGTH); - } - -#if OPENSSL_PQ -#include - - // Post-Quantum - const size_t MLDSA44_PUBLIC_KEY_LENGTH = 1312; - const size_t MLDSA44_SIGNATURE_LENGTH = 2420; - const size_t MLDSA44_PRIVATE_KEY_LENGTH = 2560; - class MLDSA44Verifier: public Verifier - { - public: - - MLDSA44Verifier (); - void SetPublicKey (const uint8_t * signingKey); - ~MLDSA44Verifier (); - - bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const; - - size_t GetPublicKeyLen () const { return MLDSA44_PUBLIC_KEY_LENGTH; }; - size_t GetSignatureLen () const { return MLDSA44_SIGNATURE_LENGTH; }; - size_t GetPrivateKeyLen () const { return MLDSA44_PRIVATE_KEY_LENGTH; }; - - private: - - EVP_PKEY * m_Pkey; - }; - - class MLDSA44Signer: public Signer - { - public: - - MLDSA44Signer (const uint8_t * signingPrivateKey); - ~MLDSA44Signer (); - - void Sign (const uint8_t * buf, int len, uint8_t * signature) const; - - private: - - EVP_PKEY * m_Pkey; - }; - - inline void CreateMLDSA44RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) - { - EVP_PKEY * pkey = EVP_PKEY_Q_keygen (NULL, NULL, "ML-DSA-44"); - size_t len = MLDSA44_PUBLIC_KEY_LENGTH; - EVP_PKEY_get_octet_string_param (pkey, OSSL_PKEY_PARAM_PUB_KEY, signingPublicKey, MLDSA44_PUBLIC_KEY_LENGTH, &len); - len = MLDSA44_PRIVATE_KEY_LENGTH; - EVP_PKEY_get_octet_string_param (pkey, OSSL_PKEY_PARAM_PRIV_KEY, signingPrivateKey, MLDSA44_PRIVATE_KEY_LENGTH, &len); - EVP_PKEY_free (pkey); - } -#endif -} -} - -#endif diff --git a/libi2pd/Siphash.h b/libi2pd/Siphash.h deleted file mode 100644 index 78b22b90..00000000 --- a/libi2pd/Siphash.h +++ /dev/null @@ -1,155 +0,0 @@ -/** - * This code is licensed under the MCGSI Public License - * Copyright 2018 Jeff Becker - * - * Kovri go write your own code - * - */ -#ifndef SIPHASH_H -#define SIPHASH_H - -#include -#include "Crypto.h" - -#if !OPENSSL_SIPHASH -namespace i2p -{ -namespace crypto -{ - namespace siphash - { - constexpr int crounds = 2; - constexpr int drounds = 4; - - inline uint64_t rotl(const uint64_t & x, int b) - { - uint64_t ret = x << b; - ret |= x >> (64 - b); - return ret; - } - - inline void u32to8le(const uint32_t & v, uint8_t * p) - { - p[0] = (uint8_t) v; - p[1] = (uint8_t) (v >> 8); - p[2] = (uint8_t) (v >> 16); - p[3] = (uint8_t) (v >> 24); - } - - inline void u64to8le(const uint64_t & v, uint8_t * p) - { - p[0] = v & 0xff; - p[1] = (v >> 8) & 0xff; - p[2] = (v >> 16) & 0xff; - p[3] = (v >> 24) & 0xff; - p[4] = (v >> 32) & 0xff; - p[5] = (v >> 40) & 0xff; - p[6] = (v >> 48) & 0xff; - p[7] = (v >> 56) & 0xff; - } - - inline uint64_t u8to64le(const uint8_t * p) - { - uint64_t i = 0; - int idx = 0; - while(idx < 8) - { - i |= ((uint64_t) p[idx]) << (idx * 8); - ++idx; - } - return i; - } - - inline void round(uint64_t & _v0, uint64_t & _v1, uint64_t & _v2, uint64_t & _v3) - { - _v0 += _v1; - _v1 = rotl(_v1, 13); - _v1 ^= _v0; - _v0 = rotl(_v0, 32); - _v2 += _v3; - _v3 = rotl(_v3, 16); - _v3 ^= _v2; - _v0 += _v3; - _v3 = rotl(_v3, 21); - _v3 ^= _v0; - _v2 += _v1; - _v1 = rotl(_v1, 17); - _v1 ^= _v2; - _v2 = rotl(_v2, 32); - } - } - - /** hashsz must be 8 or 16 */ - template - inline void Siphash(uint8_t * h, const uint8_t * buf, std::size_t bufsz, const uint8_t * key) - { - uint64_t v0 = 0x736f6d6570736575ULL; - uint64_t v1 = 0x646f72616e646f6dULL; - uint64_t v2 = 0x6c7967656e657261ULL; - uint64_t v3 = 0x7465646279746573ULL; - const uint64_t k0 = siphash::u8to64le(key); - const uint64_t k1 = siphash::u8to64le(key + 8); - uint64_t msg; - int i; - const uint8_t * end = buf + bufsz - (bufsz % sizeof(uint64_t)); - auto left = bufsz & 7; - uint64_t b = ((uint64_t)bufsz) << 56; - v3 ^= k1; - v2 ^= k0; - v1 ^= k1; - v0 ^= k0; - - if(hashsz == 16) v1 ^= 0xee; - - while(buf != end) - { - msg = siphash::u8to64le(buf); - v3 ^= msg; - for(i = 0; i < siphash::crounds; ++i) - siphash::round(v0, v1, v2, v3); - - v0 ^= msg; - buf += 8; - } - - while(left) - { - --left; - b |= ((uint64_t)(buf[left])) << (left * 8); - } - - v3 ^= b; - - for(i = 0; i < siphash::crounds; ++i) - siphash::round(v0, v1, v2, v3); - - v0 ^= b; - - - if(hashsz == 16) - v2 ^= 0xee; - else - v2 ^= 0xff; - - for(i = 0; i < siphash::drounds; ++i) - siphash::round(v0, v1, v2, v3); - - b = v0 ^ v1 ^ v2 ^ v3; - - siphash::u64to8le(b, h); - - if(hashsz == 8) return; - - v1 ^= 0xdd; - - for (i = 0; i < siphash::drounds; ++i) - siphash::round(v0, v1, v2, v3); - - b = v0 ^ v1 ^ v2 ^ v3; - siphash::u64to8le(b, h + 8); - } -} -} -#endif - -#endif diff --git a/libi2pd/Socks5.h b/libi2pd/Socks5.h deleted file mode 100644 index 8db8939b..00000000 --- a/libi2pd/Socks5.h +++ /dev/null @@ -1,210 +0,0 @@ -/* -* Copyright (c) 2024, 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 SOCKS5_H__ -#define SOCKS5_H__ - -#include -#include -#include -#include "I2PEndian.h" - -namespace i2p -{ -namespace transport -{ - // SOCKS5 constants - const uint8_t SOCKS5_VER = 0x05; - const uint8_t SOCKS5_CMD_CONNECT = 0x01; - const uint8_t SOCKS5_CMD_UDP_ASSOCIATE = 0x03; - const uint8_t SOCKS5_ATYP_IPV4 = 0x01; - const uint8_t SOCKS5_ATYP_IPV6 = 0x04; - const uint8_t SOCKS5_ATYP_NAME = 0x03; - const size_t SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE = 10; - const size_t SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE = 22; - - const uint8_t SOCKS5_REPLY_SUCCESS = 0x00; - const uint8_t SOCKS5_REPLY_SERVER_FAILURE = 0x01; - const uint8_t SOCKS5_REPLY_CONNECTION_NOT_ALLOWED = 0x02; - const uint8_t SOCKS5_REPLY_NETWORK_UNREACHABLE = 0x03; - const uint8_t SOCKS5_REPLY_HOST_UNREACHABLE = 0x04; - const uint8_t SOCKS5_REPLY_CONNECTION_REFUSED = 0x05; - const uint8_t SOCKS5_REPLY_TTL_EXPIRED = 0x06; - const uint8_t SOCKS5_REPLY_COMMAND_NOT_SUPPORTED = 0x07; - const uint8_t SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED = 0x08; - - // SOCKS5 handshake - template - void Socks5ReadReply (Socket& s, Handler handler) - { - auto readbuff = std::make_shared >(258); // max possible - boost::asio::async_read(s, boost::asio::buffer(readbuff->data (), 5), boost::asio::transfer_all(), // read 4 bytes of header + first byte of address - [readbuff, &s, handler](const boost::system::error_code& ec, std::size_t transferred) - { - if (!ec) - { - if ((*readbuff)[1] == SOCKS5_REPLY_SUCCESS) - { - size_t len = 0; - switch ((*readbuff)[3]) // ATYP - { - case SOCKS5_ATYP_IPV4: len = 3; break; // address length 4 bytes - case SOCKS5_ATYP_IPV6: len = 15; break; // address length 16 bytes - case SOCKS5_ATYP_NAME: len += (*readbuff)[4]; break; // first byte of address is length - default: ; - } - if (len) - { - len += 2; // port - boost::asio::async_read(s, boost::asio::buffer(readbuff->data (), len), boost::asio::transfer_all(), - [readbuff, handler](const boost::system::error_code& ec, std::size_t transferred) - { - if (!ec) - handler (boost::system::error_code ()); // success - else - handler (boost::asio::error::make_error_code (boost::asio::error::connection_aborted)); - }); - } - else - handler (boost::asio::error::make_error_code (boost::asio::error::fault)); // unknown address type - } - else - switch ((*readbuff)[1]) // REP - { - case SOCKS5_REPLY_SERVER_FAILURE: - handler (boost::asio::error::make_error_code (boost::asio::error::access_denied )); - break; - case SOCKS5_REPLY_CONNECTION_NOT_ALLOWED: - handler (boost::asio::error::make_error_code (boost::asio::error::no_permission)); - break; - case SOCKS5_REPLY_HOST_UNREACHABLE: - handler (boost::asio::error::make_error_code (boost::asio::error::host_unreachable)); - break; - case SOCKS5_REPLY_NETWORK_UNREACHABLE: - handler (boost::asio::error::make_error_code (boost::asio::error::network_unreachable)); - break; - case SOCKS5_REPLY_CONNECTION_REFUSED: - handler (boost::asio::error::make_error_code (boost::asio::error::connection_refused)); - break; - case SOCKS5_REPLY_TTL_EXPIRED: - handler (boost::asio::error::make_error_code (boost::asio::error::timed_out)); - break; - case SOCKS5_REPLY_COMMAND_NOT_SUPPORTED: - handler (boost::asio::error::make_error_code (boost::asio::error::operation_not_supported)); - break; - case SOCKS5_REPLY_ADDRESS_TYPE_NOT_SUPPORTED: - handler (boost::asio::error::make_error_code (boost::asio::error::no_protocol_option)); - break; - default: - handler (boost::asio::error::make_error_code (boost::asio::error::connection_aborted)); - } - } - else - handler (ec); - }); - } - - template - void Socks5Connect (Socket& s, Handler handler, std::shared_ptr > buff, uint16_t port) - { - if (buff && buff->size () >= 6) - { - (*buff)[0] = SOCKS5_VER; - (*buff)[1] = SOCKS5_CMD_CONNECT; - (*buff)[2] = 0x00; - htobe16buf(buff->data () + buff->size () - 2, port); - boost::asio::async_write(s, boost::asio::buffer(*buff), boost::asio::transfer_all(), - [buff, &s, handler](const boost::system::error_code& ec, std::size_t transferred) - { - (void) transferred; - if (!ec) - Socks5ReadReply (s, handler); - else - handler (ec); - }); - } - else - handler (boost::asio::error::make_error_code (boost::asio::error::no_buffer_space)); - } - - template - void Socks5Connect (Socket& s, const boost::asio::ip::tcp::endpoint& ep, Handler handler) - { - std::shared_ptr > buff; - if(ep.address ().is_v4 ()) - { - buff = std::make_shared >(10); - (*buff)[3] = SOCKS5_ATYP_IPV4; - auto addrbytes = ep.address ().to_v4().to_bytes(); - memcpy(buff->data () + 4, addrbytes.data(), 4); - } - else if (ep.address ().is_v6 ()) - { - buff = std::make_shared >(22); - (*buff)[3] = SOCKS5_ATYP_IPV6; - auto addrbytes = ep.address ().to_v6().to_bytes(); - memcpy(buff->data () + 4, addrbytes.data(), 16); - } - if (buff) - Socks5Connect (s, handler, buff, ep.port ()); - else - handler (boost::asio::error::make_error_code (boost::asio::error::fault)); - } - - template - void Socks5Connect (Socket& s, const std::pair& ep, Handler handler) - { - auto& addr = ep.first; - if (addr.length () <= 255) - { - auto buff = std::make_shared >(addr.length () + 7); - (*buff)[3] = SOCKS5_ATYP_NAME; - (*buff)[4] = addr.length (); - memcpy (buff->data () + 5, addr.c_str (), addr.length ()); - Socks5Connect (s, handler, buff, ep.second); - } - else - handler (boost::asio::error::make_error_code (boost::asio::error::name_too_long)); - } - - - template - void Socks5Handshake (Socket& s, Endpoint ep, Handler handler) - { - static const uint8_t methodSelection[3] = { SOCKS5_VER, 0x01, 0x00 }; // 1 method, no auth - boost::asio::async_write(s, boost::asio::buffer(methodSelection, 3), boost::asio::transfer_all(), - [&s, ep, handler] (const boost::system::error_code& ec, std::size_t transferred) - { - (void) transferred; - if (!ec) - { - auto readbuff = std::make_shared >(2); - boost::asio::async_read(s, boost::asio::buffer(*readbuff), boost::asio::transfer_all(), - [&s, ep, handler, readbuff] (const boost::system::error_code& ec, std::size_t transferred) - { - if (!ec) - { - if (transferred == 2 && (*readbuff)[1] == 0x00) // no auth - Socks5Connect (s, ep, handler); - else - handler (boost::asio::error::make_error_code (boost::asio::error::invalid_argument)); - } - else - handler (ec); - }); - } - else - handler (ec); - }); - } - -} -} - -#endif diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp deleted file mode 100644 index 99da5fd2..00000000 --- a/libi2pd/Streaming.cpp +++ /dev/null @@ -1,2102 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 "Crypto.h" -#include "Log.h" -#include "RouterInfo.h" -#include "RouterContext.h" -#include "Tunnel.h" -#include "Timestamp.h" -#include "Destination.h" -#include "Streaming.h" - -namespace i2p -{ -namespace stream -{ - void SendBufferQueue::Add (std::shared_ptr&& buf) - { - if (buf) - { - m_Size += buf->len; - m_Buffers.push_back (std::move (buf)); - } - } - - size_t SendBufferQueue::Get (uint8_t * buf, size_t len) - { - if (!m_Size) return 0; - size_t offset = 0; - if (len >= m_Size) - { - for (auto& it: m_Buffers) - { - auto rem = it->GetRemainingSize (); - memcpy (buf + offset, it->GetRemaningBuffer (), rem); - offset += rem; - } - m_Buffers.clear (); - m_Size = 0; - return offset; - } - else - { - while (!m_Buffers.empty () && offset < len) - { - auto nextBuffer = m_Buffers.front (); - auto rem = nextBuffer->GetRemainingSize (); - if (offset + rem <= len) - { - // whole buffer - memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), rem); - offset += rem; - m_Buffers.pop_front (); // delete it - } - else - { - // partially - rem = len - offset; - memcpy (buf + offset, nextBuffer->GetRemaningBuffer (), rem); - nextBuffer->offset += rem; - offset = len; // break - } - } - m_Size -= offset; - } - return offset; - } - - void SendBufferQueue::CleanUp () - { - if (!m_Buffers.empty ()) - { - for (auto& it: m_Buffers) - it->Cancel (); - m_Buffers.clear (); - m_Size = 0; - } - } - - Stream::Stream (boost::asio::io_context& service, StreamingDestination& local, - std::shared_ptr remote, int port): m_Service (service), - m_SendStreamID (0), m_SequenceNumber (0), m_DropWindowDelaySequenceNumber (0), - m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1), - m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed - m_Status (eStreamStatusNew), m_IsIncoming (false), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false), - m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false), m_IsClientChoked (false), - m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_DoubleWinIncCounter (false), m_LocalDestination (local), - m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), - m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), - m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_SlowRTT2 (INITIAL_RTT), m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), - m_WindowDropTargetSize (0), m_WindowIncCounter (0), m_RTO (INITIAL_RTO), - m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_PrevRTTSample (INITIAL_RTT), m_WindowSizeTail (0), - m_Jitter (0), m_MinPacingTime (0), - m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0), m_LastWindowIncTime (0), - m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed - m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU) - { - RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); - m_RemoteIdentity = remote->GetIdentity (); - auto outboundSpeed = local.GetOwner ()->GetStreamingOutboundSpeed (); - if (outboundSpeed) - m_MinPacingTime = (1000000LL*STREAMING_MTU)/outboundSpeed; - - auto inboundSpeed = local.GetOwner ()->GetStreamingInboundSpeed (); // for limit inbound speed - if (inboundSpeed) - m_PacketACKInterval = (1000000LL*STREAMING_MTU)/inboundSpeed; - } - - Stream::Stream (boost::asio::io_context& service, StreamingDestination& local): - m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_DropWindowDelaySequenceNumber (0), - m_TunnelsChangeSequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_PreviousReceivedSequenceNumber (-1), - m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed - m_Status (eStreamStatusNew), m_IsIncoming (true), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false), - m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false), m_IsClientChoked (false), - m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_DoubleWinIncCounter (false), m_LocalDestination (local), - m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), - m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_SlowRTT2 (INITIAL_RTT), - m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0), - m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), - m_PrevRTTSample (INITIAL_RTT), m_WindowSizeTail (0), m_Jitter (0), m_MinPacingTime (0), - m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_LastACKRecieveTime (0), m_ACKRecieveInterval (local.GetOwner ()->GetStreamingAckDelay ()), m_RemoteLeaseChangeTime (0), m_LastWindowIncTime (0), - m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed - m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU) - { - RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); - auto outboundSpeed = local.GetOwner ()->GetStreamingOutboundSpeed (); - if (outboundSpeed) - m_MinPacingTime = (1000000LL*STREAMING_MTU)/outboundSpeed; - - auto inboundSpeed = local.GetOwner ()->GetStreamingInboundSpeed (); // for limit inbound speed - if (inboundSpeed) - m_PacketACKInterval = (1000000LL*STREAMING_MTU)/inboundSpeed; - } - - Stream::~Stream () - { - CleanUp (); - LogPrint (eLogDebug, "Streaming: Stream deleted"); - } - - void Stream::Terminate (bool deleteFromDestination) // should be called from StreamingDestination::Stop only - { - m_Status = eStreamStatusTerminated; - m_AckSendTimer.cancel (); - m_ReceiveTimer.cancel (); - m_ResendTimer.cancel (); - m_SendTimer.cancel (); - //CleanUp (); /* Need to recheck - broke working on windows */ - if (deleteFromDestination) - m_LocalDestination.DeleteStream (shared_from_this ()); - } - - void Stream::CleanUp () - { - m_SendBuffer.CleanUp (); - while (!m_ReceiveQueue.empty ()) - { - auto packet = m_ReceiveQueue.front (); - m_ReceiveQueue.pop (); - m_LocalDestination.DeletePacket (packet); - } - - m_NACKedPackets.clear (); - - for (auto it: m_SentPackets) - m_LocalDestination.DeletePacket (it); - m_SentPackets.clear (); - - for (auto it: m_SavedPackets) - m_LocalDestination.DeletePacket (it); - m_SavedPackets.clear (); - } - - void Stream::HandleNextPacket (Packet * packet) - { - if (m_Status == eStreamStatusTerminated) - { - m_LocalDestination.DeletePacket (packet); - return; - } - m_NumReceivedBytes += packet->GetLength (); - if (!m_SendStreamID) - { - m_SendStreamID = packet->GetReceiveStreamID (); - if (!m_RemoteIdentity && packet->GetNACKCount () == 8 && // first incoming packet - memcmp (packet->GetNACKs (), m_LocalDestination.GetOwner ()->GetIdentHash (), 32)) - { - LogPrint (eLogWarning, "Streaming: Destination mismatch for ", m_LocalDestination.GetOwner ()->GetIdentHash ().ToBase32 ()); - m_LocalDestination.DeletePacket (packet); - return; - } - } - - if (!packet->IsNoAck ()) // ack received - ProcessAck (packet); - - int32_t receivedSeqn = packet->GetSeqn (); - if (!receivedSeqn && m_LastReceivedSequenceNumber >= 0) - { - uint16_t flags = packet->GetFlags (); - if (flags) - // plain ack with options - ProcessOptions (flags, packet); - else - // plain ack - { - LogPrint (eLogDebug, "Streaming: Plain ACK received"); - if (m_IsImmediateAckRequested) - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (m_IsFirstRttSample) - { - m_RTT = ts - m_LastSendTime; - m_IsFirstRttSample = false; - } - else - m_RTT = (m_RTT + (ts - m_LastSendTime)) / 2; - m_IsImmediateAckRequested = false; - } - } - m_LocalDestination.DeletePacket (packet); - return; - } - - LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn, " on sSID=", m_SendStreamID); - if (receivedSeqn == m_LastReceivedSequenceNumber + 1) - { - // we have received next in sequence message - ProcessPacket (packet); - if (m_Status == eStreamStatusTerminated) return; - - // we should also try stored messages if any - for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();) - { - if ((*it)->GetSeqn () == (uint32_t)(m_LastReceivedSequenceNumber + 1)) - { - Packet * savedPacket = *it; - m_SavedPackets.erase (it++); - - ProcessPacket (savedPacket); - if (m_Status == eStreamStatusTerminated) return; - } - else - break; - } - - // schedule ack for last message - if (m_Status == eStreamStatusOpen) - { - if (!m_IsAckSendScheduled) - { - auto ackTimeout = m_RTT/10; - if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; - ScheduleAck (ackTimeout); - } - } - else if (packet->IsSYN ()) - // we have to send SYN back to incoming connection - SendBuffer (); // also sets m_IsOpen - } - else - { - if (receivedSeqn <= m_LastReceivedSequenceNumber) - { - // we have received duplicate - LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " on sSID=", m_SendStreamID); - if (receivedSeqn <= m_PreviousReceivedSequenceNumber || receivedSeqn == m_LastReceivedSequenceNumber) - { - m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); - CancelRemoteLeaseChange (); - UpdateCurrentRemoteLease (); - } - m_PreviousReceivedSequenceNumber = receivedSeqn; - m_LocalDestination.DeletePacket (packet); // packet dropped - if (!m_IsAckSendScheduled) - { - SendQuickAck (); // resend ack for previous message again - auto ackTimeout = m_RTT/10; - if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; - ScheduleAck (ackTimeout); - } - } - else - { - LogPrint (eLogWarning, "Streaming: Missing messages on sSID=", m_SendStreamID, ": from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1); - // save message and wait for missing message again - SavePacket (packet); - if (m_LastReceivedSequenceNumber >= 0) - { - if (!m_IsAckSendScheduled) - { - // send NACKs for missing messages - SendQuickAck (); - auto ackTimeout = m_RTT/10; - if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; - ScheduleAck (ackTimeout); - } - } - else - // wait for SYN - ScheduleAck (SYN_TIMEOUT); - } - } - } - - void Stream::SavePacket (Packet * packet) - { - if (!m_SavedPackets.insert (packet).second) - m_LocalDestination.DeletePacket (packet); - } - - void Stream::ProcessPacket (Packet * packet) - { - uint32_t receivedSeqn = packet->GetSeqn (); - uint16_t flags = packet->GetFlags (); - LogPrint (eLogDebug, "Streaming: Process seqn=", receivedSeqn, ", flags=", flags); - - if (!ProcessOptions (flags, packet)) - { - m_LocalDestination.DeletePacket (packet); - Terminate (); - return; - } - - packet->offset = packet->GetPayload () - packet->buf; - if (packet->GetLength () > 0) - { - m_ReceiveQueue.push (packet); - m_ReceiveTimer.cancel (); - } - else - m_LocalDestination.DeletePacket (packet); - - m_LastReceivedSequenceNumber = receivedSeqn; - - if (flags & PACKET_FLAG_RESET) - { - LogPrint (eLogDebug, "Streaming: closing stream sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ": reset flag received in packet #", receivedSeqn); - m_Status = eStreamStatusReset; - Close (); - } - else if (flags & PACKET_FLAG_CLOSE) - { - if (m_Status != eStreamStatusClosed) - SendClose (); - m_Status = eStreamStatusClosed; - Terminate (); - } - } - - bool Stream::ProcessOptions (uint16_t flags, Packet * packet) - { - const uint8_t * optionData = packet->GetOptionData (); - size_t optionSize = packet->GetOptionSize (); - if (optionSize > packet->len) - { - LogPrint (eLogInfo, "Streaming: Invalid option size ", optionSize, " Discarded"); - return false; - } - if (!flags) return true; - bool immediateAckRequested = false; - if (flags & PACKET_FLAG_DELAY_REQUESTED) - { - uint16_t delayRequested = bufbe16toh (optionData); - if (!delayRequested) // 0 requests an immediate ack - immediateAckRequested = true; - else if (!m_IsAckSendScheduled) - { - if (delayRequested < m_RTT) - { - m_IsAckSendScheduled = true; - m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(delayRequested)); - m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, - shared_from_this (), std::placeholders::_1)); - } - if (delayRequested >= DELAY_CHOKING) - { - if (!m_IsClientChoked) - { - LogPrint (eLogDebug, "Streaming: Client choked, set min. window size"); - m_WindowDropTargetSize = MIN_WINDOW_SIZE; - m_LastWindowDropSize = 0; - m_WindowIncCounter = 0; - m_IsClientChoked = true; - m_IsWinDropped = false; - m_DropWindowDelaySequenceNumber = m_SequenceNumber; - UpdatePacingTime (); - } - } - } - optionData += 2; - } - - if (flags & PACKET_FLAG_FROM_INCLUDED) - { - if (m_RemoteLeaseSet) m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity (); - if (!m_RemoteIdentity) - m_RemoteIdentity = std::make_shared(optionData, optionSize); - if (m_RemoteIdentity->IsRSA ()) - { - LogPrint (eLogInfo, "Streaming: Incoming stream from RSA destination ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " Discarded"); - return false; - } - optionData += m_RemoteIdentity->GetFullLen (); - if (!m_RemoteLeaseSet) - LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); - } - - if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) - { - uint16_t maxPacketSize = bufbe16toh (optionData); - LogPrint (eLogDebug, "Streaming: Max packet size ", maxPacketSize); - optionData += 2; - } - - if (flags & PACKET_FLAG_OFFLINE_SIGNATURE) - { - if (!m_RemoteIdentity) - { - LogPrint (eLogInfo, "Streaming: offline signature without identity"); - return false; - } - // if we have it in LeaseSet already we don't need to parse it again - if (m_RemoteLeaseSet) m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier (); - if (m_TransientVerifier) - { - // skip option data - optionData += 6; // timestamp and key type - optionData += m_TransientVerifier->GetPublicKeyLen (); // public key - optionData += m_RemoteIdentity->GetSignatureLen (); // signature - } - else - { - // transient key - size_t offset = 0; - m_TransientVerifier = i2p::data::ProcessOfflineSignature (m_RemoteIdentity, optionData, optionSize - (optionData - packet->GetOptionData ()), offset); - optionData += offset; - if (!m_TransientVerifier) - { - LogPrint (eLogError, "Streaming: offline signature failed"); - return false; - } - } - } - - if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) - { - bool verified = false; - auto signatureLen = m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : m_RemoteIdentity->GetSignatureLen (); - if (signatureLen > packet->GetLength ()) - { - LogPrint (eLogError, "Streaming: Signature too big, ", signatureLen, " bytes"); - return false; - } - if(signatureLen <= 256) - { - // standard - uint8_t signature[256]; - memcpy (signature, optionData, signatureLen); - memset (const_cast(optionData), 0, signatureLen); - verified = m_TransientVerifier ? - m_TransientVerifier->Verify (packet->GetBuffer (), packet->GetLength (), signature) : - m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature); - if (verified) - memcpy (const_cast(optionData), signature, signatureLen); - } - else - { - // post quantum - std::vector signature(signatureLen); - memcpy (signature.data (), optionData, signatureLen); - memset (const_cast(optionData), 0, signatureLen); - verified = m_TransientVerifier ? - m_TransientVerifier->Verify (packet->GetBuffer (), packet->GetLength (), signature.data ()) : - m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature.data ()); - } - if (verified) - optionData += signatureLen; - else - { - LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); - return false; - } - } - if (immediateAckRequested) - SendQuickAck (); - 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 - auto payloadLen = int(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 { &p }); - LogPrint (eLogDebug, "Streaming: Pong of ", p.len, " bytes sent"); - } - m_LocalDestination.DeletePacket (packet); - } - - void Stream::ProcessAck (Packet * packet) - { - bool acknowledged = false; - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - uint32_t ackThrough = packet->GetAckThrough (); - m_NACKedPackets.clear (); - if (ackThrough > m_SequenceNumber) - { - LogPrint (eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber); - return; - } - int rttSample = INT_MAX; - int incCounter = 0; - m_IsNAcked = false; - m_IsResendNeeded = false; - int nackCount = packet->GetNACKCount (); - for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();) - { - auto seqn = (*it)->GetSeqn (); - if (seqn <= ackThrough) - { - if (nackCount > 0) - { - bool nacked = false; - for (int i = 0; i < nackCount; i++) - if (seqn == packet->GetNACK (i)) - { - m_NACKedPackets.insert (*it); - m_IsNAcked = true; - nacked = true; - break; - } - if (nacked) - { - LogPrint (eLogDebug, "Streaming: Packet ", seqn, " NACK"); - ++it; - continue; - } - } - auto sentPacket = *it; - int64_t rtt = (int64_t)ts - (int64_t)sentPacket->sendTime; - if (rtt < 0) - LogPrint (eLogError, "Streaming: Packet ", seqn, "sent from the future, sendTime=", sentPacket->sendTime); - if (!seqn) - { - m_IsFirstRttSample = true; - rttSample = rtt < 0 ? 1 : rtt; - } - else if (!sentPacket->resent && seqn > m_TunnelsChangeSequenceNumber && rtt >= 0) - rttSample = std::min (rttSample, (int)rtt); - LogPrint (eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt, " sentTime=", sentPacket->sendTime); - m_SentPackets.erase (it++); - m_LocalDestination.DeletePacket (sentPacket); - acknowledged = true; - if (m_WindowIncCounter < MAX_WINDOW_SIZE && !m_IsFirstACK && !m_IsWinDropped) - incCounter++; - } - else - break; - } - if (m_LastACKRecieveTime) - { - uint64_t interval = ts - m_LastACKRecieveTime; - if (m_ACKRecieveInterval) - m_ACKRecieveInterval = (m_ACKRecieveInterval + interval) / 2; - else - m_ACKRecieveInterval = interval; - } - m_LastACKRecieveTime = ts; - if (rttSample != INT_MAX) - { - if (m_IsFirstRttSample && !m_IsFirstACK) - { - m_RTT = rttSample; - m_SlowRTT = rttSample; - m_SlowRTT2 = rttSample; - m_PrevRTTSample = rttSample; - m_Jitter = rttSample / 10; // 10% - m_Jitter += 15; // for low-latency connections - m_IsFirstRttSample = false; - } - else - m_RTT = (m_PrevRTTSample + rttSample) / 2; - if (!m_IsWinDropped) - { - m_SlowRTT = SLOWRTT_EWMA_ALPHA * m_RTT + (1.0 - SLOWRTT_EWMA_ALPHA) * m_SlowRTT; - m_SlowRTT2 = RTT_EWMA_ALPHA * m_RTT + (1.0 - RTT_EWMA_ALPHA) * m_SlowRTT2; - // calculate jitter - double jitter = 0; - if (rttSample > m_PrevRTTSample) - jitter = rttSample - m_PrevRTTSample; - else if (rttSample < m_PrevRTTSample) - jitter = m_PrevRTTSample - rttSample; - else - jitter = rttSample / 10; // 10% - jitter += 15; // for low-latency connections - m_Jitter = (0.05 * jitter) + (1.0 - 0.05) * m_Jitter; - } - if (rttSample > m_SlowRTT) - { - incCounter = 0; - m_DoubleWinIncCounter = 1; - } - else if (rttSample < m_SlowRTT) - { - if (m_DoubleWinIncCounter) - { - incCounter = incCounter * 2; - m_DoubleWinIncCounter = 0; - } - } - m_WindowIncCounter = m_WindowIncCounter + incCounter; - // - // delay-based CC - if ((m_SlowRTT2 > m_SlowRTT + m_Jitter && rttSample > m_SlowRTT2 && rttSample > m_PrevRTTSample) && !m_IsWinDropped && !m_IsClientChoked) // Drop window if RTT grows too fast, late detection - { - LogPrint (eLogDebug, "Streaming: Congestion detected, reduce window size"); - ProcessWindowDrop (); - } - UpdatePacingTime (); - m_PrevRTTSample = rttSample; - - bool wasInitial = m_RTO == INITIAL_RTO; - m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.3 + m_Jitter + m_ACKRecieveInterval)); // TODO: implement it better - - if (wasInitial) - ScheduleResend (); - } - if (m_IsClientChoked && ackThrough >= m_DropWindowDelaySequenceNumber) - m_IsClientChoked = false; - if (m_IsWinDropped && ackThrough > m_DropWindowDelaySequenceNumber) - { - m_IsFirstRttSample = true; - m_IsWinDropped = false; - } - if (m_WindowDropTargetSize && int(m_SentPackets.size ()) <= m_WindowDropTargetSize) - { - m_WindowSize = m_WindowDropTargetSize; - m_WindowDropTargetSize = 0; - } - if (acknowledged || m_IsNAcked) - { - ScheduleResend (); - } - if (m_SendBuffer.IsEmpty () && m_SentPackets.size () > 0) // tail loss - { - m_IsResendNeeded = true; - m_RTO = std::max (MIN_RTO, (int)(m_RTT * 1.5 + m_Jitter + m_ACKRecieveInterval)); // to prevent spurious retransmit - } - if (m_SentPackets.empty () && m_SendBuffer.IsEmpty ()) - { - m_ResendTimer.cancel (); - m_SendTimer.cancel (); - m_LastACKRecieveTime = 0; - m_ACKRecieveInterval = m_AckDelay; - } - if (acknowledged && m_IsFirstACK) - { - if (m_RoutingSession) - m_RoutingSession->SetSharedRoutingPath ( - std::make_shared ( - i2p::garlic::GarlicRoutingPath{m_CurrentOutboundTunnel, m_CurrentRemoteLease, (int)m_RTT, 0})); - m_IsFirstACK = false; - } - if (acknowledged) - { - m_NumResendAttempts = 0; - m_IsTimeOutResend = false; - SendBuffer (); - } - if (m_Status == eStreamStatusClosed) - Terminate (); - else if (m_Status == eStreamStatusClosing) - Close (); // check is all outgoing messages have been sent and we can send close - } - - size_t Stream::Receive (uint8_t * buf, size_t len, int timeout) - { - if (!len) return 0; - size_t ret = 0; - volatile bool done = false; - std::condition_variable newDataReceived; - std::mutex newDataReceivedMutex; - AsyncReceive (boost::asio::buffer (buf, len), - [&ret, &done, &newDataReceived, &newDataReceivedMutex](const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode == boost::asio::error::timed_out) - ret = 0; - else - ret = bytes_transferred; - std::unique_lock l(newDataReceivedMutex); - newDataReceived.notify_all (); - done = true; - }, - timeout); - if (!done) - { std::unique_lock l(newDataReceivedMutex); - if (!done && newDataReceived.wait_for (l, std::chrono::seconds (timeout)) == std::cv_status::timeout) - ret = 0; - } - if (!done) - { - // make sure that AsycReceive complete - auto s = shared_from_this(); - boost::asio::post (m_Service, [s]() - { - s->m_ReceiveTimer.cancel (); - }); - int i = 0; - while (!done && i < 100) // 1 sec - { - std::this_thread::sleep_for (std::chrono::milliseconds(10)); - i++; - } - } - return ret; - } - - size_t Stream::Send (const uint8_t * buf, size_t len) - { - AsyncSend (buf, len, nullptr); - return len; - } - - void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler) - { - std::shared_ptr buffer; - if (len > 0 && buf) - buffer = std::make_shared(buf, len, handler); - else if (handler) - handler(boost::system::error_code ()); - auto s = shared_from_this (); - boost::asio::post (m_Service, [s, buffer = std::move(buffer)]() mutable - { - if (buffer) - s->m_SendBuffer.Add (std::move(buffer)); - s->SendBuffer (); - }); - } - - void Stream::SendBuffer () - { - if (m_RemoteLeaseSet) // don't scheudle send for first SYN for incoming stream - ScheduleSend (); - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - int numMsgs = m_WindowSize - m_SentPackets.size (); - if (numMsgs <= 0 || !m_IsSendTime) // window is full - { - m_LastSendTime = ts; - return; - } - else if (numMsgs > m_NumPacketsToSend) - numMsgs = m_NumPacketsToSend; - bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet - std::vector packets; - while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.IsEmpty () && numMsgs > 0)) - { - Packet * p = m_LocalDestination.NewPacket (); - uint8_t * packet = p->GetBuffer (); - // TODO: implement setters - size_t size = 0; - htobe32buf (packet + size, m_SendStreamID); - size += 4; // sendStreamID - htobe32buf (packet + size, m_RecvStreamID); - size += 4; // receiveStreamID - htobe32buf (packet + size, m_SequenceNumber++); - size += 4; // sequenceNum - if (isNoAck) - htobuf32 (packet + size, 0); - else - htobe32buf (packet + size, m_LastReceivedSequenceNumber); - size += 4; // ack Through - if (m_Status == eStreamStatusNew && !m_SendStreamID && m_RemoteIdentity) - { - // first SYN packet - packet[size] = 8; - size++; // NACK count - memcpy (packet + size, m_RemoteIdentity->GetIdentHash (), 32); - size += 32; - } - else - { - packet[size] = 0; - size++; // NACK count - } - packet[size] = m_RTO/1000; - size++; // resend delay - if (m_Status == eStreamStatusNew) - { - // initial packet - m_Status = eStreamStatusOpen; - if (!m_RemoteLeaseSet) m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ());; - if (m_RemoteLeaseSet) - { - m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true, !m_IsIncoming); - m_MTU = (m_RoutingSession && m_RoutingSession->IsRatchets ()) ? STREAMING_MTU_RATCHETS : STREAMING_MTU; - } - uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | - PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; - if (isNoAck) flags |= PACKET_FLAG_NO_ACK; - bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature (); - if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; - htobe16buf (packet + size, flags); - size += 2; // flags - size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen (); - size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); - uint8_t * optionsSize = packet + size; // set options size later - size += 2; // options size - m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); - size += identityLen; // from - htobe16buf (packet + size, m_MTU); - size += 2; // max packet size - if (isOfflineSignature) - { - const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); - memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); - size += offlineSignature.size (); // offline signature - } - uint8_t * signature = packet + size; // set it later - memset (signature, 0, signatureLen); // zeroes for now - size += signatureLen; // signature - htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size - size += m_SendBuffer.Get (packet + size, m_MTU); // payload - m_LocalDestination.GetOwner ()->Sign (packet, size, signature); - } - else - { - // follow on packet - htobuf16 (packet + size, 0); - size += 2; // flags - htobuf16 (packet + size, 0); // no options - size += 2; // options size - size += m_SendBuffer.Get(packet + size, m_MTU); // payload - } - p->len = size; - packets.push_back (p); - numMsgs--; - } - if (packets.size () > 0) - { - if (m_SavedPackets.empty ()) // no NACKS - { - m_IsAckSendScheduled = false; - m_AckSendTimer.cancel (); - } - bool isEmpty = m_SentPackets.empty (); -// auto ts = i2p::util::GetMillisecondsSinceEpoch (); - for (auto& it: packets) - { - it->sendTime = ts; - m_SentPackets.insert (it); - } - SendPackets (packets); - m_LastSendTime = ts; - m_IsSendTime = false; - if (m_Status == eStreamStatusClosing && m_SendBuffer.IsEmpty ()) - SendClose (); - if (isEmpty) - ScheduleResend (); - } - } - - void Stream::SendQuickAck () - { - int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber; - // for limit inbound speed - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - int numPackets = 0; - bool lostPackets = false; - int64_t passedTime = m_PacketACKInterval * INITIAL_WINDOW_SIZE; // in microseconds // while m_LastACKSendTime == 0 - if (m_LastACKSendTime) - passedTime = (ts - m_LastACKSendTime)*1000; // in microseconds - numPackets = (passedTime + m_PacketACKIntervalRem) / m_PacketACKInterval; - m_PacketACKIntervalRem = (passedTime + m_PacketACKIntervalRem) - (numPackets * m_PacketACKInterval); - if (m_LastConfirmedReceivedSequenceNumber + numPackets < m_LastReceivedSequenceNumber) - { - lastReceivedSeqn = m_LastConfirmedReceivedSequenceNumber + numPackets; - if (!m_IsAckSendScheduled) - { - auto ackTimeout = m_RTT/10; - if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; - ScheduleAck (ackTimeout); - } - } - if (numPackets == 0) return; - // for limit inbound speed - if (!m_SavedPackets.empty ()) - { - for (auto it: m_SavedPackets) - { - auto seqn = it->GetSeqn (); - // for limit inbound speed - if (m_LastConfirmedReceivedSequenceNumber + numPackets < int(seqn)) - { - if (!m_IsAckSendScheduled) - { - auto ackTimeout = m_RTT/10; - if (ackTimeout > m_AckDelay) ackTimeout = m_AckDelay; - ScheduleAck (ackTimeout); - } - if (lostPackets) - break; - else - return; - } - // for limit inbound speed - if ((int)seqn > lastReceivedSeqn) - { - lastReceivedSeqn = seqn; - lostPackets = true; // for limit inbound speed - } - } - } - if (lastReceivedSeqn < 0) - { - LogPrint (eLogError, "Streaming: No packets have been received yet"); - return; - } - - Packet p; - uint8_t * packet = p.GetBuffer (); - size_t size = 0; - htobe32buf (packet + size, m_SendStreamID); - size += 4; // sendStreamID - htobe32buf (packet + size, m_RecvStreamID); - size += 4; // receiveStreamID - htobuf32 (packet + size, 0); // this is plain Ack message - size += 4; // sequenceNum - htobe32buf (packet + size, lastReceivedSeqn); - size += 4; // ack Through - uint8_t numNacks = 0; - bool choking = false; - if (lastReceivedSeqn > m_LastReceivedSequenceNumber) - { - // fill NACKs - uint8_t * nacks = packet + size + 1; - auto nextSeqn = m_LastReceivedSequenceNumber + 1; - for (auto it: m_SavedPackets) - { - auto seqn = it->GetSeqn (); - if (m_LastConfirmedReceivedSequenceNumber + numPackets < int(seqn)) // for limit inbound speed - { - htobe32buf (packet + 12, nextSeqn - 1); - break; - } - if (numNacks + (seqn - nextSeqn) >= 256) - { - LogPrint (eLogError, "Streaming: Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn); - htobe32buf (packet + 12, nextSeqn - 1); // change ack Through back - choking = true; - break; - } - for (uint32_t i = nextSeqn; i < seqn; i++) - { - htobe32buf (nacks, i); - nacks += 4; - numNacks++; - } - nextSeqn = seqn + 1; - } - packet[size] = numNacks; - size++; // NACK count - size += numNacks*4; // NACKs - } - else - { - // No NACKs - packet[size] = 0; - size++; // NACK count - } - packet[size] = 0; - size++; // resend delay - bool requestImmediateAck = false; - if (!choking) - requestImmediateAck = m_LastSendTime && ts > m_LastSendTime + REQUEST_IMMEDIATE_ACK_INTERVAL && - ts > m_LastSendTime + REQUEST_IMMEDIATE_ACK_INTERVAL + m_LocalDestination.GetRandom () % REQUEST_IMMEDIATE_ACK_INTERVAL_VARIANCE; - htobe16buf (packet + size, (choking || requestImmediateAck) ? PACKET_FLAG_DELAY_REQUESTED : 0); // no flags set or delay requested - size += 2; // flags - if (choking || requestImmediateAck) - { - htobe16buf (packet + size, 2); // 2 bytes delay interval - htobe16buf (packet + size + 2, choking ? DELAY_CHOKING : 0); // set choking or immediate ack interval - size += 2; - if (requestImmediateAck) // ack request sent - { - m_LastSendTime = ts; - m_IsImmediateAckRequested = true; - } - } - else - htobuf16 (packet + size, 0); // no options - size += 2; // options size - p.len = size; - - SendPackets (std::vector { &p }); - m_LastACKSendTime = ts; // for limit inbound speed - m_LastConfirmedReceivedSequenceNumber = lastReceivedSeqn; // for limit inbound speed - LogPrint (eLogDebug, "Streaming: Quick Ack sent. ", (int)numNacks, " NACKs"); - } - - void Stream::SendPing () - { - Packet p; - uint8_t * packet = p.GetBuffer (); - size_t size = 0; - htobe32buf (packet, m_RecvStreamID); - size += 4; // sendStreamID - memset (packet + size, 0, 14); - size += 14; // all zeroes - uint16_t flags = PACKET_FLAG_ECHO | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_FROM_INCLUDED; - bool isOfflineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().IsOfflineSignature (); - if (isOfflineSignature) flags |= PACKET_FLAG_OFFLINE_SIGNATURE; - htobe16buf (packet + size, flags); - size += 2; // flags - size_t identityLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetFullLen (); - size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); - uint8_t * optionsSize = packet + size; // set options size later - size += 2; // options size - m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); - size += identityLen; // from - if (isOfflineSignature) - { - const auto& offlineSignature = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetOfflineSignature (); - memcpy (packet + size, offlineSignature.data (), offlineSignature.size ()); - size += offlineSignature.size (); // offline signature - } - uint8_t * signature = packet + size; // set it later - memset (signature, 0, signatureLen); // zeroes for now - size += signatureLen; // signature - htobe16buf (optionsSize, packet + size - 2 - optionsSize); // actual options size - m_LocalDestination.GetOwner ()->Sign (packet, size, signature); - p.len = size; - SendPackets (std::vector { &p }); - LogPrint (eLogDebug, "Streaming: Ping of ", p.len, " bytes sent"); - } - - void Stream::Close () - { - LogPrint(eLogDebug, "Streaming: closing stream with sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID, ", status=", m_Status); - switch (m_Status) - { - case eStreamStatusOpen: - m_Status = eStreamStatusClosing; - Close (); // recursion - if (m_Status == eStreamStatusClosing) //still closing - LogPrint (eLogDebug, "Streaming: Trying to send stream data before closing, sSID=", m_SendStreamID); - break; - case eStreamStatusReset: - // TODO: send reset - Terminate (); - break; - case eStreamStatusClosing: - if (m_SentPackets.empty () && m_SendBuffer.IsEmpty ()) // nothing to send - { - m_Status = eStreamStatusClosed; - SendClose(); - } - break; - case eStreamStatusClosed: - // already closed - Terminate (); - break; - default: - LogPrint (eLogWarning, "Streaming: Unexpected stream status=", (int)m_Status, " for sSID=", m_SendStreamID); - }; - } - - void Stream::SendClose () - { - Packet * p = m_LocalDestination.NewPacket (); - uint8_t * packet = p->GetBuffer (); - size_t size = 0; - htobe32buf (packet + size, m_SendStreamID); - size += 4; // sendStreamID - htobe32buf (packet + size, m_RecvStreamID); - size += 4; // receiveStreamID - htobe32buf (packet + size, m_SequenceNumber++); - size += 4; // sequenceNum - htobe32buf (packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0); - size += 4; // ack Through - packet[size] = 0; - size++; // NACK count - packet[size] = 0; - size++; // resend delay - htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); - size += 2; // flags - size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen (); - htobe16buf (packet + size, signatureLen); // signature only - size += 2; // options size - uint8_t * signature = packet + size; - memset (packet + size, 0, signatureLen); - size += signatureLen; // signature - m_LocalDestination.GetOwner ()->Sign (packet, size, signature); - - p->len = size; - boost::asio::post (m_Service, std::bind (&Stream::SendPacket, shared_from_this (), p)); - LogPrint (eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID); - } - - size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len) - { - size_t pos = 0; - while (pos < len && !m_ReceiveQueue.empty ()) - { - Packet * packet = m_ReceiveQueue.front (); - size_t l = std::min (packet->GetLength (), len - pos); - memcpy (buf + pos, packet->GetBuffer (), l); - pos += l; - packet->offset += l; - if (!packet->GetLength ()) - { - m_ReceiveQueue.pop (); - m_LocalDestination.DeletePacket (packet); - } - } - return pos; - } - - bool Stream::SendPacket (Packet * packet) - { - if (packet) - { - if (m_IsAckSendScheduled) - { - m_IsAckSendScheduled = false; - m_AckSendTimer.cancel (); - } - if (!packet->sendTime) packet->sendTime = i2p::util::GetMillisecondsSinceEpoch (); - SendPackets (std::vector { packet }); - bool isEmpty = m_SentPackets.empty (); - m_SentPackets.insert (packet); - if (isEmpty) - ScheduleResend (); - return true; - } - else - return false; - } - - void Stream::SendPackets (const std::vector& packets) - { - if (!m_RemoteLeaseSet) - { - CancelRemoteLeaseChange (); - UpdateCurrentRemoteLease (); - if (!m_RemoteLeaseSet) - { - LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet, sSID=", m_SendStreamID); - return; - } - } - if (!m_RoutingSession || m_RoutingSession->IsTerminated () || !m_RoutingSession->IsReadyToSend ()) // expired and detached or new session sent - { - m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true, !m_IsIncoming || m_SequenceNumber > 1); - if (!m_RoutingSession) - { - LogPrint (eLogError, "Streaming: Can't obtain routing session, sSID=", m_SendStreamID); - Terminate (); - return; - } - } - if (!m_CurrentOutboundTunnel && m_RoutingSession) // first message to send - { - // try to get shared path first - auto routingPath = m_RoutingSession->GetSharedRoutingPath (); - if (routingPath) - { - m_CurrentOutboundTunnel = routingPath->outboundTunnel; - m_CurrentRemoteLease = routingPath->remoteLease; - m_RTT = routingPath->rtt; - } - } - - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate) // excluded from LeaseSet - { - CancelRemoteLeaseChange (); - UpdateCurrentRemoteLease (true); - } - if (m_RemoteLeaseChangeTime && m_IsRemoteLeaseChangeInProgress && ts > m_RemoteLeaseChangeTime + INITIAL_RTO) - { - LogPrint (eLogDebug, "Streaming: RemoteLease changed, set initial window size"); - CancelRemoteLeaseChange (); - m_CurrentRemoteLease = m_NextRemoteLease; - ResetWindowSize (); - } - auto currentRemoteLease = m_CurrentRemoteLease; - if (!m_IsRemoteLeaseChangeInProgress && m_RemoteLeaseSet && m_CurrentRemoteLease && ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD) - { - auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false); - if (leases.size ()) - { - m_IsRemoteLeaseChangeInProgress = true; - UpdateCurrentRemoteLease (true); - m_NextRemoteLease = m_CurrentRemoteLease; - } - else - UpdateCurrentRemoteLease (true); - } - if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) - { - bool freshTunnel = false; - if (!m_CurrentOutboundTunnel) - { - auto leaseRouter = i2p::data::netdb.FindRouter (m_CurrentRemoteLease->tunnelGateway); - m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (nullptr, - leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports); - freshTunnel = true; - } - else if (!m_CurrentOutboundTunnel->IsEstablished ()) - std::tie(m_CurrentOutboundTunnel, freshTunnel) = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); - if (!m_CurrentOutboundTunnel) - { - LogPrint (eLogError, "Streaming: No outbound tunnels in the pool, sSID=", m_SendStreamID); - m_CurrentRemoteLease = nullptr; - return; - } - if (freshTunnel) - { - LogPrint (eLogDebug, "Streaming: OutboundTunnel changed, set initial window size"); - ResetWindowSize (); -// m_TunnelsChangeSequenceNumber = m_SequenceNumber; // should be determined more precisely - } - - std::vector msgs; - for (const auto& it: packets) - { - auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage ( - it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets (), it->IsSYN ())); - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeTunnel, - m_CurrentRemoteLease->tunnelGateway, m_CurrentRemoteLease->tunnelID, - msg - }); - m_NumSentBytes += it->GetLength (); - if (m_IsRemoteLeaseChangeInProgress && !m_RemoteLeaseChangeTime) - { - m_RemoteLeaseChangeTime = ts; - m_CurrentRemoteLease = currentRemoteLease; // change it back before new lease is confirmed - } - } - m_CurrentOutboundTunnel->SendTunnelDataMsgs (msgs); - } - else - { - LogPrint (eLogWarning, "Streaming: Remote lease is not available, sSID=", m_SendStreamID); - if (m_RoutingSession) - m_RoutingSession->SetSharedRoutingPath (nullptr); // invalidate routing path - } - } - - void Stream::SendUpdatedLeaseSet () - { - if (m_RoutingSession && !m_RoutingSession->IsTerminated ()) - { - if (m_RoutingSession->IsLeaseSetNonConfirmed ()) - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (ts > m_RoutingSession->GetLeaseSetSubmissionTime () + i2p::garlic::LEASESET_CONFIRMATION_TIMEOUT) - { - // LeaseSet was not confirmed, should try other tunnels - LogPrint (eLogWarning, "Streaming: LeaseSet was not confirmed in ", i2p::garlic::LEASESET_CONFIRMATION_TIMEOUT, " milliseconds. Trying to resubmit"); - m_RoutingSession->SetSharedRoutingPath (nullptr); - m_CurrentOutboundTunnel = nullptr; - m_CurrentRemoteLease = nullptr; - SendQuickAck (); - } - } - else if (m_RoutingSession->IsLeaseSetUpdated ()) - { - LogPrint (eLogDebug, "Streaming: sending updated LeaseSet"); - SendQuickAck (); - } - } - else - SendQuickAck (); - } - - void Stream::ScheduleSend () - { - if (m_Status != eStreamStatusTerminated) - { - m_SendTimer.cancel (); - m_SendTimer.expires_from_now (boost::posix_time::microseconds( - SEND_INTERVAL + m_LocalDestination.GetRandom () % SEND_INTERVAL_VARIANCE)); - m_SendTimer.async_wait (std::bind (&Stream::HandleSendTimer, - shared_from_this (), std::placeholders::_1)); - } - } - - void Stream::HandleSendTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (m_LastSendTime && ts*1000 > m_LastSendTime*1000 + m_PacingTime) - { - if (m_PacingTime) - { - auto numPackets = std::lldiv (m_PacingTimeRem + ts*1000 - m_LastSendTime*1000, m_PacingTime); - m_NumPacketsToSend = numPackets.quot; - m_PacingTimeRem = numPackets.rem; - } - else - { - LogPrint (eLogError, "Streaming: pacing time is zero"); - m_NumPacketsToSend = 1; m_PacingTimeRem = 0; - } - m_IsSendTime = true; - if (m_WindowIncCounter && (m_WindowSize < MAX_WINDOW_SIZE || m_WindowDropTargetSize) && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime && m_RTT <= m_SlowRTT) - { - float winSize = m_WindowSize; - if (m_WindowDropTargetSize) - winSize = m_WindowDropTargetSize; - float maxWinSize = MAX_WINDOW_SIZE; - if (m_LastWindowIncTime) - maxWinSize = (ts - m_LastWindowIncTime) / (m_RTT / MAX_WINDOW_SIZE_INC_PER_RTT) + winSize; - for (int i = 0; i < m_NumPacketsToSend; i++) - { - if (m_WindowIncCounter) - { - if (m_WindowDropTargetSize) - { - if (m_LastWindowDropSize && (m_LastWindowDropSize >= m_WindowDropTargetSize)) - m_WindowDropTargetSize += 1 - (1 / ((m_LastWindowDropSize + PREV_SPEED_KEEP_TIME_COEFF) / m_WindowDropTargetSize)); // some magic here - else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowDropTargetSize)) - m_WindowDropTargetSize += (m_WindowDropTargetSize - (m_LastWindowDropSize - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize; // some magic here - else - m_WindowDropTargetSize += (m_WindowDropTargetSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize; - if (m_WindowDropTargetSize > MAX_WINDOW_SIZE) m_WindowDropTargetSize = MAX_WINDOW_SIZE; - m_WindowIncCounter--; - if (m_WindowDropTargetSize >= maxWinSize) - { - m_WindowDropTargetSize = maxWinSize; - break; - } - } - else - { - if (m_LastWindowDropSize && (m_LastWindowDropSize >= m_WindowSize)) - m_WindowSize += 1 - (1 / ((m_LastWindowDropSize + PREV_SPEED_KEEP_TIME_COEFF) / m_WindowSize)); // some magic here - else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowSize)) - m_WindowSize += (m_WindowSize - (m_LastWindowDropSize - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowSize; // some magic here - else - m_WindowSize += (m_WindowSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowSize; - if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE; - m_WindowIncCounter--; - if (m_WindowSize >= maxWinSize) - { - m_WindowSize = maxWinSize; - break; - } - } - } - else - break; - } - m_LastWindowIncTime = ts; - UpdatePacingTime (); - } - else if (m_WindowIncCounter && m_WindowSize == MAX_WINDOW_SIZE && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime) - { - m_WindowSizeTail = m_WindowSizeTail + m_WindowIncCounter; - if (m_WindowSizeTail > MAX_WINDOW_SIZE) m_WindowSizeTail = MAX_WINDOW_SIZE; - } - if (m_IsNAcked || m_IsResendNeeded || m_IsClientChoked) // resend packets - ResendPacket (); - else if (m_WindowSize > int(m_SentPackets.size ())) // send packets - SendBuffer (); - } - else // pass - ScheduleSend (); - } - } - - void Stream::ScheduleResend () - { - if (m_Status != eStreamStatusTerminated) - { - m_ResendTimer.cancel (); - // check for invalid value - if (m_RTO <= 0) m_RTO = INITIAL_RTO; - m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO)); - m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, - shared_from_this (), std::placeholders::_1)); - } - } - - void Stream::HandleResendTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - m_IsSendTime = true; - if (m_RTO > INITIAL_RTO) m_RTO = INITIAL_RTO; - m_SendTimer.cancel (); // if no ack's in RTO, disable fast retransmit - m_IsTimeOutResend = true; - m_IsNAcked = false; - m_IsResendNeeded = false; - m_NumPacketsToSend = 1; - ResendPacket (); // send one packet per RTO, waiting for ack - } - } - - void Stream::ResendPacket () - { - // check for resend attempts - if (m_IsIncoming && m_SequenceNumber == 1 && m_NumResendAttempts > 0) - { - LogPrint (eLogWarning, "Streaming: SYNACK packet was not ACKed after ", m_NumResendAttempts, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); - m_Status = eStreamStatusReset; - Close (); - return; - } - if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS) - { - LogPrint (eLogWarning, "Streaming: packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts, terminate, rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); - m_Status = eStreamStatusReset; - Close (); - return; - } - - // collect packets to resend - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - std::vector packets; - if (m_IsNAcked) - { - for (auto it : m_NACKedPackets) - { - if (ts >= it->sendTime + m_RTO) - { - if (ts < it->sendTime + m_RTO*2) - it->resent = true; - else - it->resent = false; - it->sendTime = ts; - packets.push_back (it); - if ((int)packets.size () >= m_NumPacketsToSend) break; - } - } - } - else - { - for (auto it : m_SentPackets) - { - if (ts >= it->sendTime + m_RTO) - { - if (ts < it->sendTime + m_RTO*2) - it->resent = true; - else - it->resent = false; - it->sendTime = ts; - packets.push_back (it); - if ((int)packets.size () >= m_NumPacketsToSend) break; - } - } - } - - // select tunnels if necessary and send - if (packets.size () > 0 && m_IsSendTime) - { - if (m_IsNAcked) m_NumResendAttempts = 1; - else if (m_IsTimeOutResend) m_NumResendAttempts++; - if (m_NumResendAttempts == 1 && m_RTO != INITIAL_RTO) - { - // loss-based CC - if (!m_IsWinDropped && LOSS_BASED_CONTROL_ENABLED && !m_IsClientChoked) - { - LogPrint (eLogDebug, "Streaming: Packet loss, reduce window size"); - ProcessWindowDrop (); - } - } - else if (m_IsTimeOutResend) - { - m_IsTimeOutResend = false; - m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change - m_WindowDropTargetSize = INITIAL_WINDOW_SIZE; - m_LastWindowDropSize = 0; - m_WindowIncCounter = 0; - m_IsWinDropped = true; - m_IsFirstRttSample = true; - m_DropWindowDelaySequenceNumber = 0; - m_IsFirstACK = true; - m_LastACKRecieveTime = 0; - m_ACKRecieveInterval = m_AckDelay; - UpdatePacingTime (); - if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); - if (m_NumResendAttempts & 1) - { - // pick another outbound tunnel - m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); - LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts, - ", another outbound tunnel has been selected for stream with sSID=", m_SendStreamID); - } - else - { - CancelRemoteLeaseChange (); - UpdateCurrentRemoteLease (); // pick another lease - LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts, - ", another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); - } - } - SendPackets (packets); - m_LastSendTime = ts; - m_IsSendTime = false; - if (m_IsNAcked || m_IsResendNeeded || m_IsClientChoked) ScheduleSend (); - } - else if (!m_IsClientChoked) - SendBuffer (); - if (!m_IsNAcked && !m_IsResendNeeded) ScheduleResend (); - if (m_IsClientChoked) ScheduleSend (); - } - - void Stream::ScheduleAck (int timeout) - { - if (m_IsAckSendScheduled) - m_AckSendTimer.cancel (); - m_IsAckSendScheduled = true; - if (timeout < MIN_SEND_ACK_TIMEOUT) timeout = MIN_SEND_ACK_TIMEOUT; - m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(timeout)); - m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, - shared_from_this (), std::placeholders::_1)); - } - - void Stream::HandleAckSendTimer (const boost::system::error_code& ecode) - { - if (m_IsAckSendScheduled) - { - if (m_LastReceivedSequenceNumber < 0) - { - LogPrint (eLogWarning, "Streaming: SYN has not been received after ", SYN_TIMEOUT, " milliseconds after follow on, terminate rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); - m_Status = eStreamStatusReset; - Close (); - return; - } - if (m_Status == eStreamStatusOpen) - { - if (m_RoutingSession && m_RoutingSession->IsLeaseSetNonConfirmed ()) - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (ts > m_RoutingSession->GetLeaseSetSubmissionTime () + i2p::garlic::LEASESET_CONFIRMATION_TIMEOUT) - { - // seems something went wrong and we should re-select tunnels - m_CurrentOutboundTunnel = nullptr; - m_CurrentRemoteLease = nullptr; - } - } - SendQuickAck (); - } - m_IsAckSendScheduled = false; - } - } - - void Stream::UpdateCurrentRemoteLease (bool expired) - { - bool isLeaseChanged = true; - if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ()) - { - auto remoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ()); - if (!remoteLeaseSet) - { - LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), m_RemoteLeaseSet ? " expired" : " not found"); - if (!m_IsIncoming) // outgoing - { - if (m_RemoteLeaseSet && m_RemoteLeaseSet->IsPublishedEncrypted ()) - { - m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet ( - std::make_shared(m_RemoteIdentity)); - return; // we keep m_RemoteLeaseSet for possible next request - } - else - { - m_RemoteLeaseSet = nullptr; - m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt - } - } - else // incoming - { - // just close the socket without sending FIN or RST - m_Status = eStreamStatusClosed; - AsyncClose (); - } - } - else - { - // LeaseSet updated - m_RemoteLeaseSet = remoteLeaseSet; - m_RemoteIdentity = m_RemoteLeaseSet->GetIdentity (); - m_TransientVerifier = m_RemoteLeaseSet->GetTransientVerifier (); - } - } - if (m_RemoteLeaseSet) - { - if (!m_RoutingSession) - m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); - auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false); // try without threshold first - if (leases.empty ()) - { - expired = false; - // time to request - if (m_RemoteLeaseSet->IsPublishedEncrypted ()) - m_LocalDestination.GetOwner ()->RequestDestinationWithEncryptedLeaseSet ( - std::make_shared(m_RemoteIdentity)); - else - m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); - leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold - } - if (!leases.empty ()) - { - bool updated = false; - if (expired && m_CurrentRemoteLease) - { - for (const auto& it: leases) - if ((it->tunnelGateway == m_CurrentRemoteLease->tunnelGateway) && (it->tunnelID != m_CurrentRemoteLease->tunnelID)) - { - m_CurrentRemoteLease = it; - updated = true; - break; - } - } - if (!updated) - { - uint32_t i = m_LocalDestination.GetRandom () % leases.size (); - if (m_CurrentRemoteLease && leases[i]->tunnelID == m_CurrentRemoteLease->tunnelID) - { - // make sure we don't select previous - if (leases.size () > 1) - i = (i + 1) % leases.size (); // if so, pick next - else - isLeaseChanged = false; - } - m_CurrentRemoteLease = leases[i]; - } - } - else - { - LogPrint (eLogWarning, "Streaming: All remote leases are expired"); - m_RemoteLeaseSet = nullptr; - m_CurrentRemoteLease = nullptr; - // we have requested expired before, no need to do it twice - } - } - else - { - LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found"); - m_CurrentRemoteLease = nullptr; - } - if (isLeaseChanged && !m_IsRemoteLeaseChangeInProgress) - { - LogPrint (eLogDebug, "Streaming: RemoteLease changed, set initial window size"); - ResetWindowSize (); - } - } - - void Stream::ResetRoutingPath () - { - m_CurrentOutboundTunnel = nullptr; - m_CurrentRemoteLease = nullptr; - m_RTT = INITIAL_RTT; - m_RTO = INITIAL_RTO; - if (m_RoutingSession) - m_RoutingSession->SetSharedRoutingPath (nullptr); // TODO: count failures - } - - void Stream::UpdatePacingTime () - { - if (m_WindowDropTargetSize) - m_PacingTime = std::round (m_RTT*1000/m_WindowDropTargetSize); - else - m_PacingTime = std::round (m_RTT*1000/m_WindowSize); - if (m_MinPacingTime && m_PacingTime < m_MinPacingTime) - m_PacingTime = m_MinPacingTime; - } - - void Stream::ProcessWindowDrop () - { - if (m_WindowDropTargetSize) - m_WindowDropTargetSize = (m_WindowDropTargetSize / 2) * 0.75; // congestion window size and -25% to drain queue - else - { - if (m_WindowSize < m_LastWindowDropSize) - { - m_LastWindowDropSize = std::max ((m_WindowSize - MAX_WINDOW_SIZE_INC_PER_RTT), (m_WindowSize - (m_LastWindowDropSize - m_WindowSize))); - if (m_LastWindowDropSize < MIN_WINDOW_SIZE) m_LastWindowDropSize = MIN_WINDOW_SIZE; - } - else - { - m_LastWindowDropSize = std::max ((m_WindowSize - MAX_WINDOW_SIZE_INC_PER_RTT), ((m_LastWindowDropSize + m_WindowSize + m_WindowSizeTail) / 2)); - if (m_LastWindowDropSize > MAX_WINDOW_SIZE) m_LastWindowDropSize = MAX_WINDOW_SIZE; - } - m_WindowDropTargetSize = m_LastWindowDropSize * 0.75; // -25% to drain queue - } - if (m_WindowDropTargetSize < MIN_WINDOW_SIZE) - m_WindowDropTargetSize = MIN_WINDOW_SIZE; - m_WindowIncCounter = 0; // disable window growth - m_DropWindowDelaySequenceNumber = m_SequenceNumber + int(m_WindowDropTargetSize); - m_IsFirstACK = true; // ignore first RTT sample - m_IsWinDropped = true; // don't drop window twice - m_WindowSizeTail = 0; - UpdatePacingTime (); - } - - void Stream::ResetWindowSize () - { - m_RTO = INITIAL_RTO; - if (!m_IsClientChoked) - { - if (m_WindowSize > INITIAL_WINDOW_SIZE) - { - m_WindowDropTargetSize = (float)INITIAL_WINDOW_SIZE; - m_IsWinDropped = true; - } - else - m_WindowSize = INITIAL_WINDOW_SIZE; - } - m_LastWindowDropSize = 0; - m_WindowIncCounter = 0; - m_IsFirstRttSample = true; - m_IsFirstACK = true; - m_WindowSizeTail = 0; - UpdatePacingTime (); - } - - void Stream::CancelRemoteLeaseChange () - { - m_RemoteLeaseChangeTime = 0; - m_IsRemoteLeaseChangeInProgress = false; - } - - StreamingDestination::StreamingDestination (std::shared_ptr owner, uint16_t localPort, bool gzip): - m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), - m_PendingIncomingTimer (m_Owner->GetService ()), - m_LastCleanupTime (i2p::util::GetSecondsSinceEpoch ()) - { - } - - StreamingDestination::~StreamingDestination () - { - for (auto& it: m_SavedPackets) - { - for (auto it1: it.second) DeletePacket (it1); - it.second.clear (); - } - m_SavedPackets.clear (); - } - - void StreamingDestination::Start () - { - } - - void StreamingDestination::Stop () - { - ResetAcceptor (); - m_PendingIncomingTimer.cancel (); - m_PendingIncomingStreams.clear (); - { - std::unique_lock l(m_StreamsMutex); - for (auto it: m_Streams) - it.second->Terminate (false); // we delete here - m_Streams.clear (); - m_IncomingStreams.clear (); - m_LastStream = nullptr; - } - } - - void StreamingDestination::HandleNextPacket (Packet * packet) - { - uint32_t sendStreamID = packet->GetSendStreamID (); - if (sendStreamID) - { - if (!m_LastStream || sendStreamID != m_LastStream->GetRecvStreamID ()) - { - auto it = m_Streams.find (sendStreamID); - if (it != m_Streams.end ()) - m_LastStream = it->second; - else - m_LastStream = nullptr; - } - if (m_LastStream) - m_LastStream->HandleNextPacket (packet); - else if (packet->IsEcho () && m_Owner->IsStreamingAnswerPings ()) - { - // ping - LogPrint (eLogInfo, "Streaming: Ping received sSID=", sendStreamID); - auto s = std::make_shared (m_Owner->GetService (), *this); - s->HandlePing (packet); - } - else - { - LogPrint (eLogInfo, "Streaming: Unknown stream sSID=", sendStreamID); - DeletePacket (packet); - } - } - else - { - if (packet->IsEcho ()) - { - // pong - LogPrint (eLogInfo, "Streaming: Pong received rSID=", packet->GetReceiveStreamID ()); - DeletePacket (packet); - return; - } - if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream - { - uint32_t receiveStreamID = packet->GetReceiveStreamID (); - auto it1 = m_IncomingStreams.find (receiveStreamID); - if (it1 != m_IncomingStreams.end ()) - { - // already pending - LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists"); - it1->second->ResetRoutingPath (); // Ack was not delivered, changing path - DeletePacket (packet); // drop it, because previous should be connected - return; - } - if (m_Owner->GetStreamingMaxConcurrentStreams () > 0 && (int)m_Streams.size () > m_Owner->GetStreamingMaxConcurrentStreams ()) - { - LogPrint(eLogWarning, "Streaming: Number of streams exceeds ", m_Owner->GetStreamingMaxConcurrentStreams ()); - DeletePacket (packet); - return; - } - auto incomingStream = CreateNewIncomingStream (receiveStreamID); - incomingStream->HandleNextPacket (packet); // SYN - if (!incomingStream->GetRemoteLeaseSet ()) - { - LogPrint (eLogWarning, "Streaming: No remote LeaseSet for incoming stream. Terminated"); - incomingStream->Terminate (); // can't send FIN anyway - return; - } - - // handle saved packets if any - { - auto it = m_SavedPackets.find (receiveStreamID); - if (it != m_SavedPackets.end ()) - { - LogPrint (eLogDebug, "Streaming: Processing ", it->second.size (), " saved packets for rSID=", receiveStreamID); - for (auto it1: it->second) - incomingStream->HandleNextPacket (it1); - m_SavedPackets.erase (it); - } - } - // accept - if (m_Acceptor != nullptr) - m_Acceptor (incomingStream); - else - { - LogPrint (eLogWarning, "Streaming: Acceptor for incoming stream is not set"); - if (m_PendingIncomingStreams.size () < MAX_PENDING_INCOMING_BACKLOG) - { - m_PendingIncomingStreams.push_back (incomingStream); - m_PendingIncomingTimer.cancel (); - m_PendingIncomingTimer.expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT)); - m_PendingIncomingTimer.async_wait (std::bind (&StreamingDestination::HandlePendingIncomingTimer, - shared_from_this (), std::placeholders::_1)); - LogPrint (eLogDebug, "Streaming: Pending incoming stream added, rSID=", receiveStreamID); - } - else - { - LogPrint (eLogWarning, "Streaming: Pending incoming streams backlog exceeds ", MAX_PENDING_INCOMING_BACKLOG); - incomingStream->Close (); - } - } - } - else // follow on packet without SYN - { - uint32_t receiveStreamID = packet->GetReceiveStreamID (); - auto it1 = m_IncomingStreams.find (receiveStreamID); - if (it1 != m_IncomingStreams.end ()) - { - // found - it1->second->HandleNextPacket (packet); - return; - } - // save follow on packet - auto it = m_SavedPackets.find (receiveStreamID); - if (it != m_SavedPackets.end ()) - it->second.push_back (packet); - else - { - m_SavedPackets[receiveStreamID] = std::list{ packet }; - auto timer = std::make_shared (m_Owner->GetService ()); - timer->expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT)); - auto s = shared_from_this (); - timer->async_wait ([s,timer,receiveStreamID](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto it = s->m_SavedPackets.find (receiveStreamID); - if (it != s->m_SavedPackets.end ()) - { - for (auto it1: it->second) s->DeletePacket (it1); - it->second.clear (); - s->m_SavedPackets.erase (it); - } - } - }); - } - } - } - } - - std::shared_ptr StreamingDestination::CreateNewOutgoingStream (std::shared_ptr remote, int port) - { - auto s = std::make_shared (m_Owner->GetService (), *this, remote, port); - std::unique_lock l(m_StreamsMutex); - m_Streams.emplace (s->GetRecvStreamID (), s); - return s; - } - - void StreamingDestination::SendPing (std::shared_ptr remote) - { - auto s = std::make_shared (m_Owner->GetService (), *this, remote, 0); - s->SendPing (); - } - - std::shared_ptr StreamingDestination::CreateNewIncomingStream (uint32_t receiveStreamID) - { - auto s = std::make_shared (m_Owner->GetService (), *this); - std::unique_lock l(m_StreamsMutex); - m_Streams.emplace (s->GetRecvStreamID (), s); - m_IncomingStreams.emplace (receiveStreamID, s); - return s; - } - - void StreamingDestination::DeleteStream (std::shared_ptr stream) - { - if (stream) - { - std::unique_lock l(m_StreamsMutex); - m_Streams.erase (stream->GetRecvStreamID ()); - if (stream->IsIncoming ()) - m_IncomingStreams.erase (stream->GetSendStreamID ()); - if (m_LastStream == stream) m_LastStream = nullptr; - } - auto ts = i2p::util::GetSecondsSinceEpoch (); - if (m_Streams.empty () || ts > m_LastCleanupTime + STREAMING_DESTINATION_POOLS_CLEANUP_INTERVAL) - { - m_PacketsPool.CleanUp (); - m_I2NPMsgsPool.CleanUp (); - m_LastCleanupTime = ts; - } - } - - bool StreamingDestination::DeleteStream (uint32_t recvStreamID) - { - auto it = m_Streams.find (recvStreamID); - if (it == m_Streams.end ()) - return false; - auto s = it->second; - boost::asio::post (m_Owner->GetService (), [this, s] () - { - s->Close (); // try to send FIN - s->Terminate (false); - DeleteStream (s); - }); - return true; - } - - void StreamingDestination::SetAcceptor (const Acceptor& acceptor) - { - m_Acceptor = acceptor; // we must set it immediately for IsAcceptorSet - auto s = shared_from_this (); - boost::asio::post (m_Owner->GetService (), [s](void) - { - // take care about incoming queue - for (auto& it: s->m_PendingIncomingStreams) - if (it->GetStatus () == eStreamStatusOpen) // still open? - s->m_Acceptor (it); - s->m_PendingIncomingStreams.clear (); - s->m_PendingIncomingTimer.cancel (); - }); - } - - void StreamingDestination::ResetAcceptor () - { - if (m_Acceptor) m_Acceptor (nullptr); - m_Acceptor = nullptr; - } - - void StreamingDestination::AcceptOnce (const Acceptor& acceptor) - { - boost::asio::post (m_Owner->GetService (), [acceptor, this](void) - { - if (!m_PendingIncomingStreams.empty ()) - { - acceptor (m_PendingIncomingStreams.front ()); - m_PendingIncomingStreams.pop_front (); - if (m_PendingIncomingStreams.empty ()) - m_PendingIncomingTimer.cancel (); - } - else // we must save old acceptor and set it back - { - m_Acceptor = std::bind (&StreamingDestination::AcceptOnceAcceptor, this, - std::placeholders::_1, acceptor, m_Acceptor); - } - }); - } - - void StreamingDestination::AcceptOnceAcceptor (std::shared_ptr stream, Acceptor acceptor, Acceptor prev) - { - m_Acceptor = prev; - acceptor (stream); - } - - std::shared_ptr StreamingDestination::AcceptStream (int timeout) - { - std::shared_ptr stream; - std::condition_variable streamAccept; - std::mutex streamAcceptMutex; - std::unique_lock l(streamAcceptMutex); - AcceptOnce ( - [&streamAccept, &streamAcceptMutex, &stream](std::shared_ptr s) - { - stream = s; - std::unique_lock l(streamAcceptMutex); - streamAccept.notify_all (); - }); - if (timeout) - streamAccept.wait_for (l, std::chrono::seconds (timeout)); - else - streamAccept.wait (l); - return stream; - } - - void StreamingDestination::HandlePendingIncomingTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogWarning, "Streaming: Pending incoming timeout expired"); - for (auto& it: m_PendingIncomingStreams) - it->Close (); - m_PendingIncomingStreams.clear (); - } - } - - void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len) - { - // unzip it - Packet * uncompressed = NewPacket (); - uncompressed->offset = 0; - uncompressed->len = m_Inflator.Inflate (buf, len, uncompressed->buf, MAX_PACKET_SIZE); - if (uncompressed->len) - HandleNextPacket (uncompressed); - else - DeletePacket (uncompressed); - } - - std::shared_ptr StreamingDestination::CreateDataMessage ( - const uint8_t * payload, size_t len, uint16_t toPort, bool checksum, bool gzip) - { - size_t size; - auto msg = (len <= STREAMING_MTU_RATCHETS) ? m_I2NPMsgsPool.AcquireShared () : NewI2NPMessage (); - uint8_t * buf = msg->GetPayload (); - buf += 4; // reserve for lengthlength - msg->len += 4; - - if (m_Gzip || gzip) - size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len); - else - size = i2p::data::GzipNoCompression (payload, len, buf, msg->maxLen - msg->len); - - if (size) - { - htobe32buf (msg->GetPayload (), size); // length - htobe16buf (buf + 4, m_LocalPort); // source port - htobe16buf (buf + 6, toPort); // destination port - buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol - msg->len += size; - msg->FillI2NPMessageHeader (eI2NPData, 0, checksum); - } - else - msg = nullptr; - return msg; - } - - uint32_t StreamingDestination::GetRandom () - { - if (m_Owner) - { - auto pool = m_Owner->GetTunnelPool (); - if (pool) - return pool->GetRng ()(); - } - return rand (); - } -} -} diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h deleted file mode 100644 index 570fdd1d..00000000 --- a/libi2pd/Streaming.h +++ /dev/null @@ -1,435 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 STREAMING_H__ -#define STREAMING_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "Base.h" -#include "Gzip.h" -#include "I2PEndian.h" -#include "Identity.h" -#include "LeaseSet.h" -#include "I2NPProtocol.h" -#include "Garlic.h" -#include "Tunnel.h" -#include "util.h" // MemoryPool - -namespace i2p -{ -namespace client -{ - class ClientDestination; -} -namespace stream -{ - const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001; - const uint16_t PACKET_FLAG_CLOSE = 0x0002; - const uint16_t PACKET_FLAG_RESET = 0x0004; - const uint16_t PACKET_FLAG_SIGNATURE_INCLUDED = 0x0008; - const uint16_t PACKET_FLAG_SIGNATURE_REQUESTED = 0x0010; - const uint16_t PACKET_FLAG_FROM_INCLUDED = 0x0020; - const uint16_t PACKET_FLAG_DELAY_REQUESTED = 0x0040; - const uint16_t PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED = 0x0080; - const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100; - const uint16_t PACKET_FLAG_ECHO = 0x0200; - const uint16_t PACKET_FLAG_NO_ACK = 0x0400; - const uint16_t PACKET_FLAG_OFFLINE_SIGNATURE = 0x0800; - - const size_t STREAMING_MTU = 1730; - const size_t STREAMING_MTU_RATCHETS = 1812; -#if OPENSSL_PQ - const size_t MAX_PACKET_SIZE = 8192; -#else - const size_t MAX_PACKET_SIZE = 4096; -#endif - const size_t COMPRESSION_THRESHOLD_SIZE = 66; - const int MAX_NUM_RESEND_ATTEMPTS = 10; - const int INITIAL_WINDOW_SIZE = 10; - const int MIN_WINDOW_SIZE = 3; - const int MAX_WINDOW_SIZE = 512; - const int MAX_WINDOW_SIZE_INC_PER_RTT = 16; - const double RTT_EWMA_ALPHA = 0.25; - const double SLOWRTT_EWMA_ALPHA = 0.05; - const double PREV_SPEED_KEEP_TIME_COEFF = 0.35; // 0.1 - 1 // how long will the window size stay around the previous drop level, less is longer - const int MIN_RTO = 20; // in milliseconds - const int INITIAL_RTT = 1500; // in milliseconds - const int INITIAL_RTO = 9000; // in milliseconds - const int INITIAL_PACING_TIME = 1000 * INITIAL_RTT / INITIAL_WINDOW_SIZE; // in microseconds - const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds - const int SYN_TIMEOUT = 200; // how long we wait for SYN after follow-on, in milliseconds - const size_t MAX_PENDING_INCOMING_BACKLOG = 1024; - const int PENDING_INCOMING_TIMEOUT = 10; // in seconds - const int MAX_RECEIVE_TIMEOUT = 20; // in seconds - const uint16_t DELAY_CHOKING = 60000; // in milliseconds - const uint64_t SEND_INTERVAL = 10000; // in microseconds - const uint64_t SEND_INTERVAL_VARIANCE = 2000; // in microseconds - const uint64_t REQUEST_IMMEDIATE_ACK_INTERVAL = 7500; // in milliseconds - const uint64_t REQUEST_IMMEDIATE_ACK_INTERVAL_VARIANCE = 3200; // in milliseconds - const bool LOSS_BASED_CONTROL_ENABLED = 1; // 0/1 - const uint64_t STREAMING_DESTINATION_POOLS_CLEANUP_INTERVAL = 646; // in seconds - - struct Packet - { - size_t len, offset; - uint8_t buf[MAX_PACKET_SIZE]; - uint64_t sendTime; - bool resent; - - Packet (): len (0), offset (0), sendTime (0), resent (false) {}; - uint8_t * GetBuffer () { return buf + offset; }; - size_t GetLength () const { return len > offset ? len - offset : 0; }; - - uint32_t GetSendStreamID () const { return bufbe32toh (buf); }; - uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); }; - uint32_t GetSeqn () const { return bufbe32toh (buf + 8); }; - uint32_t GetAckThrough () const { return bufbe32toh (buf + 12); }; - uint8_t GetNACKCount () const { return buf[16]; }; - uint32_t GetNACK (int i) const { return bufbe32toh (buf + 17 + 4 * i); }; - const uint8_t * GetNACKs () const { return buf + 17; }; - const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags - uint16_t GetFlags () const { return bufbe16toh (GetOption () - 2); }; - uint16_t GetOptionSize () const { return bufbe16toh (GetOption ()); }; - const uint8_t * GetOptionData () const { return GetOption () + 2; }; - const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); }; - - bool IsSYN () const { return GetFlags () & PACKET_FLAG_SYNCHRONIZE; }; - bool IsNoAck () const { return GetFlags () & PACKET_FLAG_NO_ACK; }; - bool IsEcho () const { return GetFlags () & PACKET_FLAG_ECHO; }; - }; - - struct PacketCmp - { - bool operator() (const Packet * p1, const Packet * p2) const - { - return p1->GetSeqn () < p2->GetSeqn (); - }; - }; - - typedef std::function SendHandler; - struct SendBuffer - { - uint8_t * buf; - size_t len, offset; - SendHandler handler; - - SendBuffer (const uint8_t * b, size_t l, SendHandler h): - len(l), offset (0), handler(h) - { - buf = new uint8_t[len]; - memcpy (buf, b, len); - } - SendBuffer (size_t l): // create empty buffer - len(l), offset (0) - { - buf = new uint8_t[len]; - } - ~SendBuffer () - { - delete[] buf; - if (handler) handler(boost::system::error_code ()); - } - size_t GetRemainingSize () const { return len - offset; }; - const uint8_t * GetRemaningBuffer () const { return buf + offset; }; - void Cancel () { if (handler) handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted)); handler = nullptr; }; - }; - - class SendBufferQueue - { - public: - - SendBufferQueue (): m_Size (0) {}; - ~SendBufferQueue () { CleanUp (); }; - - void Add (std::shared_ptr&& buf); - size_t Get (uint8_t * buf, size_t len); - size_t GetSize () const { return m_Size; }; - bool IsEmpty () const { return m_Buffers.empty (); }; - void CleanUp (); - - private: - - std::list > m_Buffers; - size_t m_Size; - }; - - enum StreamStatus - { - eStreamStatusNew = 0, - eStreamStatusOpen, - eStreamStatusReset, - eStreamStatusClosing, - eStreamStatusClosed, - eStreamStatusTerminated - }; - - class StreamingDestination; - class Stream: public std::enable_shared_from_this - { - public: - - Stream (boost::asio::io_context& service, StreamingDestination& local, - std::shared_ptr remote, int port = 0); // outgoing - Stream (boost::asio::io_context& service, StreamingDestination& local); // incoming - - ~Stream (); - uint32_t GetSendStreamID () const { return m_SendStreamID; }; - uint32_t GetRecvStreamID () const { return m_RecvStreamID; }; - std::shared_ptr GetRemoteLeaseSet () const { return m_RemoteLeaseSet; }; - std::shared_ptr GetRemoteIdentity () const { return m_RemoteIdentity; }; - bool IsOpen () const { return m_Status == eStreamStatusOpen; }; - bool IsEstablished () const { return m_SendStreamID; }; - bool IsIncoming () const { return m_IsIncoming; }; - StreamStatus GetStatus () const { return m_Status; }; - StreamingDestination& GetLocalDestination () { return m_LocalDestination; }; - void ResetRoutingPath (); - - void HandleNextPacket (Packet * packet); - void HandlePing (Packet * packet); - size_t Send (const uint8_t * buf, size_t len); - void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler); - void SendPing (); - - template - void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); - size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; - size_t Receive (uint8_t * buf, size_t len, int timeout); - - void AsyncClose() { boost::asio::post(m_Service, std::bind(&Stream::Close, shared_from_this())); }; - - /** only call close from destination thread, use Stream::AsyncClose for other threads */ - void Close (); - void Cancel () { m_ReceiveTimer.cancel (); }; - - size_t GetNumSentBytes () const { return m_NumSentBytes; }; - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; - size_t GetSendQueueSize () const { return m_SentPackets.size (); }; - size_t GetReceiveQueueSize () const { return m_ReceiveQueue.size (); }; - size_t GetSendBufferSize () const { return m_SendBuffer.GetSize (); }; - int GetWindowSize () const { return m_WindowSize; }; - int GetRTT () const { return m_RTT; }; - - void Terminate (bool deleteFromDestination = true); - - private: - - void CleanUp (); - - void SendBuffer (); - void SendQuickAck (); - void SendClose (); - bool SendPacket (Packet * packet); - void SendPackets (const std::vector& packets); - void SendUpdatedLeaseSet (); - - void SavePacket (Packet * packet); - void ProcessPacket (Packet * packet); - bool ProcessOptions (uint16_t flags, Packet * packet); - void ProcessAck (Packet * packet); - size_t ConcatenatePackets (uint8_t * buf, size_t len); - - void UpdateCurrentRemoteLease (bool expired = false); - - template - void HandleReceiveTimer (const boost::system::error_code& ecode, Buffer& buffer, ReceiveHandler handler, int remainingTimeout); - - void ScheduleSend (); - void HandleSendTimer (const boost::system::error_code& ecode); - void ScheduleResend (); - void HandleResendTimer (const boost::system::error_code& ecode); - void ResendPacket (); - void ScheduleAck (int timeout); - void HandleAckSendTimer (const boost::system::error_code& ecode); - - void UpdatePacingTime (); - void ProcessWindowDrop (); - void ResetWindowSize (); - void CancelRemoteLeaseChange (); - - private: - - boost::asio::io_context& m_Service; - uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber; - uint32_t m_DropWindowDelaySequenceNumber; - uint32_t m_TunnelsChangeSequenceNumber; - int32_t m_LastReceivedSequenceNumber; - int32_t m_PreviousReceivedSequenceNumber; - int32_t m_LastConfirmedReceivedSequenceNumber; // for limit inbound speed - StreamStatus m_Status; - bool m_IsIncoming; - bool m_IsAckSendScheduled; - bool m_IsNAcked; - bool m_IsFirstACK; - bool m_IsResendNeeded; - bool m_IsFirstRttSample; - bool m_IsSendTime; - bool m_IsWinDropped; - bool m_IsClientChoked; - bool m_IsTimeOutResend; - bool m_IsImmediateAckRequested; - bool m_IsRemoteLeaseChangeInProgress; - bool m_DoubleWinIncCounter; - StreamingDestination& m_LocalDestination; - std::shared_ptr m_RemoteIdentity; - std::shared_ptr m_TransientVerifier; // in case of offline key - std::shared_ptr m_RemoteLeaseSet; - std::shared_ptr m_RoutingSession; - std::shared_ptr m_CurrentRemoteLease; - std::shared_ptr m_NextRemoteLease; - std::shared_ptr m_CurrentOutboundTunnel; - std::queue m_ReceiveQueue; - std::set m_SavedPackets; - std::set m_SentPackets; - std::set m_NACKedPackets; - boost::asio::deadline_timer m_ReceiveTimer, m_SendTimer, m_ResendTimer, m_AckSendTimer; - size_t m_NumSentBytes, m_NumReceivedBytes; - uint16_t m_Port; - - SendBufferQueue m_SendBuffer; - double m_RTT, m_SlowRTT, m_SlowRTT2; - float m_WindowSize, m_LastWindowDropSize, m_WindowDropTargetSize; - int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample, m_WindowSizeTail; - double m_Jitter; - uint64_t m_MinPacingTime, m_PacingTime, m_PacingTimeRem, // microseconds - m_LastSendTime, m_LastACKRecieveTime, m_ACKRecieveInterval, m_RemoteLeaseChangeTime, m_LastWindowIncTime; // milliseconds - uint64_t m_LastACKSendTime, m_PacketACKInterval, m_PacketACKIntervalRem; // for limit inbound speed - int m_NumResendAttempts, m_NumPacketsToSend; - size_t m_MTU; - }; - - class StreamingDestination: public std::enable_shared_from_this - { - public: - - typedef std::function)> Acceptor; - - StreamingDestination (std::shared_ptr owner, uint16_t localPort = 0, bool gzip = false); - ~StreamingDestination (); - - void Start (); - void Stop (); - - std::shared_ptr CreateNewOutgoingStream (std::shared_ptr remote, int port = 0); - void SendPing (std::shared_ptr remote); - void DeleteStream (std::shared_ptr stream); - bool DeleteStream (uint32_t recvStreamID); - size_t GetNumStreams () const { return m_Streams.size (); }; - void SetAcceptor (const Acceptor& acceptor); - void ResetAcceptor (); - bool IsAcceptorSet () const { return m_Acceptor != nullptr; }; - void AcceptOnce (const Acceptor& acceptor); - void AcceptOnceAcceptor (std::shared_ptr stream, Acceptor acceptor, Acceptor prev); - std::shared_ptr AcceptStream (int timeout = 0); // sync - - std::shared_ptr GetOwner () const { return m_Owner; }; - void SetOwner (std::shared_ptr owner) { m_Owner = owner; }; - uint16_t GetLocalPort () const { return m_LocalPort; }; - - void HandleDataMessagePayload (const uint8_t * buf, size_t len); - std::shared_ptr CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort, bool checksum = true, bool gzip = false); - - Packet * NewPacket () { return m_PacketsPool.Acquire(); } - void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); } - uint32_t GetRandom (); - - private: - - void HandleNextPacket (Packet * packet); - std::shared_ptr CreateNewIncomingStream (uint32_t receiveStreamID); - void HandlePendingIncomingTimer (const boost::system::error_code& ecode); - - private: - - std::shared_ptr m_Owner; - uint16_t m_LocalPort; - bool m_Gzip; // gzip compression of data messages - std::mutex m_StreamsMutex; - std::unordered_map > m_Streams; // sendStreamID->stream - std::unordered_map > m_IncomingStreams; // receiveStreamID->stream - std::shared_ptr m_LastStream; - Acceptor m_Acceptor; - std::list > m_PendingIncomingStreams; - boost::asio::deadline_timer m_PendingIncomingTimer; - std::unordered_map > m_SavedPackets; // receiveStreamID->packets, arrived before SYN - - i2p::util::MemoryPool m_PacketsPool; - i2p::util::MemoryPool > m_I2NPMsgsPool; - uint64_t m_LastCleanupTime; // in seconds - - public: - - i2p::data::GzipInflator m_Inflator; - i2p::data::GzipDeflator m_Deflator; - - // for HTTP only - const decltype(m_Streams)& GetStreams () const { return m_Streams; }; - }; - -//------------------------------------------------- - - template - void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout) - { - auto s = shared_from_this(); - boost::asio::post (m_Service, [s, buffer, handler, timeout](void) - { - if (!s->m_ReceiveQueue.empty () || s->m_Status == eStreamStatusReset) - s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0); - else - { - int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout; - s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t)); - int left = timeout - t; - s->m_ReceiveTimer.async_wait ( - [s, buffer, handler, left](const boost::system::error_code & ec) - { - s->HandleReceiveTimer(ec, buffer, handler, left); - }); - } - }); - } - - template - void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, Buffer& buffer, ReceiveHandler handler, int remainingTimeout) - { - size_t received = ConcatenatePackets ((uint8_t *)buffer.data (), buffer.size ()); - if (received > 0) - handler (boost::system::error_code (), received); - else if (ecode == boost::asio::error::operation_aborted) - { - // timeout not expired - if (m_Status == eStreamStatusReset) - handler (boost::asio::error::make_error_code (boost::asio::error::connection_reset), 0); - else - handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0); - } - else - { - // timeout expired - if (remainingTimeout <= 0) - handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received); - else - { - // itermediate interrupt - SendUpdatedLeaseSet (); // send our leaseset if applicable - AsyncReceive (buffer, handler, remainingTimeout); - } - } - } -} -} - -#endif diff --git a/libi2pd/Tag.h b/libi2pd/Tag.h deleted file mode 100644 index 30b7708d..00000000 --- a/libi2pd/Tag.h +++ /dev/null @@ -1,113 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 TAG_H__ -#define TAG_H__ - -#include -#include -#include -#include -#include -#include "Base.h" - -namespace i2p -{ -namespace data -{ - template - class Tag - { - BOOST_STATIC_ASSERT_MSG(sz % 8 == 0, "Tag size must be multiple of 8 bytes"); - - public: - - Tag () = default; - Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); } - - bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); } - bool operator!= (const Tag& other) const { return !(*this == other); } - bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; } - - uint8_t * operator()() { return m_Buf; } - const uint8_t * operator()() const { return m_Buf; } - - operator uint8_t * () { return m_Buf; } - operator const uint8_t * () const { return m_Buf; } - - const uint8_t * data() const { return m_Buf; } - const uint64_t * GetLL () const { return ll; } - - bool IsZero () const - { - for (size_t i = 0; i < sz/8; ++i) - if (ll[i]) return false; - return true; - } - - void Fill(uint8_t c) - { - memset(m_Buf, c, sz); - } - - void Randomize() - { - RAND_bytes(m_Buf, sz); - } - - std::string ToBase64 (size_t len = sz) const - { - return i2p::data::ByteStreamToBase64 (m_Buf, len); - } - - std::string ToBase32 (size_t len = sz) const - { - return i2p::data::ByteStreamToBase32 (m_Buf, len); - } - - size_t FromBase32 (std::string_view s) - { - return i2p::data::Base32ToByteStream (s, m_Buf, sz); - } - - size_t FromBase64 (std::string_view s) - { - return i2p::data::Base64ToByteStream (s, m_Buf, sz); - } - - uint8_t GetBit (int i) const - { - int pos = i >> 3; // /8 - if (pos >= (int)sz) return 0; - return m_Buf[pos] & (0x80 >> (i & 0x07)); - } - - private: - - union // 8 bytes aligned - { - uint8_t m_Buf[sz]; - uint64_t ll[sz/8]; - }; - }; -} // data -} // i2p - -namespace std -{ - // hash for std::unordered_map - template struct hash > - { - size_t operator()(const i2p::data::Tag& s) const - { - return s.GetLL ()[0]; - } - }; -} - -#endif /* TAG_H__ */ diff --git a/libi2pd/Timestamp.cpp b/libi2pd/Timestamp.cpp deleted file mode 100644 index a22e9bde..00000000 --- a/libi2pd/Timestamp.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* -* Copyright (c) 2013-2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "Config.h" -#include "Log.h" -#include "RouterContext.h" -#include "I2PEndian.h" -#include "Timestamp.h" -#include "util.h" - -#ifdef _WIN32 - #ifndef _WIN64 - #define _USE_32BIT_TIME_T - #endif -#endif - -namespace i2p -{ -namespace util -{ - static uint64_t GetLocalMillisecondsSinceEpoch () - { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); - } - - static uint64_t GetLocalSecondsSinceEpoch () - { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); - } - - static uint32_t GetLocalMinutesSinceEpoch () - { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); - } - - static uint32_t GetLocalHoursSinceEpoch () - { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count (); - } - - static int64_t g_TimeOffset = 0; // in seconds - - static void SyncTimeWithNTP (const std::string& address) - { - LogPrint (eLogInfo, "Timestamp: NTP request to ", address); - boost::asio::io_context service; - boost::system::error_code ec; - auto endpoints = boost::asio::ip::udp::resolver (service).resolve (address, "ntp", ec); - if (!ec) - { - bool found = false; - boost::asio::ip::udp::endpoint ep; - for (const auto& it: endpoints) - { - ep = it; - if (!ep.address ().is_unspecified ()) - { - if (ep.address ().is_v4 ()) - { - if (i2p::context.SupportsV4 ()) found = true; - } - else if (ep.address ().is_v6 ()) - { - if (i2p::util::net::IsYggdrasilAddress (ep.address ())) - { - if (i2p::context.SupportsMesh ()) found = true; - } - else if (i2p::context.SupportsV6 ()) found = true; - } - } - if (found) break; - } - if (!found) - { - LogPrint (eLogError, "Timestamp: can't find compatible address for ", address); - return; - } - - boost::asio::ip::udp::socket socket (service); - socket.open (ep.protocol (), ec); - if (!ec) - { - uint8_t buf[48];// 48 bytes NTP request/response - memset (buf, 0, 48); - htobe32buf (buf, (3 << 27) | (3 << 24)); // RFC 4330 - size_t len = 0; - try - { - socket.send_to (boost::asio::buffer (buf, 48), ep); - int i = 0; - while (!socket.available() && i < 10) // 10 seconds max - { - std::this_thread::sleep_for (std::chrono::seconds(1)); - i++; - } - if (socket.available ()) - len = socket.receive_from (boost::asio::buffer (buf, 48), ep); - } - catch (std::exception& e) - { - LogPrint (eLogError, "Timestamp: NTP error: ", e.what ()); - } - if (len >= 8) - { - auto ourTs = GetLocalSecondsSinceEpoch (); - uint32_t ts = bufbe32toh (buf + 32); - if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900 - g_TimeOffset = ts - ourTs; - LogPrint (eLogInfo, "Timestamp: ", address, " time offset from system time is ", g_TimeOffset, " seconds"); - } - } - else - LogPrint (eLogError, "Timestamp: Couldn't open UDP socket"); - } - else - LogPrint (eLogError, "Timestamp: Couldn't resolve address ", address); - } - - NTPTimeSync::NTPTimeSync (): m_IsRunning (false), m_Timer (m_Service) - { - i2p::config::GetOption("nettime.ntpsyncinterval", m_SyncInterval); - std::string ntpservers; i2p::config::GetOption("nettime.ntpservers", ntpservers); - boost::split (m_NTPServersList, ntpservers, boost::is_any_of(","), boost::token_compress_on); - } - - NTPTimeSync::~NTPTimeSync () - { - Stop (); - } - - void NTPTimeSync::Start() - { - if (m_NTPServersList.size () > 0) - { - m_IsRunning = true; - LogPrint(eLogInfo, "Timestamp: NTP time sync starting"); - boost::asio::post (m_Service, std::bind (&NTPTimeSync::Sync, this)); - m_Thread.reset (new std::thread (std::bind (&NTPTimeSync::Run, this))); - } - else - LogPrint (eLogWarning, "Timestamp: No NTP server found"); - } - - void NTPTimeSync::Stop () - { - if (m_IsRunning) - { - LogPrint(eLogInfo, "Timestamp: NTP time sync stopping"); - m_IsRunning = false; - m_Timer.cancel (); - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - m_Thread.reset (nullptr); - } - } - } - - void NTPTimeSync::Run () - { - i2p::util::SetThreadName("Timesync"); - - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Timestamp: NTP time sync exception: ", ex.what ()); - } - } - } - - void NTPTimeSync::Sync () - { - if (m_NTPServersList.size () > 0) - SyncTimeWithNTP (m_NTPServersList[rand () % m_NTPServersList.size ()]); - else - m_IsRunning = false; - - if (m_IsRunning) - { - m_Timer.expires_from_now (boost::posix_time::hours (m_SyncInterval)); - m_Timer.async_wait ([this](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - Sync (); - }); - } - } - - uint64_t GetMillisecondsSinceEpoch () - { - return GetLocalMillisecondsSinceEpoch () + g_TimeOffset*1000; - } - - uint64_t GetSecondsSinceEpoch () - { - return GetLocalSecondsSinceEpoch () + g_TimeOffset; - } - - uint32_t GetMinutesSinceEpoch () - { - return GetLocalMinutesSinceEpoch () + g_TimeOffset/60; - } - - uint32_t GetHoursSinceEpoch () - { - return GetLocalHoursSinceEpoch () + g_TimeOffset/3600; - } - - uint64_t GetMonotonicMicroseconds() - { - return std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()).count(); - } - - uint64_t GetMonotonicMilliseconds() - { - return std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()).count(); - } - - uint64_t GetMonotonicSeconds () - { - return std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()).count(); - } - - void GetCurrentDate (char * date) - { - GetDateString (GetSecondsSinceEpoch (), date); - } - - void GetNextDayDate (char * date) - { - GetDateString (GetSecondsSinceEpoch () + 24*60*60, date); - } - - void GetDateString (uint64_t timestamp, char * date) - { - using clock = std::chrono::system_clock; - auto t = clock::to_time_t (clock::time_point (std::chrono::seconds(timestamp))); - struct tm tm; -#ifdef _WIN32 - gmtime_s(&tm, &t); - sprintf_s(date, 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); -#else - gmtime_r(&t, &tm); - sprintf(date, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); -#endif - } - - void AdjustTimeOffset (int64_t offset) - { - g_TimeOffset += offset; - } -} -} diff --git a/libi2pd/Timestamp.h b/libi2pd/Timestamp.h deleted file mode 100644 index 00c60433..00000000 --- a/libi2pd/Timestamp.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -* Copyright (c) 2013-2024, 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 TIMESTAMP_H__ -#define TIMESTAMP_H__ - -#include -#include -#include -#include -#include - -namespace i2p -{ -namespace util -{ - uint64_t GetMillisecondsSinceEpoch (); - uint64_t GetSecondsSinceEpoch (); - uint32_t GetMinutesSinceEpoch (); - uint32_t GetHoursSinceEpoch (); - - uint64_t GetMonotonicMicroseconds (); - uint64_t GetMonotonicMilliseconds (); - uint64_t GetMonotonicSeconds (); - - void GetCurrentDate (char * date); // returns UTC date as YYYYMMDD string, 9 bytes - void GetNextDayDate (char * date); // returns next UTC day as YYYYMMDD string, 9 bytes - void GetDateString (uint64_t timestamp, char * date); // timestamp is seconds since epoch, returns date as YYYYMMDD string, 9 bytes - void AdjustTimeOffset (int64_t offset); // in seconds from current - - class NTPTimeSync - { - public: - - NTPTimeSync (); - ~NTPTimeSync (); - - void Start (); - void Stop (); - - private: - - void Run (); - void Sync (); - - private: - - bool m_IsRunning; - std::unique_ptr m_Thread; - boost::asio::io_context m_Service; - boost::asio::deadline_timer m_Timer; - int m_SyncInterval; - std::vector m_NTPServersList; - }; -} -} - -#endif diff --git a/libi2pd/TransitTunnel.cpp b/libi2pd/TransitTunnel.cpp deleted file mode 100644 index b24c8ac5..00000000 --- a/libi2pd/TransitTunnel.cpp +++ /dev/null @@ -1,633 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include "I2PEndian.h" -#include "Crypto.h" -#include "Log.h" -#include "Identity.h" -#include "RouterInfo.h" -#include "RouterContext.h" -#include "I2NPProtocol.h" -#include "Garlic.h" -#include "ECIESX25519AEADRatchetSession.h" -#include "Tunnel.h" -#include "Transports.h" -#include "TransitTunnel.h" - -namespace i2p -{ -namespace tunnel -{ - TransitTunnel::TransitTunnel (uint32_t receiveTunnelID, - const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, - const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey): - TunnelBase (receiveTunnelID, nextTunnelID, nextIdent), - m_LayerKey (layerKey), m_IVKey (ivKey) - { - } - - void TransitTunnel::EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) - { - if (!m_Encryption) - { - m_Encryption.reset (new i2p::crypto::TunnelEncryption); - m_Encryption->SetKeys (m_LayerKey, m_IVKey); - } - m_Encryption->Encrypt (in->GetPayload () + 4, out->GetPayload () + 4); - i2p::transport::transports.UpdateTotalTransitTransmittedBytes (TUNNEL_DATA_MSG_SIZE); - } - - std::string TransitTunnel::GetNextPeerName () const - { - return i2p::data::GetIdentHashAbbreviation (GetNextIdentHash ()); - } - - void TransitTunnel::SendTunnelDataMsg (std::shared_ptr msg) - { - LogPrint (eLogError, "TransitTunnel: We are not a gateway for ", GetTunnelID ()); - } - - void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) - { - LogPrint (eLogError, "TransitTunnel: Incoming tunnel message is not supported ", GetTunnelID ()); - } - - TransitTunnelParticipant::~TransitTunnelParticipant () - { - } - - void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) - { - EncryptTunnelMsg (tunnelMsg, tunnelMsg); - - m_NumTransmittedBytes += tunnelMsg->GetLength (); - htobe32buf (tunnelMsg->GetPayload (), GetNextTunnelID ()); - tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData); - m_TunnelDataMsgs.push_back (tunnelMsg); - } - - void TransitTunnelParticipant::FlushTunnelDataMsgs () - { - if (!m_TunnelDataMsgs.empty ()) - { - auto num = m_TunnelDataMsgs.size (); - if (num > 1) - LogPrint (eLogDebug, "TransitTunnel: ", GetTunnelID (), "->", GetNextTunnelID (), " ", num); - if (!m_Sender) m_Sender = std::make_unique(); - m_Sender->SendMessagesTo (GetNextIdentHash (), m_TunnelDataMsgs); // send and clear - } - } - - std::string TransitTunnelParticipant::GetNextPeerName () const - { - if (m_Sender) - { - auto transport = m_Sender->GetCurrentTransport (); - if (transport) - return TransitTunnel::GetNextPeerName () + "-" + - i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ()); - } - return TransitTunnel::GetNextPeerName (); - } - - void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr msg) - { - TunnelMessageBlock block; - block.deliveryType = eDeliveryTypeLocal; - block.data = msg; - std::lock_guard l(m_SendMutex); - m_Gateway.PutTunnelDataMsg (block); - } - - void TransitTunnelGateway::FlushTunnelDataMsgs () - { - std::lock_guard l(m_SendMutex); - m_Gateway.SendBuffer (); - } - - std::string TransitTunnelGateway::GetNextPeerName () const - { - const auto& sender = m_Gateway.GetSender (); - if (sender) - { - auto transport = sender->GetCurrentTransport (); - if (transport) - return TransitTunnel::GetNextPeerName () + "-" + - i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ()); - } - return TransitTunnel::GetNextPeerName (); - } - - void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) - { - auto newMsg = CreateEmptyTunnelDataMsg (true); - EncryptTunnelMsg (tunnelMsg, newMsg); - - LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ()); - std::lock_guard l(m_HandleMutex); - if (!m_Endpoint) m_Endpoint = std::make_unique(false); // transit endpoint is always outbound - m_Endpoint->HandleDecryptedTunnelDataMsg (newMsg); - } - - void TransitTunnelEndpoint::FlushTunnelDataMsgs () - { - if (m_Endpoint) - { - std::lock_guard l(m_HandleMutex); - m_Endpoint->FlushI2NPMsgs (); - } - } - - void TransitTunnelEndpoint::Cleanup () - { - if (m_Endpoint) - { - std::lock_guard l(m_HandleMutex); - m_Endpoint->Cleanup (); - } - } - - std::string TransitTunnelEndpoint::GetNextPeerName () const - { - if (!m_Endpoint) return ""; - auto hash = m_Endpoint->GetCurrentHash (); - if (hash) - { - const auto& sender = m_Endpoint->GetSender (); - if (sender) - { - auto transport = sender->GetCurrentTransport (); - if (transport) - return i2p::data::GetIdentHashAbbreviation (*hash) + "-" + - i2p::data::RouterInfo::GetTransportName (transport->GetTransportType ()); - else - return i2p::data::GetIdentHashAbbreviation (*hash); - } - } - return ""; - } - - std::shared_ptr CreateTransitTunnel (uint32_t receiveTunnelID, - const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, - const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey, - bool isGateway, bool isEndpoint) - { - if (isEndpoint) - { - LogPrint (eLogDebug, "TransitTunnel: endpoint ", receiveTunnelID, " created"); - return std::make_shared (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); - } - else if (isGateway) - { - LogPrint (eLogInfo, "TransitTunnel: gateway ", receiveTunnelID, " created"); - return std::make_shared (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); - } - else - { - LogPrint (eLogDebug, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created"); - return std::make_shared (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey); - } - } - - TransitTunnels::TransitTunnels (): - m_IsRunning (false), m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL) - { - } - - TransitTunnels::~TransitTunnels () - { - Stop (); - } - - void TransitTunnels::Start () - { - m_IsRunning = true; - m_Thread.reset (new std::thread (std::bind (&TransitTunnels::Run, this))); - } - - void TransitTunnels::Stop () - { - m_IsRunning = false; - m_TunnelBuildMsgQueue.WakeUp (); - if (m_Thread) - { - m_Thread->join (); - m_Thread = nullptr; - } - m_TransitTunnels.clear (); - } - - void TransitTunnels::Run () - { - i2p::util::SetThreadName("TBM"); - uint64_t lastTs = 0; - std::list > msgs; - while (m_IsRunning) - { - try - { - if (m_TunnelBuildMsgQueue.Wait (TRANSIT_TUNNELS_QUEUE_WAIT_INTERVAL, 0)) - { - m_TunnelBuildMsgQueue.GetWholeQueue (msgs); - while (!msgs.empty ()) - { - auto msg = msgs.front (); msgs.pop_front (); - if (!msg) continue; - uint8_t typeID = msg->GetTypeID (); - switch (typeID) - { - case eI2NPShortTunnelBuild: - HandleShortTransitTunnelBuildMsg (std::move (msg)); - break; - case eI2NPVariableTunnelBuild: - HandleVariableTransitTunnelBuildMsg (std::move (msg)); - break; - default: - LogPrint (eLogWarning, "TransitTunnel: Unexpected message type ", (int) typeID); - } - if (!m_IsRunning) break; - } - } - if (m_IsRunning) - { - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts >= lastTs + TUNNEL_MANAGE_INTERVAL || ts + TUNNEL_MANAGE_INTERVAL < lastTs) - { - ManageTransitTunnels (ts); - lastTs = ts; - } - } - } - catch (std::exception& ex) - { - LogPrint (eLogError, "TransitTunnel: Runtime exception: ", ex.what ()); - } - } - } - - void TransitTunnels::PostTransitTunnelBuildMsg (std::shared_ptr&& msg) - { - if (msg) m_TunnelBuildMsgQueue.Put (msg); - } - - void TransitTunnels::HandleShortTransitTunnelBuildMsg (std::shared_ptr&& msg) - { - if (!msg) return; - uint8_t * buf = msg->GetPayload(); - size_t len = msg->GetPayloadLength(); - int num = buf[0]; - LogPrint (eLogDebug, "TransitTunnel: ShortTunnelBuild ", num, " records"); - if (num > i2p::tunnel::MAX_NUM_RECORDS) - { - LogPrint (eLogError, "TransitTunnel: Too many records in ShortTunnelBuild message ", num); - return; - } - if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) - { - LogPrint (eLogError, "TransitTunnel: ShortTunnelBuild message of ", num, " records is too short ", len); - return; - } - const uint8_t * record = buf + 1; - for (int i = 0; i < num; i++) - { - if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) - { - LogPrint (eLogDebug, "TransitTunnel: Short request record ", i, " is ours"); - uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) - { - LogPrint (eLogWarning, "TransitTunnel: Can't decrypt short request record ", i); - return; - } - if (clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE]) // not AES - { - LogPrint (eLogWarning, "TransitTunnel: Unknown layer encryption type ", clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE], " in short request record"); - return; - } - auto& noiseState = i2p::context.GetCurrentNoiseState (); - uint8_t replyKey[32]; // AEAD/Chacha20/Poly1305 - i2p::crypto::AESKey layerKey, ivKey; // AES - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelReplyKey", noiseState.m_CK); - memcpy (replyKey, noiseState.m_CK + 32, 32); - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "SMTunnelLayerKey", noiseState.m_CK); - memcpy (layerKey, noiseState.m_CK + 32, 32); - bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - if (isEndpoint) - { - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "TunnelLayerIVKey", noiseState.m_CK); - memcpy (ivKey, noiseState.m_CK + 32, 32); - } - else - { - if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // if next ident is now ours - { - LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in short request record"); - return; - } - memcpy (ivKey, noiseState.m_CK , 32); - } - - // check if we accept this tunnel - std::shared_ptr transitTunnel; - uint8_t retCode = 0; - if (i2p::context.AcceptsTunnels ()) - { - auto congestionLevel = i2p::context.GetCongestionLevel (false); - if (congestionLevel < CONGESTION_LEVEL_FULL) - { - if (congestionLevel >= CONGESTION_LEVEL_MEDIUM) - { - // random reject depending on congestion level - int level = m_Rng () % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM; - if (congestionLevel > level) - retCode = 30; - } - } - else - retCode = 30; - } - else - retCode = 30; - - if (!retCode) - { - i2p::data::IdentHash nextIdent(clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET); - bool isEndpoint = clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - if (isEndpoint || !i2p::data::IsRouterDuplicated (nextIdent)) - { - // create new transit tunnel - transitTunnel = CreateTransitTunnel ( - bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), - nextIdent, - bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - layerKey, ivKey, - clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, - isEndpoint); - if (!AddTransitTunnel (transitTunnel)) - retCode = 30; - } - else - // decline tunnel going to duplicated router - retCode = 30; - } - - // encrypt reply - uint8_t nonce[12]; - memset (nonce, 0, 12); - uint8_t * reply = buf + 1; - for (int j = 0; j < num; j++) - { - nonce[4] = j; // nonce is record # - if (j == i) - { - memset (reply + SHORT_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options - reply[SHORT_RESPONSE_RECORD_RET_OFFSET] = retCode; - if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState.m_H, 32, replyKey, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt - { - LogPrint (eLogWarning, "TransitTunnel: Short reply AEAD encryption failed"); - return; - } - } - else - i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, reply); - reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; - } - // send reply - auto onDrop = [transitTunnel]() - { - if (transitTunnel) - { - LogPrint (eLogDebug, "TransitTunnel: Failed to send reply for transit tunnel ", transitTunnel->GetTunnelID ()); - auto t = transitTunnel->GetCreationTime (); - if (t > i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT) - // make transit tunnel expired - transitTunnel->SetCreationTime (t - i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT); - } - }; - if (isEndpoint) - { - auto replyMsg = NewI2NPShortMessage (); - replyMsg->Concat (buf, len); - replyMsg->FillI2NPMessageHeader (eI2NPShortTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); - if (transitTunnel) replyMsg->onDrop = onDrop; - if (memcmp ((const uint8_t *)i2p::context.GetIdentHash (), - clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32)) // reply IBGW is not local? - { - i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "RGarlicKeyAndTag", noiseState.m_CK); - uint64_t tag; - memcpy (&tag, noiseState.m_CK, 8); - // we send it to reply tunnel - i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - i2p::garlic::WrapECIESX25519Message (replyMsg, noiseState.m_CK + 32, tag))); - } - else - { - // IBGW is local - uint32_t tunnelID = bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET); - auto tunnel = i2p::tunnel::tunnels.GetTunnel (tunnelID); - if (tunnel) - { - tunnel->SendTunnelDataMsg (replyMsg); - tunnel->FlushTunnelDataMsgs (); - } - else - LogPrint (eLogWarning, "I2NP: Tunnel ", tunnelID, " not found for short tunnel build reply"); - } - } - else - { - auto msg = CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, - bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); - if (transitTunnel) msg->onDrop = onDrop; - i2p::transport::transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, msg); - } - return; - } - record += SHORT_TUNNEL_BUILD_RECORD_SIZE; - } - } - - bool TransitTunnels::HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText) - { - for (int i = 0; i < num; i++) - { - uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE; - if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) - { - LogPrint (eLogDebug, "TransitTunnel: Build request record ", i, " is ours"); - if (!i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) - { - LogPrint (eLogWarning, "TransitTunnel: Failed to decrypt tunnel build record"); - return false; - } - if (!memcmp ((const uint8_t *)i2p::context.GetIdentHash (), clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, 32) && // if next ident is now ours - !(clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG)) // and not endpoint - { - LogPrint (eLogWarning, "TransitTunnel: Next ident is ours in tunnel build record"); - return false; - } - uint8_t retCode = 0; - // decide if we should accept tunnel - bool accept = i2p::context.AcceptsTunnels (); - if (accept) - { - auto congestionLevel = i2p::context.GetCongestionLevel (false); - if (congestionLevel >= CONGESTION_LEVEL_MEDIUM) - { - if (congestionLevel < CONGESTION_LEVEL_FULL) - { - // random reject depending on congestion level - int level = m_Rng () % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM; - if (congestionLevel > level) - accept = false; - } - else - accept = false; - } - } - - if (accept) - { - i2p::data::IdentHash nextIdent(clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET); - bool isEndpoint = clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - if (isEndpoint || !i2p::data::IsRouterDuplicated (nextIdent)) - { - auto transitTunnel = CreateTransitTunnel ( - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), - nextIdent, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, - clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, - isEndpoint); - if (!AddTransitTunnel (transitTunnel)) - retCode = 30; - } - else - // decline tunnel going to duplicated router - retCode = 30; - } - else - retCode = 30; // always reject with bandwidth reason (30) - - // replace record to reply - memset (record + ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET, 0, 2); // no options - record[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET] = retCode; - // encrypt reply - i2p::crypto::CBCEncryption encryption; - for (int j = 0; j < num; j++) - { - uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE; - if (j == i) - { - uint8_t nonce[12]; - memset (nonce, 0, 12); - auto& noiseState = i2p::context.GetCurrentNoiseState (); - if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt - { - LogPrint (eLogWarning, "TransitTunnel: Reply AEAD encryption failed"); - return false; - } - } - else - { - encryption.SetKey (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET); - encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, reply); - } - } - return true; - } - } - return false; - } - - void TransitTunnels::HandleVariableTransitTunnelBuildMsg (std::shared_ptr&& msg) - { - if (!msg) return; - uint8_t * buf = msg->GetPayload(); - size_t len = msg->GetPayloadLength(); - int num = buf[0]; - LogPrint (eLogDebug, "TransitTunnel: VariableTunnelBuild ", num, " records"); - if (num > i2p::tunnel::MAX_NUM_RECORDS) - { - LogPrint (eLogError, "TransitTunnle: Too many records in VaribleTunnelBuild message ", num); - return; - } - if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1) - { - LogPrint (eLogError, "TransitTunnel: VaribleTunnelBuild message of ", num, " records is too short ", len); - return; - } - uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - if (HandleBuildRequestRecords (num, buf + 1, clearText)) - { - if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel - { - // so we send it to reply tunnel - i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateTunnelGatewayMsg (bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), - eI2NPVariableTunnelBuildReply, buf, len, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - else - i2p::transport::transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, - CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, - bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); - } - } - - bool TransitTunnels::AddTransitTunnel (std::shared_ptr tunnel) - { - if (tunnels.AddTunnel (tunnel)) - m_TransitTunnels.push_back (tunnel); - else - { - LogPrint (eLogError, "TransitTunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists"); - return false; - } - return true; - } - - void TransitTunnels::ManageTransitTunnels (uint64_t ts) - { - for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();) - { - auto tunnel = *it; - if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT || - ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ()) - { - LogPrint (eLogDebug, "TransitTunnel: Transit tunnel with id ", tunnel->GetTunnelID (), " expired"); - tunnels.RemoveTunnel (tunnel->GetTunnelID ()); - it = m_TransitTunnels.erase (it); - } - else - { - tunnel->Cleanup (); - it++; - } - } - } - - int TransitTunnels::GetTransitTunnelsExpirationTimeout () - { - int timeout = 0; - uint32_t ts = i2p::util::GetSecondsSinceEpoch (); - // TODO: possible race condition with I2PControl - for (const auto& it : m_TransitTunnels) - { - int t = it->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts; - if (t > timeout) timeout = t; - } - return timeout; - } -} -} diff --git a/libi2pd/TransitTunnel.h b/libi2pd/TransitTunnel.h deleted file mode 100644 index 34bcc79f..00000000 --- a/libi2pd/TransitTunnel.h +++ /dev/null @@ -1,165 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 TRANSIT_TUNNEL_H__ -#define TRANSIT_TUNNEL_H__ - -#include -#include -#include -#include -#include "Crypto.h" -#include "Queue.h" -#include "I2NPProtocol.h" -#include "TunnelEndpoint.h" -#include "TunnelGateway.h" -#include "TunnelBase.h" - -namespace i2p -{ -namespace tunnel -{ - class TransitTunnel: public TunnelBase - { - public: - - TransitTunnel (uint32_t receiveTunnelID, - const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, - const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey); - - virtual size_t GetNumTransmittedBytes () const { return 0; }; - virtual std::string GetNextPeerName () const; - - // implements TunnelBase - void SendTunnelDataMsg (std::shared_ptr msg) override; - void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) override; - void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) override; - - private: - - i2p::crypto::AESKey m_LayerKey, m_IVKey; - std::unique_ptr m_Encryption; - }; - - class TransitTunnelParticipant: public TransitTunnel - { - public: - - TransitTunnelParticipant (uint32_t receiveTunnelID, - const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, - const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey): - TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, - layerKey, ivKey), m_NumTransmittedBytes (0) {}; - ~TransitTunnelParticipant (); - - size_t GetNumTransmittedBytes () const override { return m_NumTransmittedBytes; }; - std::string GetNextPeerName () const override; - void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) override; - void FlushTunnelDataMsgs () override; - - private: - - size_t m_NumTransmittedBytes; - std::list > m_TunnelDataMsgs; - std::unique_ptr m_Sender; - }; - - class TransitTunnelGateway: public TransitTunnel - { - public: - - TransitTunnelGateway (uint32_t receiveTunnelID, - const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, - const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey): - TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, - layerKey, ivKey), m_Gateway(*this) {}; - - void SendTunnelDataMsg (std::shared_ptr msg) override; - void FlushTunnelDataMsgs () override; - size_t GetNumTransmittedBytes () const override { return m_Gateway.GetNumSentBytes (); }; - std::string GetNextPeerName () const override; - - private: - - std::mutex m_SendMutex; - TunnelGateway m_Gateway; - }; - - class TransitTunnelEndpoint: public TransitTunnel - { - public: - - TransitTunnelEndpoint (uint32_t receiveTunnelID, - const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, - const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey): - TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey) {}; - - void Cleanup () override; - - void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) override; - void FlushTunnelDataMsgs () override; - size_t GetNumTransmittedBytes () const override { return m_Endpoint ? m_Endpoint->GetNumReceivedBytes () : 0; } - std::string GetNextPeerName () const override; - - private: - - std::mutex m_HandleMutex; - std::unique_ptr m_Endpoint; - }; - - std::shared_ptr CreateTransitTunnel (uint32_t receiveTunnelID, - const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, - const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey, - bool isGateway, bool isEndpoint); - - - const int TRANSIT_TUNNELS_QUEUE_WAIT_INTERVAL = 10; // in seconds - - class TransitTunnels - { - public: - - TransitTunnels (); - ~TransitTunnels (); - - void Start (); - void Stop (); - void PostTransitTunnelBuildMsg (std::shared_ptr&& msg); - - size_t GetNumTransitTunnels () const { return m_TransitTunnels.size (); } - int GetTransitTunnelsExpirationTimeout (); - - private: - - bool AddTransitTunnel (std::shared_ptr tunnel); - void ManageTransitTunnels (uint64_t ts); - - void HandleShortTransitTunnelBuildMsg (std::shared_ptr&& msg); - void HandleVariableTransitTunnelBuildMsg (std::shared_ptr&& msg); - bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText); - - void Run (); - - private: - - volatile bool m_IsRunning; - std::unique_ptr m_Thread; - std::list > m_TransitTunnels; - i2p::util::Queue > m_TunnelBuildMsgQueue; - std::mt19937 m_Rng; - - public: - - // for HTTP only - const auto& GetTransitTunnels () const { return m_TransitTunnels; }; - size_t GetTunnelBuildMsgQueueSize () const { return m_TunnelBuildMsgQueue.GetSize (); }; - }; -} -} - -#endif diff --git a/libi2pd/TransportSession.h b/libi2pd/TransportSession.h deleted file mode 100644 index 2cff0b1f..00000000 --- a/libi2pd/TransportSession.h +++ /dev/null @@ -1,204 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 TRANSPORT_SESSION_H__ -#define TRANSPORT_SESSION_H__ - -#include -#include -#include -#include -#include -#include "Identity.h" -#include "Crypto.h" -#include "RouterInfo.h" -#include "I2NPProtocol.h" -#include "Timestamp.h" - -namespace i2p -{ -namespace transport -{ - const size_t IPV4_HEADER_SIZE = 20; - const size_t IPV6_HEADER_SIZE = 40; - const size_t UDP_HEADER_SIZE = 8; - - template - class SignedData - { - public: - - SignedData (): m_Size(0) {} - SignedData (const SignedData& other) - { - m_Size = other.m_Size; - memcpy (m_Buf, other.m_Buf, m_Size); - } - - void Reset () - { - m_Size = 0; - } - - size_t Insert (const uint8_t * buf, size_t len) - { - if (m_Size + len > sz) len = sz - m_Size; - memcpy (m_Buf + m_Size, buf, len); - m_Size += len; - return len; - } - - template - void Insert (T t) - { - Insert ((const uint8_t *)&t, sizeof (T)); - } - - bool Verify (std::shared_ptr ident, const uint8_t * signature) const - { - return ident->Verify (m_Buf, m_Size, signature); - } - - void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const - { - keys.Sign (m_Buf, m_Size, signature); - } - - private: - - uint8_t m_Buf[sz]; - size_t m_Size; - }; - - const int64_t TRANSPORT_SESSION_SLOWNESS_THRESHOLD = 500; // in milliseconds - const int64_t TRANSPORT_SESSION_MAX_HANDSHAKE_INTERVAL = 10000; // in milliseconds - const uint64_t TRANSPORT_SESSION_BANDWIDTH_UPDATE_MIN_INTERVAL = 5; // in seconds - class TransportSession - { - public: - - TransportSession (std::shared_ptr router, int terminationTimeout): - m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout), m_HandshakeInterval (0), - m_SendQueueSize (0), m_NumSentBytes (0), m_NumReceivedBytes (0), - m_LastBandWidthUpdateNumSentBytes (0), m_LastBandWidthUpdateNumReceivedBytes (0), - m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ()), - m_LastBandwidthUpdateTimestamp (m_LastActivityTimestamp), m_InBandwidth (0), m_OutBandwidth (0) - { - if (router) - m_RemoteIdentity = router->GetRouterIdentity (); - m_CreationTime = m_LastActivityTimestamp; - } - - virtual ~TransportSession () {}; - virtual void Done () = 0; - - std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; } - - std::shared_ptr GetRemoteIdentity () - { - std::lock_guard l(m_RemoteIdentityMutex); - return m_RemoteIdentity; - } - void SetRemoteIdentity (std::shared_ptr ident) - { - std::lock_guard l(m_RemoteIdentityMutex); - m_RemoteIdentity = ident; - } - - size_t GetNumSentBytes () const { return m_NumSentBytes; }; - void UpdateNumSentBytes (size_t len) - { - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - m_NumSentBytes += len; - UpdateBandwidth (); - } - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; - void UpdateNumReceivedBytes (size_t len) - { - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - m_NumReceivedBytes += len; - UpdateBandwidth (); - } - size_t GetSendQueueSize () const { return m_SendQueueSize; }; - void SetSendQueueSize (size_t s) { m_SendQueueSize = s; }; - bool IsOutgoing () const { return m_IsOutgoing; }; - bool IsSlow () const { return m_HandshakeInterval > TRANSPORT_SESSION_SLOWNESS_THRESHOLD && - m_HandshakeInterval < TRANSPORT_SESSION_MAX_HANDSHAKE_INTERVAL; }; - bool IsBandwidthExceeded (bool isHighBandwidth) const - { - auto limit = isHighBandwidth ? i2p::data::HIGH_BANDWIDTH_LIMIT*1024 : i2p::data::LOW_BANDWIDTH_LIMIT*1024; // convert to bytes - return std::max (m_InBandwidth, m_OutBandwidth) > limit; - } - - int GetTerminationTimeout () const { return m_TerminationTimeout; }; - void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; }; - bool IsTerminationTimeoutExpired (uint64_t ts) const - { - return ts >= m_LastActivityTimestamp + GetTerminationTimeout () || - ts + GetTerminationTimeout () < m_LastActivityTimestamp; - }; - - uint32_t GetCreationTime () const { return m_CreationTime; }; - void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers - - uint64_t GetLastActivityTimestamp () const { return m_LastActivityTimestamp; }; - void SetLastActivityTimestamp (uint64_t ts) { m_LastActivityTimestamp = ts; }; - - virtual uint32_t GetRelayTag () const { return 0; }; - virtual void SendLocalRouterInfo (bool update = false) - { - std::list > msgs{ CreateDatabaseStoreMsg () }; - SendI2NPMessages (msgs); - }; - virtual void SendI2NPMessages (std::list >& msgs) = 0; - virtual bool IsEstablished () const = 0; - virtual i2p::data::RouterInfo::SupportedTransports GetTransportType () const = 0; - - private: - - void UpdateBandwidth () - { - int64_t interval = m_LastActivityTimestamp - m_LastBandwidthUpdateTimestamp; - if (interval < 0 || interval > 60*10) // 10 minutes - { - // clock was adjusted, copy new values - m_LastBandWidthUpdateNumSentBytes = m_NumSentBytes; - m_LastBandWidthUpdateNumReceivedBytes = m_NumReceivedBytes; - m_LastBandwidthUpdateTimestamp = m_LastActivityTimestamp; - return; - } - if ((uint64_t)interval > TRANSPORT_SESSION_BANDWIDTH_UPDATE_MIN_INTERVAL) - { - m_OutBandwidth = (m_NumSentBytes - m_LastBandWidthUpdateNumSentBytes)/interval; - m_LastBandWidthUpdateNumSentBytes = m_NumSentBytes; - m_InBandwidth = (m_NumReceivedBytes - m_LastBandWidthUpdateNumReceivedBytes)/interval; - m_LastBandWidthUpdateNumReceivedBytes = m_NumReceivedBytes; - m_LastBandwidthUpdateTimestamp = m_LastActivityTimestamp; - } - } - - protected: - - std::shared_ptr m_RemoteIdentity; - mutable std::mutex m_RemoteIdentityMutex; - bool m_IsOutgoing; - int m_TerminationTimeout; - uint32_t m_CreationTime; // seconds since epoch - int64_t m_HandshakeInterval; // in milliseconds between SessionRequest->SessionCreated or SessionCreated->SessionConfirmed - - private: - - size_t m_SendQueueSize, m_NumSentBytes, m_NumReceivedBytes, - m_LastBandWidthUpdateNumSentBytes, m_LastBandWidthUpdateNumReceivedBytes; - uint64_t m_LastActivityTimestamp, m_LastBandwidthUpdateTimestamp; - uint32_t m_InBandwidth, m_OutBandwidth; - }; -} -} - -#endif diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp deleted file mode 100644 index 98dbcd94..00000000 --- a/libi2pd/Transports.cpp +++ /dev/null @@ -1,1404 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 // for boost::to_lower -#include "Log.h" -#include "Crypto.h" -#include "RouterContext.h" -#include "I2NPProtocol.h" -#include "NetDb.hpp" -#include "Transports.h" -#include "Config.h" -#include "HTTP.h" -#include "util.h" - -using namespace i2p::data; - -namespace i2p -{ -namespace transport -{ - template - EphemeralKeysSupplier::EphemeralKeysSupplier (int size): - m_QueueSize (size), m_IsRunning (false) - { - } - - template - EphemeralKeysSupplier::~EphemeralKeysSupplier () - { - Stop (); - } - - template - void EphemeralKeysSupplier::Start () - { - m_IsRunning = true; - m_Thread.reset (new std::thread (std::bind (&EphemeralKeysSupplier::Run, this))); - } - - template - void EphemeralKeysSupplier::Stop () - { - { - std::unique_lock l(m_AcquiredMutex); - m_IsRunning = false; - m_Acquired.notify_one (); - } - if (m_Thread) - { - m_Thread->join (); - m_Thread = nullptr; - } - if (!m_Queue.empty ()) - { - // clean up queue - std::queue > tmp; - std::swap (m_Queue, tmp); - } - m_KeysPool.CleanUpMt (); - } - - template - void EphemeralKeysSupplier::Run () - { - i2p::util::SetThreadName("Ephemerals"); - - while (m_IsRunning) - { - int num, total = 0; - while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < m_QueueSize) - { - CreateEphemeralKeys (num); - total += num; - } - if (total > m_QueueSize) - { - LogPrint (eLogWarning, "Transports: ", total, " ephemeral keys generated at the time"); - std::this_thread::sleep_for (std::chrono::seconds(1)); // take a break - } - else - { - m_KeysPool.CleanUpMt (); - std::unique_lock l(m_AcquiredMutex); - if (!m_IsRunning) break; - m_Acquired.wait (l); // wait for element gets acquired - } - } - } - - template - void EphemeralKeysSupplier::CreateEphemeralKeys (int num) - { - if (num > 0) - { - for (int i = 0; i < num; i++) - { - auto pair = m_KeysPool.AcquireSharedMt (); - pair->GenerateKeys (); - std::unique_lock l(m_AcquiredMutex); - m_Queue.push (pair); - } - } - } - - template - std::shared_ptr EphemeralKeysSupplier::Acquire () - { - { - std::unique_lock l(m_AcquiredMutex); - if (!m_Queue.empty ()) - { - auto pair = m_Queue.front (); - m_Queue.pop (); - m_Acquired.notify_one (); - return pair; - } - } - // queue is empty, create new - auto pair = m_KeysPool.AcquireSharedMt (); - pair->GenerateKeys (); - return pair; - } - - template - void EphemeralKeysSupplier::Return (std::shared_ptr pair) - { - if (pair) - { - std::unique_lock l(m_AcquiredMutex); - if ((int)m_Queue.size () < 2*m_QueueSize) - m_Queue.push (pair); - } - else - LogPrint(eLogError, "Transports: Return null keys"); - } - - void Peer::UpdateParams (std::shared_ptr router) - { - if (router) - { - isHighBandwidth = router->IsHighBandwidth (); - isEligible =(bool)router->GetCompatibleTransports (true) && // reachable - router->GetCongestion () != i2p::data::RouterInfo::eRejectAll && // accepts tunnel - router->IsECIES () && router->GetVersion () >= NETDB_MIN_HIGHBANDWIDTH_VERSION; // not too old - } - } - - Transports transports; - - Transports::Transports (): - m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_CheckReserved(true), m_Thread (nullptr), - m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr), - m_UpdateBandwidthTimer (nullptr), m_SSU2Server (nullptr), m_NTCP2Server (nullptr), - m_X25519KeysPairSupplier (NUM_X25519_PRE_GENERATED_KEYS), - m_TotalSentBytes (0), m_TotalReceivedBytes (0), m_TotalTransitTransmittedBytes (0), - m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth (0), - m_InBandwidth15s (0), m_OutBandwidth15s (0), m_TransitBandwidth15s (0), - m_InBandwidth5m (0), m_OutBandwidth5m (0), m_TransitBandwidth5m (0), - m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL) - { - } - - Transports::~Transports () - { - Stop (); - if (m_Service) - { - delete m_PeerCleanupTimer; m_PeerCleanupTimer = nullptr; - delete m_PeerTestTimer; m_PeerTestTimer = nullptr; - delete m_UpdateBandwidthTimer; m_UpdateBandwidthTimer = nullptr; - delete m_Work; m_Work = nullptr; - delete m_Service; m_Service = nullptr; - } - } - - void Transports::Start (bool enableNTCP2, bool enableSSU2) - { - if (!m_Service) - { - m_Service = new boost::asio::io_context (); - m_Work = new boost::asio::executor_work_guard (m_Service->get_executor ()); - m_PeerCleanupTimer = new boost::asio::deadline_timer (*m_Service); - m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service); - m_UpdateBandwidthTimer = new boost::asio::deadline_timer (*m_Service); - } - - bool ipv4; i2p::config::GetOption("ipv4", ipv4); - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - i2p::config::GetOption("nat", m_IsNAT); - m_X25519KeysPairSupplier.Start (); - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&Transports::Run, this)); - std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); - i2p::http::URL proxyurl; - // create NTCP2. TODO: move to acceptor - if (enableNTCP2 || i2p::context.SupportsMesh ()) - { - if(!ntcp2proxy.empty() && enableNTCP2) - { - if(proxyurl.parse(ntcp2proxy)) - { - if(proxyurl.schema == "socks" || proxyurl.schema == "http") - { - m_NTCP2Server = new NTCP2Server (); - NTCP2Server::ProxyType proxytype = NTCP2Server::eSocksProxy; - - if (proxyurl.schema == "http") - proxytype = NTCP2Server::eHTTPProxy; - - m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port, proxyurl.user, proxyurl.pass); - i2p::context.SetStatus (eRouterStatusProxy); - if (ipv6) - i2p::context.SetStatusV6 (eRouterStatusProxy); - } - else - LogPrint(eLogCritical, "Transports: Unsupported NTCP2 proxy URL ", ntcp2proxy); - } - else - LogPrint(eLogCritical, "Transports: Invalid NTCP2 proxy URL ", ntcp2proxy); - } - else - m_NTCP2Server = new NTCP2Server (); - } - - // create SSU2 server - if (enableSSU2) - { - m_SSU2Server = new SSU2Server (); - std::string ssu2proxy; i2p::config::GetOption("ssu2.proxy", ssu2proxy); - if (!ssu2proxy.empty()) - { - if (proxyurl.parse (ssu2proxy) && proxyurl.schema == "socks") - { - if (m_SSU2Server->SetProxy (proxyurl.host, proxyurl.port)) - { - i2p::context.SetStatus (eRouterStatusProxy); - if (ipv6) - i2p::context.SetStatusV6 (eRouterStatusProxy); - } - else - LogPrint(eLogCritical, "Transports: Can't set SSU2 proxy ", ssu2proxy); - } - else - LogPrint(eLogCritical, "Transports: Invalid SSU2 proxy URL ", ssu2proxy); - } - } - - // bind to interfaces - if (ipv4) - { - std::string address; i2p::config::GetOption("address4", address); - if (!address.empty ()) - { - boost::system::error_code ec; - auto addr = boost::asio::ip::make_address (address, ec); - if (!ec) - { - if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr); - if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr); - } - } - - if (enableSSU2) - { - uint16_t mtu; i2p::config::GetOption ("ssu2.mtu4", mtu); - if (mtu) - { - if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; - if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE; - i2p::context.SetMTU (mtu, true); - } - } - } - - if (ipv6) - { - std::string address; i2p::config::GetOption("address6", address); - if (!address.empty ()) - { - boost::system::error_code ec; - auto addr = boost::asio::ip::make_address (address, ec); - if (!ec) - { - if (m_NTCP2Server) m_NTCP2Server->SetLocalAddress (addr); - if (m_SSU2Server) m_SSU2Server->SetLocalAddress (addr); - } - } - - if (enableSSU2) - { - uint16_t mtu; i2p::config::GetOption ("ssu2.mtu6", mtu); - if (mtu) - { - if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE; - if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE; - i2p::context.SetMTU (mtu, false); - } - } - } - - bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); - if (ygg) - { - std::string address; i2p::config::GetOption("meshnets.yggaddress", address); - if (!address.empty ()) - { - boost::system::error_code ec; - auto addr = boost::asio::ip::make_address (address, ec); - if (!ec && m_NTCP2Server && i2p::util::net::IsYggdrasilAddress (addr)) - m_NTCP2Server->SetLocalAddress (addr); - } - } - - // start servers - if (m_NTCP2Server) m_NTCP2Server->Start (); - if (m_SSU2Server) m_SSU2Server->Start (); - if (m_SSU2Server) DetectExternalIP (); - - m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5 * SESSION_CREATION_TIMEOUT)); - m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); - - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); - for (int i = 0; i < TRAFFIC_SAMPLE_COUNT; i++) - { - m_TrafficSamples[i].Timestamp = ts - (TRAFFIC_SAMPLE_COUNT - i - 1) * 1000; - m_TrafficSamples[i].TotalReceivedBytes = 0; - m_TrafficSamples[i].TotalSentBytes = 0; - m_TrafficSamples[i].TotalTransitTransmittedBytes = 0; - } - m_TrafficSamplePtr = TRAFFIC_SAMPLE_COUNT - 1; - - m_UpdateBandwidthTimer->expires_from_now (boost::posix_time::seconds(1)); - m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1)); - - if (m_IsNAT) - { - m_PeerTestTimer->expires_from_now (boost::posix_time::seconds(PEER_TEST_INTERVAL + m_Rng() % PEER_TEST_INTERVAL_VARIANCE)); - m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); - } - } - - void Transports::Stop () - { - if (m_PeerCleanupTimer) m_PeerCleanupTimer->cancel (); - if (m_PeerTestTimer) m_PeerTestTimer->cancel (); - - if (m_SSU2Server) - { - m_SSU2Server->Stop (); - delete m_SSU2Server; - m_SSU2Server = nullptr; - } - - if (m_NTCP2Server) - { - m_NTCP2Server->Stop (); - delete m_NTCP2Server; - m_NTCP2Server = nullptr; - } - - m_X25519KeysPairSupplier.Stop (); - m_IsRunning = false; - if (m_Service) m_Service->stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - m_Peers.clear (); - } - - void Transports::Run () - { - i2p::util::SetThreadName("Transports"); - - while (m_IsRunning && m_Service) - { - try - { - m_Service->run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Transports: Runtime exception: ", ex.what ()); - } - } - } - - void Transports::UpdateBandwidthValues(int interval, uint32_t& in, uint32_t& out, uint32_t& transit) - { - TrafficSample& sample1 = m_TrafficSamples[m_TrafficSamplePtr]; - TrafficSample& sample2 = m_TrafficSamples[(TRAFFIC_SAMPLE_COUNT + m_TrafficSamplePtr - interval) % TRAFFIC_SAMPLE_COUNT]; - auto delta = (int64_t)sample1.Timestamp - (int64_t)sample2.Timestamp; - if (delta <= 0) - { - LogPrint (eLogError, "Transports: Backward clock jump detected, got ", delta, " instead of ", interval * 1000); - return; - } - in = (sample1.TotalReceivedBytes - sample2.TotalReceivedBytes) * 1000 / delta; - out = (sample1.TotalSentBytes - sample2.TotalSentBytes) * 1000 / delta; - transit = (sample1.TotalTransitTransmittedBytes - sample2.TotalTransitTransmittedBytes) * 1000 / delta; - } - - void Transports::HandleUpdateBandwidthTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - m_TrafficSamplePtr++; - if (m_TrafficSamplePtr == TRAFFIC_SAMPLE_COUNT) - m_TrafficSamplePtr = 0; - - TrafficSample& sample = m_TrafficSamples[m_TrafficSamplePtr]; - sample.Timestamp = i2p::util::GetMillisecondsSinceEpoch(); - sample.TotalReceivedBytes = m_TotalReceivedBytes; - sample.TotalSentBytes = m_TotalSentBytes; - sample.TotalTransitTransmittedBytes = m_TotalTransitTransmittedBytes; - - UpdateBandwidthValues (1, m_InBandwidth, m_OutBandwidth, m_TransitBandwidth); - UpdateBandwidthValues (15, m_InBandwidth15s, m_OutBandwidth15s, m_TransitBandwidth15s); - UpdateBandwidthValues (300, m_InBandwidth5m, m_OutBandwidth5m, m_TransitBandwidth5m); - - m_UpdateBandwidthTimer->expires_from_now (boost::posix_time::seconds(1)); - m_UpdateBandwidthTimer->async_wait (std::bind (&Transports::HandleUpdateBandwidthTimer, this, std::placeholders::_1)); - } - } - - int Transports::GetCongestionLevel (bool longTerm) const - { - auto bwLimit = i2p::context.GetBandwidthLimit () * 1024; // convert to bytes - auto tbwLimit = i2p::context.GetTransitBandwidthLimit () * 1024; // convert to bytes - - if (tbwLimit == 0 || bwLimit == 0) - return CONGESTION_LEVEL_FULL; - - uint32_t bw; - uint32_t tbw; - if (longTerm) - { - bw = std::max (m_InBandwidth5m, m_OutBandwidth5m); - tbw = m_TransitBandwidth5m; - } - else - { - bw = std::max (m_InBandwidth15s, m_OutBandwidth15s); - tbw = m_TransitBandwidth; - } - auto bwCongestionLevel = CONGESTION_LEVEL_FULL * bw / bwLimit; - auto tbwCongestionLevel = CONGESTION_LEVEL_FULL * tbw / tbwLimit; - return std::max (bwCongestionLevel, tbwCongestionLevel); - } - - std::future > Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg) - { - if (m_IsOnline) - return SendMessages (ident, { msg }); - return {}; // invalid future - } - - std::future > Transports::SendMessages (const i2p::data::IdentHash& ident, std::list >&& msgs) - { - return boost::asio::post (*m_Service, boost::asio::use_future ([this, ident, msgs = std::move(msgs)] () mutable - { - return PostMessages (ident, msgs); - })); - } - - std::shared_ptr Transports::PostMessages (const i2p::data::IdentHash& ident, std::list >& msgs) - { - if (ident == i2p::context.GetRouterInfo ().GetIdentHash ()) - { - // we send it to ourself - for (auto& it: msgs) - m_LoopbackHandler.PutNextMessage (std::move (it)); - m_LoopbackHandler.Flush (); - return nullptr; - } - if(RoutesRestricted() && !IsRestrictedPeer(ident)) return nullptr; - std::shared_ptr peer; - { - std::lock_guard l(m_PeersMutex); - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) - peer = it->second; - } - if (!peer) - { - // check if not banned - if (i2p::data::IsRouterBanned (ident)) return nullptr; // don't create peer to unreachable router - // try to connect - bool connected = false; - try - { - auto r = netdb.FindRouter (ident); - if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return nullptr; // router found but non-reachable - - peer = std::make_shared(r, i2p::util::GetSecondsSinceEpoch ()); - { - std::lock_guard l(m_PeersMutex); - peer = m_Peers.emplace (ident, peer).first->second; - } - if (peer) - connected = ConnectToPeer (ident, peer); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Transports: PostMessages exception:", ex.what ()); - } - if (!connected) return nullptr; - } - - if (!peer) return nullptr; - if (peer->IsConnected ()) - { - auto session = peer->sessions.front (); - if (session) session->SendI2NPMessages (msgs); - return session; - } - else - { - auto sz = peer->delayedMessages.size (); - if (sz < MAX_NUM_DELAYED_MESSAGES) - { - if (sz < CHECK_PROFILE_NUM_DELAYED_MESSAGES && sz + msgs.size () >= CHECK_PROFILE_NUM_DELAYED_MESSAGES) - { - if (i2p::data::IsRouterBanned (ident)) - { - LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped"); - std::lock_guard l(m_PeersMutex); - m_Peers.erase (ident); - return nullptr; - } - } - if (sz > MAX_NUM_DELAYED_MESSAGES/2) - { - for (auto& it1: msgs) - if (it1->onDrop) - it1->Drop (); // drop earlier because we can handle it - else - peer->delayedMessages.push_back (it1); - } - else - peer->delayedMessages.splice (peer->delayedMessages.end (), msgs); - } - else - { - LogPrint (eLogWarning, "Transports: Delayed messages queue size to ", - ident.ToBase64 (), " exceeds ", MAX_NUM_DELAYED_MESSAGES); - std::lock_guard l(m_PeersMutex); - m_Peers.erase (ident); - } - } - return nullptr; - } - - bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr peer) - { - if (!peer->router) // reconnect - { - auto r = netdb.FindRouter (ident); // try to get new one from netdb - if (r) - { - peer->SetRouter (r); - r->CancelBufferToDelete (); - } - } - if (peer->router) // we have RI already - { - if (peer->priority.empty ()) - SetPriority (peer); - while (peer->numAttempts < (int)peer->priority.size ()) - { - auto tr = peer->priority[peer->numAttempts]; - peer->numAttempts++; - switch (tr) - { - case i2p::data::RouterInfo::eNTCP2V4: - case i2p::data::RouterInfo::eNTCP2V6: - { - if (!m_NTCP2Server) continue; - std::shared_ptr address = (tr == i2p::data::RouterInfo::eNTCP2V6) ? - peer->router->GetPublishedNTCP2V6Address () : peer->router->GetPublishedNTCP2V4Address (); - if (address && IsInReservedRange(address->host)) - address = nullptr; - if (address) - { - auto s = std::make_shared (*m_NTCP2Server, peer->router, address); - if( m_NTCP2Server->UsingProxy()) - m_NTCP2Server->ConnectWithProxy(s); - else - m_NTCP2Server->Connect (s); - return true; - } - break; - } - case i2p::data::RouterInfo::eSSU2V4: - case i2p::data::RouterInfo::eSSU2V6: - { - if (!m_SSU2Server) continue; - std::shared_ptr address = (tr == i2p::data::RouterInfo::eSSU2V6) ? - peer->router->GetSSU2V6Address () : peer->router->GetSSU2V4Address (); - if (address && IsInReservedRange(address->host)) - address = nullptr; - if (address && address->IsReachableSSU ()) - { - if (m_SSU2Server->CreateSession (peer->router, address)) - return true; - } - break; - } - case i2p::data::RouterInfo::eNTCP2V6Mesh: - { - if (!m_NTCP2Server) continue; - auto address = peer->router->GetYggdrasilAddress (); - if (address) - { - auto s = std::make_shared (*m_NTCP2Server, peer->router, address); - m_NTCP2Server->Connect (s); - return true; - } - break; - } - default: - LogPrint (eLogError, "Transports: Unknown transport ", (int)tr); - } - } - - LogPrint (eLogInfo, "Transports: No compatible addresses available"); - if (!i2p::context.IsLimitedConnectivity () && peer->router->IsReachableFrom (i2p::context.GetRouterInfo ())) - i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed but router claimed them - peer->Done (); - std::lock_guard l(m_PeersMutex); - m_Peers.erase (ident); - return false; - } - else if (i2p::data::IsRouterBanned (ident)) - { - LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped"); - peer->Done (); - std::lock_guard l(m_PeersMutex); - m_Peers.erase (ident); - return false; - } - else // otherwise request RI - { - LogPrint (eLogInfo, "Transports: RouterInfo for ", ident.ToBase64 (), " not found, requested"); - i2p::data::netdb.RequestDestination (ident, std::bind ( - &Transports::RequestComplete, this, std::placeholders::_1, ident)); - } - return true; - } - - void Transports::SetPriority (std::shared_ptr peer) - { - static const std::vector - ntcp2Priority = - { - i2p::data::RouterInfo::eNTCP2V6, - i2p::data::RouterInfo::eNTCP2V4, - i2p::data::RouterInfo::eSSU2V6, - i2p::data::RouterInfo::eSSU2V4, - i2p::data::RouterInfo::eNTCP2V6Mesh - }, - ssu2Priority = - { - i2p::data::RouterInfo::eSSU2V6, - i2p::data::RouterInfo::eSSU2V4, - i2p::data::RouterInfo::eNTCP2V6, - i2p::data::RouterInfo::eNTCP2V4, - i2p::data::RouterInfo::eNTCP2V6Mesh - }; - if (!peer || !peer->router) return; - auto compatibleTransports = context.GetRouterInfo ().GetCompatibleTransports (false) & - peer->router->GetCompatibleTransports (true); - auto directTransports = compatibleTransports & peer->router->GetPublishedTransports (); - peer->numAttempts = 0; - peer->priority.clear (); - - std::shared_ptr profile; - if (peer->router->HasProfile ()) profile = peer->router->GetProfile (); // only if in memory - bool ssu2 = false; // NTCP2 by default - bool isReal = profile ? profile->IsReal () : true; - if (isReal) - { - ssu2 = m_Rng () & 1; // 1/2 - if (ssu2 && !profile) - { - profile = peer->router->GetProfile (); // load profile if necessary - isReal = profile->IsReal (); - if (!isReal) ssu2 = false; // try NTCP2 if router is not confirmed real - } - } - const auto& priority = ssu2 ? ssu2Priority : ntcp2Priority; - if (directTransports) - { - // direct connections have higher priority - if (!isReal && (directTransports & (i2p::data::RouterInfo::eNTCP2V4 | i2p::data::RouterInfo::eNTCP2V6))) - { - // Non-confirmed router and a NTCP2 direct connection is presented - compatibleTransports &= ~directTransports; // exclude SSU2 direct connections - directTransports &= ~(i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6); - } - for (auto transport: priority) - if (transport & directTransports) - peer->priority.push_back (transport); - compatibleTransports &= ~directTransports; - } - if (compatibleTransports) - { - // then remaining - for (auto transport: priority) - if (transport & compatibleTransports) - peer->priority.push_back (transport); - } - if (peer->priority.empty ()) - { - // try recently connected SSU2 if any - auto supportedTransports = context.GetRouterInfo ().GetCompatibleTransports (false) & - peer->router->GetCompatibleTransports (false); - if ((supportedTransports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) && - peer->router->HasProfile ()) - { - auto ep = peer->router->GetProfile ()->GetLastEndpoint (); - if (!ep.address ().is_unspecified () && ep.port ()) - { - if (ep.address ().is_v4 ()) - { - if ((supportedTransports & i2p::data::RouterInfo::eSSU2V4) && - m_SSU2Server->IsConnectedRecently (ep, false)) - peer->priority.push_back (i2p::data::RouterInfo::eSSU2V4); - } - else if (ep.address ().is_v6 ()) - { - if ((supportedTransports & i2p::data::RouterInfo::eSSU2V6) && - m_SSU2Server->IsConnectedRecently (ep)) - peer->priority.push_back (i2p::data::RouterInfo::eSSU2V6); - } - } - } - } - } - - void Transports::RequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident) - { - boost::asio::post (*m_Service, std::bind (&Transports::HandleRequestComplete, this, r, ident)); - } - - void Transports::HandleRequestComplete (std::shared_ptr r, i2p::data::IdentHash ident) - { - std::shared_ptr peer; - { - std::lock_guard l(m_PeersMutex); - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) - { - if (r) - peer = it->second; - else - m_Peers.erase (it); - } - } - - if (peer && !peer->router && r) - { - LogPrint (eLogDebug, "Transports: RouterInfo for ", ident.ToBase64 (), " found, trying to connect"); - peer->SetRouter (r); - if (!peer->IsConnected ()) - ConnectToPeer (ident, peer); - } - else if (!r) - LogPrint (eLogInfo, "Transports: RouterInfo not found, failed to send messages"); - - } - - void Transports::DetectExternalIP () - { - if (RoutesRestricted()) - { - LogPrint(eLogInfo, "Transports: Restricted routes enabled, not detecting IP"); - i2p::context.SetStatus (eRouterStatusOK); - return; - } - if (m_SSU2Server) - PeerTest (); - else - LogPrint (eLogWarning, "Transports: Can't detect external IP. SSU or SSU2 is not available"); - } - - void Transports::PeerTest (bool ipv4, bool ipv6) - { - if (RoutesRestricted() || !m_SSU2Server || m_SSU2Server->UsesProxy ()) return; - if (ipv4 && i2p::context.SupportsV4 ()) - { - LogPrint (eLogInfo, "Transports: Started peer test IPv4"); - std::unordered_set excluded; - excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router - int testDelay = 0; - for (int i = 0; i < 5; i++) - { - auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4 - if (router) - { - if (!i2p::context.GetTesting ()) - { - i2p::context.SetTesting (true); - // send first peer test immediately - m_SSU2Server->StartPeerTest (router, true); - } - else - { - testDelay += PEER_TEST_DELAY_INTERVAL + m_Rng() % PEER_TEST_DELAY_INTERVAL_VARIANCE; - if (m_Service) - { - auto delayTimer = std::make_shared(*m_Service); - delayTimer->expires_from_now (boost::posix_time::milliseconds (testDelay)); - delayTimer->async_wait ( - [this, router, delayTimer](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - m_SSU2Server->StartPeerTest (router, true); - }); - } - } - excluded.insert (router->GetIdentHash ()); - } - } - if (excluded.size () <= 1) - LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4"); - } - if (ipv6 && i2p::context.SupportsV6 ()) - { - LogPrint (eLogInfo, "Transports: Started peer test IPv6"); - std::unordered_set excluded; - excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router - int testDelay = 0; - for (int i = 0; i < 5; i++) - { - auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6 - if (router) - { - if (!i2p::context.GetTestingV6 ()) - { - i2p::context.SetTestingV6 (true); - // send first peer test immediately - m_SSU2Server->StartPeerTest (router, false); - } - else - { - testDelay += PEER_TEST_DELAY_INTERVAL + m_Rng() % PEER_TEST_DELAY_INTERVAL_VARIANCE; - if (m_Service) - { - auto delayTimer = std::make_shared(*m_Service); - delayTimer->expires_from_now (boost::posix_time::milliseconds (testDelay)); - delayTimer->async_wait ( - [this, router, delayTimer](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - m_SSU2Server->StartPeerTest (router, false); - }); - } - } - excluded.insert (router->GetIdentHash ()); - } - } - if (excluded.size () <= 1) - LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6"); - } - } - - std::shared_ptr Transports::GetNextX25519KeysPair () - { - return m_X25519KeysPairSupplier.Acquire (); - } - - void Transports::ReuseX25519KeysPair (std::shared_ptr pair) - { - m_X25519KeysPairSupplier.Return (pair); - } - - void Transports::PeerConnected (std::shared_ptr session) - { - boost::asio::post (*m_Service, [session, this]() - { - auto remoteIdentity = session->GetRemoteIdentity (); - if (!remoteIdentity) return; - auto ident = remoteIdentity->GetIdentHash (); - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) - { - auto peer = it->second; - if (peer->numAttempts > 1) - { - // exclude failed transports - i2p::data::RouterInfo::CompatibleTransports transports = 0; - int numExcluded = peer->numAttempts - 1; - if (numExcluded > (int)peer->priority.size ()) numExcluded = peer->priority.size (); - for (int i = 0; i < numExcluded; i++) - transports |= peer->priority[i]; - i2p::data::netdb.ExcludeReachableTransports (ident, transports); - } - if (peer->router && peer->numAttempts) - { - auto transport = peer->priority[peer->numAttempts-1]; - if (transport == i2p::data::RouterInfo::eNTCP2V4 || - transport == i2p::data::RouterInfo::eNTCP2V6 || transport == i2p::data::RouterInfo::eNTCP2V6Mesh) - i2p::data::UpdateRouterProfile (ident, - [](std::shared_ptr profile) - { - if (profile) profile->Connected (); // outgoing NTCP2 connection if always real - }); - i2p::data::netdb.SetUnreachable (ident, false); // clear unreachable - } - peer->numAttempts = 0; - peer->router = nullptr; // we don't need RouterInfo after successive connect - bool sendDatabaseStore = true; - if (it->second->delayedMessages.size () > 0) - { - // check if first message is our DatabaseStore (publishing) - auto firstMsg = peer->delayedMessages.front (); - if (firstMsg && firstMsg->GetTypeID () == eI2NPDatabaseStore && - i2p::data::IdentHash(firstMsg->GetPayload () + DATABASE_STORE_KEY_OFFSET) == i2p::context.GetIdentHash ()) - sendDatabaseStore = false; // we have it in the list already - } - if (sendDatabaseStore) - session->SendLocalRouterInfo (); - else - session->SetTerminationTimeout (10); // most likely it's publishing, no follow-up messages expected, set timeout to 10 seconds - peer->sessions.push_back (session); - session->SendI2NPMessages (peer->delayedMessages); // send and clear - } - else // incoming connection or peer test - { - if(RoutesRestricted() && ! IsRestrictedPeer(ident)) { - // not trusted - LogPrint(eLogWarning, "Transports: Closing untrusted inbound connection from ", ident.ToBase64()); - session->Done(); - return; - } - if (!session->IsOutgoing ()) // incoming - { - std::list > msgs{ CreateDatabaseStoreMsg () }; - session->SendI2NPMessages (msgs); // send DatabaseStore - } - auto r = i2p::data::netdb.FindRouter (ident); // router should be in netdb after SessionConfirmed - i2p::data::UpdateRouterProfile (ident, - [](std::shared_ptr profile) - { - if (profile) profile->Connected (); - }); - auto ts = i2p::util::GetSecondsSinceEpoch (); - auto peer = std::make_shared(r, ts); - peer->sessions.push_back (session); - peer->router = nullptr; - std::lock_guard l(m_PeersMutex); - m_Peers.emplace (ident, peer); - } - }); - } - - void Transports::PeerDisconnected (std::shared_ptr session) - { - boost::asio::post (*m_Service, [session, this]() - { - auto remoteIdentity = session->GetRemoteIdentity (); - if (!remoteIdentity) return; - auto ident = remoteIdentity->GetIdentHash (); - auto it = m_Peers.find (ident); - if (it != m_Peers.end ()) - { - auto peer = it->second; - bool wasConnected = peer->IsConnected (); - peer->sessions.remove (session); - if (!peer->IsConnected ()) - { - if (peer->delayedMessages.size () > 0) - { - if (wasConnected) // we had an active session before - peer->numAttempts = 0; // start over - ConnectToPeer (ident, peer); - } - else - { - { - std::lock_guard l(m_PeersMutex); - m_Peers.erase (it); - } - // delete buffer of just disconnected router - auto r = i2p::data::netdb.FindRouter (ident); - if (r && !r->IsUpdated ()) r->ScheduleBufferToDelete (); - } - } - } - }); - } - - bool Transports::IsConnected (const i2p::data::IdentHash& ident) const - { - std::lock_guard l(m_PeersMutex); -#if __cplusplus >= 202002L // C++20 - return m_Peers.contains (ident); -#else - auto it = m_Peers.find (ident); - return it != m_Peers.end (); -#endif - } - - void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - for (auto it = m_Peers.begin (); it != m_Peers.end (); ) - { - it->second->sessions.remove_if ( - [](std::shared_ptr session)->bool - { - return !session || !session->IsEstablished (); - }); - if (!it->second->IsConnected () && ts > it->second->creationTime + SESSION_CREATION_TIMEOUT) - { - LogPrint (eLogWarning, "Transports: Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds"); - /* if (!it->second.router) - { - // if router for ident not found mark it unreachable - auto profile = i2p::data::GetRouterProfile (it->first); - if (profile) profile->Unreachable (); - } */ - std::lock_guard l(m_PeersMutex); - it = m_Peers.erase (it); - } - else - { - if (ts > it->second->nextRouterInfoUpdateTime) - { - auto session = it->second->sessions.front (); - if (session) - session->SendLocalRouterInfo (true); - it->second->nextRouterInfoUpdateTime = ts + PEER_ROUTER_INFO_UPDATE_INTERVAL + - m_Rng() % PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE; - } - ++it; - } - } - bool ipv4Testing = i2p::context.GetTesting (); - if (!ipv4Testing) - ipv4Testing = i2p::context.GetRouterInfo ().IsSSU2V4 () && (i2p::context.GetStatus() == eRouterStatusUnknown); - bool ipv6Testing = i2p::context.GetTestingV6 (); - if (!ipv6Testing) - ipv6Testing = i2p::context.GetRouterInfo ().IsSSU2V6 () && (i2p::context.GetStatusV6() == eRouterStatusUnknown); - // if still testing or unknown, repeat peer test - if (ipv4Testing || ipv6Testing) - PeerTest (ipv4Testing, ipv6Testing); - m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(2 * SESSION_CREATION_TIMEOUT + m_Rng() % SESSION_CREATION_TIMEOUT)); - m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); - } - } - - void Transports::HandlePeerTestTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - PeerTest (); - m_PeerTestTimer->expires_from_now (boost::posix_time::seconds(PEER_TEST_INTERVAL + m_Rng() % PEER_TEST_INTERVAL_VARIANCE)); - m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1)); - } - } - - template - std::shared_ptr Transports::GetRandomPeer (Filter filter) const - { - if (m_Peers.empty()) return nullptr; - auto ts = i2p::util::GetSecondsSinceEpoch (); - bool found = false; - i2p::data::IdentHash ident; - { - uint16_t inds[3]; - RAND_bytes ((uint8_t *)inds, sizeof (inds)); - std::lock_guard l(m_PeersMutex); - auto count = m_Peers.size (); - if(count == 0) return nullptr; - inds[0] %= count; - auto it = m_Peers.begin (); - std::advance (it, inds[0]); - // try random peer - if (it != m_Peers.end () && filter (it->second)) - { - ident = it->first; - found = true; - } - else - { - // try some peers around - auto it1 = m_Peers.begin (); - if (inds[0]) - { - // before - inds[1] %= inds[0]; - std::advance (it1, (inds[1] + inds[0])/2); - } - else - it1 = it; - auto it2 = it; - if (inds[0] < m_Peers.size () - 1) - { - // after - inds[2] %= (m_Peers.size () - 1 - inds[0]); inds[2] /= 2; - std::advance (it2, inds[2]); - } - // it1 - from, it2 - to - it = it1; - while (it != it2 && it != m_Peers.end ()) - { - if (ts > it->second->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL && - filter (it->second)) - { - ident = it->first; - it->second->lastSelectionTime = ts; - found = true; - break; - } - it++; - } - if (!found) - { - // still not found, try from the beginning - it = m_Peers.begin (); - while (it != it1 && it != m_Peers.end ()) - { - if (ts > it->second->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL && - filter (it->second)) - { - ident = it->first; - it->second->lastSelectionTime = ts; - found = true; - break; - } - it++; - } - if (!found) - { - // still not found, try to the beginning - it = it2; - while (it != m_Peers.end ()) - { - if (ts > it->second->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL && - filter (it->second)) - { - ident = it->first; - it->second->lastSelectionTime = ts; - found = true; - break; - } - it++; - } - } - } - } - } - return found ? i2p::data::netdb.FindRouter (ident) : nullptr; - } - - std::shared_ptr Transports::GetRandomPeer (bool isHighBandwidth) const - { - return GetRandomPeer ( - [isHighBandwidth](std::shared_ptr peer)->bool - { - // connected, not overloaded and not slow - return !peer->router && peer->IsConnected () && peer->isEligible && - peer->sessions.front ()->GetSendQueueSize () <= PEER_ROUTER_INFO_OVERLOAD_QUEUE_SIZE && - !peer->sessions.front ()->IsSlow () && !peer->sessions.front ()->IsBandwidthExceeded (peer->isHighBandwidth) && - (!isHighBandwidth || peer->isHighBandwidth); - }); - } - - void Transports::RestrictRoutesToFamilies(const std::set& families) - { - std::lock_guard lock(m_FamilyMutex); - m_TrustedFamilies.clear(); - for (auto fam : families) - { - boost::to_lower (fam); - auto id = i2p::data::netdb.GetFamilies ().GetFamilyID (fam); - if (id) - m_TrustedFamilies.push_back (id); - } - } - - void Transports::RestrictRoutesToRouters(const std::set& routers) - { - std::lock_guard lock(m_TrustedRoutersMutex); - m_TrustedRouters.clear(); - for (const auto & ri : routers ) - m_TrustedRouters.insert(ri); - } - - bool Transports::RoutesRestricted() const - { - { - std::lock_guard routerslock(m_TrustedRoutersMutex); - if (!m_TrustedRouters.empty ()) return true; - } - { - std::lock_guard famlock(m_FamilyMutex); - if (!m_TrustedFamilies.empty ()) return true; - } - return false; - } - - /** XXX: if routes are not restricted this dies */ - std::shared_ptr Transports::GetRestrictedPeer() - { - { - std::lock_guard l(m_FamilyMutex); - i2p::data::FamilyID fam = 0; - auto sz = m_TrustedFamilies.size(); - if(sz > 1) - { - auto it = m_TrustedFamilies.begin (); - std::advance(it, m_Rng() % sz); - fam = *it; - } - else if (sz == 1) - { - fam = m_TrustedFamilies[0]; - } - if (fam) - return i2p::data::netdb.GetRandomRouterInFamily(fam); - } - { - std::lock_guard l(m_TrustedRoutersMutex); - auto sz = m_TrustedRouters.size(); - if (sz) - { - auto it = m_TrustedRouters.begin(); - if(sz > 1) - std::advance(it, m_Rng() % sz); - return i2p::data::netdb.FindRouter(*it); - } - } - return nullptr; - } - - bool Transports::IsTrustedRouter (const i2p::data::IdentHash& ih) const - { - if (m_TrustedRouters.empty ()) return false; - std::lock_guard l(m_TrustedRoutersMutex); -#if __cplusplus >= 202002L // C++20 - if (m_TrustedRouters.contains (ih)) -#else - if (m_TrustedRouters.count (ih) > 0) -#endif - return true; - return false; - } - - bool Transports::IsRestrictedPeer(const i2p::data::IdentHash& ih) const - { - if (IsTrustedRouter (ih)) return true; - - { - std::lock_guard l(m_FamilyMutex); - auto ri = i2p::data::netdb.FindRouter(ih); - for (const auto & fam : m_TrustedFamilies) - if(ri->IsFamily(fam)) return true; - } - return false; - } - - void Transports::SetOnline (bool online) - { - if (m_IsOnline != online) - { - m_IsOnline = online; - if (online) - PeerTest (); - else - i2p::context.SetError (eRouterErrorOffline); - } - } - - bool Transports::IsInReservedRange (const boost::asio::ip::address& host) const - { - return IsCheckReserved () && i2p::util::net::IsInReservedRange (host); - } - - void InitAddressFromIface () - { - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool ipv4; i2p::config::GetOption("ipv4", ipv4); - - // ifname -> address - std::string ifname; i2p::config::GetOption("ifname", ifname); - if (ipv4 && i2p::config::IsDefault ("address4")) - { - std::string ifname4; i2p::config::GetOption("ifname4", ifname4); - if (!ifname4.empty ()) - i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname4, false).to_string ()); // v4 - else if (!ifname.empty ()) - i2p::config::SetOption ("address4", i2p::util::net::GetInterfaceAddress(ifname, false).to_string ()); // v4 - } - if (ipv6 && i2p::config::IsDefault ("address6")) - { - std::string ifname6; i2p::config::GetOption("ifname6", ifname6); - if (!ifname6.empty ()) - i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname6, true).to_string ()); // v6 - else if (!ifname.empty ()) - i2p::config::SetOption ("address6", i2p::util::net::GetInterfaceAddress(ifname, true).to_string ()); // v6 - } - } - - void InitTransports () - { - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool ipv4; i2p::config::GetOption("ipv4", ipv4); - bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg); - uint16_t port; i2p::config::GetOption("port", port); - - boost::asio::ip::address_v6 yggaddr; - if (ygg) - { - std::string yggaddress; i2p::config::GetOption ("meshnets.yggaddress", yggaddress); - if (!yggaddress.empty ()) - { - yggaddr = boost::asio::ip::make_address (yggaddress).to_v6 (); - if (yggaddr.is_unspecified () || !i2p::util::net::IsYggdrasilAddress (yggaddr) || - !i2p::util::net::IsLocalAddress (yggaddr)) - { - LogPrint(eLogWarning, "Transports: Can't find Yggdrasil address ", yggaddress); - ygg = false; - } - } - else - { - yggaddr = i2p::util::net::GetYggdrasilAddress (); - if (yggaddr.is_unspecified ()) - { - LogPrint(eLogWarning, "Transports: Yggdrasil is not running. Disabled"); - ygg = false; - } - } - } - - if (!i2p::config::IsDefault("port")) - { - LogPrint(eLogInfo, "Transports: Accepting incoming connections at port ", port); - i2p::context.UpdatePort (port); - } - i2p::context.SetSupportsV6 (ipv6); - i2p::context.SetSupportsV4 (ipv4); - i2p::context.SetSupportsMesh (ygg, yggaddr); - - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - if (ntcp2) - { - bool published; i2p::config::GetOption("ntcp2.published", published); - if (published) - { - std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); - if (!ntcp2proxy.empty ()) published = false; - } - if (published) - { - uint16_t ntcp2port; i2p::config::GetOption("ntcp2.port", ntcp2port); - if (!ntcp2port) ntcp2port = port; // use standard port - i2p::context.PublishNTCP2Address (ntcp2port, true, ipv4, ipv6, false); // publish - if (ipv6) - { - std::string ipv6Addr; i2p::config::GetOption("ntcp2.addressv6", ipv6Addr); - auto addr = boost::asio::ip::make_address (ipv6Addr).to_v6 (); - if (!addr.is_unspecified () && addr != boost::asio::ip::address_v6::any ()) - i2p::context.UpdateNTCP2V6Address (addr); // set ipv6 address if configured - } - } - else - i2p::context.PublishNTCP2Address (port, false, ipv4, ipv6, false); // unpublish - } - if (ygg) - { - i2p::context.PublishNTCP2Address (port, true, false, false, true); - i2p::context.UpdateNTCP2V6Address (yggaddr); - if (!ipv4 && !ipv6) - i2p::context.SetStatus (eRouterStatusMesh); - } - bool ssu2; i2p::config::GetOption("ssu2.enabled", ssu2); - if (ssu2 && i2p::config::IsDefault ("ssu2.enabled") && !ipv4 && !ipv6) - ssu2 = false; // don't enable ssu2 for yggdrasil only router - if (ssu2) - { - uint16_t ssu2port; i2p::config::GetOption("ssu2.port", ssu2port); - if (!ssu2port && port) ssu2port = port; - bool published; i2p::config::GetOption("ssu2.published", published); - if (published) - i2p::context.PublishSSU2Address (ssu2port, true, ipv4, ipv6); // publish - else - i2p::context.PublishSSU2Address (ssu2port, false, ipv4, ipv6); // unpublish - } - } -} -} diff --git a/libi2pd/Transports.h b/libi2pd/Transports.h deleted file mode 100644 index fcd2cfc6..00000000 --- a/libi2pd/Transports.h +++ /dev/null @@ -1,263 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 TRANSPORTS_H__ -#define TRANSPORTS_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "TransportSession.h" -#include "SSU2.h" -#include "NTCP2.h" -#include "RouterInfo.h" -#include "I2NPProtocol.h" -#include "Identity.h" -#include "util.h" - -namespace i2p -{ -namespace transport -{ - template - class EphemeralKeysSupplier - { - // called from this file only, so implementation is in Transports.cpp - public: - - EphemeralKeysSupplier (int size); - ~EphemeralKeysSupplier (); - void Start (); - void Stop (); - std::shared_ptr Acquire (); - void Return (std::shared_ptr pair); - - private: - - void Run (); - void CreateEphemeralKeys (int num); - - private: - - const int m_QueueSize; - i2p::util::MemoryPoolMt m_KeysPool; - std::queue > m_Queue; - - bool m_IsRunning; - std::unique_ptr m_Thread; - std::condition_variable m_Acquired; - std::mutex m_AcquiredMutex; - }; - typedef EphemeralKeysSupplier X25519KeysPairSupplier; - - const int PEER_ROUTER_INFO_UPDATE_INTERVAL = 31*60; // in seconds - const int PEER_ROUTER_INFO_UPDATE_INTERVAL_VARIANCE = 7*60; // in seconds - const size_t PEER_ROUTER_INFO_OVERLOAD_QUEUE_SIZE = 25; - const int PEER_SELECTION_MIN_INTERVAL = 20; // in seconds - struct Peer - { - int numAttempts; - std::shared_ptr router; - std::list > sessions; - uint64_t creationTime, nextRouterInfoUpdateTime, lastSelectionTime; - std::list > delayedMessages; - std::vector priority; - bool isHighBandwidth, isEligible; - - Peer (std::shared_ptr r, uint64_t ts): - numAttempts (0), router (r), creationTime (ts), - nextRouterInfoUpdateTime (ts + PEER_ROUTER_INFO_UPDATE_INTERVAL), - lastSelectionTime (0), isHighBandwidth (false), isEligible (false) - { - UpdateParams (router); - } - - void Done () - { - for (auto& it: sessions) - it->Done (); - // drop not sent delayed messages - for (auto& it: delayedMessages) - it->Drop (); - } - - void SetRouter (std::shared_ptr r) - { - router = r; - UpdateParams (router); - } - - bool IsConnected () const { return !sessions.empty (); } - void UpdateParams (std::shared_ptr router); - }; - - const uint64_t SESSION_CREATION_TIMEOUT = 15; // in seconds - const int PEER_TEST_INTERVAL = 68*60; // in seconds - const int PEER_TEST_INTERVAL_VARIANCE = 3*60; // in seconds - const int PEER_TEST_DELAY_INTERVAL = 20; // in milliseconds - const int PEER_TEST_DELAY_INTERVAL_VARIANCE = 30; // in milliseconds - const int MAX_NUM_DELAYED_MESSAGES = 150; - const int CHECK_PROFILE_NUM_DELAYED_MESSAGES = 15; // check profile after - const int NUM_X25519_PRE_GENERATED_KEYS = 25; // pre-generated x25519 keys pairs - - const int TRAFFIC_SAMPLE_COUNT = 301; // seconds - - struct TrafficSample - { - uint64_t Timestamp; - uint64_t TotalReceivedBytes; - uint64_t TotalSentBytes; - uint64_t TotalTransitTransmittedBytes; - }; - - class Transports - { - public: - - Transports (); - ~Transports (); - - void Start (bool enableNTCP2=true, bool enableSSU2=true); - void Stop (); - bool IsRunning () const { return m_IsRunning; } - - bool IsBoundSSU2() const { return m_SSU2Server != nullptr; } - bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; } - - bool IsOnline() const { return m_IsOnline; }; - void SetOnline (bool online); - - auto& GetService () { return *m_Service; }; - std::shared_ptr GetNextX25519KeysPair (); - void ReuseX25519KeysPair (std::shared_ptr pair); - - std::future > SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr msg); - std::future > SendMessages (const i2p::data::IdentHash& ident, std::list >&& msgs); - - void PeerConnected (std::shared_ptr session); - void PeerDisconnected (std::shared_ptr session); - bool IsConnected (const i2p::data::IdentHash& ident) const; - - void UpdateSentBytes (uint64_t numBytes) { m_TotalSentBytes += numBytes; }; - void UpdateReceivedBytes (uint64_t numBytes) { m_TotalReceivedBytes += numBytes; }; - uint64_t GetTotalSentBytes () const { return m_TotalSentBytes; }; - uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; }; - uint64_t GetTotalTransitTransmittedBytes () const { return m_TotalTransitTransmittedBytes; } - void UpdateTotalTransitTransmittedBytes (uint32_t add) { m_TotalTransitTransmittedBytes += add; }; - uint32_t GetInBandwidth () const { return m_InBandwidth; }; - uint32_t GetOutBandwidth () const { return m_OutBandwidth; }; - uint32_t GetTransitBandwidth () const { return m_TransitBandwidth; }; - uint32_t GetInBandwidth15s () const { return m_InBandwidth15s; }; - uint32_t GetOutBandwidth15s () const { return m_OutBandwidth15s; }; - uint32_t GetTransitBandwidth15s () const { return m_TransitBandwidth15s; }; - int GetCongestionLevel (bool longTerm) const; - size_t GetNumPeers () const { return m_Peers.size (); }; - std::shared_ptr GetRandomPeer (bool isHighBandwidth) const; - - /** get a trusted first hop for restricted routes */ - std::shared_ptr GetRestrictedPeer(); - /** do we want to use restricted routes? */ - bool RoutesRestricted() const; - /** restrict routes to use only these router families for first hops */ - void RestrictRoutesToFamilies(const std::set& families); - /** restrict routes to use only these routers for first hops */ - void RestrictRoutesToRouters(const std::set& routers); - - bool IsTrustedRouter (const i2p::data::IdentHash& ih) const; - bool IsRestrictedPeer(const i2p::data::IdentHash& ih) const; - - void PeerTest (bool ipv4 = true, bool ipv6 = true); - - void SetCheckReserved (bool check) { m_CheckReserved = check; }; - bool IsCheckReserved () const { return m_CheckReserved; }; - bool IsInReservedRange (const boost::asio::ip::address& host) const; - - private: - - void Run (); - void RequestComplete (std::shared_ptr r, const i2p::data::IdentHash& ident); - void HandleRequestComplete (std::shared_ptr r, i2p::data::IdentHash ident); - std::shared_ptr PostMessages (const i2p::data::IdentHash& ident, std::list >& msgs); - bool ConnectToPeer (const i2p::data::IdentHash& ident, std::shared_ptr peer); - void SetPriority (std::shared_ptr peer); - void HandlePeerCleanupTimer (const boost::system::error_code& ecode); - void HandlePeerTestTimer (const boost::system::error_code& ecode); - void HandleUpdateBandwidthTimer (const boost::system::error_code& ecode); - void UpdateBandwidthValues (int interval, uint32_t& in, uint32_t& out, uint32_t& transit); - - void DetectExternalIP (); - - template - std::shared_ptr GetRandomPeer (Filter filter) const; - - private: - - volatile bool m_IsOnline; - bool m_IsRunning, m_IsNAT, m_CheckReserved; - std::thread * m_Thread; - boost::asio::io_context * m_Service; - boost::asio::executor_work_guard * m_Work; - boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer, * m_UpdateBandwidthTimer; - - SSU2Server * m_SSU2Server; - NTCP2Server * m_NTCP2Server; - mutable std::mutex m_PeersMutex; - std::unordered_map > m_Peers; - - X25519KeysPairSupplier m_X25519KeysPairSupplier; - - std::atomic m_TotalSentBytes, m_TotalReceivedBytes, m_TotalTransitTransmittedBytes; - - TrafficSample m_TrafficSamples[TRAFFIC_SAMPLE_COUNT]; - int m_TrafficSamplePtr; - - // Bandwidth per second - uint32_t m_InBandwidth, m_OutBandwidth, m_TransitBandwidth; - // Bandwidth during last 15 seconds - uint32_t m_InBandwidth15s, m_OutBandwidth15s, m_TransitBandwidth15s; - // Bandwidth during last 5 minutes - uint32_t m_InBandwidth5m, m_OutBandwidth5m, m_TransitBandwidth5m; - - /** which router families to trust for first hops */ - std::vector m_TrustedFamilies; - mutable std::mutex m_FamilyMutex; - - /** which routers for first hop to trust */ - std::unordered_set m_TrustedRouters; - mutable std::mutex m_TrustedRoutersMutex; - - i2p::I2NPMessagesHandler m_LoopbackHandler; - std::mt19937 m_Rng; - - public: - - // for HTTP only - const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; }; - const SSU2Server * GetSSU2Server () const { return m_SSU2Server; }; - const decltype(m_Peers)& GetPeers () const { return m_Peers; }; - }; - - extern Transports transports; - - void InitAddressFromIface (); - void InitTransports (); -} -} - -#endif diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp deleted file mode 100644 index 1b317121..00000000 --- a/libi2pd/Tunnel.cpp +++ /dev/null @@ -1,1112 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include "I2PEndian.h" -#include -#include -#include -#include -#include "Crypto.h" -#include "RouterContext.h" -#include "Log.h" -#include "Timestamp.h" -#include "I2NPProtocol.h" -#include "Transports.h" -#include "NetDb.hpp" -#include "Config.h" -#include "Tunnel.h" -#include "TunnelPool.h" -#include "util.h" -#include "ECIESX25519AEADRatchetSession.h" - -namespace i2p -{ -namespace tunnel -{ - Tunnel::Tunnel (std::shared_ptr config): - TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()), - m_Config (config), m_IsShortBuildMessage (false), m_Pool (nullptr), - m_State (eTunnelStatePending), m_FarEndTransports (i2p::data::RouterInfo::eAllTransports), - m_IsRecreated (false), m_Latency (UNKNOWN_LATENCY) - { - } - - Tunnel::~Tunnel () - { - } - - void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel) - { - auto numHops = m_Config->GetNumHops (); - const int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS; - auto msg = numRecords <= STANDARD_NUM_RECORDS ? NewI2NPShortMessage () : NewI2NPMessage (); - *msg->GetPayload () = numRecords; - const size_t recordSize = m_Config->IsShort () ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; - msg->len += numRecords*recordSize + 1; - // shuffle records - std::vector recordIndicies; - for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); - std::shuffle (recordIndicies.begin(), recordIndicies.end(), m_Pool ? m_Pool->GetRng () : std::mt19937(std::random_device()())); - - // create real records - uint8_t * records = msg->GetPayload () + 1; - TunnelHopConfig * hop = m_Config->GetFirstHop (); - int i = 0; - while (hop) - { - uint32_t msgID; - if (hop->next) // we set replyMsgID for last hop only - RAND_bytes ((uint8_t *)&msgID, 4); - else - msgID = replyMsgID; - hop->recordIndex = recordIndicies[i]; i++; - hop->CreateBuildRequestRecord (records, msgID); - hop = hop->next; - } - // fill up fake records with random data - for (int i = numHops; i < numRecords; i++) - { - int idx = recordIndicies[i]; - RAND_bytes (records + idx*recordSize, recordSize); - } - - // decrypt real records - hop = m_Config->GetLastHop ()->prev; - while (hop) - { - // decrypt records after current hop - TunnelHopConfig * hop1 = hop->next; - while (hop1) - { - hop->DecryptRecord (records, hop1->recordIndex); - hop1 = hop1->next; - } - hop = hop->prev; - } - msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild); - auto s = shared_from_this (); - msg->onDrop = [s]() - { - LogPrint (eLogInfo, "I2NP: Tunnel ", s->GetTunnelID (), " request was not sent"); - s->SetState (i2p::tunnel::eTunnelStateBuildFailed); - }; - - // send message - if (outboundTunnel) - { - if (m_Config->IsShort ()) - { - auto ident = m_Config->GetFirstHop () ? m_Config->GetFirstHop ()->ident : nullptr; - if (ident && ident->GetIdentHash () != outboundTunnel->GetEndpointIdentHash ()) // don't encrypt if IBGW = OBEP - { - auto msg1 = i2p::garlic::WrapECIESX25519MessageForRouter (msg, ident->GetEncryptionPublicKey ()); - if (msg1) msg = msg1; - } - } - outboundTunnel->SendTunnelDataMsgTo (GetNextIdentHash (), 0, msg); - } - else - { - if (m_Config->IsShort () && m_Config->GetLastHop () && - m_Config->GetLastHop ()->ident->GetIdentHash () != m_Config->GetLastHop ()->nextIdent) - { - // add garlic key/tag for reply - uint8_t key[32]; - uint64_t tag = m_Config->GetLastHop ()->GetGarlicKey (key); - if (m_Pool && m_Pool->GetLocalDestination ()) - m_Pool->GetLocalDestination ()->SubmitECIESx25519Key (key, tag); - else - i2p::context.SubmitECIESx25519Key (key, tag); - } - i2p::transport::transports.SendMessage (GetNextIdentHash (), msg); - } - } - - bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) - { - int num = msg[0]; - LogPrint (eLogDebug, "Tunnel: TunnelBuildResponse ", num, " records."); - if (num > MAX_NUM_RECORDS) - { - LogPrint (eLogError, "Tunnel: Too many records in TunnelBuildResponse", num); - return false; - } - if (len < num*m_Config->GetRecordSize () + 1) - { - LogPrint (eLogError, "Tunnel: TunnelBuildResponse of ", num, " records is too short ", len); - return false; - } - - TunnelHopConfig * hop = m_Config->GetLastHop (); - while (hop) - { - // decrypt current hop - if (hop->recordIndex >= 0 && hop->recordIndex < msg[0]) - { - if (!hop->DecryptBuildResponseRecord (msg + 1)) - return false; - } - else - { - LogPrint (eLogWarning, "Tunnel: Hop index ", hop->recordIndex, " is out of range"); - return false; - } - - // decrypt records before current hop - TunnelHopConfig * hop1 = hop->prev; - while (hop1) - { - auto idx = hop1->recordIndex; - if (idx >= 0 && idx < num) - hop->DecryptRecord (msg + 1, idx); - else - LogPrint (eLogWarning, "Tunnel: Hop index ", idx, " is out of range"); - hop1 = hop1->prev; - } - hop = hop->prev; - } - - bool established = true; - size_t numHops = 0; - hop = m_Config->GetFirstHop (); - while (hop) - { - uint8_t ret = hop->GetRetCode (msg + 1); - LogPrint (eLogDebug, "Tunnel: Build response ret code=", (int)ret); - if (hop->ident) - i2p::data::UpdateRouterProfile (hop->ident->GetIdentHash (), - [ret](std::shared_ptr profile) - { - if (profile) profile->TunnelBuildResponse (ret); - }); - if (ret) - // if any of participants declined the tunnel is not established - established = false; - hop = hop->next; - numHops++; - } - if (established) - { - // create tunnel decryptions from layer and iv keys in reverse order - m_Hops.resize (numHops); - hop = m_Config->GetLastHop (); - int i = 0; - while (hop) - { - m_Hops[i].ident = hop->ident; - m_Hops[i].decryption.SetKeys (hop->layerKey, hop->ivKey); - hop = hop->prev; - i++; - } - m_IsShortBuildMessage = m_Config->IsShort (); - m_FarEndTransports = m_Config->GetFarEndTransports (); - m_Config = nullptr; - } - if (established) m_State = eTunnelStateEstablished; - return established; - } - - bool Tunnel::LatencyFitsRange(int lowerbound, int upperbound) const - { - auto latency = GetMeanLatency(); - return latency >= lowerbound && latency <= upperbound; - } - - void Tunnel::EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) - { - const uint8_t * inPayload = in->GetPayload () + 4; - uint8_t * outPayload = out->GetPayload () + 4; - for (auto& it: m_Hops) - { - it.decryption.Decrypt (inPayload, outPayload); - inPayload = outPayload; - } - } - - void Tunnel::SendTunnelDataMsg (std::shared_ptr msg) - { - LogPrint (eLogWarning, "Tunnel: Can't send I2NP messages without delivery instructions"); - } - - std::vector > Tunnel::GetPeers () const - { - auto peers = GetInvertedPeers (); - std::reverse (peers.begin (), peers.end ()); - return peers; - } - - std::vector > Tunnel::GetInvertedPeers () const - { - // hops are in inverted order - std::vector > ret; - for (const auto& it: m_Hops) - ret.push_back (it.ident); - return ret; - } - - void Tunnel::SetState(TunnelState state) - { - m_State = state; - } - - void Tunnel::VisitTunnelHops(TunnelHopVisitor v) - { - // hops are in inverted order, we must return in direct order - for (auto it = m_Hops.rbegin (); it != m_Hops.rend (); it++) - v((*it).ident); - } - - void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr&& msg) - { - if (!IsEstablished () && GetState () != eTunnelStateExpiring) - { - // incoming messages means a tunnel is alive - SetState (eTunnelStateEstablished); - auto pool = GetTunnelPool (); - if (pool) - { - // update LeaseSet - auto dest = pool->GetLocalDestination (); - if (dest) dest->SetLeaseSetUpdated (true); - } - } - EncryptTunnelMsg (msg, msg); - msg->from = GetSharedFromThis (); - m_Endpoint.HandleDecryptedTunnelDataMsg (msg); - } - - bool InboundTunnel::Recreate () - { - if (!IsRecreated ()) - { - auto pool = GetTunnelPool (); - if (pool) - { - SetRecreated (true); - pool->RecreateInboundTunnel (std::static_pointer_cast(shared_from_this ())); - return true; - } - } - return false; - } - - ZeroHopsInboundTunnel::ZeroHopsInboundTunnel (): - InboundTunnel (std::make_shared ()), - m_NumReceivedBytes (0) - { - } - - void ZeroHopsInboundTunnel::SendTunnelDataMsg (std::shared_ptr msg) - { - if (msg) - { - m_NumReceivedBytes += msg->GetLength (); - msg->from = GetSharedFromThis (); - HandleI2NPMessage (msg); - } - } - - void OutboundTunnel::SendTunnelDataMsgTo (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg) - { - TunnelMessageBlock block; - block.tunnelID = 0; // Initialize tunnelID to a default value - - if (gwHash) - { - block.hash = gwHash; - if (gwTunnel) - { - block.deliveryType = eDeliveryTypeTunnel; - block.tunnelID = gwTunnel; // Set tunnelID only if gwTunnel is non-zero - } - else - { - block.deliveryType = eDeliveryTypeRouter; - } - } - else - { - block.deliveryType = eDeliveryTypeLocal; - } - - block.data = msg; - SendTunnelDataMsgs({block}); - } - - void OutboundTunnel::SendTunnelDataMsgs (const std::vector& msgs) - { - std::unique_lock l(m_SendMutex); - for (auto& it : msgs) - m_Gateway.PutTunnelDataMsg (it); - m_Gateway.SendBuffer (); - } - - void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) - { - LogPrint (eLogError, "Tunnel: Incoming message for outbound tunnel ", GetTunnelID ()); - } - - bool OutboundTunnel::Recreate () - { - if (!IsRecreated ()) - { - auto pool = GetTunnelPool (); - if (pool) - { - SetRecreated (true); - pool->RecreateOutboundTunnel (std::static_pointer_cast(shared_from_this ())); - return true; - } - } - return false; - } - - ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel (): - OutboundTunnel (std::make_shared ()), - m_NumSentBytes (0) - { - } - - void ZeroHopsOutboundTunnel::SendTunnelDataMsgs (const std::vector& msgs) - { - for (auto& msg : msgs) - { - if (!msg.data) continue; - m_NumSentBytes += msg.data->GetLength (); - switch (msg.deliveryType) - { - case eDeliveryTypeLocal: - HandleI2NPMessage (msg.data); - break; - case eDeliveryTypeTunnel: - i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); - break; - case eDeliveryTypeRouter: - i2p::transport::transports.SendMessage (msg.hash, msg.data); - break; - default: - LogPrint (eLogError, "Tunnel: Unknown delivery type ", (int)msg.deliveryType); - } - } - } - - Tunnels tunnels; - - Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), m_MaxNumTransitTunnels (DEFAULT_MAX_NUM_TRANSIT_TUNNELS), - m_TotalNumSuccesiveTunnelCreations (0), m_TotalNumFailedTunnelCreations (0), // for normal average - m_TunnelCreationSuccessRate (TCSR_START_VALUE), m_TunnelCreationAttemptsNum(0), - m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL) - { - } - - Tunnels::~Tunnels () - { - DeleteTunnelPool(m_ExploratoryPool); - } - - std::shared_ptr Tunnels::GetTunnel (uint32_t tunnelID) - { - std::lock_guard l(m_TunnelsMutex); - auto it = m_Tunnels.find(tunnelID); - if (it != m_Tunnels.end ()) - return it->second; - return nullptr; - } - - bool Tunnels::AddTunnel (std::shared_ptr tunnel) - { - if (!tunnel) return false; - std::lock_guard l(m_TunnelsMutex); - return m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second; - } - - void Tunnels::RemoveTunnel (uint32_t tunnelID) - { - std::lock_guard l(m_TunnelsMutex); - m_Tunnels.erase (tunnelID); - } - - std::shared_ptr Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID) - { - return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels); - } - - std::shared_ptr Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID) - { - return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels); - } - - template - std::shared_ptr Tunnels::GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels) - { - auto it = pendingTunnels.find(replyMsgID); - if (it != pendingTunnels.end () && it->second->GetState () == eTunnelStatePending) - { - it->second->SetState (eTunnelStateBuildReplyReceived); - return it->second; - } - return nullptr; - } - - std::shared_ptr Tunnels::GetNextInboundTunnel () - { - std::shared_ptr tunnel; - size_t minReceived = 0; - for (const auto& it : m_InboundTunnels) - { - if (!it->IsEstablished ()) continue; - if (!tunnel || it->GetNumReceivedBytes () < minReceived) - { - tunnel = it; - minReceived = it->GetNumReceivedBytes (); - } - } - return tunnel; - } - - std::shared_ptr Tunnels::GetNextOutboundTunnel () - { - if (m_OutboundTunnels.empty ()) return nullptr; - uint32_t ind = m_Rng () % m_OutboundTunnels.size (), i = 0; - std::shared_ptr tunnel; - for (const auto& it: m_OutboundTunnels) - { - if (it->IsEstablished ()) - { - tunnel = it; - i++; - } - if (i > ind && tunnel) break; - } - return tunnel; - } - - std::shared_ptr Tunnels::CreateTunnelPool (int numInboundHops, - int numOutboundHops, int numInboundTunnels, int numOutboundTunnels, - int inboundVariance, int outboundVariance, bool isHighBandwidth) - { - auto pool = std::make_shared (numInboundHops, numOutboundHops, - numInboundTunnels, numOutboundTunnels, inboundVariance, outboundVariance, isHighBandwidth); - std::unique_lock l(m_PoolsMutex); - m_Pools.push_back (pool); - return pool; - } - - void Tunnels::DeleteTunnelPool (std::shared_ptr pool) - { - if (pool) - { - StopTunnelPool (pool); - { - std::unique_lock l(m_PoolsMutex); - m_Pools.remove (pool); - } - } - } - - void Tunnels::StopTunnelPool (std::shared_ptr pool) - { - if (pool) - { - pool->SetActive (false); - pool->DetachTunnels (); - } - } - - void Tunnels::Start () - { - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&Tunnels::Run, this)); - m_TransitTunnels.Start (); - } - - void Tunnels::Stop () - { - m_TransitTunnels.Stop (); - m_IsRunning = false; - m_Queue.WakeUp (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = 0; - } - } - - void Tunnels::Run () - { - i2p::util::SetThreadName("Tunnels"); - std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready - - uint64_t lastTs = 0, lastPoolsTs = 0, lastMemoryPoolTs = 0; - std::list > msgs; - while (m_IsRunning) - { - try - { - if (m_Queue.Wait (1,0)) // 1 sec - { - m_Queue.GetWholeQueue (msgs); - int numMsgs = 0; - uint32_t prevTunnelID = 0, tunnelID = 0; - std::shared_ptr prevTunnel; - while (!msgs.empty ()) - { - auto msg = msgs.front (); msgs.pop_front (); - if (!msg) continue; - std::shared_ptr tunnel; - uint8_t typeID = msg->GetTypeID (); - switch (typeID) - { - case eI2NPTunnelData: - case eI2NPTunnelGateway: - { - tunnelID = bufbe32toh (msg->GetPayload ()); - if (tunnelID == prevTunnelID) - tunnel = prevTunnel; - else if (prevTunnel) - prevTunnel->FlushTunnelDataMsgs (); - - if (!tunnel) - tunnel = GetTunnel (tunnelID); - if (tunnel) - { - if (typeID == eI2NPTunnelData) - tunnel->HandleTunnelDataMsg (std::move (msg)); - else // tunnel gateway assumed - HandleTunnelGatewayMsg (tunnel, msg); - } - else - LogPrint (eLogWarning, "Tunnel: Tunnel not found, tunnelID=", tunnelID, " previousTunnelID=", prevTunnelID, " type=", (int)typeID); - - break; - } - case eI2NPShortTunnelBuild: - HandleShortTunnelBuildMsg (msg); - break; - case eI2NPVariableTunnelBuild: - HandleVariableTunnelBuildMsg (msg); - break; - case eI2NPShortTunnelBuildReply: - HandleTunnelBuildReplyMsg (msg, true); - break; - case eI2NPVariableTunnelBuildReply: - HandleTunnelBuildReplyMsg (msg, false); - break; - case eI2NPTunnelBuild: - case eI2NPTunnelBuildReply: - LogPrint (eLogWarning, "Tunnel: TunnelBuild is too old for ECIES router"); - break; - default: - LogPrint (eLogWarning, "Tunnel: Unexpected message type ", (int) typeID); - } - - prevTunnelID = tunnelID; - prevTunnel = tunnel; - numMsgs++; - - if (msgs.empty ()) - { - if (numMsgs < MAX_TUNNEL_MSGS_BATCH_SIZE && !m_Queue.IsEmpty ()) - m_Queue.GetWholeQueue (msgs); // try more - else if (tunnel) - tunnel->FlushTunnelDataMsgs (); // otherwise flush last - } - } - } - - if (i2p::transport::transports.IsOnline()) - { - uint64_t ts = i2p::util::GetSecondsSinceEpoch (); - if (ts - lastTs >= TUNNEL_MANAGE_INTERVAL || // manage tunnels every 15 seconds - ts + TUNNEL_MANAGE_INTERVAL < lastTs) - { - ManageTunnels (ts); - lastTs = ts; - } - if (ts - lastPoolsTs >= TUNNEL_POOLS_MANAGE_INTERVAL || // manage pools every 5 seconds - ts + TUNNEL_POOLS_MANAGE_INTERVAL < lastPoolsTs) - { - ManageTunnelPools (ts); - lastPoolsTs = ts; - } - if (ts - lastMemoryPoolTs >= TUNNEL_MEMORY_POOL_MANAGE_INTERVAL || - ts + TUNNEL_MEMORY_POOL_MANAGE_INTERVAL < lastMemoryPoolTs) // manage memory pool every 2 minutes - { - m_I2NPTunnelEndpointMessagesMemoryPool.CleanUpMt (); - m_I2NPTunnelMessagesMemoryPool.CleanUpMt (); - lastMemoryPoolTs = ts; - } - } - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Tunnel: Runtime exception: ", ex.what ()); - } - } - } - - void Tunnels::HandleTunnelGatewayMsg (std::shared_ptr tunnel, std::shared_ptr msg) - { - if (!tunnel) - { - LogPrint (eLogError, "Tunnel: Missing tunnel for gateway"); - return; - } - const uint8_t * payload = msg->GetPayload (); - uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET); - // we make payload as new I2NP message to send - msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; - if (msg->offset + len > msg->len) - { - LogPrint (eLogError, "Tunnel: Gateway payload ", (int)len, " exceeds message length ", (int)msg->len); - return; - } - msg->len = msg->offset + len; - auto typeID = msg->GetTypeID (); - LogPrint (eLogDebug, "Tunnel: Gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID (), ", msg type ", (int)typeID); - - tunnel->SendTunnelDataMsg (msg); - } - - void Tunnels::HandleShortTunnelBuildMsg (std::shared_ptr msg) - { - if (!msg) return; - auto tunnel = GetPendingInboundTunnel (msg->GetMsgID()); // replyMsgID - if (tunnel) - { - // endpoint of inbound tunnel - LogPrint (eLogDebug, "Tunnel: ShortTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); - if (tunnel->HandleTunnelBuildResponse (msg->GetPayload(), msg->GetPayloadLength())) - { - LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); - tunnel->SetState (eTunnelStateEstablished); - AddInboundTunnel (tunnel); - } - else - { - LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); - tunnel->SetState (eTunnelStateBuildFailed); - } - return; - } - else - m_TransitTunnels.PostTransitTunnelBuildMsg (std::move (msg)); - } - - void Tunnels::HandleVariableTunnelBuildMsg (std::shared_ptr msg) - { - auto tunnel = GetPendingInboundTunnel (msg->GetMsgID()); // replyMsgID - if (tunnel) - { - // endpoint of inbound tunnel - LogPrint (eLogDebug, "Tunnel: VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ()); - if (tunnel->HandleTunnelBuildResponse (msg->GetPayload(), msg->GetPayloadLength())) - { - LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been created"); - tunnel->SetState (eTunnelStateEstablished); - AddInboundTunnel (tunnel); - } - else - { - LogPrint (eLogInfo, "Tunnel: Inbound tunnel ", tunnel->GetTunnelID (), " has been declined"); - tunnel->SetState (eTunnelStateBuildFailed); - } - } - else - m_TransitTunnels.PostTransitTunnelBuildMsg (std::move (msg)); - } - - void Tunnels::HandleTunnelBuildReplyMsg (std::shared_ptr msg, bool isShort) - { - auto tunnel = GetPendingOutboundTunnel (msg->GetMsgID()); // replyMsgID - if (tunnel) - { - // reply for outbound tunnel - LogPrint (eLogDebug, "Tunnel: TunnelBuildReply for tunnel ", tunnel->GetTunnelID ()); - if (tunnel->HandleTunnelBuildResponse (msg->GetPayload(), msg->GetPayloadLength())) - { - LogPrint (eLogInfo, "Tunnel: Outbound tunnel ", tunnel->GetTunnelID (), " has been created"); - tunnel->SetState (eTunnelStateEstablished); - AddOutboundTunnel (tunnel); - } - else - { - LogPrint (eLogInfo, "Tunnel: Outbound tunnel ", tunnel->GetTunnelID (), " has been declined"); - tunnel->SetState (eTunnelStateBuildFailed); - } - } - else - LogPrint (eLogWarning, "Tunnel: Pending tunnel for message ", msg->GetMsgID(), " not found"); - - } - - void Tunnels::ManageTunnels (uint64_t ts) - { - ManagePendingTunnels (ts); - std::vector > tunnelsToRecreate; - ManageInboundTunnels (ts, tunnelsToRecreate); - ManageOutboundTunnels (ts, tunnelsToRecreate); - // rec-create in random order - if (!tunnelsToRecreate.empty ()) - { - if (tunnelsToRecreate.size () > 1) - std::shuffle (tunnelsToRecreate.begin(), tunnelsToRecreate.end(), m_Rng); - for (auto& it: tunnelsToRecreate) - it->Recreate (); - } - } - - void Tunnels::ManagePendingTunnels (uint64_t ts) - { - ManagePendingTunnels (m_PendingInboundTunnels, ts); - ManagePendingTunnels (m_PendingOutboundTunnels, ts); - } - - template - void Tunnels::ManagePendingTunnels (PendingTunnels& pendingTunnels, uint64_t ts) - { - // check pending tunnel. delete failed or timeout - for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();) - { - auto tunnel = it->second; - switch (tunnel->GetState ()) - { - case eTunnelStatePending: - if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT || - ts + TUNNEL_CREATION_TIMEOUT < tunnel->GetCreationTime ()) - { - LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " timeout, deleted"); - // update stats - auto config = tunnel->GetTunnelConfig (); - if (config) - { - auto hop = config->GetFirstHop (); - while (hop) - { - if (hop->ident) - i2p::data::UpdateRouterProfile (hop->ident->GetIdentHash (), - [](std::shared_ptr profile) - { - if (profile) profile->TunnelNonReplied (); - }); - hop = hop->next; - } - } - // delete - it = pendingTunnels.erase (it); - FailedTunnelCreation(); - } - else - ++it; - break; - case eTunnelStateBuildFailed: - LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " failed, deleted"); - it = pendingTunnels.erase (it); - FailedTunnelCreation(); - break; - case eTunnelStateBuildReplyReceived: - // intermediate state, will be either established of build failed - ++it; - break; - default: - // success - it = pendingTunnels.erase (it); - SuccesiveTunnelCreation(); - } - } - } - - void Tunnels::ManageOutboundTunnels (uint64_t ts, std::vector >& toRecreate) - { - for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();) - { - auto tunnel = *it; - if (tunnel->IsFailed () || ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT || - ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ()) - { - LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired or failed"); - auto pool = tunnel->GetTunnelPool (); - if (pool) - pool->TunnelExpired (tunnel); - // we don't have outbound tunnels in m_Tunnels - it = m_OutboundTunnels.erase (it); - } - else - { - if (tunnel->IsEstablished ()) - { - if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - auto pool = tunnel->GetTunnelPool (); - // let it die if the tunnel pool has been reconfigured and this is old - if (pool && tunnel->GetNumHops() == pool->GetNumOutboundHops()) - toRecreate.push_back (tunnel); - } - if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - tunnel->SetState (eTunnelStateExpiring); - } - ++it; - } - } - - if (m_OutboundTunnels.size () < 3) - { - // trying to create one more outbound tunnel - auto inboundTunnel = GetNextInboundTunnel (); - auto router = i2p::transport::transports.RoutesRestricted() ? - i2p::transport::transports.GetRestrictedPeer() : - i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, true, false); // reachable by us - if (!inboundTunnel || !router) return; - LogPrint (eLogDebug, "Tunnel: Creating one hop outbound tunnel"); - CreateTunnel ( - std::make_shared (std::vector > { router->GetRouterIdentity () }, - inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash (), false), nullptr - ); - } - } - - void Tunnels::ManageInboundTunnels (uint64_t ts, std::vector >& toRecreate) - { - for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();) - { - auto tunnel = *it; - if (tunnel->IsFailed () || ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT || - ts + TUNNEL_EXPIRATION_TIMEOUT < tunnel->GetCreationTime ()) - { - LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired or failed"); - auto pool = tunnel->GetTunnelPool (); - if (pool) - pool->TunnelExpired (tunnel); - RemoveTunnel (tunnel->GetTunnelID ()); - it = m_InboundTunnels.erase (it); - } - else - { - if (tunnel->IsEstablished ()) - { - if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - { - auto pool = tunnel->GetTunnelPool (); - // let it die if the tunnel pool was reconfigured and has different number of hops - if (pool && tunnel->GetNumHops() == pool->GetNumInboundHops()) - toRecreate.push_back (tunnel); - } - - if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) - tunnel->SetState (eTunnelStateExpiring); - else // we don't need to cleanup expiring tunnels - tunnel->Cleanup (); - } - it++; - } - } - - if (m_InboundTunnels.empty ()) - { - LogPrint (eLogDebug, "Tunnel: Creating zero hops inbound tunnel"); - CreateZeroHopsInboundTunnel (nullptr); - CreateZeroHopsOutboundTunnel (nullptr); - if (!m_ExploratoryPool) - { - int ibLen; i2p::config::GetOption("exploratory.inbound.length", ibLen); - int obLen; i2p::config::GetOption("exploratory.outbound.length", obLen); - int ibNum; i2p::config::GetOption("exploratory.inbound.quantity", ibNum); - int obNum; i2p::config::GetOption("exploratory.outbound.quantity", obNum); - m_ExploratoryPool = CreateTunnelPool (ibLen, obLen, ibNum, obNum, 0, 0, false); - m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ()); - } - return; - } - - if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3) - { - // trying to create one more inbound tunnel - auto router = i2p::transport::transports.RoutesRestricted() ? - i2p::transport::transports.GetRestrictedPeer() : - // should be reachable by us because we send build request directly - i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false, true, false); - if (!router) { - LogPrint (eLogWarning, "Tunnel: Can't find any router, skip creating tunnel"); - return; - } - LogPrint (eLogDebug, "Tunnel: Creating one hop inbound tunnel"); - CreateTunnel ( - std::make_shared (std::vector > { router->GetRouterIdentity () }, false), nullptr - ); - } - } - - void Tunnels::ManageTunnelPools (uint64_t ts) - { - std::unique_lock l(m_PoolsMutex); - for (auto& pool : m_Pools) - { - if (pool && pool->IsActive ()) - pool->ManageTunnels (ts); - } - } - - void Tunnels::PostTunnelData (std::shared_ptr msg) - { - if (msg) m_Queue.Put (msg); - } - - void Tunnels::PostTunnelData (std::list >& msgs) - { - m_Queue.Put (msgs); - } - - template - std::shared_ptr Tunnels::CreateTunnel (std::shared_ptr config, - std::shared_ptr pool, std::shared_ptr outboundTunnel) - { - auto newTunnel = std::make_shared (config); - newTunnel->SetTunnelPool (pool); - uint32_t replyMsgID; - RAND_bytes ((uint8_t *)&replyMsgID, 4); - AddPendingTunnel (replyMsgID, newTunnel); - newTunnel->Build (replyMsgID, outboundTunnel); - return newTunnel; - } - - std::shared_ptr Tunnels::CreateInboundTunnel (std::shared_ptr config, - std::shared_ptr pool, std::shared_ptr outboundTunnel) - { - if (config) - return CreateTunnel(config, pool, outboundTunnel); - else - return CreateZeroHopsInboundTunnel (pool); - } - - std::shared_ptr Tunnels::CreateOutboundTunnel (std::shared_ptr config, std::shared_ptr pool) - { - if (config) - return CreateTunnel(config, pool); - else - return CreateZeroHopsOutboundTunnel (pool); - } - - void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel) - { - m_PendingInboundTunnels[replyMsgID] = tunnel; - } - - void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel) - { - m_PendingOutboundTunnels[replyMsgID] = tunnel; - } - - void Tunnels::AddOutboundTunnel (std::shared_ptr newTunnel) - { - // we don't need to insert it to m_Tunnels - m_OutboundTunnels.push_back (newTunnel); - auto pool = newTunnel->GetTunnelPool (); - if (pool && pool->IsActive ()) - pool->TunnelCreated (newTunnel); - else - newTunnel->SetTunnelPool (nullptr); - } - - void Tunnels::AddInboundTunnel (std::shared_ptr newTunnel) - { - if (AddTunnel (newTunnel)) - { - m_InboundTunnels.push_back (newTunnel); - auto pool = newTunnel->GetTunnelPool (); - if (!pool) - { - // build symmetric outbound tunnel - CreateTunnel (std::make_shared(newTunnel->GetInvertedPeers (), - newTunnel->GetNextTunnelID (), newTunnel->GetNextIdentHash (), false), nullptr, - GetNextOutboundTunnel ()); - } - else - { - if (pool->IsActive ()) - pool->TunnelCreated (newTunnel); - else - newTunnel->SetTunnelPool (nullptr); - } - } - else - LogPrint (eLogError, "Tunnel: Tunnel with id ", newTunnel->GetTunnelID (), " already exists"); - } - - - std::shared_ptr Tunnels::CreateZeroHopsInboundTunnel (std::shared_ptr pool) - { - auto inboundTunnel = std::make_shared (); - inboundTunnel->SetTunnelPool (pool); - inboundTunnel->SetState (eTunnelStateEstablished); - m_InboundTunnels.push_back (inboundTunnel); - AddTunnel (inboundTunnel); - return inboundTunnel; - } - - std::shared_ptr Tunnels::CreateZeroHopsOutboundTunnel (std::shared_ptr pool) - { - auto outboundTunnel = std::make_shared (); - outboundTunnel->SetTunnelPool (pool); - outboundTunnel->SetState (eTunnelStateEstablished); - m_OutboundTunnels.push_back (outboundTunnel); - // we don't insert into m_Tunnels - return outboundTunnel; - } - - std::shared_ptr Tunnels::NewI2NPTunnelMessage (bool endpoint) - { - if (endpoint) - { - // should fit two tunnel message + tunnel gateway header, enough for one garlic encrypted streaming packet - auto msg = m_I2NPTunnelEndpointMessagesMemoryPool.AcquireSharedMt (); - msg->Align (6); - msg->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header - return msg; - } - else - { - auto msg = m_I2NPTunnelMessagesMemoryPool.AcquireSharedMt (); - msg->Align (12); - return msg; - } - } - - int Tunnels::GetTransitTunnelsExpirationTimeout () - { - return m_TransitTunnels.GetTransitTunnelsExpirationTimeout (); - } - - size_t Tunnels::CountTransitTunnels() const - { - return m_TransitTunnels.GetNumTransitTunnels (); - } - - size_t Tunnels::CountInboundTunnels() const - { - // TODO: locking - return m_InboundTunnels.size(); - } - - size_t Tunnels::CountOutboundTunnels() const - { - // TODO: locking - return m_OutboundTunnels.size(); - } - - void Tunnels::SetMaxNumTransitTunnels (uint32_t maxNumTransitTunnels) - { - if (maxNumTransitTunnels > 0 && m_MaxNumTransitTunnels != maxNumTransitTunnels) - { - LogPrint (eLogDebug, "Tunnel: Max number of transit tunnels set to ", maxNumTransitTunnels); - m_MaxNumTransitTunnels = maxNumTransitTunnels; - } - } -} -} diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h deleted file mode 100644 index 5d21cd8b..00000000 --- a/libi2pd/Tunnel.h +++ /dev/null @@ -1,346 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 TUNNEL_H__ -#define TUNNEL_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "util.h" -#include "Queue.h" -#include "Crypto.h" -#include "TunnelConfig.h" -#include "TunnelPool.h" -#include "TransitTunnel.h" -#include "TunnelEndpoint.h" -#include "TunnelGateway.h" -#include "TunnelBase.h" -#include "I2NPProtocol.h" - -namespace i2p -{ -namespace tunnel -{ - const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes - const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute - const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes - const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds - const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message - const int MAX_NUM_RECORDS = 8; - const int UNKNOWN_LATENCY = -1; - const int HIGH_LATENCY_PER_HOP = 250000; // in microseconds - const int MAX_TUNNEL_MSGS_BATCH_SIZE = 100; // handle messages without interrupt - const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 5000; - const int TUNNEL_MANAGE_INTERVAL = 15; // in seconds - const int TUNNEL_POOLS_MANAGE_INTERVAL = 5; // in seconds - const int TUNNEL_MEMORY_POOL_MANAGE_INTERVAL = 120; // in seconds - - const size_t I2NP_TUNNEL_MESSAGE_SIZE = TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34; // reserved for alignment and NTCP 16 + 6 + 12 - const size_t I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE = 2*TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28; // reserved for alignment and NTCP 16 + 6 + 6 - - const double TCSR_SMOOTHING_CONSTANT = 0.0005; // smoothing constant in exponentially weighted moving average - const double TCSR_START_VALUE = 0.1; // start value of tunnel creation success rate - - enum TunnelState - { - eTunnelStatePending, - eTunnelStateBuildReplyReceived, - eTunnelStateBuildFailed, - eTunnelStateEstablished, - eTunnelStateTestFailed, - eTunnelStateFailed, - eTunnelStateExpiring - }; - - class OutboundTunnel; - class InboundTunnel; - class Tunnel: public TunnelBase, - public std::enable_shared_from_this - { - struct TunnelHop - { - std::shared_ptr ident; - i2p::crypto::TunnelDecryption decryption; - }; - - public: - - /** function for visiting a hops stored in a tunnel */ - typedef std::function)> TunnelHopVisitor; - - Tunnel (std::shared_ptr config); - ~Tunnel (); - - void Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel = nullptr); - - std::shared_ptr GetTunnelConfig () const { return m_Config; } - std::vector > GetPeers () const; - std::vector > GetInvertedPeers () const; - bool IsShortBuildMessage () const { return m_IsShortBuildMessage; }; - i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const { return m_FarEndTransports; }; - TunnelState GetState () const { return m_State; }; - void SetState (TunnelState state); - bool IsEstablished () const { return m_State == eTunnelStateEstablished || m_State == eTunnelStateTestFailed; }; - bool IsFailed () const { return m_State == eTunnelStateFailed; }; - bool IsRecreated () const { return m_IsRecreated; }; - void SetRecreated (bool recreated) { m_IsRecreated = recreated; }; - int GetNumHops () const { return m_Hops.size (); }; - virtual bool IsInbound() const = 0; - virtual bool Recreate () = 0; - - std::shared_ptr GetTunnelPool () const { return m_Pool; }; - void SetTunnelPool (std::shared_ptr pool) { m_Pool = pool; }; - - bool HandleTunnelBuildResponse (uint8_t * msg, size_t len); - - // implements TunnelBase - void SendTunnelDataMsg (std::shared_ptr msg) override; - void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) override; - - /** @brief add latency sample */ - void AddLatencySample(const int us) { m_Latency = LatencyIsKnown() ? (m_Latency + us) >> 1 : us; } - /** @brief get this tunnel's estimated latency */ - int GetMeanLatency() const { return (m_Latency + 500) / 1000; } - /** @brief return true if this tunnel's latency fits in range [lowerbound, upperbound] */ - bool LatencyFitsRange(int lowerbound, int upperbound) const; - - bool LatencyIsKnown() const { return m_Latency != UNKNOWN_LATENCY; } - bool IsSlow () const { return LatencyIsKnown() && m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); } - - /** visit all hops we currently store */ - void VisitTunnelHops(TunnelHopVisitor v); - - private: - - std::shared_ptr m_Config; - std::vector m_Hops; - bool m_IsShortBuildMessage; - std::shared_ptr m_Pool; // pool, tunnel belongs to, or null - TunnelState m_State; - i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; - bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace - int m_Latency; // in microseconds - }; - - class OutboundTunnel: public Tunnel - { - public: - - OutboundTunnel (std::shared_ptr config): - Tunnel (config), m_Gateway (*this), m_EndpointIdentHash (config->GetLastIdentHash ()) {}; - - void SendTunnelDataMsgTo (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg); - virtual void SendTunnelDataMsgs (const std::vector& msgs); // multiple messages - const i2p::data::IdentHash& GetEndpointIdentHash () const { return m_EndpointIdentHash; }; - virtual size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); }; - - // implements TunnelBase - void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) override; - - bool IsInbound() const override { return false; } - bool Recreate () override; - - private: - - std::mutex m_SendMutex; - TunnelGateway m_Gateway; - i2p::data::IdentHash m_EndpointIdentHash; - }; - - class InboundTunnel: public Tunnel - { - public: - - InboundTunnel (std::shared_ptr config): Tunnel (config), m_Endpoint (true) {}; - void HandleTunnelDataMsg (std::shared_ptr&& msg) override; - virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }; - bool IsInbound() const override { return true; } - bool Recreate () override; - - // override TunnelBase - void Cleanup () override { m_Endpoint.Cleanup (); }; - - protected: - - std::shared_ptr GetSharedFromThis () - { - return std::static_pointer_cast(shared_from_this ()); - } - - private: - - TunnelEndpoint m_Endpoint; - }; - - class ZeroHopsInboundTunnel: public InboundTunnel - { - public: - - ZeroHopsInboundTunnel (); - void SendTunnelDataMsg (std::shared_ptr msg) override; - size_t GetNumReceivedBytes () const override { return m_NumReceivedBytes; }; - - private: - - size_t m_NumReceivedBytes; - }; - - class ZeroHopsOutboundTunnel: public OutboundTunnel - { - public: - - ZeroHopsOutboundTunnel (); - void SendTunnelDataMsgs (const std::vector& msgs) override; - size_t GetNumSentBytes () const override { return m_NumSentBytes; }; - - private: - - size_t m_NumSentBytes; - }; - - class Tunnels - { - public: - - Tunnels (); - ~Tunnels (); - void Start (); - void Stop (); - - std::shared_ptr GetPendingInboundTunnel (uint32_t replyMsgID); - std::shared_ptr GetPendingOutboundTunnel (uint32_t replyMsgID); - std::shared_ptr GetNextInboundTunnel (); - std::shared_ptr GetNextOutboundTunnel (); - std::shared_ptr GetExploratoryPool () const { return m_ExploratoryPool; }; - std::shared_ptr GetTunnel (uint32_t tunnelID); - bool AddTunnel (std::shared_ptr tunnel); - void RemoveTunnel (uint32_t tunnelID); - int GetTransitTunnelsExpirationTimeout (); - void AddOutboundTunnel (std::shared_ptr newTunnel); - void AddInboundTunnel (std::shared_ptr newTunnel); - std::shared_ptr CreateInboundTunnel (std::shared_ptr config, std::shared_ptr pool, std::shared_ptr outboundTunnel); - std::shared_ptr CreateOutboundTunnel (std::shared_ptr config, std::shared_ptr pool); - void PostTunnelData (std::shared_ptr msg); - void PostTunnelData (std::list >& msgs); // and cleanup msgs - void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); - void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); - std::shared_ptr CreateTunnelPool (int numInboundHops, - int numOuboundHops, int numInboundTunnels, int numOutboundTunnels, - int inboundVariance, int outboundVariance, bool isHighBandwidth); - void DeleteTunnelPool (std::shared_ptr pool); - void StopTunnelPool (std::shared_ptr pool); - - std::shared_ptr NewI2NPTunnelMessage (bool endpoint); - - void SetMaxNumTransitTunnels (uint32_t maxNumTransitTunnels); - uint32_t GetMaxNumTransitTunnels () const { return m_MaxNumTransitTunnels; }; - int GetCongestionLevel() const { return m_MaxNumTransitTunnels ? CONGESTION_LEVEL_FULL * m_TransitTunnels.GetNumTransitTunnels () / m_MaxNumTransitTunnels : CONGESTION_LEVEL_FULL; } - - private: - - template - std::shared_ptr CreateTunnel (std::shared_ptr config, - std::shared_ptr pool, std::shared_ptr outboundTunnel = nullptr); - - template - std::shared_ptr GetPendingTunnel (uint32_t replyMsgID, const std::map >& pendingTunnels); - - void HandleTunnelGatewayMsg (std::shared_ptr tunnel, std::shared_ptr msg); - void HandleShortTunnelBuildMsg (std::shared_ptr msg); - void HandleVariableTunnelBuildMsg (std::shared_ptr msg); - void HandleTunnelBuildReplyMsg (std::shared_ptr msg, bool isShort); - - void Run (); - void ManageTunnels (uint64_t ts); - void ManageOutboundTunnels (uint64_t ts, std::vector >& toRecreate); - void ManageInboundTunnels (uint64_t ts, std::vector >& toRecreate); - void ManagePendingTunnels (uint64_t ts); - template - void ManagePendingTunnels (PendingTunnels& pendingTunnels, uint64_t ts); - void ManageTunnelPools (uint64_t ts); - - std::shared_ptr CreateZeroHopsInboundTunnel (std::shared_ptr pool); - std::shared_ptr CreateZeroHopsOutboundTunnel (std::shared_ptr pool); - - // Calculating of tunnel creation success rate - void SuccesiveTunnelCreation() - { - // total TCSR - m_TotalNumSuccesiveTunnelCreations++; - // A modified version of the EWMA algorithm, where alpha is increased at the beginning to accelerate similarity - double alpha = TCSR_SMOOTHING_CONSTANT + (1 - TCSR_SMOOTHING_CONSTANT)/++m_TunnelCreationAttemptsNum; - m_TunnelCreationSuccessRate = alpha * 1 + (1 - alpha) * m_TunnelCreationSuccessRate; - - } - void FailedTunnelCreation() - { - m_TotalNumFailedTunnelCreations++; - - double alpha = TCSR_SMOOTHING_CONSTANT + (1 - TCSR_SMOOTHING_CONSTANT)/++m_TunnelCreationAttemptsNum; - m_TunnelCreationSuccessRate = alpha * 0 + (1 - alpha) * m_TunnelCreationSuccessRate; - } - - private: - - bool m_IsRunning; - std::thread * m_Thread; - i2p::util::MemoryPoolMt > m_I2NPTunnelEndpointMessagesMemoryPool; - i2p::util::MemoryPoolMt > m_I2NPTunnelMessagesMemoryPool; - std::map > m_PendingInboundTunnels; // by replyMsgID - std::map > m_PendingOutboundTunnels; // by replyMsgID - std::list > m_InboundTunnels; - std::list > m_OutboundTunnels; - mutable std::mutex m_TunnelsMutex; - std::unordered_map > m_Tunnels; // tunnelID->tunnel known by this id - mutable std::mutex m_PoolsMutex; - std::list> m_Pools; - std::shared_ptr m_ExploratoryPool; - i2p::util::Queue > m_Queue; - uint32_t m_MaxNumTransitTunnels; - // count of tunnels for total TCSR algorithm - int m_TotalNumSuccesiveTunnelCreations, m_TotalNumFailedTunnelCreations; - double m_TunnelCreationSuccessRate; - int m_TunnelCreationAttemptsNum; - std::mt19937 m_Rng; - TransitTunnels m_TransitTunnels; - - public: - - // for HTTP only - const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; - const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; - const auto& GetTransitTunnels () const { return m_TransitTunnels.GetTransitTunnels (); }; - - size_t CountTransitTunnels() const; - size_t CountInboundTunnels() const; - size_t CountOutboundTunnels() const; - - size_t GetQueueSize () const { return m_Queue.GetSize (); }; - size_t GetTBMQueueSize () const { return m_TransitTunnels.GetTunnelBuildMsgQueueSize (); }; - int GetTunnelCreationSuccessRate () const { return std::round(m_TunnelCreationSuccessRate * 100); } // in percents - double GetPreciseTunnelCreationSuccessRate () const { return m_TunnelCreationSuccessRate * 100; } // in percents - int GetTotalTunnelCreationSuccessRate () const // in percents - { - int totalNum = m_TotalNumSuccesiveTunnelCreations + m_TotalNumFailedTunnelCreations; - return totalNum ? m_TotalNumSuccesiveTunnelCreations*100/totalNum : 0; - } - }; - - extern Tunnels tunnels; -} -} - -#endif diff --git a/libi2pd/TunnelBase.cpp b/libi2pd/TunnelBase.cpp deleted file mode 100644 index b5a4a0b3..00000000 --- a/libi2pd/TunnelBase.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* -* Copyright (c) 2024, 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 "Transports.h" -#include "TunnelBase.h" - -namespace i2p -{ -namespace tunnel -{ - void TunnelTransportSender::SendMessagesTo (const i2p::data::IdentHash& to, - std::list >&& msgs) - { - if (msgs.empty ()) return; - auto currentTransport = m_CurrentTransport.lock (); - if (!currentTransport) - { - // try to obtain transport from pending request or send thought transport is not complete - if (m_PendingTransport.valid ()) // pending request? - { - if (m_PendingTransport.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - // pending request complete - currentTransport = m_PendingTransport.get (); // take transports used in pending request - if (currentTransport) - { - if (currentTransport->IsEstablished ()) - m_CurrentTransport = currentTransport; - else - currentTransport = nullptr; - } - } - else // still pending - { - // send through transports, but don't update pending transport - i2p::transport::transports.SendMessages (to, std::move (msgs)); - return; - } - } - } - if (currentTransport) // session is good - // send to session directly - currentTransport->SendI2NPMessages (msgs); - else // no session yet - // send through transports - m_PendingTransport = i2p::transport::transports.SendMessages (to, std::move (msgs)); - - } - - void TunnelTransportSender::SendMessagesTo (const i2p::data::IdentHash& to, - std::list >& msgs) - { - std::list > msgs1; - msgs.swap (msgs1); - SendMessagesTo (to, std::move (msgs1)); - } - - void TunnelTransportSender::Reset () - { - m_CurrentTransport.reset (); - if (m_PendingTransport.valid ()) - m_PendingTransport = std::future >(); - } -} -} diff --git a/libi2pd/TunnelBase.h b/libi2pd/TunnelBase.h deleted file mode 100644 index 39d6e780..00000000 --- a/libi2pd/TunnelBase.h +++ /dev/null @@ -1,108 +0,0 @@ -/* -* Copyright (c) 2013-2024, 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 TUNNEL_BASE_H__ -#define TUNNEL_BASE_H__ - -#include -#include -#include -#include -#include "Timestamp.h" -#include "I2NPProtocol.h" -#include "Identity.h" - -namespace i2p -{ -namespace transport -{ - class TransportSession; -} - -namespace tunnel -{ - const size_t TUNNEL_DATA_MSG_SIZE = 1028; - const size_t TUNNEL_DATA_ENCRYPTED_SIZE = 1008; - const size_t TUNNEL_DATA_MAX_PAYLOAD_SIZE = 1003; - - enum TunnelDeliveryType - { - eDeliveryTypeLocal = 0, - eDeliveryTypeTunnel = 1, - eDeliveryTypeRouter = 2 - }; - struct TunnelMessageBlock - { - TunnelDeliveryType deliveryType; - i2p::data::IdentHash hash; - uint32_t tunnelID; - std::shared_ptr data; - }; - - class TunnelBase - { - public: - - TunnelBase (uint32_t tunnelID, uint32_t nextTunnelID, const i2p::data::IdentHash& nextIdent): - m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent), - m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {}; - virtual ~TunnelBase () {}; - virtual void Cleanup () {}; - - virtual void HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) = 0; - virtual void SendTunnelDataMsg (std::shared_ptr msg) = 0; - virtual void FlushTunnelDataMsgs () {}; - virtual void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) = 0; - uint32_t GetNextTunnelID () const { return m_NextTunnelID; }; - const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; }; - virtual uint32_t GetTunnelID () const { return m_TunnelID; }; // as known at our side - - uint32_t GetCreationTime () const { return m_CreationTime; }; - void SetCreationTime (uint32_t t) { m_CreationTime = t; }; - - private: - - uint32_t m_TunnelID, m_NextTunnelID; - i2p::data::IdentHash m_NextIdent; - uint32_t m_CreationTime; // seconds since epoch - }; - - struct TunnelCreationTimeCmp - { - template - bool operator() (const std::shared_ptr & t1, const std::shared_ptr & t2) const - { - if (t1->GetCreationTime () != t2->GetCreationTime ()) - return t1->GetCreationTime () > t2->GetCreationTime (); - else - return t1 < t2; - } - }; - - class TunnelTransportSender final - { - public: - - TunnelTransportSender () = default; - ~TunnelTransportSender () = default; - - void SendMessagesTo (const i2p::data::IdentHash& to, std::list >&& msgs); - void SendMessagesTo (const i2p::data::IdentHash& to, std::list >& msgs); // send and clear - - std::shared_ptr GetCurrentTransport () const { return m_CurrentTransport.lock (); } - void Reset (); - - private: - - std::weak_ptr m_CurrentTransport; - std::future > m_PendingTransport; - }; -} -} - -#endif diff --git a/libi2pd/TunnelConfig.cpp b/libi2pd/TunnelConfig.cpp deleted file mode 100644 index fe0e8573..00000000 --- a/libi2pd/TunnelConfig.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/* -* Copyright (c) 2013-2021, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -* -*/ - -#include -#include -#include -#include "Log.h" -#include "Transports.h" -#include "Timestamp.h" -#include "I2PEndian.h" -#include "I2NPProtocol.h" -#include "TunnelConfig.h" - -namespace i2p -{ -namespace tunnel -{ - TunnelHopConfig::TunnelHopConfig (std::shared_ptr r) - { - RAND_bytes ((uint8_t *)&tunnelID, 4); - if (!tunnelID) tunnelID = 1; // tunnelID can't be zero - isGateway = true; - isEndpoint = true; - ident = r; - //nextRouter = nullptr; - nextTunnelID = 0; - - next = nullptr; - prev = nullptr; - } - - void TunnelHopConfig::SetNextIdent (const i2p::data::IdentHash& ident) - { - nextIdent = ident; - isEndpoint = false; - RAND_bytes ((uint8_t *)&nextTunnelID, 4); - if (!nextTunnelID) nextTunnelID = 1; // tunnelID can't be zero - } - - void TunnelHopConfig::SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent) - { - nextIdent = replyIdent; - nextTunnelID = replyTunnelID; - isEndpoint = true; - } - - void TunnelHopConfig::SetNext (TunnelHopConfig * n) - { - next = n; - if (next) - { - next->prev = this; - next->isGateway = false; - isEndpoint = false; - nextIdent = next->ident->GetIdentHash (); - nextTunnelID = next->tunnelID; - } - } - - void TunnelHopConfig::SetPrev (TunnelHopConfig * p) - { - prev = p; - if (prev) - { - prev->next = this; - prev->isEndpoint = false; - isGateway = false; - } - } - - void TunnelHopConfig::DecryptRecord (uint8_t * records, int index) const - { - uint8_t * record = records + index*TUNNEL_BUILD_RECORD_SIZE; - i2p::crypto::CBCDecryption decryption; - decryption.SetKey (replyKey); - decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, replyIV, record); - } - - void ECIESTunnelHopConfig::EncryptECIES (const uint8_t * plainText, size_t len, uint8_t * encrypted) - { - if (!ident) return; - i2p::crypto::InitNoiseNState (*this, ident->GetEncryptionPublicKey ()); - auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); - memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32); - MixHash (encrypted, 32); // h = SHA256(h || sepk) - encrypted += 32; - uint8_t sharedSecret[32]; - ephemeralKeys->Agree (ident->GetEncryptionPublicKey (), sharedSecret); // x25519(sesk, hepk) - MixKey (sharedSecret); - uint8_t nonce[12]; - memset (nonce, 0, 12); - if (!i2p::crypto::AEADChaCha20Poly1305 (plainText, len, m_H, 32, m_CK + 32, nonce, encrypted, len + 16, true)) // encrypt - { - LogPrint (eLogWarning, "Tunnel: Plaintext AEAD encryption failed"); - return; - } - MixHash (encrypted, len + 16); // h = SHA256(h || ciphertext) - } - - bool ECIESTunnelHopConfig::DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const - { - return i2p::crypto::AEADChaCha20Poly1305 (encrypted, len - 16, m_H, 32, key, nonce, clearText, len - 16, false); // decrypt - } - - void LongECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) - { - // generate keys - RAND_bytes (layerKey, 32); - RAND_bytes (ivKey, 32); - RAND_bytes (replyKey, 32); - RAND_bytes (replyIV, 16); - // fill clear text - uint8_t flag = 0; - if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; - if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); - memcpy (clearText + ECIES_BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16); - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag; - memset (clearText + ECIES_BUILD_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 3); // set to 0 for compatibility - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ()); - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET, 600); // +10 minutes - htobe32buf (clearText + ECIES_BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); - memset (clearText + ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET, 0, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - ECIES_BUILD_REQUEST_RECORD_PADDING_OFFSET); - // encrypt - uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE; - EncryptECIES (clearText, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET); - memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); - } - - bool LongECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const - { - uint8_t * record = records + recordIndex*TUNNEL_BUILD_RECORD_SIZE; - uint8_t nonce[12]; - memset (nonce, 0, 12); - if (!DecryptECIES (m_CK, nonce, record, TUNNEL_BUILD_RECORD_SIZE, record)) - { - LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); - return false; - } - return true; - } - - void ShortECIESTunnelHopConfig::CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) - { - // fill clear text - uint8_t flag = 0; - if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; - if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; - uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE ]; - htobe32buf (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); - htobe32buf (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); - memcpy (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextIdent, 32); - clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] = flag; - memset (clearText + SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET, 0, 2); - clearText[SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE] = 0; // AES - htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetMinutesSinceEpoch ()); - htobe32buf (clearText + SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET , 600); // +10 minutes - htobe32buf (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); - memset (clearText + SHORT_REQUEST_RECORD_PADDING_OFFSET, 0, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE - SHORT_REQUEST_RECORD_PADDING_OFFSET); - // encrypt - uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE; - EncryptECIES (clearText, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET); - // derive keys - i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelReplyKey", m_CK); - memcpy (replyKey, m_CK + 32, 32); - i2p::crypto::HKDF (m_CK, nullptr, 0, "SMTunnelLayerKey", m_CK); - memcpy (layerKey, m_CK + 32, 32); - if (isEndpoint) - { - i2p::crypto::HKDF (m_CK, nullptr, 0, "TunnelLayerIVKey", m_CK); - memcpy (ivKey, m_CK + 32, 32); - i2p::crypto::HKDF (m_CK, nullptr, 0, "RGarlicKeyAndTag", m_CK); // OTBRM garlic key m_CK + 32, tag first 8 bytes of m_CK - } - else - memcpy (ivKey, m_CK, 32); // last HKDF - memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); - } - - bool ShortECIESTunnelHopConfig::DecryptBuildResponseRecord (uint8_t * records) const - { - uint8_t * record = records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE; - uint8_t nonce[12]; - memset (nonce, 0, 12); - nonce[4] = recordIndex; // nonce is record index - if (!DecryptECIES (replyKey, nonce, record, SHORT_TUNNEL_BUILD_RECORD_SIZE, record)) - { - LogPrint (eLogWarning, "Tunnel: Response AEAD decryption failed"); - return false; - } - return true; - } - - void ShortECIESTunnelHopConfig::DecryptRecord (uint8_t * records, int index) const - { - uint8_t * record = records + index*SHORT_TUNNEL_BUILD_RECORD_SIZE; - uint8_t nonce[12]; - memset (nonce, 0, 12); - nonce[4] = index; // nonce is index - i2p::crypto::ChaCha20 (record, SHORT_TUNNEL_BUILD_RECORD_SIZE, replyKey, nonce, record); - } - - uint64_t ShortECIESTunnelHopConfig::GetGarlicKey (uint8_t * key) const - { - uint64_t tag; - memcpy (&tag, m_CK, 8); - memcpy (key, m_CK + 32, 32); - return tag; - } - - void TunnelConfig::CreatePeers (const std::vector >& peers) - { - TunnelHopConfig * prev = nullptr; - for (const auto& it: peers) - { - TunnelHopConfig * hop = nullptr; - if (m_IsShort) - hop = new ShortECIESTunnelHopConfig (it); - else - { - if (it->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - hop = new LongECIESTunnelHopConfig (it); - else - LogPrint (eLogError, "Tunnel: ElGamal router is not supported"); - } - if (hop) - { - if (prev) - prev->SetNext (hop); - else - m_FirstHop = hop; - prev = hop; - } - } - m_LastHop = prev; - } -} -} \ No newline at end of file diff --git a/libi2pd/TunnelConfig.h b/libi2pd/TunnelConfig.h deleted file mode 100644 index 718a6fdb..00000000 --- a/libi2pd/TunnelConfig.h +++ /dev/null @@ -1,225 +0,0 @@ -/* -* Copyright (c) 2013-2024, 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 TUNNEL_CONFIG_H__ -#define TUNNEL_CONFIG_H__ - -#include -#include "Identity.h" -#include "RouterContext.h" -#include "Crypto.h" - -namespace i2p -{ -namespace tunnel -{ - struct TunnelHopConfig - { - std::shared_ptr ident; - i2p::data::IdentHash nextIdent; - uint32_t tunnelID, nextTunnelID; - uint8_t layerKey[32]; - uint8_t ivKey[32]; - uint8_t replyKey[32]; - uint8_t replyIV[16]; - bool isGateway, isEndpoint; - - TunnelHopConfig * next, * prev; - int recordIndex; // record # in tunnel build message - - TunnelHopConfig (std::shared_ptr r); - virtual ~TunnelHopConfig () {}; - - void SetNextIdent (const i2p::data::IdentHash& ident); - void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent); - void SetNext (TunnelHopConfig * n); - void SetPrev (TunnelHopConfig * p); - - virtual uint8_t GetRetCode (const uint8_t * records) const = 0; - virtual void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) = 0; - virtual bool DecryptBuildResponseRecord (uint8_t * records) const = 0; - virtual void DecryptRecord (uint8_t * records, int index) const; // AES - virtual uint64_t GetGarlicKey (uint8_t * key) const { return 0; }; // return tag - }; - - struct ECIESTunnelHopConfig: public TunnelHopConfig, public i2p::crypto::NoiseSymmetricState - { - ECIESTunnelHopConfig (std::shared_ptr r): - TunnelHopConfig (r) {}; - void EncryptECIES (const uint8_t * clearText, size_t len, uint8_t * encrypted); - bool DecryptECIES (const uint8_t * key, const uint8_t * nonce, const uint8_t * encrypted, size_t len, uint8_t * clearText) const; - }; - - struct LongECIESTunnelHopConfig: public ECIESTunnelHopConfig - { - LongECIESTunnelHopConfig (std::shared_ptr r): - ECIESTunnelHopConfig (r) {}; - uint8_t GetRetCode (const uint8_t * records) const override - { return (records + recordIndex*TUNNEL_BUILD_RECORD_SIZE)[ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET]; }; - void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override; - bool DecryptBuildResponseRecord (uint8_t * records) const override; - }; - - struct ShortECIESTunnelHopConfig: public ECIESTunnelHopConfig - { - ShortECIESTunnelHopConfig (std::shared_ptr r): - ECIESTunnelHopConfig (r) {}; - uint8_t GetRetCode (const uint8_t * records) const override - { return (records + recordIndex*SHORT_TUNNEL_BUILD_RECORD_SIZE)[SHORT_RESPONSE_RECORD_RET_OFFSET]; }; - void CreateBuildRequestRecord (uint8_t * records, uint32_t replyMsgID) override; - bool DecryptBuildResponseRecord (uint8_t * records) const override; - void DecryptRecord (uint8_t * records, int index) const override; // Chacha20 - uint64_t GetGarlicKey (uint8_t * key) const override; - }; - - class TunnelConfig - { - public: - - TunnelConfig (const std::vector >& peers, - bool isShort, i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // inbound - m_IsShort (isShort), m_FarEndTransports (farEndTransports) - { - CreatePeers (peers); - m_LastHop->SetNextIdent (i2p::context.GetIdentHash ()); - } - - TunnelConfig (const std::vector >& peers, - uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent, bool isShort, - i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports): // outbound - m_IsShort (isShort), m_FarEndTransports (farEndTransports) - { - CreatePeers (peers); - m_FirstHop->isGateway = false; - m_LastHop->SetReplyHop (replyTunnelID, replyIdent); - } - - virtual ~TunnelConfig () - { - TunnelHopConfig * hop = m_FirstHop; - - while (hop) - { - auto tmp = hop; - hop = hop->next; - delete tmp; - } - } - - bool IsShort () const { return m_IsShort; } - - i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const - { - return m_FarEndTransports; - } - - TunnelHopConfig * GetFirstHop () const - { - return m_FirstHop; - } - - TunnelHopConfig * GetLastHop () const - { - return m_LastHop; - } - - int GetNumHops () const - { - int num = 0; - TunnelHopConfig * hop = m_FirstHop; - while (hop) - { - num++; - hop = hop->next; - } - return num; - } - - bool IsEmpty () const - { - return !m_FirstHop; - } - - virtual bool IsInbound () const { return m_FirstHop->isGateway; } - - virtual uint32_t GetTunnelID () const - { - if (!m_FirstHop) return 0; - return IsInbound () ? m_LastHop->nextTunnelID : m_FirstHop->tunnelID; - } - - virtual uint32_t GetNextTunnelID () const - { - if (!m_FirstHop) return 0; - return m_FirstHop->tunnelID; - } - - virtual const i2p::data::IdentHash& GetNextIdentHash () const - { - return m_FirstHop->ident->GetIdentHash (); - } - - virtual const i2p::data::IdentHash& GetLastIdentHash () const - { - return m_LastHop->ident->GetIdentHash (); - } - - std::vector > GetPeers () const - { - std::vector > peers; - TunnelHopConfig * hop = m_FirstHop; - while (hop) - { - peers.push_back (hop->ident); - hop = hop->next; - } - return peers; - } - - size_t GetRecordSize () const { return m_IsShort ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; }; - - protected: - - // this constructor can't be called from outside - TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr), m_IsShort (false), - m_FarEndTransports (i2p::data::RouterInfo::eAllTransports) - { - } - - private: - - void CreatePeers (const std::vector >& peers); - - private: - - TunnelHopConfig * m_FirstHop, * m_LastHop; - bool m_IsShort; - i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; - }; - - class ZeroHopsTunnelConfig: public TunnelConfig - { - public: - - ZeroHopsTunnelConfig () { RAND_bytes ((uint8_t *)&m_TunnelID, 4);}; - - bool IsInbound () const { return true; }; // TODO: - uint32_t GetTunnelID () const { return m_TunnelID; }; - uint32_t GetNextTunnelID () const { return m_TunnelID; }; - const i2p::data::IdentHash& GetNextIdentHash () const { return i2p::context.GetIdentHash (); }; - const i2p::data::IdentHash& GetLastIdentHash () const { return i2p::context.GetIdentHash (); }; - - - private: - - uint32_t m_TunnelID; - }; -} -} - -#endif diff --git a/libi2pd/TunnelEndpoint.cpp b/libi2pd/TunnelEndpoint.cpp deleted file mode 100644 index 66b7effa..00000000 --- a/libi2pd/TunnelEndpoint.cpp +++ /dev/null @@ -1,396 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 "I2PEndian.h" -#include -#include "Crypto.h" -#include "Log.h" -#include "NetDb.hpp" -#include "I2NPProtocol.h" -#include "Transports.h" -#include "RouterContext.h" -#include "Timestamp.h" -#include "TunnelEndpoint.h" - -namespace i2p -{ -namespace tunnel -{ - - void TunnelEndpoint::HandleDecryptedTunnelDataMsg (std::shared_ptr msg) - { - m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE; - - uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16 - uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // without 4-byte checksum - if (zero) - { - uint8_t * fragment = zero + 1; - // verify checksum - memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end - uint8_t hash[32]; - SHA256(fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16, hash); // payload + iv - if (memcmp (hash, decrypted, 4)) - { - LogPrint (eLogError, "TunnelMessage: Checksum verification failed"); - return; - } - // process fragments - while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) - { - uint8_t flag = fragment[0]; - fragment++; - - bool isFollowOnFragment = flag & 0x80, isLastFragment = true; - uint32_t msgID = 0; - int fragmentNum = 0; - if (!isFollowOnFragment) - { - // first fragment - if (m_CurrentMsgID) - AddIncompleteCurrentMessage (); // we have got a new message while previous is not complete - - m_CurrentMessage.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03); - switch (m_CurrentMessage.deliveryType) - { - case eDeliveryTypeLocal: // 0 - break; - case eDeliveryTypeTunnel: // 1 - m_CurrentMessage.tunnelID = bufbe32toh (fragment); - fragment += 4; // tunnelID - m_CurrentMessage.hash = i2p::data::IdentHash (fragment); - fragment += 32; // hash - break; - case eDeliveryTypeRouter: // 2 - m_CurrentMessage.hash = i2p::data::IdentHash (fragment); - fragment += 32; // to hash - break; - default: ; - } - - bool isFragmented = flag & 0x08; - if (isFragmented) - { - // Message ID - msgID = bufbe32toh (fragment); - fragment += 4; - m_CurrentMsgID = msgID; - isLastFragment = false; - } - } - else - { - // follow on - msgID = bufbe32toh (fragment); // MessageID - fragment += 4; - fragmentNum = (flag >> 1) & 0x3F; // 6 bits - isLastFragment = flag & 0x01; - } - - uint16_t size = bufbe16toh (fragment); - fragment += 2; - - // handle fragment - if (isFollowOnFragment) - { - // existing message - if (m_CurrentMsgID && m_CurrentMsgID == msgID && m_CurrentMessage.nextFragmentNum == fragmentNum) - HandleCurrenMessageFollowOnFragment (fragment, size, isLastFragment); // previous - else - { - HandleFollowOnFragment (msgID, isLastFragment, fragmentNum, fragment, size); // another - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - } - else - { - // new message - msg->offset = fragment - msg->buf; - msg->len = msg->offset + size; - // check message size - if (msg->len > msg->maxLen) - { - LogPrint (eLogError, "TunnelMessage: Fragment is too long ", (int)size); - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - return; - } - // create new or assign I2NP message - if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) - { - // this is not last message. we have to copy it - m_CurrentMessage.data = NewI2NPTunnelMessage (true); - *(m_CurrentMessage.data) = *msg; - } - else - m_CurrentMessage.data = msg; - - if (isLastFragment) - { - // single message - HandleNextMessage (m_CurrentMessage); - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - else if (msgID) - { - // first fragment of a new message - m_CurrentMessage.nextFragmentNum = 1; - m_CurrentMessage.receiveTime = i2p::util::GetMillisecondsSinceEpoch (); - HandleOutOfSequenceFragments (msgID, m_CurrentMessage); - } - else - { - LogPrint (eLogError, "TunnelMessage: Message is fragmented, but msgID is not presented"); - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - } - - fragment += size; - } - } - else - LogPrint (eLogError, "TunnelMessage: Zero not found"); - } - - void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, - uint8_t fragmentNum, const uint8_t * fragment, size_t size) - { - auto it = m_IncompleteMessages.find (msgID); - if (it != m_IncompleteMessages.end()) - { - auto& msg = it->second; - if (fragmentNum == msg.nextFragmentNum) - { - if (ConcatFollowOnFragment (msg, fragment, size)) - { - if (isLastFragment) - { - // message complete - HandleNextMessage (msg); - m_IncompleteMessages.erase (it); - } - else - { - msg.nextFragmentNum++; - HandleOutOfSequenceFragments (msgID, msg); - } - } - else - { - LogPrint (eLogError, "TunnelMessage: Fragment ", fragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped"); - m_IncompleteMessages.erase (it); - } - } - else - { - LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)fragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved"); - AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size); - } - } - else - { - LogPrint (eLogDebug, "TunnelMessage: First fragment of message ", msgID, " not found, saved"); - AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size); - } - } - - bool TunnelEndpoint::ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const - { - if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long - { - if (msg.data->len + size > msg.data->maxLen) - { - // LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough"); - auto newMsg = NewI2NPMessage (msg.data->len + size); - *newMsg = *(msg.data); - msg.data = newMsg; - } - if (msg.data->Concat (fragment, size) < size) // concatenate fragment - { - LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen); - return false; - } - } - else - return false; - return true; - } - - void TunnelEndpoint::HandleCurrenMessageFollowOnFragment (const uint8_t * fragment, size_t size, bool isLastFragment) - { - if (ConcatFollowOnFragment (m_CurrentMessage, fragment, size)) - { - if (isLastFragment) - { - // message complete - HandleNextMessage (m_CurrentMessage); - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - else - { - m_CurrentMessage.nextFragmentNum++; - HandleOutOfSequenceFragments (m_CurrentMsgID, m_CurrentMessage); - } - } - else - { - LogPrint (eLogError, "TunnelMessage: Fragment ", m_CurrentMessage.nextFragmentNum, " of message ", m_CurrentMsgID, " exceeds max I2NP message size, message dropped"); - m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; - } - } - - void TunnelEndpoint::AddIncompleteCurrentMessage () - { - if (m_CurrentMsgID) - { - auto ret = m_IncompleteMessages.emplace (m_CurrentMsgID, m_CurrentMessage); - if (!ret.second) - LogPrint (eLogError, "TunnelMessage: Incomplete message ", m_CurrentMsgID, " already exists"); - m_CurrentMessage.data = nullptr; - m_CurrentMsgID = 0; - } - } - - void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, - bool isLastFragment, const uint8_t * fragment, size_t size) - { - if (!m_OutOfSequenceFragments.try_emplace ((uint64_t)msgID << 32 | fragmentNum, - isLastFragment, i2p::util::GetMillisecondsSinceEpoch (), fragment, size).second) - LogPrint (eLogInfo, "TunnelMessage: Duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID); - } - - void TunnelEndpoint::HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg) - { - while (ConcatNextOutOfSequenceFragment (msgID, msg)) - { - if (!msg.nextFragmentNum) // message complete - { - HandleNextMessage (msg); - if (&msg == &m_CurrentMessage) - { - m_CurrentMsgID = 0; - m_CurrentMessage.data = nullptr; - } - else - m_IncompleteMessages.erase (msgID); - LogPrint (eLogDebug, "TunnelMessage: All fragments of message ", msgID, " found"); - break; - } - } - } - - bool TunnelEndpoint::ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg) - { - auto it = m_OutOfSequenceFragments.find ((uint64_t)msgID << 32 | msg.nextFragmentNum); - if (it != m_OutOfSequenceFragments.end ()) - { - LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found"); - size_t size = it->second.data.size (); - if (msg.data->len + size > msg.data->maxLen) - { - LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough"); - auto newMsg = NewI2NPMessage (msg.data->len + size); - *newMsg = *(msg.data); - msg.data = newMsg; - } - if (msg.data->Concat (it->second.data.data (), size) < size) // concatenate out-of-sync fragment - LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen); - if (it->second.isLastFragment) - // message complete - msg.nextFragmentNum = 0; - else - msg.nextFragmentNum++; - m_OutOfSequenceFragments.erase (it); - return true; - } - return false; - } - - void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg) - { - if (!m_IsInbound && msg.data->IsExpired ()) - { - LogPrint (eLogInfo, "TunnelMessage: Message expired"); - return; - } - uint8_t typeID = msg.data->GetTypeID (); - LogPrint (eLogDebug, "TunnelMessage: Handle fragment of ", msg.data->GetLength (), " bytes, msg type ", (int)typeID); - - switch (msg.deliveryType) - { - case eDeliveryTypeLocal: - i2p::HandleI2NPMessage (msg.data); - break; - case eDeliveryTypeTunnel: - if (!m_IsInbound) // outbound transit tunnel - SendMessageTo (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data)); - else - LogPrint (eLogError, "TunnelMessage: Delivery type 'tunnel' arrived from an inbound tunnel, dropped"); - break; - case eDeliveryTypeRouter: - if (!m_IsInbound) // outbound transit tunnel - i2p::transport::transports.SendMessage (msg.hash, msg.data); // send right away, because most likely it's single message - else // we shouldn't send this message. possible leakage - LogPrint (eLogError, "TunnelMessage: Delivery type 'router' arrived from an inbound tunnel, dropped"); - break; - default: - LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType); - }; - } - - void TunnelEndpoint::Cleanup () - { - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - // out-of-sequence fragments - for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();) - { - if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) - it = m_OutOfSequenceFragments.erase (it); - else - ++it; - } - // incomplete messages - for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();) - { - if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT) - it = m_IncompleteMessages.erase (it); - else - ++it; - } - } - - void TunnelEndpoint::SendMessageTo (const i2p::data::IdentHash& to, std::shared_ptr msg) - { - if (msg) - { - if (!m_Sender && m_I2NPMsgs.empty ()) // first message - m_CurrentHash = to; - else if (m_CurrentHash != to) // new target router - { - FlushI2NPMsgs (); // flush message to previous - if (m_Sender) m_Sender->Reset (); // reset sender - m_CurrentHash = to; // set new target router - } // otherwise add msg to the list for current target router - m_I2NPMsgs.push_back (msg); - } - } - - void TunnelEndpoint::FlushI2NPMsgs () - { - if (!m_I2NPMsgs.empty ()) - { - if (!m_Sender) m_Sender = std::make_unique(); - m_Sender->SendMessagesTo (m_CurrentHash, m_I2NPMsgs); // send and clear - } - } - - const i2p::data::IdentHash * TunnelEndpoint::GetCurrentHash () const - { - return (m_Sender || !m_I2NPMsgs.empty ()) ? &m_CurrentHash : nullptr; - } -} -} diff --git a/libi2pd/TunnelEndpoint.h b/libi2pd/TunnelEndpoint.h deleted file mode 100644 index 1e81c445..00000000 --- a/libi2pd/TunnelEndpoint.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 TUNNEL_ENDPOINT_H__ -#define TUNNEL_ENDPOINT_H__ - -#include -#include -#include -#include -#include -#include -#include "I2NPProtocol.h" -#include "TunnelBase.h" - -namespace i2p -{ -namespace tunnel -{ - class TunnelEndpoint final - { - struct TunnelMessageBlockEx: public TunnelMessageBlock - { - uint64_t receiveTime; // milliseconds since epoch - uint8_t nextFragmentNum; - }; - - struct Fragment - { - Fragment (bool last, uint64_t t, const uint8_t * buf, size_t size): - isLastFragment (last), receiveTime (t), data (size) { memcpy (data.data(), buf, size); }; - bool isLastFragment; - uint64_t receiveTime; // milliseconds since epoch - std::vector data; - }; - - public: - - TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0), m_CurrentMsgID (0) {}; - ~TunnelEndpoint () = default; - size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; }; - void Cleanup (); - - void HandleDecryptedTunnelDataMsg (std::shared_ptr msg); - void FlushI2NPMsgs (); - - const i2p::data::IdentHash * GetCurrentHash () const; // return null if not available - const std::unique_ptr& GetSender () const { return m_Sender; }; - - private: - - void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, uint8_t fragmentNum, const uint8_t * fragment, size_t size); - bool ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const; // true if success - void HandleCurrenMessageFollowOnFragment (const uint8_t * fragment, size_t size, bool isLastFragment); - void HandleNextMessage (const TunnelMessageBlock& msg); - void SendMessageTo (const i2p::data::IdentHash& to, std::shared_ptr msg); - - void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, const uint8_t * fragment, size_t size); - bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added - void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg); - void AddIncompleteCurrentMessage (); - - private: - - std::unordered_map m_IncompleteMessages; - std::unordered_map m_OutOfSequenceFragments; // ((msgID << 8) + fragment#)->fragment - bool m_IsInbound; - size_t m_NumReceivedBytes; - TunnelMessageBlockEx m_CurrentMessage; - uint32_t m_CurrentMsgID; - // I2NP messages to send - std::list > m_I2NPMsgs; // to send - i2p::data::IdentHash m_CurrentHash; // send msgs to - std::unique_ptr m_Sender; - }; -} -} - -#endif diff --git a/libi2pd/TunnelGateway.h b/libi2pd/TunnelGateway.h deleted file mode 100644 index 75f27581..00000000 --- a/libi2pd/TunnelGateway.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -* Copyright (c) 2013-2024, 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 TUNNEL_GATEWAY_H__ -#define TUNNEL_GATEWAY_H__ - -#include -#include -#include -#include "I2NPProtocol.h" -#include "TunnelBase.h" - -namespace i2p -{ -namespace tunnel -{ - class TunnelGatewayBuffer - { - public: - TunnelGatewayBuffer (); - ~TunnelGatewayBuffer (); - void PutI2NPMsg (const TunnelMessageBlock& block); - const std::vector >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; }; - void ClearTunnelDataMsgs (); - void CompleteCurrentTunnelDataMessage (); - - private: - - void CreateCurrentTunnelDataMessage (); - - private: - - std::vector > m_TunnelDataMsgs; - std::shared_ptr m_CurrentTunnelDataMsg; - size_t m_RemainingSize; - uint8_t * m_NonZeroRandomBuffer; - }; - - class TunnelGateway - { - public: - - TunnelGateway (TunnelBase& tunnel): - m_Tunnel (tunnel), m_NumSentBytes (0) {}; - void SendTunnelDataMsg (const TunnelMessageBlock& block); - void PutTunnelDataMsg (const TunnelMessageBlock& block); - void SendBuffer (); - size_t GetNumSentBytes () const { return m_NumSentBytes; }; - const std::unique_ptr& GetSender () const { return m_Sender; }; - - private: - - TunnelBase& m_Tunnel; - TunnelGatewayBuffer m_Buffer; - size_t m_NumSentBytes; - std::unique_ptr m_Sender; - }; -} -} - -#endif diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp deleted file mode 100644 index 26367aa6..00000000 --- a/libi2pd/TunnelPool.cpp +++ /dev/null @@ -1,896 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include "I2PEndian.h" -#include "Crypto.h" -#include "Tunnel.h" -#include "NetDb.hpp" -#include "Timestamp.h" -#include "Garlic.h" -#include "ECIESX25519AEADRatchetSession.h" -#include "Transports.h" -#include "Log.h" -#include "Tunnel.h" -#include "TunnelPool.h" -#include "Destination.h" - -namespace i2p -{ -namespace tunnel -{ - void Path::Add (std::shared_ptr r) - { - if (r) - { - peers.push_back (r->GetRouterIdentity ()); - if (r->GetVersion () < i2p::data::NETDB_MIN_SHORT_TUNNEL_BUILD_VERSION || - r->GetRouterIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - isShort = false; - } - } - - void Path::Reverse () - { - std::reverse (peers.begin (), peers.end ()); - } - - TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, - int numOutboundTunnels, int inboundVariance, int outboundVariance, bool isHighBandwidth): - m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops), - m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), - m_InboundVariance (inboundVariance), m_OutboundVariance (outboundVariance), - m_IsActive (true), m_IsHighBandwidth (isHighBandwidth), m_CustomPeerSelector(nullptr), - m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL) - { - if (m_NumInboundTunnels > TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY) - m_NumInboundTunnels = TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY; - if (m_NumOutboundTunnels > TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY) - m_NumOutboundTunnels = TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY; - if (m_InboundVariance < 0 && m_NumInboundHops + m_InboundVariance <= 0) - m_InboundVariance = m_NumInboundHops ? -m_NumInboundHops + 1 : 0; - if (m_OutboundVariance < 0 && m_NumOutboundHops + m_OutboundVariance <= 0) - m_OutboundVariance = m_NumOutboundHops ? -m_NumOutboundHops + 1 : 0; - if (m_InboundVariance > 0 && m_NumInboundHops + m_InboundVariance > STANDARD_NUM_RECORDS) - m_InboundVariance = (m_NumInboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumInboundHops : 0; - if (m_OutboundVariance > 0 && m_NumOutboundHops + m_OutboundVariance > STANDARD_NUM_RECORDS) - m_OutboundVariance = (m_NumOutboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumOutboundHops : 0; - m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + m_Rng () % TUNNEL_POOL_MANAGE_INTERVAL; - } - - TunnelPool::~TunnelPool () - { - DetachTunnels (); - } - - void TunnelPool::SetExplicitPeers (std::shared_ptr > explicitPeers) - { - m_ExplicitPeers = explicitPeers; - if (m_ExplicitPeers) - { - int size = m_ExplicitPeers->size (); - if (m_NumInboundHops > size) - { - m_NumInboundHops = size; - LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has been adjusted to ", size, " for explicit peers"); - } - if (m_NumOutboundHops > size) - { - m_NumOutboundHops = size; - LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has been adjusted to ", size, " for explicit peers"); - } - m_NumInboundTunnels = 1; - m_NumOutboundTunnels = 1; - } - } - - void TunnelPool::DetachTunnels () - { - { - std::unique_lock l(m_InboundTunnelsMutex); - for (auto& it: m_InboundTunnels) - it->SetTunnelPool (nullptr); - m_InboundTunnels.clear (); - } - { - std::unique_lock l(m_OutboundTunnelsMutex); - for (auto& it: m_OutboundTunnels) - it->SetTunnelPool (nullptr); - m_OutboundTunnels.clear (); - } - { - std::unique_lock l(m_TestsMutex); - m_Tests.clear (); - } - } - - bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant) - { - if( inHops >= 0 && outHops >= 0 && inQuant > 0 && outQuant > 0) - { - m_NumInboundHops = inHops; - m_NumOutboundHops = outHops; - m_NumInboundTunnels = inQuant; - m_NumOutboundTunnels = outQuant; - return true; - } - return false; - } - - void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) - { - if (!m_IsActive) return; - { - std::unique_lock l(m_InboundTunnelsMutex); - if (createdTunnel->IsRecreated ()) - { - // find and mark old tunnel as expired - createdTunnel->SetRecreated (false); - for (auto& it: m_InboundTunnels) - if (it->IsRecreated () && it->GetNextIdentHash () == createdTunnel->GetNextIdentHash ()) - { - it->SetState (eTunnelStateExpiring); - break; - } - } - m_InboundTunnels.insert (createdTunnel); - } - if (m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (true); - } - - void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) - { - if (expiredTunnel) - { - expiredTunnel->SetTunnelPool (nullptr); - { - std::unique_lock l(m_TestsMutex); - for (auto& it: m_Tests) - if (it.second.second == expiredTunnel) it.second.second = nullptr; - } - - std::unique_lock l(m_InboundTunnelsMutex); - m_InboundTunnels.erase (expiredTunnel); - } - } - - void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) - { - if (!m_IsActive) return; - { - std::unique_lock l(m_OutboundTunnelsMutex); - m_OutboundTunnels.insert (createdTunnel); - } - } - - void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) - { - if (expiredTunnel) - { - expiredTunnel->SetTunnelPool (nullptr); - { - std::unique_lock l(m_TestsMutex); - for (auto& it: m_Tests) - if (it.second.first == expiredTunnel) it.second.first = nullptr; - } - - std::unique_lock l(m_OutboundTunnelsMutex); - m_OutboundTunnels.erase (expiredTunnel); - } - } - - std::vector > TunnelPool::GetInboundTunnels (int num) const - { - std::vector > v; - int i = 0; - std::shared_ptr slowTunnel; - std::unique_lock l(m_InboundTunnelsMutex); - for (const auto& it : m_InboundTunnels) - { - if (i >= num) break; - if (it->IsEstablished ()) - { - if (it->IsSlow () && !slowTunnel) - slowTunnel = it; - else - { - v.push_back (it); - i++; - } - } - } - if (slowTunnel && (int)v.size () < (num/2+1)) - v.push_back (slowTunnel); - return v; - } - - std::shared_ptr TunnelPool::GetNextOutboundTunnel (std::shared_ptr excluded, - i2p::data::RouterInfo::CompatibleTransports compatible) - { - std::unique_lock l(m_OutboundTunnelsMutex); - return GetNextTunnel (m_OutboundTunnels, excluded, compatible); - } - - std::shared_ptr TunnelPool::GetNextInboundTunnel (std::shared_ptr excluded, - i2p::data::RouterInfo::CompatibleTransports compatible) - { - std::unique_lock l(m_InboundTunnelsMutex); - return GetNextTunnel (m_InboundTunnels, excluded, compatible); - } - - template - typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, - typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible) - { - if (tunnels.empty ()) return nullptr; - uint32_t ind = m_Rng () % (tunnels.size ()/2 + 1), i = 0; - bool skipped = false; - typename TTunnels::value_type tunnel = nullptr; - for (const auto& it: tunnels) - { - if (it->IsEstablished () && it != excluded && (compatible & it->GetFarEndTransports ())) - { - if (it->IsSlow () || (HasLatencyRequirement() && it->LatencyIsKnown() && - !it->LatencyFitsRange(m_MinLatency, m_MaxLatency))) - { - i++; skipped = true; - continue; - } - tunnel = it; - i++; - } - if (i > ind && tunnel) break; - } - if (!tunnel && skipped) - { - ind = m_Rng () % (tunnels.size ()/2 + 1), i = 0; - for (const auto& it: tunnels) - { - if (it->IsEstablished () && it != excluded) - { - tunnel = it; - i++; - } - if (i > ind && tunnel) break; - } - } - if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded; - return tunnel; - } - - std::pair, bool> TunnelPool::GetNewOutboundTunnel (std::shared_ptr old) - { - if (old && old->IsEstablished ()) return std::make_pair(old, false); - std::shared_ptr tunnel; - bool freshTunnel = false; - if (old) - { - std::unique_lock l(m_OutboundTunnelsMutex); - for (const auto& it: m_OutboundTunnels) - if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ()) - { - tunnel = it; - break; - } - } - - if (!tunnel) - { - tunnel = GetNextOutboundTunnel (); - freshTunnel = true; - } - return std::make_pair(tunnel, freshTunnel); - } - - void TunnelPool::CreateTunnels () - { - int num = 0; - { - std::unique_lock l(m_OutboundTunnelsMutex); - for (const auto& it : m_OutboundTunnels) - if (it->IsEstablished ()) num++; - } - num = m_NumOutboundTunnels - num; - if (num > 0) - { - if (num > TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS) num = TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS; - for (int i = 0; i < num; i++) - CreateOutboundTunnel (); - } - - num = 0; - { - std::unique_lock l(m_InboundTunnelsMutex); - for (const auto& it : m_InboundTunnels) - if (it->IsEstablished ()) num++; - } - if (!num && !m_OutboundTunnels.empty () && m_NumOutboundHops > 0 && - m_NumInboundHops == m_NumOutboundHops) - { - for (auto it: m_OutboundTunnels) - { - // try to create inbound tunnel through the same path as successive outbound - CreatePairedInboundTunnel (it); - num++; - if (num >= m_NumInboundTunnels) break; - } - } - num = m_NumInboundTunnels - num; - if (num > 0) - { - if (num > TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS) num = TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS; - for (int i = 0; i < num; i++) - CreateInboundTunnel (); - } - - if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB - m_LocalDestination->SetLeaseSetUpdated (true); // update LeaseSet immediately - } - - void TunnelPool::TestTunnels () - { - decltype(m_Tests) tests; - { - std::unique_lock l(m_TestsMutex); - tests.swap(m_Tests); - } - - for (auto& it: tests) - { - LogPrint (eLogWarning, "Tunnels: Test of tunnel ", it.first, " failed"); - // if test failed again with another tunnel we consider it failed - if (it.second.first) - { - if (it.second.first->GetState () == eTunnelStateTestFailed) - { - it.second.first->SetState (eTunnelStateFailed); - std::unique_lock l(m_OutboundTunnelsMutex); - if (m_OutboundTunnels.size () > 1) // don't fail last tunnel - m_OutboundTunnels.erase (it.second.first); - else - { - it.second.first->SetState (eTunnelStateTestFailed); - CreateOutboundTunnel (); // create new tunnel immediately because last one failed - } - } - else if (it.second.first->GetState () != eTunnelStateExpiring) - it.second.first->SetState (eTunnelStateTestFailed); - } - if (it.second.second) - { - if (it.second.second->GetState () == eTunnelStateTestFailed) - { - it.second.second->SetState (eTunnelStateFailed); - { - bool failed = false; - { - std::unique_lock l(m_InboundTunnelsMutex); - if (m_InboundTunnels.size () > 1) // don't fail last tunnel - { - m_InboundTunnels.erase (it.second.second); - failed = true; - } - else - { - it.second.second->SetState (eTunnelStateTestFailed); - CreateInboundTunnel (); // create new tunnel immediately because last one failed - } - } - if (failed && m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (true); - } - if (m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (true); - } - else if (it.second.second->GetState () != eTunnelStateExpiring) - it.second.second->SetState (eTunnelStateTestFailed); - } - } - - // new tests - if (!m_LocalDestination) return; - std::vector, std::shared_ptr > > newTests; - std::vector > outboundTunnels; - { - std::unique_lock l(m_OutboundTunnelsMutex); - for (auto& it: m_OutboundTunnels) - if (it->IsEstablished ()) - outboundTunnels.push_back (it); - } - std::shuffle (outboundTunnels.begin(), outboundTunnels.end(), m_Rng); - std::vector > inboundTunnels; - { - std::unique_lock l(m_InboundTunnelsMutex); - for (auto& it: m_InboundTunnels) - if (it->IsEstablished ()) - inboundTunnels.push_back (it); - } - std::shuffle (inboundTunnels.begin(), inboundTunnels.end(), m_Rng); - auto it1 = outboundTunnels.begin (); - auto it2 = inboundTunnels.begin (); - while (it1 != outboundTunnels.end () && it2 != inboundTunnels.end ()) - { - newTests.push_back(std::make_pair (*it1, *it2)); - ++it1; ++it2; - } - bool isECIES = m_LocalDestination->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); - for (auto& it: newTests) - { - uint32_t msgID; - RAND_bytes ((uint8_t *)&msgID, 4); - { - std::unique_lock l(m_TestsMutex); - m_Tests[msgID] = it; - } - auto msg = CreateTunnelTestMsg (msgID); - auto outbound = it.first; - auto s = shared_from_this (); - msg->onDrop = [msgID, outbound, s]() - { - // if test msg dropped locally it's outbound tunnel to blame - outbound->SetState (eTunnelStateFailed); - { - std::unique_lock l(s->m_TestsMutex); - s->m_Tests.erase (msgID); - } - { - std::unique_lock l(s->m_OutboundTunnelsMutex); - s->m_OutboundTunnels.erase (outbound); - } - }; - // encrypt - if (isECIES) - { - uint8_t key[32]; RAND_bytes (key, 32); - uint64_t tag; RAND_bytes ((uint8_t *)&tag, 8); - m_LocalDestination->SubmitECIESx25519Key (key, tag); - msg = i2p::garlic::WrapECIESX25519Message (msg, key, tag); - } - else - { - uint8_t key[32], tag[32]; - RAND_bytes (key, 32); RAND_bytes (tag, 32); - m_LocalDestination->SubmitSessionKey (key, tag); - i2p::garlic::ElGamalAESSession garlic (key, tag); - msg = garlic.WrapSingleMessage (msg); - } - outbound->SendTunnelDataMsgTo (it.second->GetNextIdentHash (), it.second->GetNextTunnelID (), msg); - } - } - - void TunnelPool::ManageTunnels (uint64_t ts) - { - if (ts > m_NextManageTime || ts + 2*TUNNEL_POOL_MANAGE_INTERVAL < m_NextManageTime) // in case if clock was adjusted - { - CreateTunnels (); - TestTunnels (); - m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (m_Rng () % TUNNEL_POOL_MANAGE_INTERVAL)/2; - } - } - - void TunnelPool::ProcessGarlicMessage (std::shared_ptr msg) - { - if (m_LocalDestination) - m_LocalDestination->ProcessGarlicMessage (msg); - else - LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); - } - - void TunnelPool::ProcessDeliveryStatus (std::shared_ptr msg) - { - if (m_LocalDestination) - m_LocalDestination->ProcessDeliveryStatusMessage (msg); - else - LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); - } - - void TunnelPool::ProcessTunnelTest (std::shared_ptr msg) - { - const uint8_t * buf = msg->GetPayload (); - uint32_t msgID = bufbe32toh (buf); - buf += 4; - uint64_t timestamp = bufbe64toh (buf); - - ProcessTunnelTest (msgID, timestamp); - } - - bool TunnelPool::ProcessTunnelTest (uint32_t msgID, uint64_t timestamp) - { - decltype(m_Tests)::mapped_type test; - bool found = false; - { - std::unique_lock l(m_TestsMutex); - auto it = m_Tests.find (msgID); - if (it != m_Tests.end ()) - { - found = true; - test = it->second; - m_Tests.erase (it); - } - } - if (found) - { - int dlt = (uint64_t)i2p::util::GetMonotonicMicroseconds () - (int64_t)timestamp; - LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " microseconds"); - if (dlt < 0) dlt = 0; // should not happen - int numHops = 0; - if (test.first) numHops += test.first->GetNumHops (); - if (test.second) numHops += test.second->GetNumHops (); - // restore from test failed state if any - if (test.first) - { - if (test.first->GetState () != eTunnelStateExpiring) - test.first->SetState (eTunnelStateEstablished); - // update latency - int latency = 0; - if (numHops) latency = dlt*test.first->GetNumHops ()/numHops; - if (!latency) latency = dlt/2; - test.first->AddLatencySample (latency); - } - if (test.second) - { - if (test.second->GetState () != eTunnelStateExpiring) - test.second->SetState (eTunnelStateEstablished); - // update latency - int latency = 0; - if (numHops) latency = dlt*test.second->GetNumHops ()/numHops; - if (!latency) latency = dlt/2; - test.second->AddLatencySample (latency); - } - } - return found; - } - - bool TunnelPool::IsExploratory () const - { - return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this (); - } - - std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop, - bool reverse, bool endpoint) const - { - bool tryClient = !IsExploratory () && !i2p::context.IsLimitedConnectivity (); - std::shared_ptr hop; - for (int i = 0; i < TUNNEL_POOL_MAX_HOP_SELECTION_ATTEMPTS; i++) - { - hop = tryClient ? - (m_IsHighBandwidth ? - i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse, endpoint) : - i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint, true)): - i2p::data::netdb.GetRandomRouter (prevHop, reverse, endpoint, false); - if (hop) - { - if (!hop->HasProfile () || !hop->GetProfile ()->IsBad ()) - break; - } - else if (tryClient) - tryClient = false; - else - return nullptr; - } - return hop; - } - - bool TunnelPool::StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop) - { - int start = 0; - std::shared_ptr prevHop = i2p::context.GetSharedRouterInfo (); - if(i2p::transport::transports.RoutesRestricted()) - { - /** if routes are restricted prepend trusted first hop */ - auto hop = i2p::transport::transports.GetRestrictedPeer(); - if(!hop) return false; - path.Add (hop); - prevHop = hop; - start++; - } - else if (i2p::transport::transports.GetNumPeers () > 100 || - (inbound && i2p::transport::transports.GetNumPeers () > 25)) - { - auto r = i2p::transport::transports.GetRandomPeer (m_IsHighBandwidth && !i2p::context.IsLimitedConnectivity ()); - if (r && r->IsECIES () && (!r->HasProfile () || !r->GetProfile ()->IsBad ()) && - (numHops > 1 || (r->IsV4 () && (!inbound || r->IsPublished (true))))) // first inbound must be published ipv4 - { - prevHop = r; - path.Add (r); - start++; - } - } - - for(int i = start; i < numHops; i++ ) - { - auto hop = nextHop (prevHop, inbound, i == numHops - 1); - if (!hop && !i) // if no suitable peer found for first hop, try already connected - { - LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected"); - hop = i2p::transport::transports.GetRandomPeer (false); - if (hop && !hop->IsECIES ()) hop = nullptr; - } - if (!hop) - { - LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ()); - return false; - } - prevHop = hop; - path.Add (hop); - } - path.farEndTransports = prevHop->GetCompatibleTransports (inbound); // last hop - return true; - } - - bool TunnelPool::SelectPeers (Path& path, bool isInbound) - { - // explicit peers in use - if (m_ExplicitPeers) return SelectExplicitPeers (path, isInbound); - // calculate num hops - int numHops; - if (isInbound) - { - numHops = m_NumInboundHops; - if (m_InboundVariance) - { - int offset = m_Rng () % (std::abs (m_InboundVariance) + 1); - if (m_InboundVariance < 0) offset = -offset; - numHops += offset; - } - } - else - { - numHops = m_NumOutboundHops; - if (m_OutboundVariance) - { - int offset = m_Rng () % (std::abs (m_OutboundVariance) + 1); - if (m_OutboundVariance < 0) offset = -offset; - numHops += offset; - } - } - // peers is empty - if (numHops <= 0) return true; - // custom peer selector in use ? - { - std::lock_guard lock(m_CustomPeerSelectorMutex); - if (m_CustomPeerSelector) - return m_CustomPeerSelector->SelectPeers(path, numHops, isInbound); - } - return StandardSelectPeers(path, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - } - - bool TunnelPool::SelectExplicitPeers (Path& path, bool isInbound) - { - if (!m_ExplicitPeers->size ()) return false; - int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops; - if (numHops > (int)m_ExplicitPeers->size ()) numHops = m_ExplicitPeers->size (); - for (int i = 0; i < numHops; i++) - { - auto& ident = (*m_ExplicitPeers)[i]; - auto r = i2p::data::netdb.FindRouter (ident); - if (r) - { - if (r->IsECIES ()) - { - path.Add (r); - if (i == numHops - 1) - path.farEndTransports = r->GetCompatibleTransports (isInbound); - } - else - { - LogPrint (eLogError, "Tunnels: ElGamal router ", ident.ToBase64 (), " is not supported"); - return false; - } - } - else - { - LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ()); - i2p::data::netdb.RequestDestination (ident); - return false; - } - } - return true; - } - - void TunnelPool::CreateInboundTunnel () - { - LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel..."); - Path path; - if (SelectPeers (path, true)) - { - auto outboundTunnel = GetNextOutboundTunnel (nullptr, path.farEndTransports); - if (!outboundTunnel) - outboundTunnel = tunnels.GetNextOutboundTunnel (); - std::shared_ptr config; - if (m_NumInboundHops > 0) - { - path.Reverse (); - config = std::make_shared (path.peers, path.isShort, path.farEndTransports); - } - auto tunnel = tunnels.CreateInboundTunnel (config, shared_from_this (), outboundTunnel); - if (tunnel->IsEstablished ()) // zero hops - TunnelCreated (tunnel); - } - else - LogPrint (eLogError, "Tunnels: Can't create inbound tunnel, no peers available"); - } - - void TunnelPool::RecreateInboundTunnel (std::shared_ptr tunnel) - { - if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow - { - CreateInboundTunnel (); - return; - } - auto outboundTunnel = GetNextOutboundTunnel (nullptr, tunnel->GetFarEndTransports ()); - if (!outboundTunnel) - outboundTunnel = tunnels.GetNextOutboundTunnel (); - LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel..."); - std::shared_ptr config; - if (m_NumInboundHops > 0) - { - auto peers = tunnel->GetPeers(); - if (peers.size ()&& ValidatePeers (peers)) - config = std::make_shared(tunnel->GetPeers (), - tunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ()); - } - if (!m_NumInboundHops || config) - { - auto newTunnel = tunnels.CreateInboundTunnel (config, shared_from_this(), outboundTunnel); - if (newTunnel->IsEstablished ()) // zero hops - TunnelCreated (newTunnel); - else - newTunnel->SetRecreated (true); - } - } - - void TunnelPool::CreateOutboundTunnel () - { - LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel..."); - Path path; - if (SelectPeers (path, false)) - { - auto inboundTunnel = GetNextInboundTunnel (nullptr, path.farEndTransports); - if (!inboundTunnel) - inboundTunnel = tunnels.GetNextInboundTunnel (); - if (!inboundTunnel) - { - LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no inbound tunnels found"); - return; - } - - if (m_LocalDestination && !m_LocalDestination->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) - path.isShort = false; // because can't handle ECIES encrypted reply - - std::shared_ptr config; - if (m_NumOutboundHops > 0) - config = std::make_shared(path.peers, inboundTunnel->GetNextTunnelID (), - inboundTunnel->GetNextIdentHash (), path.isShort, path.farEndTransports); - - std::shared_ptr tunnel; - if (path.isShort) - { - // TODO: implement it better - tunnel = tunnels.CreateOutboundTunnel (config, inboundTunnel->GetTunnelPool ()); - tunnel->SetTunnelPool (shared_from_this ()); - } - else - tunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ()); - if (tunnel && tunnel->IsEstablished ()) // zero hops - TunnelCreated (tunnel); - } - else - LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available"); - } - - void TunnelPool::RecreateOutboundTunnel (std::shared_ptr tunnel) - { - if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow - { - CreateOutboundTunnel (); - return; - } - auto inboundTunnel = GetNextInboundTunnel (nullptr, tunnel->GetFarEndTransports ()); - if (!inboundTunnel) - inboundTunnel = tunnels.GetNextInboundTunnel (); - if (inboundTunnel) - { - LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel..."); - std::shared_ptr config; - if (m_NumOutboundHops > 0) - { - auto peers = tunnel->GetPeers(); - if (peers.size () && ValidatePeers (peers)) - config = std::make_shared(peers, inboundTunnel->GetNextTunnelID (), - inboundTunnel->GetNextIdentHash (), inboundTunnel->IsShortBuildMessage (), tunnel->GetFarEndTransports ()); - } - if (!m_NumOutboundHops || config) - { - auto newTunnel = tunnels.CreateOutboundTunnel (config, shared_from_this ()); - if (newTunnel->IsEstablished ()) // zero hops - TunnelCreated (newTunnel); - } - } - else - LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found"); - } - - void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr outboundTunnel) - { - LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel..."); - auto tunnel = tunnels.CreateInboundTunnel ( - m_NumOutboundHops > 0 ? std::make_shared(outboundTunnel->GetInvertedPeers (), - outboundTunnel->IsShortBuildMessage ()) : nullptr, - shared_from_this (), outboundTunnel); - if (tunnel->IsEstablished ()) // zero hops - TunnelCreated (tunnel); - } - - void TunnelPool::SetCustomPeerSelector(ITunnelPeerSelector * selector) - { - std::lock_guard lock(m_CustomPeerSelectorMutex); - m_CustomPeerSelector = selector; - } - - void TunnelPool::UnsetCustomPeerSelector() - { - SetCustomPeerSelector(nullptr); - } - - bool TunnelPool::HasCustomPeerSelector() - { - std::lock_guard lock(m_CustomPeerSelectorMutex); - return m_CustomPeerSelector != nullptr; - } - - bool TunnelPool::ValidatePeers (std::vector >& peers) const - { - bool highBandwidth = !IsExploratory (); - for (auto it: peers) - { - auto r = i2p::data::netdb.FindRouter (it->GetIdentHash ()); - if (r) - { - if (r->IsHighCongestion (highBandwidth)) return false; - it = r->GetIdentity (); // use identity from updated RouterInfo - } - } - return true; - } - - std::shared_ptr TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr exclude) const - { - std::shared_ptr tun = nullptr; - std::unique_lock lock(m_InboundTunnelsMutex); - int min = 1000000; - for (const auto & itr : m_InboundTunnels) { - if(!itr->LatencyIsKnown()) continue; - auto l = itr->GetMeanLatency(); - if (l >= min) continue; - tun = itr; - if(tun == exclude) continue; - min = l; - } - return tun; - } - - std::shared_ptr TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr exclude) const - { - std::shared_ptr tun = nullptr; - std::unique_lock lock(m_OutboundTunnelsMutex); - int min = 1000000; - for (const auto & itr : m_OutboundTunnels) { - if(!itr->LatencyIsKnown()) continue; - auto l = itr->GetMeanLatency(); - if (l >= min) continue; - tun = itr; - if(tun == exclude) continue; - min = l; - } - return tun; - } -} -} diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h deleted file mode 100644 index 0ebfd1ac..00000000 --- a/libi2pd/TunnelPool.h +++ /dev/null @@ -1,169 +0,0 @@ -/* -* Copyright (c) 2013-2024, 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 TUNNEL_POOL__ -#define TUNNEL_POOL__ - -#include -#include -#include -#include -#include -#include -#include -#include "Identity.h" -#include "LeaseSet.h" -#include "RouterInfo.h" -#include "I2NPProtocol.h" -#include "TunnelBase.h" -#include "RouterContext.h" -#include "Garlic.h" - -namespace i2p -{ -namespace tunnel -{ - const int TUNNEL_POOL_MANAGE_INTERVAL = 10; // in seconds - const int TUNNEL_POOL_MAX_INBOUND_TUNNELS_QUANTITY = 16; - const int TUNNEL_POOL_MAX_OUTBOUND_TUNNELS_QUANTITY = 16; - const int TUNNEL_POOL_MAX_NUM_BUILD_REQUESTS = 3; - const int TUNNEL_POOL_MAX_HOP_SELECTION_ATTEMPTS = 3; - - class Tunnel; - class InboundTunnel; - class OutboundTunnel; - - typedef std::shared_ptr Peer; - struct Path - { - std::vector peers; - bool isShort = true; - i2p::data::RouterInfo::CompatibleTransports farEndTransports = i2p::data::RouterInfo::eAllTransports; - - void Add (std::shared_ptr r); - void Reverse (); - }; - - /** interface for custom tunnel peer selection algorithm */ - struct ITunnelPeerSelector - { - virtual ~ITunnelPeerSelector() {}; - virtual bool SelectPeers(Path & peers, int hops, bool isInbound) = 0; - }; - - class TunnelPool: public std::enable_shared_from_this // per local destination - { - typedef std::function(std::shared_ptr, bool, bool)> SelectHopFunc; - public: - - TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, - int numOutboundTunnels, int inboundVariance, int outboundVariance, bool isHighBandwidth); - ~TunnelPool (); - - std::shared_ptr GetLocalDestination () const { return m_LocalDestination; }; - void SetLocalDestination (std::shared_ptr destination) { m_LocalDestination = destination; }; - void SetExplicitPeers (std::shared_ptr > explicitPeers); - - void CreateTunnels (); - void TunnelCreated (std::shared_ptr createdTunnel); - void TunnelExpired (std::shared_ptr expiredTunnel); - void TunnelCreated (std::shared_ptr createdTunnel); - void TunnelExpired (std::shared_ptr expiredTunnel); - void RecreateInboundTunnel (std::shared_ptr tunnel); - void RecreateOutboundTunnel (std::shared_ptr tunnel); - std::vector > GetInboundTunnels (int num) const; - std::shared_ptr GetNextOutboundTunnel (std::shared_ptr excluded = nullptr, - i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports); - std::shared_ptr GetNextInboundTunnel (std::shared_ptr excluded = nullptr, - i2p::data::RouterInfo::CompatibleTransports compatible = i2p::data::RouterInfo::eAllTransports); - std::pair, bool> GetNewOutboundTunnel (std::shared_ptr old); - void ManageTunnels (uint64_t ts); - void ProcessGarlicMessage (std::shared_ptr msg); - void ProcessDeliveryStatus (std::shared_ptr msg); - void ProcessTunnelTest (std::shared_ptr msg); - bool ProcessTunnelTest (uint32_t msgID, uint64_t timestamp); - - bool IsExploratory () const; - bool IsActive () const { return m_IsActive; }; - void SetActive (bool isActive) { m_IsActive = isActive; }; - void DetachTunnels (); - - int GetNumInboundTunnels () const { return m_NumInboundTunnels; }; - int GetNumOutboundTunnels () const { return m_NumOutboundTunnels; }; - int GetNumInboundHops() const { return m_NumInboundHops; }; - int GetNumOutboundHops() const { return m_NumOutboundHops; }; - - /** i2cp reconfigure */ - bool Reconfigure(int inboundHops, int outboundHops, int inboundQuant, int outboundQuant); - - void SetCustomPeerSelector(ITunnelPeerSelector * selector); - void UnsetCustomPeerSelector(); - bool HasCustomPeerSelector(); - - /** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */ - void RequireLatency(int min, int max) { m_MinLatency = min; m_MaxLatency = max; } - - /** @brief return true if this tunnel pool has a latency requirement */ - bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; } - - /** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */ - std::shared_ptr GetLowestLatencyInboundTunnel(std::shared_ptr exclude = nullptr) const; - std::shared_ptr GetLowestLatencyOutboundTunnel(std::shared_ptr exclude = nullptr) const; - - // for overriding tunnel peer selection - std::shared_ptr SelectNextHop (std::shared_ptr prevHop, bool reverse, bool endpoint) const; - bool StandardSelectPeers(Path & path, int numHops, bool inbound, SelectHopFunc nextHop); - - std::mt19937& GetRng () { return m_Rng; } - - private: - - void TestTunnels (); - void CreateInboundTunnel (); - void CreateOutboundTunnel (); - void CreatePairedInboundTunnel (std::shared_ptr outboundTunnel); - template - typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels, - typename TTunnels::value_type excluded, i2p::data::RouterInfo::CompatibleTransports compatible); - bool SelectPeers (Path& path, bool isInbound); - bool SelectExplicitPeers (Path& path, bool isInbound); - bool ValidatePeers (std::vector >& peers) const; - - private: - - std::shared_ptr m_LocalDestination; - int m_NumInboundHops, m_NumOutboundHops, m_NumInboundTunnels, m_NumOutboundTunnels, - m_InboundVariance, m_OutboundVariance; - std::shared_ptr > m_ExplicitPeers; - mutable std::mutex m_InboundTunnelsMutex; - std::set, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first - mutable std::mutex m_OutboundTunnelsMutex; - std::set, TunnelCreationTimeCmp> m_OutboundTunnels; - mutable std::mutex m_TestsMutex; - std::map, std::shared_ptr > > m_Tests; - bool m_IsActive, m_IsHighBandwidth; - uint64_t m_NextManageTime; // in seconds - std::mutex m_CustomPeerSelectorMutex; - ITunnelPeerSelector * m_CustomPeerSelector; - - int m_MinLatency = 0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms - int m_MaxLatency = 0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms - - std::mt19937 m_Rng; - - public: - - // for HTTP only - const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; }; - const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; }; - - }; -} -} - -#endif diff --git a/libi2pd/api.cpp b/libi2pd/api.cpp deleted file mode 100644 index 7dc11157..00000000 --- a/libi2pd/api.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* -* Copyright (c) 2013-2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include "Config.h" -#include "Log.h" -#include "NetDb.hpp" -#include "Transports.h" -#include "Tunnel.h" -#include "RouterContext.h" -#include "Identity.h" -#include "Destination.h" -#include "Crypto.h" -#include "FS.h" -#include "api.h" - -namespace i2p -{ -namespace api -{ - void InitI2P (int argc, char* argv[], const char * appName) - { - i2p::config::Init (); - i2p::config::ParseCmdline (argc, argv, true); // ignore unknown options and help - i2p::config::Finalize (); - - std::string datadir; i2p::config::GetOption("datadir", datadir); - - i2p::fs::SetAppName (appName); - i2p::fs::DetectDataDir(datadir, false); - i2p::fs::Init(); - - bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation); - i2p::crypto::InitCrypto (precomputation); - - int netID; i2p::config::GetOption("netid", netID); - i2p::context.SetNetID (netID); - - bool checkReserved; i2p::config::GetOption("reservedrange", checkReserved); - i2p::transport::transports.SetCheckReserved(checkReserved); - - i2p::context.Init (); - } - - void TerminateI2P () - { - i2p::crypto::TerminateCrypto (); - } - - void StartI2P (std::shared_ptr logStream) - { - if (logStream) - i2p::log::Logger().SendTo (logStream); - else - i2p::log::Logger().SendTo (i2p::fs::DataDirPath (i2p::fs::GetAppName () + ".log")); - i2p::log::Logger().Start (); - i2p::transport::InitTransports (); - LogPrint(eLogInfo, "API: Starting NetDB"); - i2p::data::netdb.Start(); - LogPrint(eLogInfo, "API: Starting Transports"); - i2p::transport::transports.Start(); - LogPrint(eLogInfo, "API: Starting Tunnels"); - i2p::tunnel::tunnels.Start(); - LogPrint(eLogInfo, "API: Starting Router context"); - i2p::context.Start(); - } - - void StopI2P () - { - LogPrint(eLogInfo, "API: Shutting down"); - LogPrint(eLogInfo, "API: Stopping Router context"); - i2p::context.Stop(); - LogPrint(eLogInfo, "API: Stopping Tunnels"); - i2p::tunnel::tunnels.Stop(); - LogPrint(eLogInfo, "API: Stopping Transports"); - i2p::transport::transports.Stop(); - LogPrint(eLogInfo, "API: Stopping NetDB"); - i2p::data::netdb.Stop(); - i2p::log::Logger().Stop (); - } - - void RunPeerTest () - { - i2p::transport::transports.PeerTest (); - } - - std::shared_ptr CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, - const std::map * params) - { - auto localDestination = std::make_shared (keys, isPublic, params); - localDestination->Start (); - return localDestination; - } - - std::shared_ptr CreateLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType, - const std::map * params) - { - i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType); - auto localDestination = std::make_shared (keys, isPublic, params); - localDestination->Start (); - return localDestination; - } - - void DestroyLocalDestination (std::shared_ptr dest) - { - if (dest) - dest->Stop (); - } - - void RequestLeaseSet (std::shared_ptr dest, const i2p::data::IdentHash& remote) - { - if (dest) - dest->RequestDestination (remote); - } - - std::shared_ptr CreateStream (std::shared_ptr dest, const i2p::data::IdentHash& remote) - { - if (!dest) return nullptr; - auto leaseSet = dest->FindLeaseSet (remote); - if (leaseSet) - { - auto stream = dest->CreateStream (leaseSet); - stream->Send (nullptr, 0); // connect - return stream; - } - else - { - RequestLeaseSet (dest, remote); - return nullptr; - } - } - - void AcceptStream (std::shared_ptr dest, const i2p::stream::StreamingDestination::Acceptor& acceptor) - { - if (dest) - dest->AcceptStreams (acceptor); - } - - void DestroyStream (std::shared_ptr stream) - { - if (stream) - stream->Close (); - } -} -} diff --git a/libi2pd/api.h b/libi2pd/api.h deleted file mode 100644 index 9b0256d8..00000000 --- a/libi2pd/api.h +++ /dev/null @@ -1,45 +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 API_H__ -#define API_H__ - -#include -#include -#include "Identity.h" -#include "Destination.h" -#include "Streaming.h" - -namespace i2p -{ -namespace api -{ - // initialization start and stop - void InitI2P (int argc, char* argv[], const char * appName); - void TerminateI2P (); - void StartI2P (std::shared_ptr logStream = nullptr); - // write system log to logStream, if not specified to .log in application's folder - void StopI2P (); - void RunPeerTest (); // should be called after UPnP - - // destinations - std::shared_ptr CreateLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, - const std::map * params = nullptr); - std::shared_ptr CreateLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256, - const std::map * params = nullptr); // transient destinations usually not published - void DestroyLocalDestination (std::shared_ptr dest); - - // streams - void RequestLeaseSet (std::shared_ptr dest, const i2p::data::IdentHash& remote); - std::shared_ptr CreateStream (std::shared_ptr dest, const i2p::data::IdentHash& remote); - void AcceptStream (std::shared_ptr dest, const i2p::stream::StreamingDestination::Acceptor& acceptor); - void DestroyStream (std::shared_ptr stream); -} -} - -#endif diff --git a/libi2pd/util.cpp b/libi2pd/util.cpp deleted file mode 100644 index 925cf629..00000000 --- a/libi2pd/util.cpp +++ /dev/null @@ -1,700 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include -#include -#include - -#include "util.h" -#include "Log.h" -#include "I2PEndian.h" - -#if !defined (__FreeBSD__) && !defined(_MSC_VER) -#include -#endif - -#if defined(__OpenBSD__) || defined(__FreeBSD__) -#include -#endif - -#if defined(__APPLE__) -# include -#endif - -#if defined(__HAIKU__) -#include -#include -#include -#include -#ifndef _DEFAULT_SOURCE -#define _DEFAULT_SOURCE -#include -#endif -#endif - -#ifdef _WIN32 -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -const DWORD MS_VC_EXCEPTION = 0x406D1388; -#pragma pack(push,8) -typedef struct tagTHREADNAME_INFO -{ - DWORD dwType; - LPCSTR szName; - DWORD dwThreadID; - DWORD dwFlags; -} THREADNAME_INFO; -#pragma pack(pop) -#endif - -#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) -#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) - -// inet_pton and inet_ntop have been in Windows since Vista, but XP doesn't have these functions! -// This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found -int inet_pton_xp (int af, const char *src, void *dst) -{ - struct sockaddr_storage ss; - int size = sizeof (ss); - char src_copy[INET6_ADDRSTRLEN + 1]; - - ZeroMemory (&ss, sizeof (ss)); - strncpy (src_copy, src, INET6_ADDRSTRLEN + 1); - src_copy[INET6_ADDRSTRLEN] = 0; - - if (WSAStringToAddress (src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) - { - switch (af) - { - case AF_INET: - *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; - return 1; - case AF_INET6: - *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; - return 1; - } - } - return 0; -} - -const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size) -{ - struct sockaddr_storage ss; - unsigned long s = size; - - ZeroMemory(&ss, sizeof(ss)); - ss.ss_family = af; - - switch (af) - { - case AF_INET: - ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src; - break; - case AF_INET6: - ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src; - break; - default: - return NULL; - } - /* cannot directly use &size because of strict aliasing rules */ - return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? dst : NULL; -} - -#else /* !_WIN32 => UNIX */ -#include -#ifdef ANDROID -#include "ifaddrs.h" -#else -#include -#endif -#endif - -#define address_pair_v4(a,b) std::pair{ boost::asio::ip::make_address (a).to_v4 ().to_uint (), boost::asio::ip::make_address(b).to_v4 ().to_uint () } -#define address_pair_v6(a,b) std::pair{ boost::asio::ip::make_address (a).to_v6 ().to_bytes (), boost::asio::ip::make_address(b).to_v6 ().to_bytes () } - -namespace i2p -{ -namespace util -{ - - void RunnableService::StartIOService () - { - if (!m_IsRunning) - { - m_IsRunning = true; - m_Thread.reset (new std::thread (std::bind (& RunnableService::Run, this))); - } - } - - void RunnableService::StopIOService () - { - if (m_IsRunning) - { - m_IsRunning = false; - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - m_Thread = nullptr; - } - } - } - - void RunnableService::Run () - { - SetThreadName(m_Name.c_str()); - - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, m_Name, ": Runtime exception: ", ex.what ()); - } - } - } - - void RunnableService::SetName (std::string_view name) - { - if (name.length() < 16) - m_Name = name; - else - m_Name = name.substr(0,15); - } - - void SetThreadName (const char *name) { -#if defined(__APPLE__) -# if (!defined(MAC_OS_X_VERSION_10_6) || \ - (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || \ - defined(__POWERPC__)) - /* pthread_setname_np is not there on <10.6 and all PPC. - So do nothing. */ -# else - pthread_setname_np((char*)name); -# endif -#elif defined(__FreeBSD__) || defined(__OpenBSD__) - pthread_set_name_np(pthread_self(), name); -#elif defined(__NetBSD__) - pthread_setname_np(pthread_self(), "%s", (void *)name); -#elif !defined(__gnu_hurd__) - #if defined(_MSC_VER) - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = name; - info.dwThreadID = -1; - info.dwFlags = 0; - #pragma warning(push) - #pragma warning(disable: 6320 6322) - __try { - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); - } - __except (EXCEPTION_EXECUTE_HANDLER) { - } - #pragma warning(pop) - #else - pthread_setname_np(pthread_self(), name); - #endif -#endif - } - -namespace net -{ -#ifdef _WIN32 - int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback) - { - typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size); - IPN inetntop = (IPN)(void*)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); - if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found - - ULONG outBufLen = 0; - PIP_ADAPTER_ADDRESSES pAddresses = nullptr; - PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; - PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; - - if (GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) - == ERROR_BUFFER_OVERFLOW) - { - FREE(pAddresses); - pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); - } - - DWORD dwRetVal = GetAdaptersAddresses( - AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen - ); - - if (dwRetVal != NO_ERROR) - { - LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed"); - FREE(pAddresses); - return fallback; - } - - pCurrAddresses = pAddresses; - while (pCurrAddresses) - { - pUnicast = pCurrAddresses->FirstUnicastAddress; - if (pUnicast == nullptr) - LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv4 address, this is not supported"); - - while (pUnicast != nullptr) - { - LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; - sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr; - if (localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr) - { - char addr[INET_ADDRSTRLEN]; - inetntop(AF_INET, &(((struct sockaddr_in *)localInterfaceAddress)->sin_addr), addr, INET_ADDRSTRLEN); - - auto result = pCurrAddresses->Mtu; - FREE(pAddresses); - pAddresses = nullptr; - LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv4 address ", addr); - return result; - } - pUnicast = pUnicast->Next; - } - pCurrAddresses = pCurrAddresses->Next; - } - - LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv4 addresses found"); - FREE(pAddresses); - return fallback; - } - - int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback) - { - typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size); - IPN inetntop = (IPN)(void*)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop"); - if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found - - ULONG outBufLen = 0; - PIP_ADAPTER_ADDRESSES pAddresses = nullptr; - PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; - PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; - - if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) - == ERROR_BUFFER_OVERFLOW) - { - FREE(pAddresses); - pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); - } - - DWORD dwRetVal = GetAdaptersAddresses( - AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen - ); - - if (dwRetVal != NO_ERROR) - { - LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed"); - FREE(pAddresses); - return fallback; - } - - bool found_address = false; - pCurrAddresses = pAddresses; - while (pCurrAddresses) - { - pUnicast = pCurrAddresses->FirstUnicastAddress; - if (pUnicast == nullptr) - LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv6 address, this is not supported"); - - while (pUnicast != nullptr) - { - LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; - sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr; - - for (int j = 0; j != 8; ++j) - { - if (localInterfaceAddress->sin6_addr.u.Word[j] != inputAddress.sin6_addr.u.Word[j]) - break; - else - found_address = true; - } - - if (found_address) - { - char addr[INET6_ADDRSTRLEN]; - inetntop(AF_INET6, &(((struct sockaddr_in6 *)localInterfaceAddress)->sin6_addr), addr, INET6_ADDRSTRLEN); - - auto result = pCurrAddresses->Mtu; - FREE(pAddresses); - pAddresses = nullptr; - LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv6 address ", addr); - return result; - } - pUnicast = pUnicast->Next; - } - - pCurrAddresses = pCurrAddresses->Next; - } - - LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv6 addresses found"); - FREE(pAddresses); - return fallback; - } - - int GetMTUWindows (const boost::asio::ip::address& localAddress, int fallback) - { -#ifdef UNICODE - string localAddress_temporary = localAddress.to_string(); - wstring localAddressUniversal(localAddress_temporary.begin(), localAddress_temporary.end()); -#else - std::string localAddressUniversal = localAddress.to_string(); -#endif - - typedef int (* IPN)(int af, const char *src, void *dst); - IPN inetpton = (IPN)(void*)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetPton"); - if (!inetpton) inetpton = inet_pton_xp; // use own implementation if not found - - if (localAddress.is_v4()) - { - sockaddr_in inputAddress; - inetpton(AF_INET, localAddressUniversal.c_str(), &(inputAddress.sin_addr)); - return GetMTUWindowsIpv4(inputAddress, fallback); - } - else if (localAddress.is_v6()) - { - sockaddr_in6 inputAddress; - inetpton(AF_INET6, localAddressUniversal.c_str(), &(inputAddress.sin6_addr)); - return GetMTUWindowsIpv6(inputAddress, fallback); - } - else - { - LogPrint(eLogError, "NetIface: GetMTU: Address family is not supported"); - return fallback; - } - } -#else // assume unix - int GetMTUUnix (const boost::asio::ip::address& localAddress, int fallback) - { - ifaddrs* ifaddr, *ifa = nullptr; - if (getifaddrs(&ifaddr) == -1) - { - LogPrint(eLogError, "NetIface: Can't call getifaddrs(): ", strerror(errno)); - return fallback; - } - - int family = 0; - // look for interface matching local address - for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) - { - if (!ifa->ifa_addr) - continue; - - family = ifa->ifa_addr->sa_family; - if (family == AF_INET && localAddress.is_v4()) - { - sockaddr_in* sa = (sockaddr_in*) ifa->ifa_addr; - if (!memcmp(&sa->sin_addr, localAddress.to_v4().to_bytes().data(), 4)) - break; // address matches - } - else if (family == AF_INET6 && localAddress.is_v6()) - { - sockaddr_in6* sa = (sockaddr_in6*) ifa->ifa_addr; - if (!memcmp(&sa->sin6_addr, localAddress.to_v6().to_bytes().data(), 16)) - break; // address matches - } - } - int mtu = fallback; - if (ifa && family) - { // interface found? - int fd = socket(family, SOCK_DGRAM, 0); - if (fd > 0) - { - ifreq ifr; - strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ-1); // set interface for query - if (ioctl(fd, SIOCGIFMTU, &ifr) >= 0) - mtu = ifr.ifr_mtu; // MTU - else - LogPrint (eLogError, "NetIface: Failed to run ioctl: ", strerror(errno)); - close(fd); - } - else - LogPrint(eLogError, "NetIface: Failed to create datagram socket"); - } - else - LogPrint(eLogWarning, "NetIface: Interface for local address", localAddress.to_string(), " not found"); - freeifaddrs(ifaddr); - - return mtu; - } -#endif // _WIN32 - - int GetMTU (const boost::asio::ip::address& localAddress) - { - int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU - -#ifdef _WIN32 - return GetMTUWindows(localAddress, fallback); -#else - return GetMTUUnix(localAddress, fallback); -#endif - return fallback; - } - - const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6) - { -#ifdef _WIN32 - LogPrint(eLogError, "NetIface: Cannot get address by interface name, not implemented on WIN32"); - if (ipv6) - return boost::asio::ip::make_address("::1"); - else - return boost::asio::ip::make_address("127.0.0.1"); -#else - int af = (ipv6 ? AF_INET6 : AF_INET); - ifaddrs *addrs; - try - { - if (!getifaddrs(&addrs)) - { - for (auto cur = addrs; cur; cur = cur->ifa_next) - { - std::string cur_ifname(cur->ifa_name); - if (cur_ifname == ifname && cur->ifa_addr && cur->ifa_addr->sa_family == af) - { - // match - char addr[INET6_ADDRSTRLEN]; - memset (addr, 0, INET6_ADDRSTRLEN); - if (af == AF_INET) - inet_ntop(af, &((sockaddr_in *)cur->ifa_addr)->sin_addr, addr, INET6_ADDRSTRLEN); - else - inet_ntop(af, &((sockaddr_in6 *)cur->ifa_addr)->sin6_addr, addr, INET6_ADDRSTRLEN); - freeifaddrs(addrs); - std::string cur_ifaddr(addr); - return boost::asio::ip::make_address(cur_ifaddr); - } - } - } - } - catch (std::exception& ex) - { - LogPrint(eLogError, "NetIface: Exception while searching address using ifaddr: ", ex.what()); - } - - if (addrs) freeifaddrs(addrs); - std::string fallback; - if (ipv6) - { - fallback = "::1"; - LogPrint(eLogWarning, "NetIface: Cannot find IPv6 address for interface ", ifname); - } else { - fallback = "127.0.0.1"; - LogPrint(eLogWarning, "NetIface: Cannot find IPv4 address for interface ", ifname); - } - return boost::asio::ip::make_address(fallback); -#endif - } - - int GetMaxMTU (const boost::asio::ip::address_v6& localAddress) - { - uint32_t prefix = bufbe32toh (localAddress.to_bytes ().data ()); - switch (prefix) - { - case 0x20010470: - case 0x260070ff: - // Hurricane Electric - return 1480; - break; - case 0x2a06a003: - case 0x2a06a004: - case 0x2a06a005: - // route48 - return 1420; - break; - default: ; - } - return 1500; - } - - static bool IsYggdrasilAddress (const uint8_t addr[16]) - { - return addr[0] == 0x02 || addr[0] == 0x03; - } - - bool IsYggdrasilAddress (const boost::asio::ip::address& addr) - { - if (!addr.is_v6 ()) return false; - return IsYggdrasilAddress (addr.to_v6 ().to_bytes ().data ()); - } - - bool IsPortInReservedRange (const uint16_t port) noexcept - { - // https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers (Feb. 3, 2023) + Tor browser (9150) - static const std::unordered_set reservedPorts{ - 9119,9150,9306,9312,9389,9418,9535,9536,9695, - 9800,9899,10000,10050,10051,10110,10212, - 10933,11001,11112,11235,11371,12222,12223, - 13075,13400,13720,13721,13724,13782,13783, - 13785,13786,15345,17224,17225,17500,18104, - 19788,19812,19813,19814,19999,20000,24465, - 24554,26000,27000,27001,27002,27003,27004, - 27005,27006,27007,27008,27009,28000}; - - return (reservedPorts.find(port) != reservedPorts.end()); - } - - boost::asio::ip::address_v6 GetYggdrasilAddress () - { -#if defined(_WIN32) - ULONG outBufLen = 0; - PIP_ADAPTER_ADDRESSES pAddresses = nullptr; - PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; - PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr; - - if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen) - == ERROR_BUFFER_OVERFLOW) - { - FREE(pAddresses); - pAddresses = (IP_ADAPTER_ADDRESSES*) MALLOC(outBufLen); - } - - DWORD dwRetVal = GetAdaptersAddresses( - AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAddresses, &outBufLen - ); - - if (dwRetVal != NO_ERROR) - { - LogPrint(eLogError, "NetIface: GetYggdrasilAddress(): enclosed GetAdaptersAddresses() call has failed"); - FREE(pAddresses); - return boost::asio::ip::address_v6 (); - } - - pCurrAddresses = pAddresses; - while (pCurrAddresses) - { - pUnicast = pCurrAddresses->FirstUnicastAddress; - - while (pUnicast != nullptr) - { - LPSOCKADDR lpAddr = pUnicast->Address.lpSockaddr; - sockaddr_in6 *localInterfaceAddress = (sockaddr_in6*) lpAddr; - if (IsYggdrasilAddress(localInterfaceAddress->sin6_addr.u.Byte)) { - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), &localInterfaceAddress->sin6_addr.u.Byte, 16); - FREE(pAddresses); - return boost::asio::ip::address_v6 (bytes); - } - pUnicast = pUnicast->Next; - } - pCurrAddresses = pCurrAddresses->Next; - } - LogPrint(eLogWarning, "NetIface: Interface with Yggdrasil network address not found"); - FREE(pAddresses); - return boost::asio::ip::address_v6 (); -#else - ifaddrs *addrs; - try - { - if (!getifaddrs(&addrs)) - { - for (auto cur = addrs; cur; cur = cur->ifa_next) - { - if (cur->ifa_addr && cur->ifa_addr->sa_family == AF_INET6) - { - sockaddr_in6* sa = (sockaddr_in6*)cur->ifa_addr; - if (IsYggdrasilAddress(sa->sin6_addr.s6_addr)) - { - boost::asio::ip::address_v6::bytes_type bytes; - memcpy (bytes.data (), &sa->sin6_addr, 16); - freeifaddrs(addrs); - return boost::asio::ip::address_v6 (bytes); - } - } - } - } - } - catch (std::exception& ex) - { - LogPrint(eLogError, "NetIface: Exception while searching Yggdrasill address using ifaddr: ", ex.what()); - } - LogPrint(eLogWarning, "NetIface: Interface with Yggdrasil network address not found"); - if (addrs) freeifaddrs(addrs); - return boost::asio::ip::address_v6 (); -#endif - } - - bool IsLocalAddress (const boost::asio::ip::address& addr) - { - auto mtu = // TODO: implement better -#ifdef _WIN32 - GetMTUWindows(addr, 0); -#else - GetMTUUnix(addr, 0); -#endif - return mtu > 0; - } - - bool IsInReservedRange (const boost::asio::ip::address& host) - { - // https://en.wikipedia.org/wiki/Reserved_IP_addresses - if (host.is_unspecified ()) return false; - if (host.is_v4()) - { - static const std::array, 14> 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_uint (); - for (const auto& it : reservedIPv4Ranges) { - if (ipv4_address >= it.first && ipv4_address <= it.second) - return true; - } - } - if (host.is_v6()) - { - static const std::array, 7> reservedIPv6Ranges - { - address_pair_v6("64:ff9b::", "64:ff9b:ffff:ffff:ffff:ffff:ffff:ffff"), // NAT64 - address_pair_v6("2001:db8::", "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"), - address_pair_v6("fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), - address_pair_v6("fe80::", "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), - address_pair_v6("ff00::", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), - address_pair_v6("::", "::"), - address_pair_v6("::1", "::1") - }; - - boost::asio::ip::address_v6::bytes_type ipv6_address = host.to_v6 ().to_bytes (); - for (const auto& it : reservedIPv6Ranges) { - if (ipv6_address >= it.first && ipv6_address <= it.second) - return true; - } - if (IsYggdrasilAddress (ipv6_address.data ())) // yggdrasil? - return true; - } - return false; - } -} // net -} // util -} // i2p diff --git a/libi2pd/util.h b/libi2pd/util.h deleted file mode 100644 index 7bd35e67..00000000 --- a/libi2pd/util.h +++ /dev/null @@ -1,242 +0,0 @@ -/* -* Copyright (c) 2013-2024, 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 UTIL_H -#define UTIL_H - -#include -#include -#include -#include -#include -#include -#include - -#ifdef ANDROID -#ifndef __clang__ -#include -namespace std -{ - template - std::string to_string(T value) - { - return boost::lexical_cast(value); - } - - inline int stoi(const std::string& str) - { - return boost::lexical_cast(str); - } -} -#endif -#endif - -namespace i2p -{ -namespace util -{ - - template - class MemoryPool - { - //BOOST_STATIC_ASSERT_MSG(sizeof(T) >= sizeof(void*), "size cannot be less that general pointer size"); - - public: - - MemoryPool (): m_Head (nullptr) {} - ~MemoryPool () - { - CleanUp (); - } - - void CleanUp () - { - CleanUp (m_Head); - m_Head = nullptr; - } - - template - T * Acquire (TArgs&&... args) - { - if (!m_Head) return new T(std::forward(args)...); - else - { - auto tmp = m_Head; - m_Head = static_cast(*(void * *)m_Head); // next - return new (tmp)T(std::forward(args)...); - } - } - - void Release (T * t) - { - if (!t) return; - t->~T (); - *(void * *)t = m_Head; // next - m_Head = t; - } - - template - std::unique_ptr > AcquireUnique (TArgs&&... args) - { - return std::unique_ptr >(Acquire (std::forward(args)...), - std::bind (&MemoryPool::Release, this, std::placeholders::_1)); - } - - template - std::shared_ptr AcquireShared (TArgs&&... args) - { - return std::shared_ptr(Acquire (std::forward(args)...), - std::bind (&MemoryPool::Release, this, std::placeholders::_1)); - } - - protected: - - void CleanUp (T * head) - { - while (head) - { - auto tmp = head; - head = static_cast(*(void * *)head); // next - ::operator delete ((void *)tmp); - } - } - - protected: - - T * m_Head; - }; - - template - class MemoryPoolMt: private MemoryPool - { - public: - - MemoryPoolMt () {} - template - T * AcquireMt (TArgs&&... args) - { - if (!this->m_Head) return new T(std::forward(args)...); - std::lock_guard l(m_Mutex); - return this->Acquire (std::forward(args)...); - } - - void ReleaseMt (T * t) - { - std::lock_guard l(m_Mutex); - this->Release (t); - } - - void ReleaseMt (T * * arr, size_t num) - { - if (!arr || !num) return; - std::lock_guard l(m_Mutex); - for (size_t i = 0; i < num; i++) - this->Release (arr[i]); - } - - templateclass C, typename... R> - void ReleaseMt(const C& c) - { - std::lock_guard l(m_Mutex); - for (auto& it: c) - this->Release (it); - } - - template - std::shared_ptr AcquireSharedMt (TArgs&&... args) - { - return std::shared_ptr(AcquireMt (std::forward(args)...), - std::bind::*)(T *)> (&MemoryPoolMt::ReleaseMt, this, std::placeholders::_1)); - } - - void CleanUpMt () - { - T * head; - { - std::lock_guard l(m_Mutex); - head = this->m_Head; - this->m_Head = nullptr; - } - if (head) this->CleanUp (head); - } - - private: - - std::mutex m_Mutex; - }; - - class RunnableService - { - protected: - - RunnableService (const std::string& name): m_Name (name), m_IsRunning (false) {} - virtual ~RunnableService () {} - - auto& GetIOService () { return m_Service; } - bool IsRunning () const { return m_IsRunning; }; - - void StartIOService (); - void StopIOService (); - - void SetName (std::string_view name); - - private: - - void Run (); - - private: - - std::string m_Name; - volatile bool m_IsRunning; - std::unique_ptr m_Thread; - boost::asio::io_context m_Service; - }; - - class RunnableServiceWithWork: public RunnableService - { - protected: - - RunnableServiceWithWork (const std::string& name): - RunnableService (name), m_Work (GetIOService ().get_executor ()) {} - - private: - - boost::asio::executor_work_guard m_Work; - }; - - void SetThreadName (const char *name); - - template - class SaveStateHelper - { - public: - - SaveStateHelper (T& orig): m_Original (orig), m_Copy (orig) {}; - ~SaveStateHelper () { m_Original = m_Copy; }; - - private: - - T& m_Original; - T m_Copy; - }; - - namespace net - { - int GetMTU (const boost::asio::ip::address& localAddress); - int GetMaxMTU (const boost::asio::ip::address_v6& localAddress); // check tunnel broker for ipv6 address - const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6=false); - boost::asio::ip::address_v6 GetYggdrasilAddress (); - bool IsLocalAddress (const boost::asio::ip::address& addr); - bool IsInReservedRange (const boost::asio::ip::address& host); - bool IsYggdrasilAddress (const boost::asio::ip::address& addr); - bool IsPortInReservedRange (const uint16_t port) noexcept; - } -} -} - -#endif diff --git a/libi2pd/version.h b/libi2pd/version.h deleted file mode 100644 index 1e63ae08..00000000 --- a/libi2pd/version.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -* Copyright (c) 2013-2024, 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 _VERSION_H_ -#define _VERSION_H_ - -#define CODENAME "Purple" - -#define XSTRINGIZE(x) STRINGIZE(x) -#define STRINGIZE(x) #x - -#define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) -#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) - -#define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 56 -#define I2PD_VERSION_MICRO 0 -#define I2PD_VERSION_PATCH 0 -#ifdef GITVER - #define I2PD_VERSION XSTRINGIZE(GITVER) -#else - #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO) -#endif - -#define VERSION I2PD_VERSION - -#define I2PD_NET_ID 2 - -#define I2P_VERSION_MAJOR 0 -#define I2P_VERSION_MINOR 9 -#define I2P_VERSION_MICRO 65 -#define I2P_VERSION_PATCH 0 -#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) -#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) - -#endif diff --git a/libi2pd_client/AddressBook.cpp b/libi2pd_client/AddressBook.cpp deleted file mode 100644 index 8333e87d..00000000 --- a/libi2pd_client/AddressBook.cpp +++ /dev/null @@ -1,1134 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "Base.h" -#include "util.h" -#include "Timestamp.h" -#include "Identity.h" -#include "FS.h" -#include "Log.h" -#include "HTTP.h" -#include "NetDb.hpp" -#include "ClientContext.h" -#include "AddressBook.h" -#include "Config.h" - -#if STD_FILESYSTEM -#include -namespace fs_lib = std::filesystem; -#else -#include -namespace fs_lib = boost::filesystem; -#endif - -namespace i2p -{ -namespace client -{ - // TODO: this is actually proxy class - class AddressBookFilesystemStorage: public AddressBookStorage - { - public: - - AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") - { - i2p::config::GetOption("persist.addressbook", m_IsPersist); - if (m_IsPersist) - i2p::config::GetOption("addressbook.hostsfile", m_HostsFile); - } - std::shared_ptr GetAddress (const i2p::data::IdentHash& ident) override; - void AddAddress (std::shared_ptr address) override; - void RemoveAddress (const i2p::data::IdentHash& ident) override; - void CleanUpCache () override; - - bool Init () override; - int Load (Addresses& addresses) override; - int LoadLocal (Addresses& addresses) override; - int Save (const Addresses& addresses) override; - - void SaveEtag (const i2p::data::IdentHash& subsciption, const std::string& etag, const std::string& lastModified) override; - bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) override; - void ResetEtags () override; - - private: - - int LoadFromFile (const std::string& filename, Addresses& addresses); // returns -1 if can't open file, otherwise number of records - - private: - - i2p::fs::HashedStorage storage; - std::string etagsPath, indexPath, localPath; - bool m_IsPersist; - std::string m_HostsFile; // file to dump hosts.txt, empty if not used - std::unordered_map, uint64_t> > m_FullAddressCache; // ident hash -> (full ident buffer, last access timestamp) - std::mutex m_FullAddressCacheMutex; - }; - - bool AddressBookFilesystemStorage::Init() - { - storage.SetPlace(i2p::fs::GetDataDir()); - // init storage - if (storage.Init(i2p::data::GetBase32SubstitutionTable(), 32)) - { - // init ETags - etagsPath = i2p::fs::StorageRootPath (storage, "etags"); - if (!i2p::fs::Exists (etagsPath)) - i2p::fs::CreateDirectory (etagsPath); - // init address files - indexPath = i2p::fs::StorageRootPath (storage, "addresses.csv"); - localPath = i2p::fs::StorageRootPath (storage, "local.csv"); - return true; - } - return false; - } - - std::shared_ptr AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) - { - auto ts = i2p::util::GetMonotonicSeconds (); - { - std::lock_guard l(m_FullAddressCacheMutex); - auto it = m_FullAddressCache.find (ident); - if (it != m_FullAddressCache.end ()) - { - it->second.second = ts; - return std::make_shared(it->second.first.data (), it->second.first.size ()); - } - } - - if (!m_IsPersist) - { - LogPrint(eLogDebug, "Addressbook: Persistence is disabled"); - return nullptr; - } - std::string filename = storage.Path(ident.ToBase32()); - std::ifstream f(filename, std::ifstream::binary); - if (!f.is_open ()) - { - LogPrint(eLogDebug, "Addressbook: Requested, but not found: ", filename); - return nullptr; - } - - f.seekg (0,std::ios::end); - size_t len = f.tellg (); - if (len < i2p::data::DEFAULT_IDENTITY_SIZE) - { - LogPrint (eLogError, "Addressbook: File ", filename, " is too short: ", len); - return nullptr; - } - f.seekg(0, std::ios::beg); - std::vector buf(len); - f.read((char *)buf.data (), len); - if (!f) - { - LogPrint (eLogError, "Addressbook: Couldn't read ", filename); - return nullptr; - } - { - std::lock_guard l(m_FullAddressCacheMutex); - m_FullAddressCache.try_emplace (ident, buf, ts); - } - return std::make_shared(buf.data (), len); - } - - void AddressBookFilesystemStorage::AddAddress (std::shared_ptr address) - { - if (!address) return; - size_t len = address->GetFullLen (); - std::vector buf; - if (!len) return; // invalid address - { - std::lock_guard l(m_FullAddressCacheMutex); - auto [it, inserted] = m_FullAddressCache.try_emplace (address->GetIdentHash(), len, i2p::util::GetMonotonicSeconds ()); - if (inserted) - address->ToBuffer (it->second.first.data (), len); - if (m_IsPersist) - buf = it->second.first; - } - if (m_IsPersist && !buf.empty ()) - { - std::string path = storage.Path(address->GetIdentHash().ToBase32()); - std::ofstream f (path, std::ofstream::binary | std::ofstream::out); - if (!f.is_open ()) - { - LogPrint (eLogError, "Addressbook: Can't open file ", path); - return; - } - f.write ((const char *)buf.data (), len); - } - } - - void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident) - { - { - std::lock_guard l(m_FullAddressCacheMutex); - m_FullAddressCache.erase (ident); - } - if (!m_IsPersist) return; - storage.Remove( ident.ToBase32() ); - } - - int AddressBookFilesystemStorage::LoadFromFile (const std::string& filename, Addresses& addresses) - { - int num = 0; - std::ifstream f (filename, std::ifstream::in); // in text mode - if (!f) return -1; - - addresses.clear (); - while (!f.eof ()) - { - std::string s; - getline(f, s); - if (!s.length()) continue; // skip empty line - - std::size_t pos = s.find(','); - if (pos != std::string::npos) - { - std::string name = s.substr(0, pos++); - std::string addr = s.substr(pos); - - addresses[name] = std::make_shared
(addr); - num++; - } - } - return num; - } - - int AddressBookFilesystemStorage::Load (Addresses& addresses) - { - int num = LoadFromFile (indexPath, addresses); - if (num < 0) - { - LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath); - return 0; - } - LogPrint(eLogInfo, "Addressbook: Using index file ", indexPath); - LogPrint (eLogInfo, "Addressbook: ", num, " addresses loaded from storage"); - - return num; - } - - int AddressBookFilesystemStorage::LoadLocal (Addresses& addresses) - { - int num = LoadFromFile (localPath, addresses); - if (num < 0) return 0; - LogPrint (eLogInfo, "Addressbook: ", num, " local addresses loaded"); - return num; - } - - int AddressBookFilesystemStorage::Save (const Addresses& addresses) - { - if (addresses.empty()) - { - LogPrint(eLogWarning, "Addressbook: Not saving empty addressbook"); - return 0; - } - - int num = 0; - { - // save index file - std::ofstream f (indexPath, std::ofstream::out); // in text mode - if (f.is_open ()) - { - for (const auto& it: addresses) - { - if (it.second->IsValid ()) - { - f << it.first << ","; - if (it.second->IsIdentHash ()) - f << it.second->identHash.ToBase32 (); - else - f << it.second->blindedPublicKey->ToB33 (); - f << std::endl; - num++; - } - else - LogPrint (eLogWarning, "Addressbook: Invalid address ", it.first); - } - LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved"); - } - else - LogPrint (eLogWarning, "Addressbook: Can't open ", indexPath); - } - if (!m_HostsFile.empty ()) - { - // dump full hosts.txt - std::ofstream f (m_HostsFile, std::ofstream::out); // in text mode - if (f.is_open ()) - { - for (const auto& it: addresses) - { - std::shared_ptr addr; - if (it.second->IsIdentHash ()) - { - addr = GetAddress (it.second->identHash); - if (addr) - f << it.first << "=" << addr->ToBase64 () << std::endl; - } - } - } - else - LogPrint (eLogWarning, "Addressbook: Can't open ", m_HostsFile); - } - - return num; - } - - void AddressBookFilesystemStorage::SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) - { - std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32 () + ".txt"; - std::ofstream f (fname, std::ofstream::out | std::ofstream::trunc); - if (f) - { - f << etag << std::endl; - f<< lastModified << std::endl; - } - } - - bool AddressBookFilesystemStorage::GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) - { - std::string fname = etagsPath + i2p::fs::dirSep + subscription.ToBase32 () + ".txt"; - std::ifstream f (fname, std::ofstream::in); - if (!f || f.eof ()) return false; - std::getline (f, etag); - if (f.eof ()) return false; - std::getline (f, lastModified); - return true; - } - - void AddressBookFilesystemStorage::ResetEtags () - { - LogPrint (eLogError, "Addressbook: Resetting eTags"); - for (fs_lib::directory_iterator it (etagsPath); it != fs_lib::directory_iterator (); ++it) - { - if (!fs_lib::is_regular_file (it->status ())) - continue; - fs_lib::remove (it->path ()); - } - } - - void AddressBookFilesystemStorage::CleanUpCache () - { - auto ts = i2p::util::GetMonotonicSeconds (); - std::lock_guard l(m_FullAddressCacheMutex); - for (auto it = m_FullAddressCache.begin (); it != m_FullAddressCache.end ();) - { - if (ts > it->second.second + ADDRESS_CACHE_EXPIRATION_TIMEOUT) - it = m_FullAddressCache.erase (it); - else - it++; - } - } - -//--------------------------------------------------------------------- - - Address::Address (std::string_view b32): - addressType (eAddressInvalid) - { - if (b32.length () <= B33_ADDRESS_THRESHOLD) - { - if (identHash.FromBase32 (b32) > 0) - addressType = eAddressIndentHash; - } - else - { - blindedPublicKey = std::make_shared(b32); - if (blindedPublicKey->IsValid ()) - addressType = eAddressBlindedPublicKey; - } - } - - Address::Address (const i2p::data::IdentHash& hash) - { - addressType = eAddressIndentHash; - identHash = hash; - } - - AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), - m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr), - m_IsEnabled (true) - { - } - - AddressBook::~AddressBook () - { - Stop (); - } - - void AddressBook::Start () - { - i2p::config::GetOption("addressbook.enabled", m_IsEnabled); - if (m_IsEnabled) - { - if (!m_Storage) - m_Storage = new AddressBookFilesystemStorage; - m_Storage->Init(); - LoadHosts (); /* try storage, then hosts.txt, then download */ - StartSubscriptions (); - StartLookups (); - ScheduleCacheUpdate (); - } - } - - void AddressBook::StartResolvers () - { - LoadLocal (); - } - - void AddressBook::Stop () - { - StopLookups (); - StopSubscriptions (); - if (m_SubscriptionsUpdateTimer) - { - m_SubscriptionsUpdateTimer->cancel (); - m_SubscriptionsUpdateTimer = nullptr; - } - if (m_AddressCacheUpdateTimer) - { - m_AddressCacheUpdateTimer->cancel (); - m_AddressCacheUpdateTimer = nullptr; - } - bool isDownloading = m_Downloading.valid (); - if (isDownloading) - { - if (m_Downloading.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - isDownloading = false; - else - { - LogPrint (eLogInfo, "Addressbook: Subscriptions are downloading, abort"); - for (int i = 0; i < 30; i++) - { - if (m_Downloading.wait_for(std::chrono::seconds(1)) == std::future_status::ready) // wait for 1 seconds - { - isDownloading = false; - LogPrint (eLogInfo, "Addressbook: Subscriptions download complete"); - break; - } - } - } - if (!isDownloading) - m_Downloading.get (); - else - LogPrint (eLogError, "Addressbook: Subscription download timeout"); - } - if (m_Storage) - { - m_Storage->Save (m_Addresses); - delete m_Storage; - m_Storage = nullptr; - } - m_DefaultSubscription = nullptr; - m_Subscriptions.clear (); - } - - std::shared_ptr AddressBook::GetAddress (std::string_view address) - { - auto pos = address.find(".b32.i2p"); - if (pos != std::string::npos) - { - auto addr = std::make_shared(address.substr (0, pos)); - return addr->IsValid () ? addr : nullptr; - } - else -#if __cplusplus >= 202002L // C++20 - if (address.ends_with (".i2p")) -#else - if (address.find (".i2p") != std::string::npos) -#endif - { - if (!m_IsEnabled) return nullptr; - auto addr = FindAddress (address); - if (!addr) - LookupAddress (address); // TODO: - return addr; - } - // if not .b32 we assume full base64 address - i2p::data::IdentityEx dest; - if (!dest.FromBase64 (address)) - return nullptr; - return std::make_shared(dest.GetIdentHash ()); - } - - std::shared_ptr AddressBook::FindAddress (std::string_view address) - { - auto it = m_Addresses.find (address); - if (it != m_Addresses.end ()) - return it->second; - return nullptr; - } - - bool AddressBook::RecordExists (const std::string& address, const std::string& jump) - { - auto addr = FindAddress(address); - if (!addr) - return false; - - auto pos = jump.find(".b32.i2p"); - if (pos != std::string::npos) - { - i2p::data::IdentHash identHash; - if (identHash.FromBase32(jump.substr (0, pos)) && identHash == addr->identHash) - return true; - } - else - { - i2p::data::IdentityEx ident; - if (ident.FromBase64 (jump) && ident.GetIdentHash () == addr->identHash) - return true; - } - - return false; - } - - void AddressBook::InsertAddress (const std::string& address, const std::string& jump) - { - auto pos = jump.find(".b32.i2p"); - if (pos != std::string::npos) - { - m_Addresses[address] = std::make_shared
(jump.substr (0, pos)); - LogPrint (eLogInfo, "Addressbook: Added ", address," -> ", jump); - } - else - { - // assume base64 - auto ident = std::make_shared(); - if (ident->FromBase64 (jump)) - { - if (m_Storage) m_Storage->AddAddress (ident); - m_Addresses[address] = std::make_shared
(ident->GetIdentHash ()); - LogPrint (eLogInfo, "Addressbook: Added ", address," -> ", ToAddress(ident->GetIdentHash ())); - } - else - LogPrint (eLogError, "Addressbook: Malformed address ", jump); - } - } - - void AddressBook::InsertFullAddress (std::shared_ptr address) - { - if (m_Storage) m_Storage->AddAddress (address); - } - - std::shared_ptr AddressBook::GetFullAddress (const std::string& address) - { - auto addr = GetAddress (address); - if (!addr || !addr->IsIdentHash ()) return nullptr; - return m_Storage ? m_Storage->GetAddress (addr->identHash) : nullptr; - } - - void AddressBook::LoadHosts () - { - if (!m_Storage) return; - if (m_Storage->Load (m_Addresses) > 0) - { - m_IsLoaded = true; - return; - } - - // then try hosts.txt - std::ifstream f (i2p::fs::DataDirPath("hosts.txt"), std::ifstream::in); // in text mode - if (f.is_open ()) - { - LoadHostsFromStream (f, false); - m_IsLoaded = true; - } - - // reset eTags, because we don’t know how old hosts.txt is or can't load addressbook - m_Storage->ResetEtags (); - } - - bool AddressBook::LoadHostsFromStream (std::istream& f, bool is_update) - { - std::unique_lock l(m_AddressBookMutex); - int numAddresses = 0; - bool incomplete = false; - std::string s; - while (!f.eof ()) - { - getline(f, s); - - if (!s.length() || s[0] == '#') - continue; // skip empty or comment line - - size_t pos = s.find('='); - - if (pos != std::string::npos) - { - std::string_view name = std::string_view(s).substr(0, pos++); - std::string_view addr = std::string_view(s).substr(pos); - - size_t pos = addr.find('#'); - if (pos != addr.npos) - addr = addr.substr(0, pos); // remove comments -#if __cplusplus >= 202002L // C++20 - if (name.ends_with (".b32.i2p")) -#else - if (name.find(".b32.i2p") != name.npos) -#endif - { - LogPrint (eLogError, "Addressbook: Skipped adding of b32 address: ", name); - continue; - } - -#if __cplusplus >= 202002L // C++20 - if (!name.ends_with (".i2p")) -#else - if (name.find(".i2p") == name.npos) -#endif - { - LogPrint (eLogError, "Addressbook: Malformed domain: ", name); - continue; - } - - auto ident = std::make_shared (); - if (!ident->FromBase64(addr)) - { - LogPrint (eLogError, "Addressbook: Malformed address ", addr, " for ", name); - incomplete = f.eof (); - continue; - } - numAddresses++; - auto it = m_Addresses.find (name); - if (it != m_Addresses.end ()) // already exists ? - { - if (it->second->IsIdentHash () && it->second->identHash != ident->GetIdentHash () && // address changed? - ident->GetSigningKeyType () != i2p::data::SIGNING_KEY_TYPE_DSA_SHA1) // don't replace by DSA - { - it->second->identHash = ident->GetIdentHash (); - if (m_Storage) - { - m_Storage->AddAddress (ident); - m_Storage->RemoveAddress (it->second->identHash); - } - LogPrint (eLogInfo, "Addressbook: Updated host: ", name); - } - } - else - { - m_Addresses.emplace (name, std::make_shared
(ident->GetIdentHash ())); - if (m_Storage) m_Storage->AddAddress (ident); - if (is_update) - LogPrint (eLogInfo, "Addressbook: Added new host: ", name); - } - } - else - incomplete = f.eof (); - } - LogPrint (eLogInfo, "Addressbook: ", numAddresses, " addresses processed"); - if (numAddresses > 0) - { - if (!incomplete) m_IsLoaded = true; - if (m_Storage) m_Storage->Save (m_Addresses); - } - return !incomplete; - } - - void AddressBook::LoadSubscriptions () - { - if (!m_Subscriptions.size ()) - { - std::ifstream f (i2p::fs::DataDirPath ("subscriptions.txt"), std::ifstream::in); // in text mode - if (f.is_open ()) - { - std::string s; - while (!f.eof ()) - { - getline(f, s); - if (s.empty () || s[0] == '#') continue; // skip empty line or comment - m_Subscriptions.push_back (std::make_shared (*this, s)); - } - LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded"); - LogPrint (eLogWarning, "Addressbook: subscriptions.txt usage is deprecated, use config file instead"); - } - else - { - LogPrint (eLogInfo, "Addressbook: Loading subscriptions from config"); - // using config file items - std::string subscriptionURLs; i2p::config::GetOption("addressbook.subscriptions", subscriptionURLs); - std::vector subsList; - boost::split(subsList, subscriptionURLs, boost::is_any_of(","), boost::token_compress_on); - - for (const auto& s: subsList) - if (!s.empty ()) - m_Subscriptions.push_back (std::make_shared (*this, s)); - LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded"); - } - } - else - LogPrint (eLogError, "Addressbook: Subscriptions already loaded"); - } - - void AddressBook::LoadLocal () - { - if (!m_Storage) return; - AddressBookStorage::Addresses localAddresses; - m_Storage->LoadLocal (localAddresses); - for (const auto& it: localAddresses) - { - if (!it.second->IsIdentHash ()) continue; // skip blinded for now - auto dot = it.first.find ('.'); - if (dot != std::string::npos) - { - auto domain = it.first.substr (dot + 1); - auto it1 = m_Addresses.find (domain); // find domain in our addressbook - if (it1 != m_Addresses.end () && it1->second->IsIdentHash ()) - { - auto dest = context.FindLocalDestination (it1->second->identHash); - if (dest) - { - // address is ours - std::shared_ptr resolver; - auto it2 = m_Resolvers.find (it1->second->identHash); - if (it2 != m_Resolvers.end ()) - resolver = it2->second; // resolver exists - else - { - // create new resolver - resolver = std::make_shared(dest); - m_Resolvers.insert (std::make_pair(it1->second->identHash, resolver)); - } - resolver->AddAddress (it.first, it.second->identHash); - } - } - } - } - } - - bool AddressBook::GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) - { - if (m_Storage) - return m_Storage->GetEtag (subscription, etag, lastModified); - else - return false; - } - - void AddressBook::DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) - { - m_NumRetries++; - int nextUpdateTimeout = m_NumRetries*CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT; - if (m_NumRetries > CONTINIOUS_SUBSCRIPTION_MAX_NUM_RETRIES || nextUpdateTimeout > CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT) - nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT; - if (success) - { - m_NumRetries = 0; - if (m_DefaultSubscription) m_DefaultSubscription = nullptr; - if (m_IsLoaded) - nextUpdateTimeout = CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT; - else - m_IsLoaded = true; - if (m_Storage) m_Storage->SaveEtag (subscription, etag, lastModified); - } - if (m_SubscriptionsUpdateTimer) - { - m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(nextUpdateTimeout)); - m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, - this, std::placeholders::_1)); - } - } - - void AddressBook::StartSubscriptions () - { - LoadSubscriptions (); - if (m_IsLoaded && m_Subscriptions.empty ()) return; - - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (dest) - { - m_SubscriptionsUpdateTimer = std::make_unique(dest->GetService ()); - m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT)); - m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, - this, std::placeholders::_1)); - } - else - LogPrint (eLogCritical, "Addressbook: Can't start subscriptions: missing shared local destination"); - } - - void AddressBook::StopSubscriptions () - { - if (m_SubscriptionsUpdateTimer) - m_SubscriptionsUpdateTimer->cancel (); - } - - void AddressBook::HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (!dest) { - LogPrint(eLogWarning, "Addressbook: Missing local destination, skip subscription update"); - return; - } - bool isDownloading = m_Downloading.valid (); - if (isDownloading && m_Downloading.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // still active? - { - m_Downloading.get (); - isDownloading = false; - } - if (!isDownloading && dest->IsReady ()) - { - if (!m_IsLoaded) - { - // download it from default subscription - LogPrint (eLogInfo, "Addressbook: Trying to download it from default subscription."); - std::string defaultSubURL; i2p::config::GetOption("addressbook.defaulturl", defaultSubURL); - if (!m_DefaultSubscription) - m_DefaultSubscription = std::make_shared(*this, defaultSubURL); - m_Downloading = std::async (std::launch::async, - std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription)); - } - else if (!m_Subscriptions.empty ()) - { - // pick random subscription - auto ind = rand () % m_Subscriptions.size(); - m_Downloading = std::async (std::launch::async, - std::bind (&AddressBookSubscription::CheckUpdates, m_Subscriptions[ind])); - } - } - else - { - // try it again later - m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_RETRY_TIMEOUT)); - m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer, - this, std::placeholders::_1)); - } - } - } - - void AddressBook::StartLookups () - { - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (dest) - { - auto datagram = dest->GetDatagramDestination (); - if (!datagram) - datagram = dest->CreateDatagramDestination (); - datagram->SetReceiver (std::bind (&AddressBook::HandleLookupResponse, this, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), - ADDRESS_RESPONSE_DATAGRAM_PORT); - } - } - - void AddressBook::StopLookups () - { - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (dest) - { - auto datagram = dest->GetDatagramDestination (); - if (datagram) datagram->ResetReceiver (ADDRESS_RESPONSE_DATAGRAM_PORT); - } - } - - void AddressBook::LookupAddress (std::string_view address) - { - std::shared_ptr addr; - auto dot = address.find ('.'); - if (dot != std::string::npos) - addr = FindAddress (address.substr (dot + 1)); - if (!addr || !addr->IsIdentHash ()) // TODO: - { - LogPrint (eLogError, "Addressbook: Can't find domain for ", address); - return; - } - - auto dest = i2p::client::context.GetSharedLocalDestination (); - if (dest) - { - auto datagram = dest->GetDatagramDestination (); - if (datagram) - { - uint32_t nonce; - RAND_bytes ((uint8_t *)&nonce, 4); - { - std::unique_lock l(m_LookupsMutex); - m_Lookups[nonce] = address; - } - LogPrint (eLogDebug, "Addressbook: Lookup of ", address, " to ", addr->identHash.ToBase32 (), " nonce=", nonce); - size_t len = address.length () + 9; - uint8_t * buf = new uint8_t[len]; - memset (buf, 0, 4); - htobe32buf (buf + 4, nonce); - buf[8] = address.length (); - memcpy (buf + 9, address.data (), address.length ()); - datagram->SendDatagramTo (buf, len, addr->identHash, ADDRESS_RESPONSE_DATAGRAM_PORT, ADDRESS_RESOLVER_DATAGRAM_PORT); - delete[] buf; - } - } - } - - void AddressBook::HandleLookupResponse (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - if (len < 44) - { - LogPrint (eLogError, "Addressbook: Lookup response is too short ", len); - return; - } - uint32_t nonce = bufbe32toh (buf + 4); - LogPrint (eLogDebug, "Addressbook: Lookup response received from ", from.GetIdentHash ().ToBase32 (), " nonce=", nonce); - std::string address; - { - std::unique_lock l(m_LookupsMutex); - auto it = m_Lookups.find (nonce); - if (it != m_Lookups.end ()) - { - address = it->second; - m_Lookups.erase (it); - } - } - if (address.length () > 0) - { - // TODO: verify from - i2p::data::IdentHash hash(buf + 8); - if (!hash.IsZero ()) - m_Addresses[address] = std::make_shared
(hash); - else - LogPrint (eLogInfo, "AddressBook: Lookup response: ", address, " not found"); - } - } - - void AddressBook::ScheduleCacheUpdate () - { - if (!m_AddressCacheUpdateTimer) - { - auto dest = i2p::client::context.GetSharedLocalDestination (); - if(dest) - m_AddressCacheUpdateTimer = std::make_unique(dest->GetService ()); - } - if (m_AddressCacheUpdateTimer) - { - m_AddressCacheUpdateTimer->expires_from_now (boost::posix_time::seconds(ADDRESS_CACHE_UPDATE_INTERVAL )); - m_AddressCacheUpdateTimer->async_wait ( - [this](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - if (m_Storage) m_Storage->CleanUpCache (); - ScheduleCacheUpdate (); - } - }); - } - } - - AddressBookSubscription::AddressBookSubscription (AddressBook& book, std::string_view link): - m_Book (book), m_Link (link) - { - } - - void AddressBookSubscription::CheckUpdates () - { - i2p::util::SetThreadName("Addressbook"); - - bool result = MakeRequest (); - m_Book.DownloadComplete (result, m_Ident, m_Etag, m_LastModified); - } - - bool AddressBookSubscription::MakeRequest () - { - i2p::http::URL url; - // must be run in separate thread - LogPrint (eLogInfo, "Addressbook: Downloading hosts database from ", m_Link); - if (!url.parse(m_Link)) - { - LogPrint(eLogError, "Addressbook: Failed to parse url: ", m_Link); - return false; - } - auto addr = m_Book.GetAddress (url.host); - if (!addr || !addr->IsIdentHash ()) - { - LogPrint (eLogError, "Addressbook: Can't resolve ", url.host); - return false; - } - else - m_Ident = addr->identHash; - // save url parts for later use - std::string dest_host = url.host; - int dest_port = url.port ? url.port : 80; - // try to create stream to addressbook site - auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (m_Ident, dest_port); - if (!stream) - { - LogPrint (eLogError, "Addressbook: LeaseSet for address ", url.host, " not found"); - return false; - } - if (m_Etag.empty() && m_LastModified.empty()) - { - m_Book.GetEtag (m_Ident, m_Etag, m_LastModified); - LogPrint (eLogDebug, "Addressbook: Loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified); - } - // create http request & send it - i2p::http::HTTPReq req; - req.AddHeader("Host", dest_host); - 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("Connection", "close"); - if (!m_Etag.empty()) - req.AddHeader("If-None-Match", m_Etag); - if (!m_LastModified.empty()) - req.AddHeader("If-Modified-Since", m_LastModified); - // convert url to relative - url.schema = ""; - url.host = ""; - req.uri = url.to_string(); - req.version = "HTTP/1.1"; - std::string request = req.to_string(); - stream->Send ((const uint8_t *) request.data(), request.length()); - // read response - std::string response; - uint8_t recv_buf[4096]; - bool end = false; - int numAttempts = 0; - while (!end) - { - size_t received = stream->Receive (recv_buf, 4096, SUBSCRIPTION_REQUEST_TIMEOUT); - if (received) - { - response.append ((char *)recv_buf, received); - if (!stream->IsOpen ()) end = true; - } - else if (!stream->IsOpen ()) - end = true; - else - { - LogPrint (eLogError, "Addressbook: Subscriptions request timeout expired"); - numAttempts++; - if (numAttempts > 5) end = true; - } - } - // process remaining buffer - while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) - response.append ((char *)recv_buf, len); - // parse response - i2p::http::HTTPRes res; - int res_head_len = res.parse(response); - if (res_head_len < 0) - { - LogPrint(eLogError, "Addressbook: Can't parse http response from ", dest_host); - return false; - } - if (res_head_len == 0) - { - LogPrint(eLogError, "Addressbook: Incomplete http response from ", dest_host, ", interrupted by timeout"); - return false; - } - // assert: res_head_len > 0 - response.erase(0, res_head_len); - if (res.code == 304) - { - LogPrint (eLogInfo, "Addressbook: No updates from ", dest_host, ", code 304"); - return false; - } - if (res.code != 200) - { - LogPrint (eLogWarning, "Adressbook: Can't get updates from ", dest_host, ", response code ", res.code); - return false; - } - int len = res.content_length(); - if (response.empty()) - { - LogPrint(eLogError, "Addressbook: Empty response from ", dest_host, ", expected ", len, " bytes"); - return false; - } - if (!res.is_gzipped () && len > 0 && len != (int) response.length()) - { - LogPrint(eLogError, "Addressbook: Response size mismatch, expected: ", len, ", got: ", response.length(), "bytes"); - return false; - } - // assert: res.code == 200 - auto it = res.headers.find("ETag"); - if (it != res.headers.end()) m_Etag = it->second; - it = res.headers.find("Last-Modified"); - if (it != res.headers.end()) m_LastModified = it->second; - if (res.is_chunked()) - { - std::stringstream in(response), out; - i2p::http::MergeChunkedResponse (in, out); - response = out.str(); - } - if (res.is_gzipped()) - { - std::stringstream out; - i2p::data::GzipInflator inflator; - inflator.Inflate ((const uint8_t *) response.data(), response.length(), out); - if (out.fail()) - { - LogPrint(eLogError, "Addressbook: Can't gunzip http response"); - return false; - } - response = out.str(); - } - std::stringstream ss(response); - LogPrint (eLogInfo, "Addressbook: Got update from ", dest_host); - m_Book.LoadHostsFromStream (ss, true); - return true; - } - - AddressResolver::AddressResolver (std::shared_ptr destination): - m_LocalDestination (destination) - { - if (m_LocalDestination) - { - auto datagram = m_LocalDestination->GetDatagramDestination (); - if (!datagram) - datagram = m_LocalDestination->CreateDatagramDestination (); - datagram->SetReceiver (std::bind (&AddressResolver::HandleRequest, this, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), - ADDRESS_RESOLVER_DATAGRAM_PORT); - } - } - - AddressResolver::~AddressResolver () - { - if (m_LocalDestination) - { - auto datagram = m_LocalDestination->GetDatagramDestination (); - if (datagram) - datagram->ResetReceiver (ADDRESS_RESOLVER_DATAGRAM_PORT); - } - } - - void AddressResolver::HandleRequest (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - if (len < 9 || len < buf[8] + 9U) - { - LogPrint (eLogError, "Addressbook: Address request is too short ", len); - return; - } - // read requested address - uint8_t l = buf[8]; - char address[255]; - memcpy (address, buf + 9, l); - address[l] = 0; - LogPrint (eLogDebug, "Addressbook: Address request ", address); - // send response - uint8_t response[44]; - memset (response, 0, 4); // reserved - memcpy (response + 4, buf + 4, 4); // nonce - auto it = m_LocalAddresses.find (address); // address lookup - if (it != m_LocalAddresses.end ()) - memcpy (response + 8, it->second, 32); // ident - else - memset (response + 8, 0, 32); // not found - memset (response + 40, 0, 4); // set expiration time to zero - m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash(), toPort, fromPort); - } - - void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident) - { - m_LocalAddresses[name] = ident; - } - -} -} diff --git a/libi2pd_client/AddressBook.h b/libi2pd_client/AddressBook.h deleted file mode 100644 index 8b32aa93..00000000 --- a/libi2pd_client/AddressBook.h +++ /dev/null @@ -1,184 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 ADDRESS_BOOK_H__ -#define ADDRESS_BOOK_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "Base.h" -#include "Identity.h" -#include "Log.h" -#include "Destination.h" -#include "LeaseSet.h" - -namespace i2p -{ -namespace client -{ - const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes - const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes - const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours) - const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes - const int CONTINIOUS_SUBSCRIPTION_MAX_NUM_RETRIES = 10; // then update timeout - const int SUBSCRIPTION_REQUEST_TIMEOUT = 120; //in seconds - const int ADDRESS_CACHE_EXPIRATION_TIMEOUT = 710; // in seconds - const int ADDRESS_CACHE_UPDATE_INTERVAL = 76; // in seconds - - const uint16_t ADDRESS_RESOLVER_DATAGRAM_PORT = 53; - const uint16_t ADDRESS_RESPONSE_DATAGRAM_PORT = 54; - - const size_t B33_ADDRESS_THRESHOLD = 52; // characters - - struct Address - { - enum { eAddressIndentHash, eAddressBlindedPublicKey, eAddressInvalid } addressType; - i2p::data::IdentHash identHash; - std::shared_ptr blindedPublicKey; - - Address (std::string_view b32); - Address (const i2p::data::IdentHash& hash); - bool IsIdentHash () const { return addressType == eAddressIndentHash; }; - bool IsValid () const { return addressType != eAddressInvalid; }; - }; - - inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); } - - class AddressBookStorage // interface for storage - { - public: - - typedef std::map, std::less<> > Addresses; - - virtual ~AddressBookStorage () {}; - virtual std::shared_ptr GetAddress (const i2p::data::IdentHash& ident) = 0; - virtual void AddAddress (std::shared_ptr address) = 0; - virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0; - virtual void CleanUpCache () = 0; - - virtual bool Init () = 0; - virtual int Load (Addresses& addresses) = 0; - virtual int LoadLocal (Addresses& addresses) = 0; - virtual int Save (const Addresses& addresses) = 0; - - virtual void SaveEtag (const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified) = 0; - virtual bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified) = 0; - virtual void ResetEtags () = 0; - }; - - class AddressBookSubscription; - class AddressResolver; - class AddressBook - { - public: - - AddressBook (); - ~AddressBook (); - void Start (); - void StartResolvers (); - void Stop (); - std::shared_ptr GetAddress (std::string_view address); - std::shared_ptr GetFullAddress (const std::string& address); - std::shared_ptr FindAddress (std::string_view address); - void LookupAddress (std::string_view address); - void InsertAddress (const std::string& address, const std::string& jump); // for jump links - void InsertFullAddress (std::shared_ptr address); - - bool RecordExists (const std::string& address, const std::string& jump); - - bool LoadHostsFromStream (std::istream& f, bool is_update); - void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified); - //This method returns the ".b32.i2p" address - std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); } - std::string ToAddress(std::shared_ptr ident) { return ToAddress(ident->GetIdentHash ()); } - - bool GetEtag (const i2p::data::IdentHash& subscription, std::string& etag, std::string& lastModified); - bool IsEnabled () const { return m_IsEnabled; } - - private: - - void StartSubscriptions (); - void StopSubscriptions (); - - void LoadHosts (); - void LoadSubscriptions (); - void LoadLocal (); - - void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode); - - void StartLookups (); - void StopLookups (); - void HandleLookupResponse (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - - void ScheduleCacheUpdate (); - - private: - - std::mutex m_AddressBookMutex; - AddressBookStorage::Addresses m_Addresses; - std::map > m_Resolvers; // local destination->resolver - std::mutex m_LookupsMutex; - std::map m_Lookups; // nonce -> address - AddressBookStorage * m_Storage; - volatile bool m_IsLoaded; - std::future m_Downloading; - int m_NumRetries; - std::vector > m_Subscriptions; - std::shared_ptr m_DefaultSubscription; // in case if we don't know any addresses yet - std::unique_ptr m_SubscriptionsUpdateTimer, m_AddressCacheUpdateTimer; - bool m_IsEnabled; - }; - - class AddressBookSubscription - { - public: - - AddressBookSubscription (AddressBook& book, std::string_view link); - void CheckUpdates (); - - private: - - bool MakeRequest (); - - private: - - AddressBook& m_Book; - std::string m_Link, m_Etag, m_LastModified; - i2p::data::IdentHash m_Ident; - // m_Etag must be surrounded by "" - }; - - class AddressResolver - { - public: - - AddressResolver (std::shared_ptr destination); - ~AddressResolver (); - void AddAddress (const std::string& name, const i2p::data::IdentHash& ident); - - private: - - void HandleRequest (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - - private: - - std::shared_ptr m_LocalDestination; - std::map m_LocalAddresses; - }; -} -} - -#endif diff --git a/libi2pd_client/BOB.cpp b/libi2pd_client/BOB.cpp deleted file mode 100644 index 8d94e94b..00000000 --- a/libi2pd_client/BOB.cpp +++ /dev/null @@ -1,962 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include "Log.h" -#include "ClientContext.h" -#include "util.h" -#include "BOB.h" - -namespace i2p -{ -namespace client -{ - void BOBI2PTunnelIncomingConnection::Established () - { - if (m_IsQuiet) - StreamReceive (); - else - { - // send destination first like received from I2P - std::string dest = GetStream ()->GetRemoteIdentity ()->ToBase64 (); - dest += "\n"; - if (dest.size() <= I2P_TUNNEL_CONNECTION_BUFFER_SIZE) - memcpy (GetStreamBuffer (), dest.c_str (), dest.size ()); - else - memset (GetStreamBuffer (), 0, I2P_TUNNEL_CONNECTION_BUFFER_SIZE); - HandleStreamReceive (boost::system::error_code (), dest.size ()); - } - Receive (); - } - - BOBI2PInboundTunnel::BOBI2PInboundTunnel (const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr localDestination): - BOBI2PTunnel (localDestination), m_Acceptor (localDestination->GetService (), ep) - { - } - - BOBI2PInboundTunnel::~BOBI2PInboundTunnel () - { - Stop (); - } - - void BOBI2PInboundTunnel::Start () - { - m_Acceptor.listen (); - Accept (); - } - - void BOBI2PInboundTunnel::Stop () - { - m_Acceptor.close(); - ClearHandlers (); - } - - void BOBI2PInboundTunnel::Accept () - { - auto receiver = std::make_shared (); - receiver->socket = std::make_shared (GetService ()); - m_Acceptor.async_accept (*receiver->socket, std::bind (&BOBI2PInboundTunnel::HandleAccept, this, - std::placeholders::_1, receiver)); - } - - void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr receiver) - { - if (!ecode) - { - Accept (); - ReceiveAddress (receiver); - } - } - - void BOBI2PInboundTunnel::ReceiveAddress (std::shared_ptr receiver) - { - receiver->socket->async_read_some (boost::asio::buffer( - receiver->buffer + receiver->bufferOffset, - BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset), - std::bind(&BOBI2PInboundTunnel::HandleReceivedAddress, this, - std::placeholders::_1, std::placeholders::_2, receiver)); - } - - void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred, - std::shared_ptr receiver) - { - if (ecode) - LogPrint (eLogError, "BOB: Inbound tunnel read error: ", ecode.message ()); - else - { - receiver->bufferOffset += bytes_transferred; - receiver->buffer[receiver->bufferOffset] = 0; - char * eol = strchr (receiver->buffer, '\n'); - if (eol) - { - *eol = 0; - if (eol != receiver->buffer && eol[-1] == '\r') eol[-1] = 0; // workaround for Transmission, it sends '\r\n' terminated address - receiver->data = (uint8_t *)eol + 1; - receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1); - auto addr = context.GetAddressBook ().GetAddress (receiver->buffer); - if (!addr) - { - LogPrint (eLogError, "BOB: Address ", receiver->buffer, " not found"); - return; - } - if (addr->IsIdentHash ()) - { - auto leaseSet = GetLocalDestination ()->FindLeaseSet (addr->identHash); - if (leaseSet) - CreateConnection (receiver, leaseSet); - else - GetLocalDestination ()->RequestDestination (addr->identHash, - std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete, - this, std::placeholders::_1, receiver)); - } - else - GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, - std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete, - this, std::placeholders::_1, receiver)); - } - else - { - if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE) - ReceiveAddress (receiver); - else - LogPrint (eLogError, "BOB: Missing inbound address"); - } - } - } - - void BOBI2PInboundTunnel::HandleDestinationRequestComplete (std::shared_ptr leaseSet, std::shared_ptr receiver) - { - if (leaseSet) - CreateConnection (receiver, leaseSet); - else - LogPrint (eLogError, "BOB: LeaseSet for inbound destination not found"); - } - - void BOBI2PInboundTunnel::CreateConnection (std::shared_ptr receiver, std::shared_ptr leaseSet) - { - LogPrint (eLogDebug, "BOB: New inbound connection"); - auto connection = std::make_shared(this, receiver->socket, leaseSet); - AddHandler (connection); - connection->I2PConnect (receiver->data, receiver->dataLen); - } - - BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& outhost, uint16_t port, - std::shared_ptr localDestination, bool quiet): BOBI2PTunnel (localDestination), - m_Endpoint (boost::asio::ip::make_address (outhost), port), m_IsQuiet (quiet) - { - } - - void BOBI2POutboundTunnel::Start () - { - Accept (); - } - - void BOBI2POutboundTunnel::Stop () - { - ClearHandlers (); - } - - void BOBI2POutboundTunnel::Accept () - { - auto localDestination = GetLocalDestination (); - if (localDestination) - localDestination->AcceptStreams (std::bind (&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1)); - else - LogPrint (eLogError, "BOB: Local destination not set for server tunnel"); - } - - void BOBI2POutboundTunnel::HandleAccept (std::shared_ptr stream) - { - if (stream) - { - auto conn = std::make_shared (this, stream, m_Endpoint, m_IsQuiet); - AddHandler (conn); - conn->Connect (); - } - } - - BOBDestination::BOBDestination (std::shared_ptr localDestination, - const std::string &nickname, const std::string &inhost, const std::string &outhost, - const uint16_t inport, const uint16_t outport, const bool quiet): - m_LocalDestination (localDestination), - m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr), - m_Nickname(nickname), m_InHost(inhost), m_OutHost(outhost), - m_InPort(inport), m_OutPort(outport), m_Quiet(quiet), m_IsRunning(false) - { - } - - BOBDestination::~BOBDestination () - { - delete m_OutboundTunnel; - delete m_InboundTunnel; - i2p::client::context.DeleteLocalDestination (m_LocalDestination); - } - - void BOBDestination::Start () - { - if (m_OutboundTunnel) m_OutboundTunnel->Start (); - if (m_InboundTunnel) m_InboundTunnel->Start (); - m_IsRunning = true; - } - - void BOBDestination::Stop () - { - StopTunnels (); - m_LocalDestination->Stop (); - } - - void BOBDestination::StopTunnels () - { - m_IsRunning = false; - if (m_OutboundTunnel) - { - m_OutboundTunnel->Stop (); - delete m_OutboundTunnel; - m_OutboundTunnel = nullptr; - } - if (m_InboundTunnel) - { - m_InboundTunnel->Stop (); - delete m_InboundTunnel; - m_InboundTunnel = nullptr; - } - } - - void BOBDestination::CreateInboundTunnel (uint16_t port, const std::string& inhost) - { - if (!m_InboundTunnel) - { - // update inport and inhost (user can stop tunnel and change) - m_InPort = port; - m_InHost = inhost; - boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(), port); - if (!inhost.empty ()) - { - boost::system::error_code ec; - auto addr = boost::asio::ip::make_address (inhost, ec); - if (!ec) - ep.address (addr); - else - LogPrint (eLogError, "BOB: ", ec.message ()); - } - m_InboundTunnel = new BOBI2PInboundTunnel (ep, m_LocalDestination); - } - } - - void BOBDestination::CreateOutboundTunnel (const std::string& outhost, uint16_t port, bool quiet) - { - if (!m_OutboundTunnel) - { - // update outport and outhost (user can stop tunnel and change) - m_OutPort = port; - m_OutHost = outhost; - m_OutboundTunnel = new BOBI2POutboundTunnel (outhost, port, m_LocalDestination, quiet); - } - } - - BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner): - m_Owner (owner), m_Socket (m_Owner.GetService ()), - m_ReceiveBuffer(BOB_COMMAND_BUFFER_SIZE + 1), m_SendBuffer(BOB_COMMAND_BUFFER_SIZE + 1), - m_IsOpen (true), m_IsQuiet (false), m_IsActive (false), - m_InPort (0), m_OutPort (0), m_CurrentDestination (nullptr) - { - } - - BOBCommandSession::~BOBCommandSession () - { - } - - void BOBCommandSession::Terminate () - { - m_Socket.close (); - m_IsOpen = false; - } - - void BOBCommandSession::Receive () - { - boost::asio::async_read_until(m_Socket, m_ReceiveBuffer, '\n', - std::bind(&BOBCommandSession::HandleReceivedLine, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } - - void BOBCommandSession::HandleReceivedLine(const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if(ecode) - { - LogPrint (eLogError, "BOB: Command channel read error: ", ecode.message()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - std::string line; - - std::istream is(&m_ReceiveBuffer); - std::getline(is, line); - - std::string command, operand; - std::istringstream iss(line); - iss >> command >> operand; - - // process command - auto& handlers = m_Owner.GetCommandHandlers(); - auto it = handlers.find(command); - if(it != handlers.end()) - { - (this->*(it->second))(operand.c_str(), operand.length()); - } - else - { - LogPrint (eLogError, "BOB: Unknown command ", command.c_str()); - SendReplyError ("unknown command"); - } - } - } - - void BOBCommandSession::Send () - { - boost::asio::async_write (m_Socket, m_SendBuffer, - boost::asio::transfer_all (), - std::bind(&BOBCommandSession::HandleSent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void BOBCommandSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "BOB: Command channel send error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - if (m_IsOpen) - Receive (); - else - Terminate (); - } - } - - void BOBCommandSession::SendReplyOK (const char * msg) - { - std::ostream os(&m_SendBuffer); - os << "OK"; - if(msg) - { - os << " " << msg; - } - os << std::endl; - Send (); - } - - void BOBCommandSession::SendReplyError (const char * msg) - { - std::ostream os(&m_SendBuffer); - os << "ERROR " << msg << std::endl; - Send (); - } - - void BOBCommandSession::SendVersion () - { - std::ostream os(&m_SendBuffer); - os << "BOB 00.00.10" << std::endl; - SendReplyOK(); - } - - void BOBCommandSession::SendRaw (const char * data) - { - std::ostream os(&m_SendBuffer); - os << data << std::endl; - } - - void BOBCommandSession::BuildStatusLine(bool currentTunnel, std::shared_ptr dest, std::string &out) - { - // helper lambdas - const auto issetStr = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost - const auto issetNum = [&issetStr](const int p) { return issetStr(p == 0 ? "" : std::to_string(p)); }; // for inport, outport - const auto destExists = [](const BOBDestination * const dest) { return dest != nullptr; }; - const auto destReady = [](const BOBDestination * const dest) { return dest && dest->IsRunning(); }; - const auto bool_str = [](const bool v) { return v ? "true" : "false"; }; // bool -> str - - // tunnel info - const std::string nickname = currentTunnel ? m_Nickname : dest->GetNickname(); - const bool quiet = currentTunnel ? m_IsQuiet : dest->GetQuiet(); - const std::string inhost = issetStr(currentTunnel ? m_InHost : dest->GetInHost()); - const std::string outhost = issetStr(currentTunnel ? m_OutHost : dest->GetOutHost()); - const std::string inport = issetNum(currentTunnel ? m_InPort : dest->GetInPort()); - const std::string outport = issetNum(currentTunnel ? m_OutPort : dest->GetOutPort()); - const bool keys = destExists(dest.get ()); // key must exist when destination is created - const bool starting = destExists(dest.get ()) && !destReady(dest.get ()); - const bool running = destExists(dest.get ()) && destReady(dest.get ()); - const bool stopping = false; - - // build line - std::stringstream ss; - ss << "DATA " - << "NICKNAME: " << nickname << " " << "STARTING: " << bool_str(starting) << " " - << "RUNNING: " << bool_str(running) << " " << "STOPPING: " << bool_str(stopping) << " " - << "KEYS: " << bool_str(keys) << " " << "QUIET: " << bool_str(quiet) << " " - << "INPORT: " << inport << " " << "INHOST: " << inhost << " " - << "OUTPORT: " << outport << " " << "OUTHOST: " << outhost; - out = ss.str(); - } - - void BOBCommandSession::ZapCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: zap"); - Terminate (); - } - - void BOBCommandSession::QuitCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: quit"); - m_IsOpen = false; - SendReplyOK ("Bye!"); - } - - void BOBCommandSession::StartCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: start ", m_Nickname); - if (m_IsActive) - { - SendReplyError ("tunnel is active"); - return; - } - if (!m_Keys.GetPublic ()) // keys are set ? - { - SendReplyError("Keys must be set."); - return; - } - if (m_InPort == 0 - && m_OutHost.empty() && m_OutPort == 0) - { - SendReplyError("(inhost):inport or outhost:outport must be set."); - return; - } - if(!m_InHost.empty()) - { - // TODO: FIXME: temporary validation, until hostname support is added - boost::system::error_code ec; - boost::asio::ip::make_address(m_InHost, ec); - if (ec) - { - SendReplyError("inhost must be a valid IPv4 address."); - return; - } - } - if(!m_OutHost.empty()) - { - // TODO: FIXME: temporary validation, until hostname support is added - boost::system::error_code ec; - boost::asio::ip::make_address(m_OutHost, ec); - if (ec) - { - SendReplyError("outhost must be a IPv4 address."); - return; - } - } - - if (!m_CurrentDestination) - { - m_CurrentDestination = std::make_shared (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options), // deleted in clear command - m_Nickname, m_InHost, m_OutHost, m_InPort, m_OutPort, m_IsQuiet); - m_Owner.AddDestination (m_Nickname, m_CurrentDestination); - } - if (m_InPort) - m_CurrentDestination->CreateInboundTunnel (m_InPort, m_InHost); - if (m_OutPort && !m_OutHost.empty ()) - m_CurrentDestination->CreateOutboundTunnel (m_OutHost, m_OutPort, m_IsQuiet); - m_CurrentDestination->Start (); - SendReplyOK ("Tunnel starting"); - m_IsActive = true; - } - - void BOBCommandSession::StopCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: stop ", m_Nickname); - if (!m_IsActive) - { - SendReplyError ("tunnel is inactive"); - return; - } - auto dest = m_Owner.FindDestination (m_Nickname); - if (dest) - { - dest->StopTunnels (); - SendReplyOK ("Tunnel stopping"); - } - else - SendReplyError ("tunnel not found"); - m_IsActive = false; - } - - void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: setnick ", operand); - if(*operand) - { - auto dest = m_Owner.FindDestination (operand); - if (!dest) - { - m_Nickname = operand; - std::string msg ("Nickname set to "); - msg += m_Nickname; - SendReplyOK (msg.c_str ()); - } - else - SendReplyError ("tunnel is active"); - } - else - SendReplyError ("no nickname has been set"); - } - - void BOBCommandSession::GetNickCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: getnick ", operand); - if(*operand) - { - m_CurrentDestination = m_Owner.FindDestination (operand); - if (m_CurrentDestination) - { - m_Keys = m_CurrentDestination->GetKeys (); - m_IsActive = m_CurrentDestination->IsRunning (); - m_Nickname = operand; - } - if (m_Nickname == operand) - { - std::string msg ("Nickname set to "); - msg += m_Nickname; - SendReplyOK (msg.c_str ()); - } - else - SendReplyError ("no nickname has been set"); - } - else - SendReplyError ("no nickname has been set"); - } - - void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: newkeys"); - i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; - if (*operand) - { - try - { - char * operand1 = (char *)strchr (operand, ' '); - if (operand1) - { - *operand1 = 0; operand1++; - cryptoType = std::stoi(operand1); - } - signatureType = std::stoi(operand); - } - catch (std::invalid_argument& ex) - { - LogPrint (eLogWarning, "BOB: Error on newkeys: ", ex.what ()); - } - } - - - m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType, true); - SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ()); - } - - void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: setkeys ", operand); - if (*operand && m_Keys.FromBase64 (operand)) - SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ()); - else - SendReplyError ("invalid keys"); - } - - void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: getkeys"); - if (m_Keys.GetPublic ()) // keys are set ? - SendReplyOK (m_Keys.ToBase64 ().c_str ()); - else - SendReplyError ("keys are not set"); - } - - void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: getdest"); - if (m_Keys.GetPublic ()) // keys are set ? - SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ()); - else - SendReplyError ("keys are not set"); - } - - void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: outhost ", operand); - if (*operand) - { - m_OutHost = operand; - SendReplyOK ("outhost set"); - } - else - SendReplyError ("empty outhost"); - } - - void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: outport ", operand); - if (*operand) - { - int port = std::stoi(operand); - if (port >= 0 && port < 65536) - { - m_OutPort = port; - SendReplyOK ("outbound port set"); - } - else - SendReplyError ("port out of range"); - } - else - SendReplyError ("empty outport"); - } - - void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: inhost ", operand); - if (*operand) - { - m_InHost = operand; - SendReplyOK ("inhost set"); - } - else - SendReplyError ("empty inhost"); - } - - void BOBCommandSession::InportCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: inport ", operand); - if (*operand) - { - int port = std::stoi(operand); - if (port >= 0 && port < 65536) - { - m_InPort = port; - SendReplyOK ("inbound port set"); - } - else - SendReplyError ("port out of range"); - } - else - SendReplyError ("empty inport"); - } - - void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: quiet"); - if (m_Nickname.length () > 0) - { - if (!m_IsActive) - { - m_IsQuiet = true; - SendReplyOK ("Quiet set"); - } - else - SendReplyError ("tunnel is active"); - } - else - SendReplyError ("no nickname has been set"); - } - - void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: lookup ", operand); - if (*operand) - { - auto addr = context.GetAddressBook ().GetAddress (operand); - if (!addr) - { - SendReplyError ("Address Not found"); - return; - } - auto localDestination = (m_CurrentDestination && m_CurrentDestination->IsRunning ()) ? - m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination (); - if (!localDestination) - { - SendReplyError ("No local destination"); - return; - } - if (addr->IsIdentHash ()) - { - // we might have leaseset already - auto leaseSet = localDestination->FindLeaseSet (addr->identHash); - if (leaseSet) - { - SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ()); - return; - } - } - // trying to request - auto s = shared_from_this (); - auto requstCallback = [s](std::shared_ptr ls) - { - if (ls) - s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); - else - s->SendReplyError ("LeaseSet Not found"); - }; - if (addr->IsIdentHash ()) - localDestination->RequestDestination (addr->identHash, requstCallback); - else - localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback); - } - else - SendReplyError ("empty lookup address"); - } - - void BOBCommandSession::LookupLocalCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: lookup local ", operand); - if (*operand) - { - auto addr = context.GetAddressBook ().GetAddress (operand); - if (!addr) - { - SendReplyError ("Address Not found"); - return; - } - auto ls = i2p::data::netdb.FindLeaseSet (addr->identHash); - if (ls) - SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ()); - else - SendReplyError ("Local LeaseSet Not found"); - } - else - SendReplyError ("empty lookup address"); - } - - void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: clear"); - m_Owner.DeleteDestination (m_Nickname); - m_Nickname = ""; - SendReplyOK ("cleared"); - } - - void BOBCommandSession::ListCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: list"); - std::string statusLine; - bool sentCurrent = false; - const auto& destinations = m_Owner.GetDestinations (); - for (const auto& it: destinations) - { - BuildStatusLine(false, it.second, statusLine); - SendRaw(statusLine.c_str()); - if(m_Nickname.compare(it.second->GetNickname()) == 0) - sentCurrent = true; - } - if(!sentCurrent && !m_Nickname.empty()) - { - // add the current tunnel to the list. - // this is for the incomplete tunnel which has not been started yet. - BuildStatusLine(true, m_CurrentDestination, statusLine); - SendRaw(statusLine.c_str()); - } - SendReplyOK ("Listing done"); - } - - void BOBCommandSession::OptionCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: option ", operand); - const char * value = strchr (operand, '='); - if (value) - { - std::string msg ("option "); - *(const_cast(value)) = 0; - m_Options[operand] = value + 1; - msg += operand; - *(const_cast(value)) = '='; - msg += " set to "; - msg += value + 1; - SendReplyOK (msg.c_str ()); - } - else - SendReplyError ("malformed"); - } - - void BOBCommandSession::StatusCommandHandler (const char * operand, size_t len) - { - LogPrint (eLogDebug, "BOB: status ", operand); - const std::string name = operand; - std::string statusLine; - - // always prefer destination - auto dest = m_Owner.FindDestination(name); - if(dest) - { - // tunnel destination exists - BuildStatusLine(false, dest, statusLine); - SendReplyOK(statusLine.c_str()); - } - else - { - if(m_Nickname == name && !name.empty()) - { - // tunnel is incomplete / has not been started yet - BuildStatusLine(true, nullptr, statusLine); - SendReplyOK(statusLine.c_str()); - } - else - { - SendReplyError("no nickname has been set"); - } - } - } - void BOBCommandSession::HelpCommandHandler (const char * operand, size_t len) - { - auto helpStrings = m_Owner.GetHelpStrings(); - if(!*operand) - { - std::stringstream ss; - ss << "COMMANDS:"; - for (auto const& x : helpStrings) - { - ss << " " << x.first; - } - const std::string &str = ss.str(); - SendReplyOK(str.c_str()); - } - else - { - auto it = helpStrings.find(operand); - if (it != helpStrings.end ()) - { - SendReplyOK(it->second.c_str()); - return; - } - SendReplyError("No such command"); - } - } - - BOBCommandChannel::BOBCommandChannel (const std::string& address, uint16_t port): - RunnableService ("BOB"), - m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port)) - { - // command -> handler - m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler; - m_CommandHandlers[BOB_COMMAND_QUIT] = &BOBCommandSession::QuitCommandHandler; - m_CommandHandlers[BOB_COMMAND_START] = &BOBCommandSession::StartCommandHandler; - m_CommandHandlers[BOB_COMMAND_STOP] = &BOBCommandSession::StopCommandHandler; - m_CommandHandlers[BOB_COMMAND_SETNICK] = &BOBCommandSession::SetNickCommandHandler; - m_CommandHandlers[BOB_COMMAND_GETNICK] = &BOBCommandSession::GetNickCommandHandler; - m_CommandHandlers[BOB_COMMAND_NEWKEYS] = &BOBCommandSession::NewkeysCommandHandler; - m_CommandHandlers[BOB_COMMAND_GETKEYS] = &BOBCommandSession::GetkeysCommandHandler; - m_CommandHandlers[BOB_COMMAND_SETKEYS] = &BOBCommandSession::SetkeysCommandHandler; - m_CommandHandlers[BOB_COMMAND_GETDEST] = &BOBCommandSession::GetdestCommandHandler; - m_CommandHandlers[BOB_COMMAND_OUTHOST] = &BOBCommandSession::OuthostCommandHandler; - m_CommandHandlers[BOB_COMMAND_OUTPORT] = &BOBCommandSession::OutportCommandHandler; - m_CommandHandlers[BOB_COMMAND_INHOST] = &BOBCommandSession::InhostCommandHandler; - m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler; - m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler; - m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler; - m_CommandHandlers[BOB_COMMAND_LOOKUP_LOCAL] = &BOBCommandSession::LookupLocalCommandHandler; - m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler; - m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler; - m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler; - m_CommandHandlers[BOB_COMMAND_STATUS] = &BOBCommandSession::StatusCommandHandler; - m_CommandHandlers[BOB_COMMAND_HELP] = &BOBCommandSession::HelpCommandHandler; - // command -> help string - m_HelpStrings[BOB_COMMAND_ZAP] = BOB_HELP_ZAP; - m_HelpStrings[BOB_COMMAND_QUIT] = BOB_HELP_QUIT; - m_HelpStrings[BOB_COMMAND_START] = BOB_HELP_START; - m_HelpStrings[BOB_COMMAND_STOP] = BOB_HELP_STOP; - m_HelpStrings[BOB_COMMAND_SETNICK] = BOB_HELP_SETNICK; - m_HelpStrings[BOB_COMMAND_GETNICK] = BOB_HELP_GETNICK; - m_HelpStrings[BOB_COMMAND_NEWKEYS] = BOB_HELP_NEWKEYS; - m_HelpStrings[BOB_COMMAND_GETKEYS] = BOB_HELP_GETKEYS; - m_HelpStrings[BOB_COMMAND_SETKEYS] = BOB_HELP_SETKEYS; - m_HelpStrings[BOB_COMMAND_GETDEST] = BOB_HELP_GETDEST; - m_HelpStrings[BOB_COMMAND_OUTHOST] = BOB_HELP_OUTHOST; - m_HelpStrings[BOB_COMMAND_OUTPORT] = BOB_HELP_OUTPORT; - m_HelpStrings[BOB_COMMAND_INHOST] = BOB_HELP_INHOST; - m_HelpStrings[BOB_COMMAND_INPORT] = BOB_HELP_INPORT; - m_HelpStrings[BOB_COMMAND_QUIET] = BOB_HELP_QUIET; - m_HelpStrings[BOB_COMMAND_LOOKUP] = BOB_HELP_LOOKUP; - m_HelpStrings[BOB_COMMAND_CLEAR] = BOB_HELP_CLEAR; - m_HelpStrings[BOB_COMMAND_LIST] = BOB_HELP_LIST; - m_HelpStrings[BOB_COMMAND_OPTION] = BOB_HELP_OPTION; - m_HelpStrings[BOB_COMMAND_STATUS] = BOB_HELP_STATUS; - m_HelpStrings[BOB_COMMAND_HELP] = BOB_HELP_HELP; - } - - BOBCommandChannel::~BOBCommandChannel () - { - if (IsRunning ()) - Stop (); - } - - void BOBCommandChannel::Start () - { - Accept (); - StartIOService (); - } - - void BOBCommandChannel::Stop () - { - for (auto& it: m_Destinations) - it.second->Stop (); - m_Acceptor.cancel (); - StopIOService (); - } - - void BOBCommandChannel::AddDestination (const std::string& name, std::shared_ptr dest) - { - m_Destinations.emplace (name, dest); - } - - void BOBCommandChannel::DeleteDestination (const std::string& name) - { - auto it = m_Destinations.find (name); - if (it != m_Destinations.end ()) - { - it->second->Stop (); - m_Destinations.erase (it); - } - } - - std::shared_ptr BOBCommandChannel::FindDestination (const std::string& name) - { - auto it = m_Destinations.find (name); - if (it != m_Destinations.end ()) - return it->second; - return nullptr; - } - - void BOBCommandChannel::Accept () - { - auto newSession = std::make_shared (*this); - m_Acceptor.async_accept (newSession->GetSocket (), std::bind (&BOBCommandChannel::HandleAccept, this, - std::placeholders::_1, newSession)); - } - - void BOBCommandChannel::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session) - { - if (ecode != boost::asio::error::operation_aborted) - Accept (); - - if (!ecode) - { - LogPrint (eLogInfo, "BOB: New command connection from ", session->GetSocket ().remote_endpoint ()); - session->SendVersion (); - } - else - LogPrint (eLogError, "BOB: Accept error: ", ecode.message ()); - } -} -} diff --git a/libi2pd_client/BOB.h b/libi2pd_client/BOB.h deleted file mode 100644 index f5aefd0a..00000000 --- a/libi2pd_client/BOB.h +++ /dev/null @@ -1,300 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 BOB_H__ -#define BOB_H__ - -#include -#include -#include -#include -#include -#include -#include "util.h" -#include "I2PTunnel.h" -#include "I2PService.h" -#include "Identity.h" -#include "LeaseSet.h" - -namespace i2p -{ -namespace client -{ - const size_t BOB_COMMAND_BUFFER_SIZE = 1024; - const char BOB_COMMAND_ZAP[] = "zap"; - const char BOB_COMMAND_QUIT[] = "quit"; - const char BOB_COMMAND_START[] = "start"; - const char BOB_COMMAND_STOP[] = "stop"; - const char BOB_COMMAND_SETNICK[] = "setnick"; - const char BOB_COMMAND_GETNICK[] = "getnick"; - const char BOB_COMMAND_NEWKEYS[] = "newkeys"; - const char BOB_COMMAND_GETKEYS[] = "getkeys"; - const char BOB_COMMAND_SETKEYS[] = "setkeys"; - const char BOB_COMMAND_GETDEST[] = "getdest"; - const char BOB_COMMAND_OUTHOST[] = "outhost"; - const char BOB_COMMAND_OUTPORT[] = "outport"; - const char BOB_COMMAND_INHOST[] = "inhost"; - const char BOB_COMMAND_INPORT[] = "inport"; - const char BOB_COMMAND_QUIET[] = "quiet"; - const char BOB_COMMAND_LOOKUP[] = "lookup"; - const char BOB_COMMAND_LOOKUP_LOCAL[] = "lookuplocal"; - const char BOB_COMMAND_CLEAR[] = "clear"; - const char BOB_COMMAND_LIST[] = "list"; - const char BOB_COMMAND_OPTION[] = "option"; - const char BOB_COMMAND_STATUS[] = "status"; - const char BOB_COMMAND_HELP[] = "help"; - - const char BOB_HELP_ZAP[] = "zap - Shuts down BOB."; - const char BOB_HELP_QUIT[] = "quit - Quits this session with BOB."; - const char BOB_HELP_START[] = "start - Starts the current nicknamed tunnel."; - const char BOB_HELP_STOP[] = "stop - Stops the current nicknamed tunnel."; - const char BOB_HELP_SETNICK[] = "setnick - Creates a new nickname."; - const char BOB_HELP_GETNICK[] = "getnick - Sets the nickname from the database."; - const char BOB_HELP_NEWKEYS[] = "newkeys - Generate a new keypair for the current nickname."; - const char BOB_HELP_GETKEYS[] = "getkeys - Return the keypair for the current nickname."; - const char BOB_HELP_SETKEYS[] = "setkeys - Sets the keypair for the current nickname."; - const char BOB_HELP_GETDEST[] = "getdest - Return the destination for the current nickname."; - const char BOB_HELP_OUTHOST[] = "outhost - Set the outhound hostname or IP."; - const char BOB_HELP_OUTPORT[] = "outport - Set the outbound port that nickname contacts."; - const char BOB_HELP_INHOST[] = "inhost - Set the inbound hostname or IP."; - const char BOB_HELP_INPORT[] = "inport - Set the inbound port number nickname listens on."; - const char BOB_HELP_QUIET[] = "quiet - Whether to send the incoming destination."; - const char BOB_HELP_LOOKUP[] = "lookup - Look up an I2P hostname."; - const char BOB_HELP_CLEAR[] = "clear - Clear the current nickname out of the list."; - const char BOB_HELP_LIST[] = "list - List all tunnels."; - const char BOB_HELP_OPTION[] = "option = - Set an option. NOTE: Don't use any spaces."; - const char BOB_HELP_STATUS[] = "status - Display status of a nicknamed tunnel."; - const char BOB_HELP_HELP [] = "help - Get help on a command."; - - class BOBI2PTunnelIncomingConnection: public I2PTunnelConnection - { - public: - - BOBI2PTunnelIncomingConnection (I2PService * owner, std::shared_ptr stream, - const boost::asio::ip::tcp::endpoint& target, bool quiet): - I2PTunnelConnection (owner, stream, target), m_IsQuiet (quiet) {}; - - protected: - - void Established () override; - - private: - - bool m_IsQuiet; // don't send destination - }; - - class BOBI2PTunnel: public I2PService - { - public: - - BOBI2PTunnel (std::shared_ptr localDestination): - I2PService (localDestination) {}; - - virtual void Start () {}; - virtual void Stop () {}; - }; - - class BOBI2PInboundTunnel: public BOBI2PTunnel - { - struct AddressReceiver - { - std::shared_ptr socket; - char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address - uint8_t * data; // pointer to buffer - size_t dataLen, bufferOffset; - - AddressReceiver (): data (nullptr), dataLen (0), bufferOffset (0) {}; - }; - - public: - - BOBI2PInboundTunnel (const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr localDestination); - ~BOBI2PInboundTunnel (); - - void Start (); - void Stop (); - - private: - - void Accept (); - void HandleAccept (const boost::system::error_code& ecode, std::shared_ptr receiver); - - void ReceiveAddress (std::shared_ptr receiver); - void HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred, - std::shared_ptr receiver); - - void HandleDestinationRequestComplete (std::shared_ptr leaseSet, std::shared_ptr receiver); - - void CreateConnection (std::shared_ptr receiver, std::shared_ptr leaseSet); - - private: - - boost::asio::ip::tcp::acceptor m_Acceptor; - }; - - class BOBI2POutboundTunnel: public BOBI2PTunnel - { - public: - - BOBI2POutboundTunnel (const std::string& outhost, uint16_t port, std::shared_ptr localDestination, bool quiet); - - void Start (); - void Stop (); - - void SetQuiet () { m_IsQuiet = true; }; - - private: - - void Accept (); - void HandleAccept (std::shared_ptr stream); - - private: - - boost::asio::ip::tcp::endpoint m_Endpoint; - bool m_IsQuiet; - }; - - - class BOBDestination - { - public: - - BOBDestination (std::shared_ptr localDestination, - const std::string &nickname, const std::string &inhost, const std::string &outhost, - const uint16_t inport, const uint16_t outport, const bool quiet); - ~BOBDestination (); - - void Start (); - void Stop (); - void StopTunnels (); - void CreateInboundTunnel (uint16_t port, const std::string& inhost); - void CreateOutboundTunnel (const std::string& outhost, uint16_t port, bool quiet); - const std::string& GetNickname() const { return m_Nickname; } - const std::string& GetInHost() const { return m_InHost; } - const std::string& GetOutHost() const { return m_OutHost; } - uint16_t GetInPort() const { return m_InPort; } - uint16_t GetOutPort() const { return m_OutPort; } - bool GetQuiet() const { return m_Quiet; } - bool IsRunning() const { return m_IsRunning; } - const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); }; - std::shared_ptr GetLocalDestination () const { return m_LocalDestination; }; - - private: - - std::shared_ptr m_LocalDestination; - BOBI2POutboundTunnel * m_OutboundTunnel; - BOBI2PInboundTunnel * m_InboundTunnel; - - std::string m_Nickname; - std::string m_InHost, m_OutHost; - uint16_t m_InPort, m_OutPort; - bool m_Quiet; - bool m_IsRunning; - }; - - class BOBCommandChannel; - class BOBCommandSession: public std::enable_shared_from_this - { - public: - - BOBCommandSession (BOBCommandChannel& owner); - ~BOBCommandSession (); - void Terminate (); - - boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; - void SendVersion (); - - // command handlers - void ZapCommandHandler (const char * operand, size_t len); - void QuitCommandHandler (const char * operand, size_t len); - void StartCommandHandler (const char * operand, size_t len); - void StopCommandHandler (const char * operand, size_t len); - void SetNickCommandHandler (const char * operand, size_t len); - void GetNickCommandHandler (const char * operand, size_t len); - void NewkeysCommandHandler (const char * operand, size_t len); - void SetkeysCommandHandler (const char * operand, size_t len); - void GetkeysCommandHandler (const char * operand, size_t len); - void GetdestCommandHandler (const char * operand, size_t len); - void OuthostCommandHandler (const char * operand, size_t len); - void OutportCommandHandler (const char * operand, size_t len); - void InhostCommandHandler (const char * operand, size_t len); - void InportCommandHandler (const char * operand, size_t len); - void QuietCommandHandler (const char * operand, size_t len); - void LookupCommandHandler (const char * operand, size_t len); - void LookupLocalCommandHandler (const char * operand, size_t len); - void ClearCommandHandler (const char * operand, size_t len); - void ListCommandHandler (const char * operand, size_t len); - void OptionCommandHandler (const char * operand, size_t len); - void StatusCommandHandler (const char * operand, size_t len); - void HelpCommandHandler (const char * operand, size_t len); - - private: - - void Receive (); - void HandleReceivedLine(const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - - void Send (); - void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void SendReplyOK (const char * msg = nullptr); - void SendReplyError (const char * msg); - void SendRaw (const char * data); - - void BuildStatusLine(bool currentTunnel, std::shared_ptr destination, std::string &out); - - private: - - BOBCommandChannel& m_Owner; - boost::asio::ip::tcp::socket m_Socket; - boost::asio::streambuf m_ReceiveBuffer, m_SendBuffer; - bool m_IsOpen, m_IsQuiet, m_IsActive; - std::string m_Nickname, m_InHost, m_OutHost; - uint16_t m_InPort, m_OutPort; - i2p::data::PrivateKeys m_Keys; - std::map m_Options; - std::shared_ptr m_CurrentDestination; - }; - typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len); - - class BOBCommandChannel: private i2p::util::RunnableService - { - public: - - BOBCommandChannel (const std::string& address, uint16_t port); - ~BOBCommandChannel (); - - void Start (); - void Stop (); - - auto& GetService () { return GetIOService (); }; - void AddDestination (const std::string& name, std::shared_ptr dest); - void DeleteDestination (const std::string& name); - std::shared_ptr FindDestination (const std::string& name); - - private: - - void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr session); - - private: - - boost::asio::ip::tcp::acceptor m_Acceptor; - std::map > m_Destinations; - std::map m_CommandHandlers; - std::map m_HelpStrings; - - public: - - const decltype(m_CommandHandlers)& GetCommandHandlers () const { return m_CommandHandlers; }; - const decltype(m_HelpStrings)& GetHelpStrings () const { return m_HelpStrings; }; - const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; - }; -} -} - -#endif diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp deleted file mode 100644 index d26e33ab..00000000 --- a/libi2pd_client/ClientContext.cpp +++ /dev/null @@ -1,1070 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include -#include -#include "Config.h" -#include "FS.h" -#include "Log.h" -#include "Identity.h" -#include "util.h" -#include "ClientContext.h" -#include "HTTPProxy.h" -#include "SOCKS.h" -#include "MatchedDestination.h" - -namespace i2p -{ -namespace client -{ - ClientContext context; - - ClientContext::ClientContext (): m_SharedLocalDestination (nullptr), - m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr), - m_BOBCommandChannel (nullptr), m_I2CPServer (nullptr) - { - } - - ClientContext::~ClientContext () - { - delete m_HttpProxy; - delete m_SocksProxy; - delete m_SamBridge; - delete m_BOBCommandChannel; - delete m_I2CPServer; - } - - void ClientContext::Start () - { - // shared local destination - if (!m_SharedLocalDestination) - CreateNewSharedLocalDestination (); - - // addressbook - m_AddressBook.Start (); - - // HTTP proxy - ReadHttpProxy (); - - // SOCKS proxy - ReadSocksProxy (); - - // I2P tunnels - ReadTunnels (); - - // SAM - bool sam; i2p::config::GetOption("sam.enabled", sam); - if (sam) - { - std::string samAddr; i2p::config::GetOption("sam.address", samAddr); - uint16_t samPortTCP; i2p::config::GetOption("sam.port", samPortTCP); - uint16_t samPortUDP; i2p::config::GetOption("sam.portudp", samPortUDP); - bool singleThread; i2p::config::GetOption("sam.singlethread", singleThread); - LogPrint(eLogInfo, "Clients: Starting SAM bridge at ", samAddr, ":[", samPortTCP, "|", samPortUDP, "]"); - try - { - m_SamBridge = new SAMBridge (samAddr, samPortTCP, samPortUDP, singleThread); - m_SamBridge->Start (); - } - catch (std::exception& e) - { - LogPrint(eLogCritical, "Clients: Exception in SAM bridge: ", e.what()); - ThrowFatal ("Unable to start SAM bridge at ", samAddr, ":[", samPortTCP, "|", samPortUDP,"]: ", e.what ()); - } - } - - // BOB - bool bob; i2p::config::GetOption("bob.enabled", bob); - if (bob) { - std::string bobAddr; i2p::config::GetOption("bob.address", bobAddr); - uint16_t bobPort; i2p::config::GetOption("bob.port", bobPort); - LogPrint(eLogInfo, "Clients: Starting BOB command channel at ", bobAddr, ":", bobPort); - try - { - m_BOBCommandChannel = new BOBCommandChannel (bobAddr, bobPort); - m_BOBCommandChannel->Start (); - } - catch (std::exception& e) - { - LogPrint(eLogCritical, "Clients: Exception in BOB bridge: ", e.what()); - ThrowFatal ("Unable to start BOB bridge at ", bobAddr, ":", bobPort, ": ", e.what ()); - } - } - - // I2CP - bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp); - if (i2cp) - { - std::string i2cpAddr; i2p::config::GetOption("i2cp.address", i2cpAddr); - uint16_t i2cpPort; i2p::config::GetOption("i2cp.port", i2cpPort); - bool singleThread; i2p::config::GetOption("i2cp.singlethread", singleThread); - LogPrint(eLogInfo, "Clients: Starting I2CP at ", i2cpAddr, ":", i2cpPort); - try - { - m_I2CPServer = new I2CPServer (i2cpAddr, i2cpPort, singleThread); - m_I2CPServer->Start (); - } - catch (std::exception& e) - { - LogPrint(eLogCritical, "Clients: Exception in I2CP: ", e.what()); - ThrowFatal ("Unable to start I2CP at ", i2cpAddr, ":", i2cpPort, ": ", e.what ()); - } - } - - m_AddressBook.StartResolvers (); - - // start UDP cleanup - if (!m_ServerForwards.empty ()) - { - m_CleanupUDPTimer.reset (new boost::asio::deadline_timer(m_SharedLocalDestination->GetService ())); - ScheduleCleanupUDP(); - } - } - - void ClientContext::Stop () - { - if (m_HttpProxy) - { - LogPrint(eLogInfo, "Clients: Stopping HTTP Proxy"); - m_HttpProxy->Stop(); - delete m_HttpProxy; - m_HttpProxy = nullptr; - } - - if (m_SocksProxy) - { - LogPrint(eLogInfo, "Clients: Stopping SOCKS Proxy"); - m_SocksProxy->Stop(); - delete m_SocksProxy; - m_SocksProxy = nullptr; - } - - for (auto& it: m_ClientTunnels) - { - LogPrint(eLogInfo, "Clients: Stopping I2P client tunnel on port ", it.first); - it.second->Stop (); - } - m_ClientTunnels.clear (); - - for (auto& it: m_ServerTunnels) - { - LogPrint(eLogInfo, "Clients: Stopping I2P server tunnel"); - it.second->Stop (); - } - m_ServerTunnels.clear (); - - if (m_SamBridge) - { - LogPrint(eLogInfo, "Clients: Stopping SAM bridge"); - m_SamBridge->Stop (); - delete m_SamBridge; - m_SamBridge = nullptr; - } - - if (m_BOBCommandChannel) - { - LogPrint(eLogInfo, "Clients: Stopping BOB command channel"); - m_BOBCommandChannel->Stop (); - delete m_BOBCommandChannel; - m_BOBCommandChannel = nullptr; - } - - if (m_I2CPServer) - { - LogPrint(eLogInfo, "Clients: Stopping I2CP"); - m_I2CPServer->Stop (); - delete m_I2CPServer; - m_I2CPServer = nullptr; - } - - LogPrint(eLogInfo, "Clients: Stopping AddressBook"); - m_AddressBook.Stop (); - - LogPrint(eLogInfo, "Clients: Stopping UDP Tunnels"); - { - std::lock_guard lock(m_ForwardsMutex); - m_ServerForwards.clear(); - m_ClientForwards.clear(); - } - - LogPrint(eLogInfo, "Clients: Stopping UDP Tunnels timers"); - if (m_CleanupUDPTimer) - { - m_CleanupUDPTimer->cancel (); - m_CleanupUDPTimer = nullptr; - } - - { - LogPrint(eLogInfo, "Clients: Stopping Destinations"); - std::lock_guard lock(m_DestinationsMutex); - for (auto& it: m_Destinations) - it.second->Stop (); - LogPrint(eLogInfo, "Clients: Stopping Destinations - Clear"); - m_Destinations.clear (); - } - - LogPrint(eLogInfo, "Clients: Stopping SharedLocalDestination"); - m_SharedLocalDestination->Release (); - m_SharedLocalDestination = nullptr; - } - - void ClientContext::ReloadConfig () - { - // TODO: handle config changes - /*std::string config; i2p::config::GetOption("conf", config); - i2p::config::ParseConfig(config);*/ - - // change shared local destination - m_SharedLocalDestination->Release (); - CreateNewSharedLocalDestination (); - - // recreate HTTP proxy - if (m_HttpProxy) - { - m_HttpProxy->Stop (); - delete m_HttpProxy; - m_HttpProxy = nullptr; - } - ReadHttpProxy (); - - // recreate SOCKS proxy - if (m_SocksProxy) - { - m_SocksProxy->Stop (); - delete m_SocksProxy; - m_SocksProxy = nullptr; - } - ReadSocksProxy (); - - // handle tunnels - // reset isUpdated for each tunnel - VisitTunnels (false); - // reload tunnels - ReadTunnels(); - // delete not updated tunnels (not in config anymore) - VisitTunnels (true); - - // delete unused destinations - std::unique_lock l(m_DestinationsMutex); - for (auto it = m_Destinations.begin (); it != m_Destinations.end ();) - { - auto dest = it->second; - if (dest->GetRefCounter () > 0) ++it; // skip - else - { - dest->Stop (); - it = m_Destinations.erase (it); - } - } - } - - bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, std::string_view filename, - i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType) - { -#if __cplusplus >= 202002L // C++20 - if (filename.starts_with ("transient")) -#else - std::string_view transient("transient"); - if (!filename.compare (0, transient.length (), transient)) // starts with transient -#endif - { - keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true); - LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created"); - return true; - } - - bool success = true; - std::string fullPath = i2p::fs::DataDirPath (filename); - std::ifstream s(fullPath, std::ifstream::binary); - if (s.is_open ()) - { - s.seekg (0, std::ios::end); - size_t len = s.tellg(); - s.seekg (0, std::ios::beg); - uint8_t * buf = new uint8_t[len]; - s.read ((char *)buf, len); - if(!keys.FromBuffer (buf, len)) - { - LogPrint (eLogCritical, "Clients: Failed to load keyfile ", filename); - success = false; - } - else - LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded"); - delete[] buf; - } - else - { - LogPrint (eLogInfo, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType); - keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true); - std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); - size_t len = keys.GetFullLen (); - uint8_t * buf = new uint8_t[len]; - len = keys.ToBuffer (buf, len); - f.write ((char *)buf, len); - delete[] buf; - - LogPrint (eLogInfo, "Clients: New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created"); - } - return success; - } - - std::vector > ClientContext::GetForwardInfosFor(const i2p::data::IdentHash & destination) - { - std::vector > infos; - std::lock_guard lock(m_ForwardsMutex); - for(const auto & c : m_ClientForwards) - { - if (c.second->IsLocalDestination(destination)) - { - for (auto & i : c.second->GetSessions()) infos.push_back(i); - break; - } - } - for(const auto & s : m_ServerForwards) - { - if(std::get<0>(s.first) == destination) - { - for( auto & i : s.second->GetSessions()) infos.push_back(i); - break; - } - } - return infos; - } - - std::shared_ptr ClientContext::CreateNewLocalDestination (bool isPublic, - i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType, - const std::map * params) - { - i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true); - auto localDestination = std::make_shared (keys, isPublic, params); - AddLocalDestination (localDestination); - return localDestination; - } - - std::shared_ptr ClientContext::CreateNewLocalDestination ( - boost::asio::io_context& service, bool isPublic, - i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType, - const std::map * params) - { - i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true); - auto localDestination = std::make_shared (service, keys, isPublic, params); - AddLocalDestination (localDestination); - return localDestination; - } - - std::shared_ptr ClientContext::CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, const std::string & name, const std::map * params) - { - auto localDestination = std::make_shared(keys, name, params); - AddLocalDestination (localDestination); - return localDestination; - } - - void ClientContext::AddLocalDestination (std::shared_ptr localDestination) - { - std::unique_lock l(m_DestinationsMutex); - m_Destinations[localDestination->GetIdentHash ()] = localDestination; - localDestination->Start (); - } - - void ClientContext::DeleteLocalDestination (std::shared_ptr destination) - { - if (!destination) return; - auto it = m_Destinations.find (destination->GetIdentHash ()); - if (it != m_Destinations.end ()) - { - auto d = it->second; - { - std::unique_lock l(m_DestinationsMutex); - m_Destinations.erase (it); - } - d->Stop (); - } - } - - std::shared_ptr ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic, - const std::map * params) - { - auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ()); - if (it != m_Destinations.end ()) - { - LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists"); - it->second->Start (); // make sure to start - return it->second; - } - auto localDestination = std::make_shared (keys, isPublic, params); - AddLocalDestination (localDestination); - return localDestination; - } - - std::shared_ptr ClientContext::CreateNewLocalDestination (boost::asio::io_context& service, - const i2p::data::PrivateKeys& keys, bool isPublic, const std::map * params) - { - auto it = m_Destinations.find (keys.GetPublic ()->GetIdentHash ()); - if (it != m_Destinations.end ()) - { - LogPrint (eLogWarning, "Clients: Local destination ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " exists"); - it->second->Start (); // make sure to start - return it->second; - } - auto localDestination = std::make_shared (service, keys, isPublic, params); - AddLocalDestination (localDestination); - return localDestination; - } - - void ClientContext::CreateNewSharedLocalDestination () - { - std::map params; - ReadI2CPOptionsFromConfig ("shareddest.", params); - params[I2CP_PARAM_OUTBOUND_NICKNAME] = "SharedDest"; - - m_SharedLocalDestination = CreateNewLocalDestination (false, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, - i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, ¶ms); // non-public, EDDSA - m_SharedLocalDestination->Acquire (); - } - - std::shared_ptr ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const - { - auto it = m_Destinations.find (destination); - if (it != m_Destinations.end ()) - return it->second; - return nullptr; - } - - template - std::string ClientContext::GetI2CPOption (const Section& section, const std::string& name, const Type& value) const - { - return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value)); - } - - template - std::string ClientContext::GetI2CPStringOption (const Section& section, const std::string& name, const std::string& value) const - { - return section.second.get (boost::property_tree::ptree::path_type (name, '/'), value); - } - - template - void ClientContext::ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map& options) const - { - for (auto it: section.second) - { - if (it.first.length () >= group.length () && !it.first.compare (0, group.length (), group)) - options[it.first] = it.second.get_value (""); - } - } - - template - void ClientContext::ReadI2CPOptions (const Section& section, bool isServer, std::map& options) const - { - 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_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY); - options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY); - options[I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, DEFAULT_INBOUND_TUNNELS_LENGTH_VARIANCE); - options[I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, DEFAULT_OUTBOUND_TUNNELS_LENGTH_VARIANCE); - options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND); - options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY); - options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY); - options[I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY] = GetI2CPOption(section, I2CP_PARAM_STREAMING_INITIAL_ACK_DELAY, DEFAULT_INITIAL_ACK_DELAY); - options[I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_OUTBOUND_SPEED, DEFAULT_MAX_OUTBOUND_SPEED); - options[I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_INBOUND_SPEED, DEFAULT_MAX_INBOUND_SPEED); - options[I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_MAX_CONCURRENT_STREAMS, DEFAULT_MAX_CONCURRENT_STREAMS); - options[I2CP_PARAM_STREAMING_ANSWER_PINGS] = GetI2CPOption(section, I2CP_PARAM_STREAMING_ANSWER_PINGS, isServer ? DEFAULT_ANSWER_PINGS : false); - options[I2CP_PARAM_STREAMING_PROFILE] = GetI2CPOption(section, I2CP_PARAM_STREAMING_PROFILE, DEFAULT_STREAMING_PROFILE); - options[I2CP_PARAM_LEASESET_TYPE] = GetI2CPOption(section, I2CP_PARAM_LEASESET_TYPE, DEFAULT_LEASESET_TYPE); - std::string encType = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, isServer ? "4" : "0,4"); - if (encType.length () > 0) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = encType; - std::string privKey = GetI2CPStringOption(section, I2CP_PARAM_LEASESET_PRIV_KEY, ""); - if (privKey.length () > 0) options[I2CP_PARAM_LEASESET_PRIV_KEY] = privKey; - auto authType = GetI2CPOption(section, I2CP_PARAM_LEASESET_AUTH_TYPE, 0); - if (authType != "0") // auth is set - { - options[I2CP_PARAM_LEASESET_AUTH_TYPE] = authType; - if (authType == "1") // DH - ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_DH, options); - else if (authType == "2") // PSK - ReadI2CPOptionsGroup (section, I2CP_PARAM_LEASESET_CLIENT_PSK, options); - } - std::string explicitPeers = GetI2CPStringOption(section, I2CP_PARAM_EXPLICIT_PEERS, ""); - if (explicitPeers.length () > 0) options[I2CP_PARAM_EXPLICIT_PEERS] = explicitPeers; - std::string ratchetInboundTags = GetI2CPStringOption(section, I2CP_PARAM_RATCHET_INBOUND_TAGS, ""); - if (ratchetInboundTags.length () > 0) options[I2CP_PARAM_RATCHET_INBOUND_TAGS] = ratchetInboundTags; - } - - void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map& options) const - { - std::string value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNEL_LENGTH, value)) - options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value)) - options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE, value)) - options[I2CP_PARAM_INBOUND_TUNNELS_LENGTH_VARIANCE] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value)) - options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value)) - options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE, value)) - options[I2CP_PARAM_OUTBOUND_TUNNELS_LENGTH_VARIANCE] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value)) - options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value)) - options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_TYPE, value)) - options[I2CP_PARAM_LEASESET_TYPE] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, value)) - options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_PRIV_KEY, value) && !value.empty ()) - options[I2CP_PARAM_LEASESET_PRIV_KEY] = value; - if (i2p::config::GetOption(prefix + I2CP_PARAM_STREAMING_PROFILE, value)) - options[I2CP_PARAM_STREAMING_PROFILE] = value; - } - - void ClientContext::ReadTunnels () - { - int numClientTunnels = 0, numServerTunnels = 0; - std::string tunConf; i2p::config::GetOption("tunconf", tunConf); - if (tunConf.empty ()) - tunConf = i2p::fs::DataDirPath ("tunnels.conf"); - - LogPrint(eLogDebug, "Clients: Tunnels config file: ", tunConf); - ReadTunnels (tunConf, numClientTunnels, numServerTunnels); - - std::string tunDir; i2p::config::GetOption("tunnelsdir", tunDir); - if (tunDir.empty ()) - tunDir = i2p::fs::DataDirPath ("tunnels.d"); - - if (i2p::fs::Exists (tunDir)) - { - std::vector files; - if (i2p::fs::ReadDir (tunDir, files)) - { - for (auto& it: files) - { -#if __cplusplus >= 202002L // C++20 - if (!it.ends_with (".conf")) continue; -#else - if (it.substr(it.size() - 5) != ".conf") continue; // skip files which not ends with ".conf" -#endif - LogPrint(eLogDebug, "Clients: Tunnels extra config file: ", it); - ReadTunnels (it, numClientTunnels, numServerTunnels); - } - } - } - - LogPrint (eLogInfo, "Clients: ", numClientTunnels, " I2P client tunnels created"); - LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created"); - } - - void ClientContext::ReadTunnels (const std::string& tunConf, int& numClientTunnels, int& numServerTunnels) - { - boost::property_tree::ptree pt; - try { - boost::property_tree::read_ini (tunConf, pt); - } catch (std::exception& ex) { - LogPrint (eLogWarning, "Clients: Can't read ", tunConf, ": ", ex.what ()); - return; - } - - std::map > destinations; // keys -> destination - for (auto& section: pt) - { - std::string name = section.first; - try - { - std::string type = section.second.get (I2P_TUNNELS_SECTION_TYPE); - if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT - || type == I2P_TUNNELS_SECTION_TYPE_SOCKS - || type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS - || type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY - || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) - { - // mandatory params - std::string dest; - if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) - dest = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION); - uint16_t port = section.second.get (I2P_CLIENT_TUNNEL_PORT); - // optional params - bool matchTunnels = section.second.get (I2P_CLIENT_TUNNEL_MATCH_TUNNELS, false); - std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "transient"); - std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1"); - uint16_t destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0); - i2p::data::SigningKeyType sigType = section.second.get (I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); - i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); - // I2CP - std::map options; - ReadI2CPOptions (section, false, options); - - // Set I2CP name if not set - auto itopt = options.find (I2CP_PARAM_OUTBOUND_NICKNAME); - if (itopt == options.end ()) - options[I2CP_PARAM_OUTBOUND_NICKNAME] = name; - - std::shared_ptr localDestination = nullptr; - if (keys == "shareddest") - localDestination = m_SharedLocalDestination; - else if (keys.length () > 0) - { - auto it = destinations.find (keys); - if (it != destinations.end ()) - localDestination = it->second; - else - { - i2p::data::PrivateKeys k; - if(LoadPrivateKeys (k, keys, sigType, cryptoType)) - { - localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); - if (!localDestination) - { - if(matchTunnels) - localDestination = CreateNewMatchedTunnelDestination(k, dest, &options); - else - localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options); - if (keys != "transient") - destinations[keys] = localDestination; - } - } - } - } - - if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { - // udp client - // TODO: hostnames - boost::asio::ip::udp::endpoint end (boost::asio::ip::make_address(address), port); - if (!localDestination) - localDestination = m_SharedLocalDestination; - - bool gzip = section.second.get (I2P_CLIENT_TUNNEL_GZIP, true); - auto clientTunnel = std::make_shared (name, dest, end, localDestination, destinationPort, gzip); - - auto ins = m_ClientForwards.insert (std::make_pair (end, clientTunnel)); - if (ins.second) - { - clientTunnel->Start (); - numClientTunnels++; - } - else - { - // TODO: update - if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ()) - { - LogPrint (eLogInfo, "Clients: I2P UDP client tunnel destination updated"); - ins.first->second->Stop (); - ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ()); - ins.first->second->Start (); - } - ins.first->second->isUpdated = true; - LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists"); - } - - } else { - boost::asio::ip::tcp::endpoint clientEndpoint; - std::shared_ptr clientTunnel; - if (type == I2P_TUNNELS_SECTION_TYPE_SOCKS) - { - // socks proxy - std::string outproxy = section.second.get("outproxy", ""); - auto tun = std::make_shared(name, address, port, !outproxy.empty(), outproxy, destinationPort, localDestination); - clientTunnel = tun; - clientEndpoint = tun->GetLocalEndpoint (); - } - else if (type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY) - { - // http proxy - std::string outproxy = section.second.get("outproxy", ""); - bool addresshelper = section.second.get("addresshelper", true); - bool senduseragent = section.second.get("senduseragent", false); - auto tun = std::make_shared(name, address, port, - outproxy, addresshelper, senduseragent, localDestination); - clientTunnel = tun; - clientEndpoint = tun->GetLocalEndpoint (); - } - else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS) - { - LogPrint(eLogWarning, "Clients: I2P Client tunnel websocks is deprecated, not starting ", name, " tunnel"); - continue; - } - else - { - // tcp client - auto tun = std::make_shared (name, dest, address, port, localDestination, destinationPort); - clientTunnel = tun; - clientEndpoint = tun->GetLocalEndpoint (); - - uint32_t keepAlive = section.second.get(I2P_CLIENT_TUNNEL_KEEP_ALIVE_INTERVAL, 0); - if (keepAlive) - { - tun->SetKeepAliveInterval (keepAlive); - LogPrint(eLogInfo, "Clients: I2P Client tunnel keep alive interval set to ", keepAlive); - } - } - - uint32_t timeout = section.second.get(I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT, 0); - if(timeout) - { - clientTunnel->SetConnectTimeout(timeout); - LogPrint(eLogInfo, "Clients: I2P Client tunnel connect timeout set to ", timeout); - } - - auto ins = m_ClientTunnels.insert (std::make_pair (clientEndpoint, clientTunnel)); - if (ins.second) - { - clientTunnel->Start (); - numClientTunnels++; - } - else - { - // TODO: update - if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ()) - { - LogPrint (eLogInfo, "Clients: I2P client tunnel destination updated"); - ins.first->second->Stop (); - ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ()); - ins.first->second->Start (); - } - ins.first->second->isUpdated = true; - LogPrint (eLogInfo, "Clients: I2P client tunnel for endpoint ", clientEndpoint, " already exists"); - } - } - } - - else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER - || type == I2P_TUNNELS_SECTION_TYPE_HTTP - || type == I2P_TUNNELS_SECTION_TYPE_IRC - || type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) - { - // mandatory params - std::string host = section.second.get (I2P_SERVER_TUNNEL_HOST); - uint16_t port = section.second.get (I2P_SERVER_TUNNEL_PORT); - std::string keys = section.second.get (I2P_SERVER_TUNNEL_KEYS); - // optional params - uint16_t inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, port); - std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, ""); - if(accessList == "") - accessList = section.second.get (I2P_SERVER_TUNNEL_WHITE_LIST, ""); - std::string hostOverride = section.second.get (I2P_SERVER_TUNNEL_HOST_OVERRIDE, ""); - std::string webircpass = section.second.get (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, ""); - bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, false); - i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); - i2p::data::CryptoKeyType cryptoType = section.second.get (I2P_CLIENT_TUNNEL_CRYPTO_TYPE, i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); - - std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, ""); - bool isUniqueLocal = section.second.get (I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); - bool ssl = section.second.get (I2P_SERVER_TUNNEL_SSL, false); - - // I2CP - std::map options; - ReadI2CPOptions (section, true, options); - - // Set I2CP name if not set - auto itopt = options.find (I2CP_PARAM_INBOUND_NICKNAME); - if (itopt == options.end ()) - options[I2CP_PARAM_INBOUND_NICKNAME] = name; - - std::shared_ptr localDestination = nullptr; - if (keys == "shareddest") - localDestination = m_SharedLocalDestination; - else - { - auto it = destinations.find (keys); - if (it != destinations.end ()) - { - localDestination = it->second; - localDestination->SetPublic (true); - } - else - { - i2p::data::PrivateKeys k; - if(!LoadPrivateKeys (k, keys, sigType, cryptoType)) - continue; - localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ()); - if (!localDestination) - { - localDestination = CreateNewLocalDestination (k, true, &options); - destinations[keys] = localDestination; - } - else - localDestination->SetPublic (true); - } - } - if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER) - { - // udp server tunnel - // TODO: hostnames - boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::make_address(host), port); - if (address.empty ()) - { - if (!endpoint.address ().is_unspecified () && endpoint.address ().is_v6 ()) - address = "::1"; - else - address = "127.0.0.1"; - } - auto localAddress = boost::asio::ip::make_address(address); - auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, inPort, gzip); - if(!isUniqueLocal) - { - LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); - serverTunnel->SetUniqueLocal(isUniqueLocal); - } - std::lock_guard lock(m_ForwardsMutex); - auto ins = m_ServerForwards.insert(std::make_pair( - std::make_pair(localDestination->GetIdentHash(), port), - serverTunnel)); - if (ins.second) - { - serverTunnel->Start(); - LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32()); - } - else - { - ins.first->second->isUpdated = true; - LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, " already exists"); - } - - continue; - } - - std::shared_ptr serverTunnel; - if (type == I2P_TUNNELS_SECTION_TYPE_HTTP) - serverTunnel = std::make_shared (name, host, port, localDestination, hostOverride, inPort, gzip); - else if (type == I2P_TUNNELS_SECTION_TYPE_IRC) - serverTunnel = std::make_shared (name, host, port, localDestination, webircpass, inPort, gzip); - else // regular server tunnel by default - serverTunnel = std::make_shared (name, host, port, localDestination, inPort, gzip); - - if (!address.empty ()) - serverTunnel->SetLocalAddress (address); - if (!isUniqueLocal) - { - LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); - serverTunnel->SetUniqueLocal(isUniqueLocal); - } - if (ssl) - serverTunnel->SetSSL (true); - if (accessList.length () > 0) - { - std::set idents; - size_t pos = 0, comma; - do - { - comma = accessList.find (',', pos); - i2p::data::IdentHash ident; - ident.FromBase32 (accessList.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos)); - idents.insert (ident); - pos = comma + 1; - } - while (comma != std::string::npos); - serverTunnel->SetAccessList (idents); - } - auto ins = m_ServerTunnels.insert (std::make_pair ( - std::make_pair (localDestination->GetIdentHash (), inPort), - serverTunnel)); - if (ins.second) - { - serverTunnel->Start (); - numServerTunnels++; - } - else - { - // TODO: update - if (ins.first->second->GetLocalDestination () != serverTunnel->GetLocalDestination ()) - { - LogPrint (eLogInfo, "Clients: I2P server tunnel destination updated"); - ins.first->second->Stop (); - ins.first->second->SetLocalDestination (serverTunnel->GetLocalDestination ()); - ins.first->second->Start (); - } - ins.first->second->isUpdated = true; - LogPrint (eLogInfo, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists"); - } - - } - else - LogPrint (eLogError, "Clients: Unknown section type = ", type, " of ", name, " in ", tunConf); - } - catch (std::exception& ex) - { - LogPrint (eLogCritical, "Clients: Can't read tunnel ", name, " params: ", ex.what ()); - ThrowFatal ("Unable to start tunnel ", name, ": ", ex.what ()); - } - } - } - - void ClientContext::ReadHttpProxy () - { - std::shared_ptr localDestination; - bool httproxy; i2p::config::GetOption("httpproxy.enabled", httproxy); - if (httproxy) - { - std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys); - std::string httpProxyAddr; i2p::config::GetOption("httpproxy.address", httpProxyAddr); - uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort); - std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL); - bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper); - bool httpSendUserAgent; i2p::config::GetOption("httpproxy.senduseragent", httpSendUserAgent); - if (httpAddresshelper) - i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book - i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType); - LogPrint(eLogInfo, "Clients: Starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort); - if (httpProxyKeys == "shareddest") - { - localDestination = m_SharedLocalDestination; - localDestination->Acquire (); - } - else if (httpProxyKeys.length () > 0) - { - i2p::data::PrivateKeys keys; - if(LoadPrivateKeys (keys, httpProxyKeys, sigType)) - { - std::map params; - ReadI2CPOptionsFromConfig ("httpproxy.", params); - params[I2CP_PARAM_OUTBOUND_NICKNAME] = "HTTPProxy"; - localDestination = CreateNewLocalDestination (keys, false, ¶ms); - if (localDestination) localDestination->Acquire (); - } - else - LogPrint(eLogCritical, "Clients: Failed to load HTTP Proxy key"); - } - try - { - m_HttpProxy = new i2p::proxy::HTTPProxy("HTTP Proxy", httpProxyAddr, httpProxyPort, - httpOutProxyURL, httpAddresshelper, httpSendUserAgent, localDestination); - m_HttpProxy->Start(); - } - catch (std::exception& e) - { - LogPrint(eLogCritical, "Clients: Exception in HTTP Proxy: ", e.what()); - ThrowFatal ("Unable to start HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort, ": ", e.what ()); - } - } - } - - void ClientContext::ReadSocksProxy () - { - std::shared_ptr localDestination; - bool socksproxy; i2p::config::GetOption("socksproxy.enabled", 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 socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr); - uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort); - bool socksOutProxy; i2p::config::GetOption("socksproxy.outproxy.enabled", socksOutProxy); - std::string socksOutProxyAddr; i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr); - uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort); - i2p::data::SigningKeyType sigType; i2p::config::GetOption("socksproxy.signaturetype", sigType); - LogPrint(eLogInfo, "Clients: Starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort); - if (socksProxyKeys == "shareddest") - { - localDestination = m_SharedLocalDestination; - localDestination->Acquire (); - } - else if (httpProxyKeys == socksProxyKeys && m_HttpProxy) - { - localDestination = m_HttpProxy->GetLocalDestination (); - localDestination->Acquire (); - } - else if (socksProxyKeys.length () > 0) - { - i2p::data::PrivateKeys keys; - if (LoadPrivateKeys (keys, socksProxyKeys, sigType)) - { - std::map params; - ReadI2CPOptionsFromConfig ("socksproxy.", params); - params[I2CP_PARAM_OUTBOUND_NICKNAME] = "SOCKSProxy"; - localDestination = CreateNewLocalDestination (keys, false, ¶ms); - if (localDestination) localDestination->Acquire (); - } - else - LogPrint(eLogCritical, "Clients: Failed to load SOCKS Proxy key"); - } - try - { - m_SocksProxy = new i2p::proxy::SOCKSProxy("SOCKS", socksProxyAddr, socksProxyPort, - socksOutProxy, socksOutProxyAddr, socksOutProxyPort, localDestination); - m_SocksProxy->Start(); - } - catch (std::exception& e) - { - LogPrint(eLogCritical, "Clients: Exception in SOCKS Proxy: ", e.what()); - ThrowFatal ("Unable to start SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort, ": ", e.what ()); - } - } - } - - void ClientContext::ScheduleCleanupUDP() - { - if (m_CleanupUDPTimer) - { - // schedule cleanup in 17 seconds - m_CleanupUDPTimer->expires_from_now (boost::posix_time::seconds (17)); - m_CleanupUDPTimer->async_wait(std::bind(&ClientContext::CleanupUDP, this, std::placeholders::_1)); - } - } - - void ClientContext::CleanupUDP(const boost::system::error_code & ecode) - { - if(!ecode) - { - std::lock_guard lock(m_ForwardsMutex); - for (auto & s : m_ServerForwards ) s.second->ExpireStale(); - ScheduleCleanupUDP(); - } - } - - void ClientContext::VisitTunnels (bool clean) - { - for (auto it = m_ClientTunnels.begin (); it != m_ClientTunnels.end ();) - { - if(clean && !it->second->isUpdated) { - it->second->Stop (); - it = m_ClientTunnels.erase(it); - } else { - it->second->isUpdated = false; - it++; - } - } - - for (auto it = m_ServerTunnels.begin (); it != m_ServerTunnels.end ();) - { - if(clean && !it->second->isUpdated) { - it->second->Stop (); - it = m_ServerTunnels.erase(it); - } else { - it->second->isUpdated = false; - it++; - } - } - - // TODO: Write correct UDP tunnels stop - for (auto it = m_ClientForwards.begin (); it != m_ClientForwards.end ();) - { - if(clean && !it->second->isUpdated) { - it->second->Stop (); - it = m_ClientForwards.erase(it); - } else { - it->second->isUpdated = false; - it++; - } - } - - for (auto it = m_ServerForwards.begin (); it != m_ServerForwards.end ();) - { - if(clean && !it->second->isUpdated) { - it->second->Stop (); - it = m_ServerForwards.erase(it); - } else { - it->second->isUpdated = false; - it++; - } - } - } -} -} diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h deleted file mode 100644 index 3f7eaf9a..00000000 --- a/libi2pd_client/ClientContext.h +++ /dev/null @@ -1,175 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 CLIENT_CONTEXT_H__ -#define CLIENT_CONTEXT_H__ - -#include -#include -#include -#include -#include "Destination.h" -#include "I2PService.h" -#include "I2PTunnel.h" -#include "UDPTunnel.h" -#include "SAM.h" -#include "BOB.h" -#include "I2CP.h" -#include "AddressBook.h" -#include "I18N_langs.h" - -namespace i2p -{ -namespace client -{ - const char I2P_TUNNELS_SECTION_TYPE[] = "type"; - const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client"; - const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server"; - const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http"; - const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc"; - const char I2P_TUNNELS_SECTION_TYPE_UDPCLIENT[] = "udpclient"; - const char I2P_TUNNELS_SECTION_TYPE_UDPSERVER[] = "udpserver"; - const char I2P_TUNNELS_SECTION_TYPE_SOCKS[] = "socks"; - const char I2P_TUNNELS_SECTION_TYPE_WEBSOCKS[] = "websocks"; - const char I2P_TUNNELS_SECTION_TYPE_HTTPPROXY[] = "httpproxy"; - const char I2P_CLIENT_TUNNEL_PORT[] = "port"; - const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address"; - const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination"; - const char I2P_CLIENT_TUNNEL_KEYS[] = "keys"; - const char I2P_CLIENT_TUNNEL_GZIP[] = "gzip"; - const char I2P_CLIENT_TUNNEL_SIGNATURE_TYPE[] = "signaturetype"; - const char I2P_CLIENT_TUNNEL_CRYPTO_TYPE[] = "cryptotype"; - const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport"; - const char I2P_CLIENT_TUNNEL_MATCH_TUNNELS[] = "matchtunnels"; - const char I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT[] = "connecttimeout"; - const char I2P_CLIENT_TUNNEL_KEEP_ALIVE_INTERVAL[] = "keepaliveinterval"; - const char I2P_SERVER_TUNNEL_HOST[] = "host"; - const char I2P_SERVER_TUNNEL_HOST_OVERRIDE[] = "hostoverride"; - const char I2P_SERVER_TUNNEL_PORT[] = "port"; - const char I2P_SERVER_TUNNEL_KEYS[] = "keys"; - const char I2P_SERVER_TUNNEL_SIGNATURE_TYPE[] = "signaturetype"; - const char I2P_SERVER_TUNNEL_INPORT[] = "inport"; - const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist"; - const char I2P_SERVER_TUNNEL_WHITE_LIST[] = "whitelist"; - const char I2P_SERVER_TUNNEL_GZIP[] = "gzip"; - const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword"; - const char I2P_SERVER_TUNNEL_ADDRESS[] = "address"; - const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal"; - const char I2P_SERVER_TUNNEL_SSL[] = "ssl"; - - class ClientContext - { - public: - - ClientContext (); - ~ClientContext (); - - void Start (); - void Stop (); - - void ReloadConfig (); - - std::shared_ptr GetSharedLocalDestination () const { return m_SharedLocalDestination; }; - std::shared_ptr CreateNewLocalDestination (bool isPublic = false, // transient - i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, - const std::map * params = nullptr); // used by SAM only - std::shared_ptr CreateNewLocalDestination (boost::asio::io_context& service, - bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL, - const std::map * params = nullptr); // same as previous but on external io_service - std::shared_ptr CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true, - const std::map * params = nullptr); - std::shared_ptr CreateNewLocalDestination (boost::asio::io_context& service, - const i2p::data::PrivateKeys& keys, bool isPublic = true, - const std::map * params = nullptr); // same as previous but on external io_service - std::shared_ptr CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys, - const std::string & name, const std::map * params = nullptr); - void DeleteLocalDestination (std::shared_ptr destination); - std::shared_ptr FindLocalDestination (const i2p::data::IdentHash& destination) const; - bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, std::string_view filename, - i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519, - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL); - - AddressBook& GetAddressBook () { return m_AddressBook; }; - const BOBCommandChannel * GetBOBCommandChannel () const { return m_BOBCommandChannel; }; - const SAMBridge * GetSAMBridge () const { return m_SamBridge; }; - const I2CPServer * GetI2CPServer () const { return m_I2CPServer; }; - - std::vector > GetForwardInfosFor(const i2p::data::IdentHash & destination); - - // i18n - std::shared_ptr GetLanguage () { return m_Language; }; - void SetLanguage (const std::shared_ptr language) { m_Language = language; }; - - private: - - void ReadTunnels (); - void ReadTunnels (const std::string& tunConf, int& numClientTunnels, int& numServerTunnels); - void ReadHttpProxy (); - void ReadSocksProxy (); - template - std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const; - template - std::string GetI2CPStringOption (const Section& section, const std::string& name, const std::string& value) const; // GetI2CPOption with string default value - template - void ReadI2CPOptionsGroup (const Section& section, const std::string& group, std::map& options) const; - template - void ReadI2CPOptions (const Section& section, bool isServer, std::map& options) const; // for tunnels - void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map& options) const; // for HTTP and SOCKS proxy - - void CleanupUDP(const boost::system::error_code & ecode); - void ScheduleCleanupUDP(); - - void VisitTunnels (bool clean); - - void CreateNewSharedLocalDestination (); - void AddLocalDestination (std::shared_ptr localDestination); - - private: - - std::mutex m_DestinationsMutex; - std::map > m_Destinations; - std::shared_ptr m_SharedLocalDestination; - - AddressBook m_AddressBook; - - I2PService * m_HttpProxy, * m_SocksProxy; - std::map > m_ClientTunnels; // local endpoint -> tunnel - std::map, std::shared_ptr > m_ServerTunnels; // -> tunnel - - std::mutex m_ForwardsMutex; - std::map > m_ClientForwards; // local endpoint -> udp tunnel - std::map, std::shared_ptr > m_ServerForwards; // -> udp tunnel - - SAMBridge * m_SamBridge; - BOBCommandChannel * m_BOBCommandChannel; - I2CPServer * m_I2CPServer; - - std::unique_ptr m_CleanupUDPTimer; - - // i18n - std::shared_ptr m_Language; - - public: - - // for HTTP - const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; }; - const decltype(m_ClientTunnels)& GetClientTunnels () const { return m_ClientTunnels; }; - const decltype(m_ServerTunnels)& GetServerTunnels () const { return m_ServerTunnels; }; - const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; } - const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; } - const I2PService * GetHttpProxy () const { return m_HttpProxy; } - const I2PService * GetSocksProxy () const { return m_SocksProxy; } - }; - - extern ClientContext context; -} -} - -#endif diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp deleted file mode 100644 index 4c2771b5..00000000 --- a/libi2pd_client/HTTPProxy.cpp +++ /dev/null @@ -1,768 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "I2PService.h" -#include "Destination.h" -#include "HTTPProxy.h" -#include "util.h" -#include "Identity.h" -#include "Streaming.h" -#include "Destination.h" -#include "ClientContext.h" -#include "I2PEndian.h" -#include "I2PTunnel.h" -#include "Config.h" -#include "HTTP.h" -#include "I18N.h" -#include "Socks5.h" - -namespace i2p { -namespace proxy { - static const std::vector jumporder = { - "reg.i2p", - "stats.i2p", - "identiguy.i2p", - "notbob.i2p" - }; - - static const std::map jumpservices = { - { "reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/" }, - { "identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname=" }, - { "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" }, - { "notbob.i2p", "http://nytzrhrjjfsutowojvxi7hphesskpqqr65wpistz6wa7cpajhp7a.b32.i2p/cgi-bin/jump.cgi?q=" } - }; - - static const char *pageHead = - "\r\n" - " \r\n" - " I2Pd HTTP proxy\r\n" - " \r\n" - "\r\n" - ; - - static bool str_rmatch(std::string_view str, const char *suffix) - { - auto pos = str.rfind (suffix); - if (pos == std::string::npos) - return false; /* not found */ - if (str.length() == (pos + std::strlen(suffix))) - return true; /* match */ - return false; - } - - class HTTPReqHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this - { - private: - - bool HandleRequest(); - void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); - void Terminate(); - void AsyncSockRead(); - static bool ExtractAddressHelper(i2p::http::URL& url, std::string& jump, bool& confirm); - static bool VerifyAddressHelper (std::string_view jump); - void SanitizeHTTPRequest(i2p::http::HTTPReq& req); - void SentHTTPFailed(const boost::system::error_code & ecode); - void HandleStreamRequestComplete (std::shared_ptr stream); - /* error helpers */ - void GenericProxyError(std::string_view title, std::string_view description); - void GenericProxyInfo(std::string_view title, std::string_view description); - void HostNotFound(std::string_view host); - void SendProxyError(std::string_view content); - void SendRedirect(const std::string& address); - - void ForwardToUpstreamProxy(); - void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec); - void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec); - void HTTPConnect(std::string_view host, uint16_t port); - void HandleHTTPConnectStreamRequestComplete(std::shared_ptr stream); - - typedef std::function ProxyResolvedHandler; - - void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::results_type endpoints, ProxyResolvedHandler handler); - - void SocksProxySuccess(); - void HandoverToUpstreamProxy(); - - uint8_t m_recv_chunk[8192]; - std::string m_recv_buf; // from client - std::string m_send_buf; // to upstream - std::shared_ptr m_sock; - std::shared_ptr m_proxysock; - boost::asio::ip::tcp::resolver m_proxy_resolver; - std::string m_OutproxyUrl, m_Response; - bool m_Addresshelper, m_SendUserAgent; - i2p::http::URL m_ProxyURL; - i2p::http::URL m_RequestURL; - int m_req_len; - i2p::http::URL m_ClientRequestURL; - i2p::http::HTTPReq m_ClientRequest; - i2p::http::HTTPRes m_ClientResponse; - std::stringstream m_ClientRequestBuffer; - - public: - - HTTPReqHandler(HTTPProxy * parent, std::shared_ptr sock) : - I2PServiceHandler(parent), m_sock(sock), - m_proxysock(std::make_shared(parent->GetService())), - m_proxy_resolver(parent->GetService()), - m_OutproxyUrl(parent->GetOutproxyURL()), - m_Addresshelper(parent->GetHelperSupport()), - m_SendUserAgent (parent->GetSendUserAgent ()) {} - ~HTTPReqHandler() { Terminate(); } - void Handle () { AsyncSockRead(); } /* overload */ - }; - - void HTTPReqHandler::AsyncSockRead() - { - LogPrint(eLogDebug, "HTTPProxy: Async sock read"); - if (!m_sock) { - LogPrint(eLogError, "HTTPProxy: No socket for read"); - return; - } - m_sock->async_read_some(boost::asio::buffer(m_recv_chunk, sizeof(m_recv_chunk)), - std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } - - void HTTPReqHandler::Terminate() { - if (Kill()) return; - if (m_sock) - { - LogPrint(eLogDebug, "HTTPProxy: Close sock"); - m_sock->close(); - m_sock = nullptr; - } - if(m_proxysock) - { - LogPrint(eLogDebug, "HTTPProxy: Close proxysock"); - if(m_proxysock->is_open()) - m_proxysock->close(); - m_proxysock = nullptr; - } - Done(shared_from_this()); - } - - void HTTPReqHandler::GenericProxyError(std::string_view title, std::string_view description) - { - std::stringstream ss; - ss << "

" << tr("Proxy error") << ": " << title << "

\r\n"; - ss << "

" << description << "

\r\n"; - SendProxyError(ss.str ()); - } - - void HTTPReqHandler::GenericProxyInfo(std::string_view title, std::string_view description) - { - std::stringstream ss; - ss << "

" << tr("Proxy info") << ": " << title << "

\r\n"; - ss << "

" << description << "

\r\n"; - SendProxyError(ss.str ()); - } - - void HTTPReqHandler::HostNotFound(std::string_view host) - { - std::stringstream ss; - ss << "

" << tr("Proxy error: Host not found") << "

\r\n" - << "

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

\r\n" - << "

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

\r\n" - << "\r\n"; - SendProxyError(ss.str ()); - } - - void HTTPReqHandler::SendProxyError(std::string_view content) - { - i2p::http::HTTPRes res; - res.code = 500; - res.add_header("Content-Type", "text/html; charset=UTF-8"); - res.add_header("Connection", "close"); - std::stringstream ss; - ss << "\r\n" << pageHead - << "" << content << "\r\n" - << "\r\n"; - res.body = ss.str(); - m_Response = res.to_string(); - boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response), boost::asio::transfer_all(), - std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); - } - - void HTTPReqHandler::SendRedirect(const std::string& address) - { - i2p::http::HTTPRes res; - res.code = 302; - res.add_header("Location", address); - res.add_header("Connection", "close"); - m_Response = res.to_string(); - boost::asio::async_write(*m_sock, boost::asio::buffer(m_Response), boost::asio::transfer_all(), - std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); - } - - bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL& url, std::string& jump, bool& confirm) - { - confirm = false; - const char *param = "i2paddresshelper="; - std::size_t pos = url.query.find(param); - std::size_t len = std::strlen(param); - std::map params; - - - if (pos == std::string::npos) - return false; /* not found */ - if (!url.parse_query(params)) - return false; - - std::string value = params["i2paddresshelper"]; - len += value.length(); - jump = i2p::http::UrlDecode(value); - if (!VerifyAddressHelper (jump)) - { - LogPrint (eLogError, "HTTPProxy: Malformed jump link ", jump); - return false; - } - - // if we need update exists, request formed with update param - if (params["update"] == "true") - { - len += std::strlen("&update=true"); - confirm = true; - } - - // if helper is not only one query option and it placed after user's query - if (pos != 0 && url.query[pos-1] == '&') - { - pos--; - len++; - } - // if helper is not only one query option and it placed before user's query - else if (pos == 0 && url.query.length () > len && url.query[len] == '&') - { - // we don't touch the '?' but remove the trailing '&' - len++; - } - else - { - // there is no more query options, resetting hasquery flag - url.hasquery = false; - } - - // reset hasquery flag and remove addresshelper from URL - url.query.replace(pos, len, ""); - return true; - } - - bool HTTPReqHandler::VerifyAddressHelper (std::string_view jump) - { - auto pos = jump.find(".b32.i2p"); - if (pos != std::string::npos) - { - auto b32 = jump.substr (0, pos); - for (auto& ch: b32) - if (!i2p::data::IsBase32(ch)) return false; - return true; - } - else - { - bool padding = false; - for (auto& ch: jump) - { - if (ch == '=') - padding = true; - else - { - if (padding) return false; // other chars after padding - if (!i2p::data::IsBase64(ch)) return false; - } - } - return true; - } - return false; - } - - void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq& req) - { - /* drop common headers */ - req.RemoveHeader("Via"); - req.RemoveHeader("From"); - req.RemoveHeader("Forwarded"); - req.RemoveHeader("DNT"); // Useless DoNotTrack flag - req.RemoveHeader("Accept", "Accept-Encoding"); // Accept*, but Accept-Encoding - /* drop proxy-disclosing headers */ - req.RemoveHeader("X-Forwarded"); - req.RemoveHeader("Proxy-"); // Proxy-* - /* replace headers */ - if (!m_SendUserAgent) - req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)"); - - /** - * i2pd PR #1816: - * Android Webview send this with the value set to the application ID, so we drop it, - * but only if it does not belong to an AJAX request (*HttpRequest, like XMLHttpRequest). - */ - if(req.GetHeader("X-Requested-With") != "") { - auto h = req.GetHeader ("X-Requested-With"); - auto x = h.find("HttpRequest"); - if (x == std::string::npos) // not found - req.RemoveHeader("X-Requested-With"); - } - - /** - * according to i2p ticket #1862: - * leave Referer if requested URL with same schema, host and port, - * otherwise, drop it. - */ - if(req.GetHeader("Referer") != "") { - i2p::http::URL reqURL; reqURL.parse(req.uri); - i2p::http::URL refURL; refURL.parse(req.GetHeader("Referer")); - if(!boost::iequals(reqURL.schema, refURL.schema) || !boost::iequals(reqURL.host, refURL.host) || reqURL.port != refURL.port) - req.RemoveHeader("Referer"); - } - - /* add headers */ - /* close connection, if not Connection: (U|u)pgrade (for websocket) */ - auto h = req.GetHeader ("Connection"); - auto x = h.find("pgrade"); - if (!(x != std::string::npos && std::tolower(h[x - 1]) == 'u')) - req.UpdateHeader("Connection", "close"); - } - - /** - * @brief Try to parse request from @a m_recv_buf - * If parsing success, rebuild request and store to @a m_send_buf - * with remaining data tail - * @return true on processed request or false if more data needed - */ - bool HTTPReqHandler::HandleRequest() - { - m_req_len = m_ClientRequest.parse(m_recv_buf); - - if (m_req_len == 0) - return false; /* need more data */ - - if (m_req_len < 0) { - LogPrint(eLogError, "HTTPProxy: Unable to parse request"); - GenericProxyError(tr("Invalid request"), tr("Proxy unable to parse your request")); - return true; /* parse error */ - } - - /* parsing success, now let's look inside request */ - LogPrint(eLogDebug, "HTTPProxy: Requested: ", m_ClientRequest.uri); - m_RequestURL.parse(m_ClientRequest.uri); - bool m_Confirm; - - std::string jump; - if (ExtractAddressHelper(m_RequestURL, jump, m_Confirm)) - { - if (!m_Addresshelper || !i2p::client::context.GetAddressBook ().IsEnabled ()) - { - LogPrint(eLogWarning, "HTTPProxy: Addresshelper request rejected"); - GenericProxyError(tr("Invalid request"), tr("Addresshelper is not supported")); - return true; - } - - if (i2p::client::context.GetAddressBook ().RecordExists (m_RequestURL.host, jump)) - { - std::string full_url = m_RequestURL.to_string(); - SendRedirect(full_url); - return true; - } - else if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm) - { - const std::string referer_raw = m_ClientRequest.GetHeader("Referer"); - i2p::http::URL referer_url; - if (!referer_raw.empty ()) - { - referer_url.parse (referer_raw); - } - if (m_RequestURL.host != referer_url.host) - { - if (m_Confirm) // Attempt to forced overwriting by link with "&update=true" from harmful URL - { - LogPrint (eLogWarning, "HTTPProxy: Address update from addresshelper rejected for ", m_RequestURL.host, " (referer is ", m_RequestURL.host.empty() ? "empty" : "harmful", ")"); - std::string full_url = m_RequestURL.to_string(); - std::stringstream ss; - ss << tr("Host %s is already in router's addressbook. Be careful: source of this URL may be harmful! Click here to update record: Continue.", - m_RequestURL.host.c_str(), full_url.c_str(), (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="), jump.c_str()); - GenericProxyInfo(tr("Addresshelper forced update rejected"), ss.str()); - } - else // Preventing unauthorized additions to the address book - { - LogPrint (eLogDebug, "HTTPProxy: Adding address from addresshelper for ", m_RequestURL.host, " (generate refer-base page)"); - std::string full_url = m_RequestURL.to_string(); - std::stringstream ss; - ss << tr("To add host %s in router's addressbook, click here: Continue.", - m_RequestURL.host.c_str(), full_url.c_str(), (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="), jump.c_str()); - GenericProxyInfo(tr("Addresshelper request"), ss.str()); - } - return true; /* request processed */ - } - - i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump); - LogPrint (eLogInfo, "HTTPProxy: Added address from addresshelper for ", m_RequestURL.host); - std::string full_url = m_RequestURL.to_string(); - std::stringstream ss; - ss << tr("Host %s added to router's addressbook from helper. Click here to proceed: Continue.", - m_RequestURL.host.c_str(), full_url.c_str()); - GenericProxyInfo(tr("Addresshelper adding"), ss.str()); - return true; /* request processed */ - } - else - { - std::string full_url = m_RequestURL.to_string(); - std::stringstream ss; - ss << tr("Host %s is already in router's addressbook. Click here to update record: Continue.", - m_RequestURL.host.c_str(), full_url.c_str(), (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="), jump.c_str()); - GenericProxyInfo(tr("Addresshelper update"), ss.str()); - return true; /* request processed */ - } - } - std::string dest_host; - uint16_t dest_port; - bool useConnect = false; - if(m_ClientRequest.method == "CONNECT") - { - const std::string& uri = m_ClientRequest.uri; - auto pos = uri.find(":"); - if(pos == std::string::npos || pos == uri.size() - 1) - { - GenericProxyError(tr("Invalid request"), tr("Invalid request URI")); - return true; - } - else - { - useConnect = true; - dest_port = std::stoi(uri.substr(pos+1)); - dest_host = uri.substr(0, pos); - } - } - else - { - SanitizeHTTPRequest(m_ClientRequest); - - dest_host = m_RequestURL.host; - dest_port = m_RequestURL.port; - /* always set port, even if missing in request */ - if (!dest_port) - dest_port = (m_RequestURL.schema == "https") ? 443 : 80; - /* detect dest_host, set proper 'Host' header in upstream request */ - if (dest_host != "") - { - /* absolute url, replace 'Host' header */ - std::string h (dest_host); - if (dest_port != 0 && dest_port != 80) - h += ":" + std::to_string(dest_port); - m_ClientRequest.UpdateHeader("Host", h); - } - else - { - auto h = m_ClientRequest.GetHeader ("Host"); - if (h.length () > 0) - { - /* relative url and 'Host' header provided. transparent proxy mode? */ - i2p::http::URL u; - std::string t = "http://" + h; - u.parse(t); - dest_host = u.host; - dest_port = u.port; - } - else - { - /* relative url and missing 'Host' header */ - GenericProxyError(tr("Invalid request"), tr("Can't detect destination host from request")); - return true; - } - } - } - /* check dest_host really exists and inside I2P network */ - if (str_rmatch(dest_host, ".i2p")) { - if (!i2p::client::context.GetAddressBook ().GetAddress (dest_host)) { - HostNotFound(dest_host); - return true; /* request processed */ - } - } else { - if(m_OutproxyUrl.size()) { - LogPrint (eLogDebug, "HTTPProxy: Using outproxy ", m_OutproxyUrl); - if(m_ProxyURL.parse(m_OutproxyUrl)) - ForwardToUpstreamProxy(); - else - GenericProxyError(tr("Outproxy failure"), tr("Bad outproxy settings")); - } else { - LogPrint (eLogWarning, "HTTPProxy: Outproxy failure for ", dest_host, ": no outproxy enabled"); - std::stringstream ss; ss << tr("Host %s is not inside I2P network, but outproxy is not enabled", dest_host.c_str ()); - GenericProxyError(tr("Outproxy failure"), ss.str()); - } - return true; - } - if(useConnect) - { - HTTPConnect(dest_host, dest_port); - return true; - } - - /* make relative url */ - m_RequestURL.schema = ""; - m_RequestURL.host = ""; - m_ClientRequest.uri = m_RequestURL.to_string(); - - /* drop original request from recv buffer */ - m_recv_buf.erase(0, m_req_len); - /* build new buffer from modified request and data from original request */ - m_send_buf = m_ClientRequest.to_string(); - m_send_buf.append(m_recv_buf); - /* connect to destination */ - LogPrint(eLogDebug, "HTTPProxy: Connecting to host ", dest_host, ":", dest_port); - GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete, - shared_from_this(), std::placeholders::_1), dest_host, dest_port); - return true; - } - - void HTTPReqHandler::ForwardToUpstreamProxy() - { - LogPrint(eLogDebug, "HTTPProxy: Forwarded to upstream"); - - /* build http request */ - m_ClientRequestURL = m_RequestURL; - LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host); - m_ClientRequestURL.schema = ""; - m_ClientRequestURL.host = ""; - std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for? - m_ClientRequest.uri = m_ClientRequestURL.to_string(); - - /* update User-Agent to ESR version of Firefox, same as Tor Browser below version 13, for non-HTTPS connections */ - if(m_ClientRequest.method != "CONNECT" && !m_SendUserAgent) - m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; rv:109.0) Gecko/20100101 Firefox/115.0"); - - m_ClientRequest.write(m_ClientRequestBuffer); - m_ClientRequestBuffer << m_recv_buf.substr(m_req_len); - - /* assume http if empty schema */ - if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") - { - /* handle upstream http proxy */ - if (!m_ProxyURL.port) m_ProxyURL.port = 80; - if (m_ProxyURL.is_i2p()) - { - m_ClientRequest.uri = origURI; - auto auth = i2p::http::CreateBasicAuthorizationString (m_ProxyURL.user, m_ProxyURL.pass); - if (!auth.empty ()) - { - /* remove existing authorization if any */ - m_ClientRequest.RemoveHeader("Proxy-"); - /* add own http proxy authorization */ - m_ClientRequest.AddHeader("Proxy-Authorization", auth); - } - m_send_buf = m_ClientRequest.to_string(); - m_recv_buf.erase(0, m_req_len); - m_send_buf.append(m_recv_buf); - GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete, - shared_from_this(), std::placeholders::_1), m_ProxyURL.host, m_ProxyURL.port); - } - else - { - m_proxy_resolver.async_resolve(m_ProxyURL.host, std::to_string(m_ProxyURL.port), std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, - std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) - { - m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1)); - })); - } - } - else if (m_ProxyURL.schema == "socks") - { - /* handle upstream socks proxy */ - if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified - m_proxy_resolver.async_resolve(m_ProxyURL.host, std::to_string(m_ProxyURL.port), std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, - std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) - { - m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1)); - })); - } - else - { - /* unknown type, complain */ - GenericProxyError(tr("Unknown outproxy URL"), m_ProxyURL.to_string()); - } - } - - void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::results_type endpoints, ProxyResolvedHandler handler) - { - if(ec) GenericProxyError(tr("Cannot resolve upstream proxy"), ec.message()); - else handler(*endpoints.begin ()); - } - - void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec) - { - if(!ec) - { - if(m_RequestURL.host.size() > 255) - { - GenericProxyError(tr("Hostname is too long"), m_RequestURL.host); - return; - } - uint16_t port = m_RequestURL.port; - if(!port) port = 80; - LogPrint(eLogDebug, "HTTPProxy: Connected to SOCKS upstream"); - std::string host = m_RequestURL.host; - auto s = shared_from_this (); - i2p::transport::Socks5Handshake (*m_proxysock, std::make_pair(host, port), - [s](const boost::system::error_code& ec) - { - if (!ec) - s->SocksProxySuccess(); - else - s->GenericProxyError(tr("SOCKS proxy error"), ec.message ()); - }); - - } - else - GenericProxyError(tr("Cannot connect to upstream SOCKS proxy"), ec.message()); - } - - void HTTPReqHandler::HandoverToUpstreamProxy() - { - LogPrint(eLogDebug, "HTTPProxy: Handover to SOCKS proxy"); - auto connection = CreateSocketsPipe (GetOwner(), m_proxysock, m_sock); - m_sock = nullptr; - m_proxysock = nullptr; - GetOwner()->AddHandler(connection); - connection->Start(); - Terminate(); - } - - void HTTPReqHandler::HTTPConnect(std::string_view host, uint16_t port) - { - LogPrint(eLogDebug, "HTTPProxy: CONNECT ",host, ":", port); - if(str_rmatch(host, ".i2p")) - GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleHTTPConnectStreamRequestComplete, - shared_from_this(), std::placeholders::_1), host, port); - else - ForwardToUpstreamProxy(); - } - - void HTTPReqHandler::HandleHTTPConnectStreamRequestComplete(std::shared_ptr stream) - { - if(stream) - { - m_ClientResponse.code = 200; - m_ClientResponse.status = "OK"; - m_send_buf = m_ClientResponse.to_string(); - m_sock->send(boost::asio::buffer(m_send_buf)); - auto connection = std::make_shared(GetOwner(), m_sock, stream); - GetOwner()->AddHandler(connection); - connection->I2PConnect(); - m_sock = nullptr; - Terminate(); - } - else - { - GenericProxyError(tr("CONNECT error"), tr("Failed to connect")); - } - } - - void HTTPReqHandler::SocksProxySuccess() - { - if(m_ClientRequest.method == "CONNECT") { - m_ClientResponse.code = 200; - m_send_buf = m_ClientResponse.to_string(); - boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) - { - if(ec) GenericProxyError(tr("SOCKS proxy error"), ec.message()); - else HandoverToUpstreamProxy(); - }); - } else { - m_send_buf = m_ClientRequestBuffer.str(); - LogPrint(eLogDebug, "HTTPProxy: Send ", m_send_buf.size(), " bytes"); - boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred) - { - if(ec) GenericProxyError(tr("Failed to send request to upstream"), ec.message()); - else HandoverToUpstreamProxy(); - }); - } - } - - void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec) - { - if(!ec) { - LogPrint(eLogDebug, "HTTPProxy: Connected to http upstream"); - GenericProxyError(tr("Cannot connect"), tr("HTTP out proxy not implemented")); - } else GenericProxyError(tr("Cannot connect to upstream HTTP proxy"), ec.message()); - } - - /* will be called after some data received from client */ - void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) - { - LogPrint(eLogDebug, "HTTPProxy: Sock recv: ", len, " bytes, recv buf: ", m_recv_buf.length(), ", send buf: ", m_send_buf.length()); - if(ecode) - { - LogPrint(eLogWarning, "HTTPProxy: Sock recv got error: ", ecode); - Terminate(); - return; - } - - m_recv_buf.append(reinterpret_cast(m_recv_chunk), len); - if (HandleRequest()) { - m_recv_buf.clear(); - return; - } - AsyncSockRead(); - } - - void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode) - { - if (ecode) - LogPrint (eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message ()); - Terminate(); - } - - void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr stream) - { - if (!stream) { - LogPrint (eLogError, "HTTPProxy: Error when creating the stream, check the previous warnings for more info"); - GenericProxyError(tr("Host is down"), tr("Can't create connection to requested host, it may be down. Please try again later.")); - return; - } - if (Kill()) - return; - LogPrint (eLogDebug, "HTTPProxy: Created new I2PTunnel stream, sSID=", stream->GetSendStreamID(), ", rSID=", stream->GetRecvStreamID()); - auto connection = std::make_shared(GetOwner(), m_sock, stream); - GetOwner()->AddHandler (connection); - connection->I2PConnect (reinterpret_cast(m_send_buf.data()), m_send_buf.length()); - Done (shared_from_this()); - } - - HTTPProxy::HTTPProxy(const std::string& name, const std::string& address, uint16_t port, - const std::string & outproxy, bool addresshelper, bool senduseragent, std::shared_ptr localDestination): - TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), - m_Name (name), m_OutproxyUrl (outproxy), m_Addresshelper (addresshelper), m_SendUserAgent (senduseragent) - { - } - - std::shared_ptr HTTPProxy::CreateHandler(std::shared_ptr socket) - { - return std::make_shared (this, socket); - } -} // http -} // i2p diff --git a/libi2pd_client/HTTPProxy.h b/libi2pd_client/HTTPProxy.h deleted file mode 100644 index 507a87e2..00000000 --- a/libi2pd_client/HTTPProxy.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -* Copyright (c) 2013-2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef HTTP_PROXY_H__ -#define HTTP_PROXY_H__ - -namespace i2p { -namespace proxy { - class HTTPProxy: public i2p::client::TCPIPAcceptor - { - public: - - HTTPProxy(const std::string& name, const std::string& address, uint16_t port, const std::string & outproxy, - bool addresshelper, bool senduseragent, std::shared_ptr localDestination); - HTTPProxy(const std::string& name, const std::string& address, uint16_t port, std::shared_ptr localDestination = nullptr) : - HTTPProxy(name, address, port, "", true, false, localDestination) {} ; - ~HTTPProxy() {}; - - std::string GetOutproxyURL() const { return m_OutproxyUrl; } - bool GetHelperSupport() const { return m_Addresshelper; } - bool GetSendUserAgent () const { return m_SendUserAgent; } - - protected: - - // Implements TCPIPAcceptor - std::shared_ptr CreateHandler(std::shared_ptr socket); - const char* GetName() { return m_Name.c_str (); } - - private: - - std::string m_Name; - std::string m_OutproxyUrl; - bool m_Addresshelper, m_SendUserAgent; - }; -} // http -} // i2p - -#endif diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp deleted file mode 100644 index 11278e7a..00000000 --- a/libi2pd_client/I2CP.cpp +++ /dev/null @@ -1,1205 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include -#include "I2PEndian.h" -#include "Log.h" -#include "Timestamp.h" -#include "LeaseSet.h" -#include "ClientContext.h" -#include "Transports.h" -#include "Signature.h" -#include "Config.h" -#include "I2CP.h" - -namespace i2p -{ -namespace client -{ - - I2CPDestination::I2CPDestination (boost::asio::io_context& service, std::shared_ptr owner, - std::shared_ptr identity, bool isPublic, bool isSameThread, - const std::map& params): - LeaseSetDestination (service, isPublic, ¶ms), - m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()), - m_IsCreatingLeaseSet (false), m_IsSameThread (isSameThread), - m_LeaseSetCreationTimer (service), m_ReadinessCheckTimer (service) - { - } - - void I2CPDestination::Stop () - { - m_LeaseSetCreationTimer.cancel (); - m_ReadinessCheckTimer.cancel (); - LeaseSetDestination::Stop (); - m_Owner = nullptr; - } - - void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key) - { - m_Decryptor = i2p::data::PrivateKeys::CreateDecryptor (m_Identity->GetCryptoKeyType (), key); - } - - void I2CPDestination::SetECIESx25519EncryptionPrivateKey (const uint8_t * key) - { - if (!m_ECIESx25519Decryptor || memcmp (m_ECIESx25519PrivateKey, key, 32)) // new key? - { - m_ECIESx25519Decryptor = std::make_shared(key, true); // calculate public - memcpy (m_ECIESx25519PrivateKey, key, 32); - } - } - - bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const - { - if (preferredCrypto == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor) - return m_ECIESx25519Decryptor->Decrypt (encrypted, data); - if (m_Decryptor) - return m_Decryptor->Decrypt (encrypted, data); - else - LogPrint (eLogError, "I2CP: Decryptor is not set"); - return false; - } - - const uint8_t * I2CPDestination::GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const - { - if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD && m_ECIESx25519Decryptor) - return m_ECIESx25519Decryptor->GetPubicKey (); - return nullptr; - } - - bool I2CPDestination::SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const - { - return keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD ? (bool)m_ECIESx25519Decryptor : m_EncryptionKeyType == keyType; - } - - i2p::data::CryptoKeyType I2CPDestination::GetPreferredCryptoType () const - { - if (m_ECIESx25519Decryptor) - return i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; - return i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; - } - - void I2CPDestination::HandleDataMessage (const uint8_t * buf, size_t len) - { - uint32_t length = bufbe32toh (buf); - if (length > len - 4) length = len - 4; - if (m_Owner) - m_Owner->SendMessagePayloadMessage (buf + 4, length); - } - - void I2CPDestination::CreateNewLeaseSet (const std::vector >& tunnels) - { - boost::asio::post (GetService (), std::bind (&I2CPDestination::PostCreateNewLeaseSet, GetSharedFromThis (), tunnels)); - } - - void I2CPDestination::PostCreateNewLeaseSet (std::vector > tunnels) - { - if (m_IsCreatingLeaseSet) - { - LogPrint (eLogInfo, "I2CP: LeaseSet is being created"); - return; - } - m_ReadinessCheckTimer.cancel (); - auto pool = GetTunnelPool (); - if (!pool || pool->GetOutboundTunnels ().empty ()) - { - // try again later - m_ReadinessCheckTimer.expires_from_now (boost::posix_time::seconds(I2CP_DESTINATION_READINESS_CHECK_INTERVAL)); - m_ReadinessCheckTimer.async_wait( - [s=GetSharedFromThis (), tunnels=std::move(tunnels)](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - s->PostCreateNewLeaseSet (tunnels); - }); - return; - } - uint8_t priv[256] = {0}; - i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only - m_LeaseSetExpirationTime = ls.GetExpirationTime (); - uint8_t * leases = ls.GetLeases (); - int numLeases = leases[-1]; - if (m_Owner && numLeases) - { - uint16_t sessionID = m_Owner->GetSessionID (); - if (sessionID != 0xFFFF) - { - m_IsCreatingLeaseSet = true; - htobe16buf (leases - 3, sessionID); - size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*numLeases; - m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l); - m_LeaseSetCreationTimer.expires_from_now (boost::posix_time::seconds (I2CP_LEASESET_CREATION_TIMEOUT)); - auto s = GetSharedFromThis (); - m_LeaseSetCreationTimer.async_wait ([s](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogInfo, "I2CP: LeaseSet creation timeout expired. Terminate"); - if (s->m_Owner) s->m_Owner->Stop (); - } - }); - } - } - else - LogPrint (eLogError, "I2CP: Can't request LeaseSet"); - } - - void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len) - { - m_IsCreatingLeaseSet = false; - m_LeaseSetCreationTimer.cancel (); - auto ls = std::make_shared (m_Identity, buf, len); - ls->SetExpirationTime (m_LeaseSetExpirationTime); - SetLeaseSet (ls); - } - - void I2CPDestination::LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len) - { - m_IsCreatingLeaseSet = false; - m_LeaseSetCreationTimer.cancel (); - auto ls = (storeType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ? - std::make_shared (m_Identity, buf, len): - std::make_shared (storeType, m_Identity, buf, len); - ls->SetExpirationTime (m_LeaseSetExpirationTime); - SetLeaseSet (ls); - } - - void I2CPDestination::SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce) - { - auto msg = m_I2NPMsgsPool.AcquireSharedMt (); - uint8_t * buf = msg->GetPayload (); - htobe32buf (buf, len); - memcpy (buf + 4, payload, len); - msg->len += len + 4; - msg->FillI2NPMessageHeader (eI2NPData); - auto remote = FindLeaseSet (ident); - if (remote) - { - if (m_IsSameThread) - { - // send right a way - bool sent = SendMsg (msg, remote); - if (m_Owner) - m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure); - } - else - { - // send in destination's thread - auto s = GetSharedFromThis (); - boost::asio::post (GetService (), - [s, msg, remote, nonce]() - { - bool sent = s->SendMsg (msg, remote); - if (s->m_Owner) - s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure); - }); - } - } - else - { - auto s = GetSharedFromThis (); - RequestDestination (ident, - [s, msg, nonce](std::shared_ptr ls) - { - if (ls) - { - bool sent = s->SendMsg (msg, ls); - if (s->m_Owner) - s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure); - } - else if (s->m_Owner) - s->m_Owner->SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLeaseSet); - }); - } - } - - bool I2CPDestination::SendMsg (std::shared_ptr msg, std::shared_ptr remote) - { - auto remoteSession = GetRoutingSession (remote, true); - if (!remoteSession) - { - LogPrint (eLogError, "I2CP: Failed to create remote session"); - return false; - } - auto garlic = remoteSession->WrapSingleMessage (msg); // shared routing path mitgh be dropped here - auto path = remoteSession->GetSharedRoutingPath (); - std::shared_ptr outboundTunnel; - std::shared_ptr remoteLease; - if (path) - { - if (!remoteSession->CleanupUnconfirmedTags ()) // no stuck tags - { - outboundTunnel = path->outboundTunnel; - remoteLease = path->remoteLease; - } - else - remoteSession->SetSharedRoutingPath (nullptr); - } - if (!outboundTunnel || !remoteLease) - { - auto leases = remote->GetNonExpiredLeases (false); // without threshold - if (leases.empty ()) - leases = remote->GetNonExpiredLeases (true); // with threshold - if (!leases.empty ()) - { - auto pool = GetTunnelPool (); - remoteLease = leases[(pool ? pool->GetRng ()() : rand ()) % leases.size ()]; - auto leaseRouter = i2p::data::netdb.FindRouter (remoteLease->tunnelGateway); - outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel (nullptr, - leaseRouter ? leaseRouter->GetCompatibleTransports (false) : (i2p::data::RouterInfo::CompatibleTransports)i2p::data::RouterInfo::eAllTransports); - } - if (remoteLease && outboundTunnel) - remoteSession->SetSharedRoutingPath (std::make_shared ( - i2p::garlic::GarlicRoutingPath{outboundTunnel, remoteLease, 10000, 0})); // 10 secs RTT - else - remoteSession->SetSharedRoutingPath (nullptr); - } - m_Owner->AddRoutingSession (remote->GetIdentity ()->GetStandardIdentity ().signingKey + 96, remoteSession); // last 32 bytes - return SendMsg (garlic, outboundTunnel, remoteLease); - } - - bool I2CPDestination::SendMsg (std::shared_ptr garlic, - std::shared_ptr outboundTunnel, std::shared_ptr remoteLease) - { - if (remoteLease && outboundTunnel) - { - outboundTunnel->SendTunnelDataMsgs ( - { - i2p::tunnel::TunnelMessageBlock - { - i2p::tunnel::eDeliveryTypeTunnel, - remoteLease->tunnelGateway, remoteLease->tunnelID, - garlic - } - }); - return true; - } - else - { - if (outboundTunnel) - LogPrint (eLogWarning, "I2CP: Failed to send message. All leases expired"); - else - LogPrint (eLogWarning, "I2CP: Failed to send message. No outbound tunnels"); - return false; - } - } - - bool I2CPDestination::SendMsg (const uint8_t * payload, size_t len, - std::shared_ptr remoteSession, uint32_t nonce) - { - if (!remoteSession) return false; - auto path = remoteSession->GetSharedRoutingPath (); - if (!path) return false; - // get tunnels - std::shared_ptr outboundTunnel; - std::shared_ptr remoteLease; - if (!remoteSession->CleanupUnconfirmedTags ()) // no stuck tags - { - outboundTunnel = path->outboundTunnel; - remoteLease = path->remoteLease; - } - else - { - remoteSession->SetSharedRoutingPath (nullptr); - return false; - } - // create Data message - auto msg = m_I2NPMsgsPool.AcquireSharedMt (); - uint8_t * buf = msg->GetPayload (); - htobe32buf (buf, len); - memcpy (buf + 4, payload, len); - msg->len += len + 4; - msg->FillI2NPMessageHeader (eI2NPData); - // wrap in gralic - auto garlic = remoteSession->WrapSingleMessage (msg); - // send - bool sent = SendMsg (garlic, outboundTunnel, remoteLease); - m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure); - if (!sent) - remoteSession->SetSharedRoutingPath (nullptr); - return sent; - } - - void I2CPDestination::CleanupDestination () - { - m_I2NPMsgsPool.CleanUpMt (); - if (m_Owner) m_Owner->CleanupRoutingSessions (); - } - - RunnableI2CPDestination::RunnableI2CPDestination (std::shared_ptr owner, - std::shared_ptr identity, bool isPublic, const std::map& params): - RunnableService ("I2CP"), - I2CPDestination (GetIOService (), owner, identity, isPublic, false, 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 socket): - m_Owner (owner), m_Socket (socket), m_SessionID (0xFFFF), m_MessageID (0), - m_IsSendAccepted (true), m_IsSending (false) - { - } - - I2CPSession::~I2CPSession () - { - Terminate (); - } - - void I2CPSession::Start () - { - if (m_Socket) - { - m_Socket->set_option (boost::asio::socket_base::receive_buffer_size (I2CP_MAX_MESSAGE_LENGTH)); - m_Socket->set_option (boost::asio::socket_base::send_buffer_size (I2CP_MAX_MESSAGE_LENGTH)); - } - ReadProtocolByte (); - } - - void I2CPSession::Stop () - { - Terminate (); - } - - void I2CPSession::ReadProtocolByte () - { - if (m_Socket) - { - auto s = shared_from_this (); - m_Socket->async_read_some (boost::asio::buffer (m_Header, 1), - [s](const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (!ecode && bytes_transferred > 0 && s->m_Header[0] == I2CP_PROTOCOL_BYTE) - s->ReceiveHeader (); - else - s->Terminate (); - }); - } - } - - void I2CPSession::ReceiveHeader () - { - if (!m_Socket) - { - LogPrint (eLogError, "I2CP: Can't receive header"); - return; - } - boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Header, I2CP_HEADER_SIZE), - boost::asio::transfer_all (), - std::bind (&I2CPSession::HandleReceivedHeader, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - - void I2CPSession::HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - Terminate (); - else - { - m_PayloadLen = bufbe32toh (m_Header + I2CP_HEADER_LENGTH_OFFSET); - if (m_PayloadLen > 0) - { - if (m_PayloadLen <= I2CP_MAX_MESSAGE_LENGTH) - { - if (!m_Socket) return; - boost::system::error_code ec; - size_t moreBytes = m_Socket->available(ec); - if (!ec) - { - if (moreBytes >= m_PayloadLen) - { - // read and process payload immediately if available - moreBytes = boost::asio::read (*m_Socket, boost::asio::buffer(m_Payload, m_PayloadLen), boost::asio::transfer_all (), ec); - HandleReceivedPayload (ec, moreBytes); - } - else - ReceivePayload (); - } - else - { - LogPrint (eLogWarning, "I2CP: Socket error: ", ec.message ()); - Terminate (); - } - } - else - { - LogPrint (eLogError, "I2CP: Unexpected payload length ", m_PayloadLen); - Terminate (); - } - } - else // no following payload - { - HandleMessage (); - ReceiveHeader (); // next message - } - } - } - - void I2CPSession::ReceivePayload () - { - if (!m_Socket) - { - LogPrint (eLogError, "I2CP: Can't receive payload"); - return; - } - boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Payload, m_PayloadLen), - boost::asio::transfer_all (), - std::bind (&I2CPSession::HandleReceivedPayload, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - - void I2CPSession::HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - Terminate (); - else - { - HandleMessage (); - m_PayloadLen = 0; - ReceiveHeader (); // next message - } - } - - void I2CPSession::HandleMessage () - { - auto handler = m_Owner.GetMessagesHandlers ()[m_Header[I2CP_HEADER_TYPE_OFFSET]]; - if (handler) - (this->*handler)(m_Payload, m_PayloadLen); - else - LogPrint (eLogError, "I2CP: Unknown I2CP message ", (int)m_Header[I2CP_HEADER_TYPE_OFFSET]); - } - - void I2CPSession::Terminate () - { - if (m_Destination) - { - m_Destination->Stop (); - m_Destination = nullptr; - } - if (m_Socket) - { - m_Socket->close (); - m_Socket = nullptr; - } - if (!m_SendQueue.IsEmpty ()) - m_SendQueue.CleanUp (); - if (m_SessionID != 0xFFFF) - { - m_Owner.RemoveSession (GetSessionID ()); - LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " terminated"); - m_SessionID = 0xFFFF; - } - } - - void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len) - { - auto l = len + I2CP_HEADER_SIZE; - if (l > I2CP_MAX_MESSAGE_LENGTH) - { - LogPrint (eLogError, "I2CP: Message to send is too long ", l); - return; - } - auto sendBuf = m_IsSending ? std::make_shared (l) : nullptr; - uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer; - htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len); - buf[I2CP_HEADER_TYPE_OFFSET] = type; - memcpy (buf + I2CP_HEADER_SIZE, payload, len); - if (sendBuf) - { - if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE) - m_SendQueue.Add (std::move(sendBuf)); - else - { - LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); - return; - } - } - else - { - auto socket = m_Socket; - if (socket) - { - m_IsSending = true; - boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l), - boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent, - shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - } - } - - void I2CPSession::HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else if (!m_SendQueue.IsEmpty ()) - { - auto socket = m_Socket; - if (socket) - { - auto len = m_SendQueue.Get (m_SendBuffer, I2CP_MAX_MESSAGE_LENGTH); - boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, len), - boost::asio::transfer_all (),std::bind(&I2CPSession::HandleI2CPMessageSent, - shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - else - m_IsSending = false; - } - else - m_IsSending = false; - } - - std::string_view I2CPSession::ExtractString (const uint8_t * buf, size_t len) const - { - uint8_t l = buf[0]; - if (l > len) l = len; - return { (const char *)(buf + 1), l }; - } - - size_t I2CPSession::PutString (uint8_t * buf, size_t len, std::string_view str) - { - auto l = str.length (); - if (l + 1 >= len) l = len - 1; - if (l > 255) l = 255; // 1 byte max - buf[0] = l; - memcpy (buf + 1, str.data (), l); - return l + 1; - } - - void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map& mapping) const - // TODO: move to Base.cpp - { - size_t offset = 0; - while (offset < len) - { - auto param = ExtractString (buf + offset, len - offset); - offset += param.length () + 1; - if (buf[offset] != '=') - { - LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead '=' after ", param); - break; - } - offset++; - - auto value = ExtractString (buf + offset, len - offset); - offset += value.length () + 1; - if (buf[offset] != ';') - { - LogPrint (eLogWarning, "I2CP: Unexpected character ", buf[offset], " instead ';' after ", value); - break; - } - offset++; - mapping.emplace (param, value); - } - } - - void I2CPSession::GetDateMessageHandler (const uint8_t * buf, size_t len) - { - // get version - auto version = ExtractString (buf, len); - auto l = version.length () + 1 + 8; - uint8_t * payload = new uint8_t[l]; - // set date - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - htobe64buf (payload, ts); - // echo vesrion back - PutString (payload + 8, l - 8, version); - SendI2CPMessage (I2CP_SET_DATE_MESSAGE, payload, l); - delete[] payload; - } - - void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len) - { - RAND_bytes ((uint8_t *)&m_SessionID, 2); - auto identity = std::make_shared(); - size_t offset = identity->FromBuffer (buf, len); - if (!offset) - { - LogPrint (eLogError, "I2CP: Create session malformed identity"); - SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid - return; - } - if (m_Owner.FindSessionByIdentHash (identity->GetIdentHash ())) - { - LogPrint (eLogError, "I2CP: Create session duplicate address ", identity->GetIdentHash ().ToBase32 ()); - SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid - return; - } - uint16_t optionsSize = bufbe16toh (buf + offset); - offset += 2; - if (optionsSize > len - offset) - { - LogPrint (eLogError, "I2CP: Options size ", optionsSize, "exceeds message size"); - SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid - return; - } - std::map params; - ExtractMapping (buf + offset, optionsSize, params); - offset += optionsSize; // options - if (params[I2CP_PARAM_MESSAGE_RELIABILITY] == "none") m_IsSendAccepted = false; - - offset += 8; // date - if (identity->Verify (buf, offset, buf + offset)) // signature - { - if (!m_Destination) - { - m_Destination = m_Owner.IsSingleThread () ? - std::make_shared(m_Owner.GetService (), shared_from_this (), identity, true, true, params): - std::make_shared(shared_from_this (), identity, true, params); - if (m_Owner.InsertSession (shared_from_this ())) - { - LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " created"); - m_Destination->Start (); - SendSessionStatusMessage (eI2CPSessionStatusCreated); // created - } - else - { - LogPrint (eLogError, "I2CP: Session already exists"); - SendSessionStatusMessage (eI2CPSessionStatusRefused); - } - } - else - { - LogPrint (eLogError, "I2CP: Session already exists"); - SendSessionStatusMessage (eI2CPSessionStatusRefused); // refused - } - } - else - { - LogPrint (eLogError, "I2CP: Create session signature verification failed"); - SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid - } - } - - void I2CPSession::DestroySessionMessageHandler (const uint8_t * buf, size_t len) - { - SendSessionStatusMessage (eI2CPSessionStatusDestroyed); // destroy - LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " destroyed"); - Terminate (); - } - - void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len) - { - I2CPSessionStatus status = eI2CPSessionStatusInvalid; // rejected - if(len > sizeof(uint16_t)) - { - uint16_t sessionID = bufbe16toh(buf); - if(sessionID == m_SessionID) - { - buf += sizeof(uint16_t); - const uint8_t * body = buf; - i2p::data::IdentityEx ident; - if(ident.FromBuffer(buf, len - sizeof(uint16_t))) - { - if (ident == *m_Destination->GetIdentity()) - { - size_t identsz = ident.GetFullLen(); - buf += identsz; - uint16_t optssize = bufbe16toh(buf); - if (optssize <= len - sizeof(uint16_t) - sizeof(uint64_t) - identsz - ident.GetSignatureLen() - sizeof(uint16_t)) - { - buf += sizeof(uint16_t); - std::map opts; - ExtractMapping(buf, optssize, opts); - buf += optssize; - //uint64_t date = bufbe64toh(buf); - buf += sizeof(uint64_t); - const uint8_t * sig = buf; - if(ident.Verify(body, len - sizeof(uint16_t) - ident.GetSignatureLen(), sig)) - { - if(m_Destination->Reconfigure(opts)) - { - LogPrint(eLogInfo, "I2CP: Reconfigured destination"); - status = eI2CPSessionStatusUpdated; // updated - } - else - LogPrint(eLogWarning, "I2CP: Failed to reconfigure destination"); - } - else - LogPrint(eLogError, "I2CP: Invalid reconfigure message signature"); - } - else - LogPrint(eLogError, "I2CP: Mapping size mismatch"); - } - else - LogPrint(eLogError, "I2CP: Destination mismatch"); - } - else - LogPrint(eLogError, "I2CP: Malfromed destination"); - } - else - LogPrint(eLogError, "I2CP: Session mismatch"); - } - else - LogPrint(eLogError, "I2CP: Short message"); - SendSessionStatusMessage (status); - } - - void I2CPSession::SendSessionStatusMessage (I2CPSessionStatus status) - { - uint8_t buf[3]; - htobe16buf (buf, m_SessionID); - buf[2] = (uint8_t)status; - SendI2CPMessage (I2CP_SESSION_STATUS_MESSAGE, buf, 3); - } - - void I2CPSession::SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status) - { - if (!nonce) return; // don't send status with zero nonce - uint8_t buf[15]; - htobe16buf (buf, m_SessionID); - htobe32buf (buf + 2, m_MessageID++); - buf[6] = (uint8_t)status; - memset (buf + 7, 0, 4); // size - htobe32buf (buf + 11, nonce); - SendI2CPMessage (I2CP_MESSAGE_STATUS_MESSAGE, buf, 15); - } - - void I2CPSession::AddRoutingSession (const i2p::data::IdentHash& signingKey, std::shared_ptr remoteSession) - { - if (!remoteSession) return; - remoteSession->SetAckRequestInterval (I2CP_SESSION_ACK_REQUEST_INTERVAL); - std::lock_guard l(m_RoutingSessionsMutex); - m_RoutingSessions[signingKey] = remoteSession; - } - - void I2CPSession::CleanupRoutingSessions () - { - std::lock_guard l(m_RoutingSessionsMutex); - for (auto it = m_RoutingSessions.begin (); it != m_RoutingSessions.end ();) - { - if (it->second->IsTerminated ()) - it = m_RoutingSessions.erase (it); - else - it++; - } - } - - void I2CPSession::CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len) - { - uint16_t sessionID = bufbe16toh (buf); - if (sessionID == m_SessionID) - { - size_t offset = 2; - if (m_Destination) - { - offset += i2p::crypto::DSA_PRIVATE_KEY_LENGTH; // skip signing private key - // we always assume this field as 20 bytes (DSA) regardless actual size - // instead of - //offset += m_Destination->GetIdentity ()->GetSigningPrivateKeyLen (); - m_Destination->SetEncryptionPrivateKey (buf + offset); - offset += 256; - m_Destination->LeaseSetCreated (buf + offset, len - offset); - } - } - else - LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); - } - - void I2CPSession::CreateLeaseSet2MessageHandler (const uint8_t * buf, size_t len) - { - uint16_t sessionID = bufbe16toh (buf); - if (sessionID == m_SessionID) - { - size_t offset = 2; - if (m_Destination) - { - uint8_t storeType = buf[offset]; offset++; // store type - i2p::data::LeaseSet2 ls (storeType, buf + offset, len - offset); // outer layer only for encrypted - if (!ls.IsValid ()) - { - LogPrint (eLogError, "I2CP: Invalid LeaseSet2 of type ", storeType); - return; - } - offset += ls.GetBufferLen (); - // private keys - int numPrivateKeys = buf[offset]; offset++; - for (int i = 0; i < numPrivateKeys; i++) - { - if (offset + 4 > len) return; - uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption type - uint16_t keyLen = bufbe16toh (buf + offset); offset += 2; // private key length - if (offset + keyLen > len) return; - if (keyType == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) - m_Destination->SetECIESx25519EncryptionPrivateKey (buf + offset); - else - { - m_Destination->SetEncryptionType (keyType); - m_Destination->SetEncryptionPrivateKey (buf + offset); - } - offset += keyLen; - } - - m_Destination->LeaseSet2Created (storeType, ls.GetBuffer (), ls.GetBufferLen ()); - } - } - else - LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); - } - - void I2CPSession::SendMessageMessageHandler (const uint8_t * buf, size_t len) - { - uint16_t sessionID = bufbe16toh (buf); - if (sessionID == m_SessionID) - { - size_t offset = 2; - if (m_Destination) - { - const uint8_t * ident = buf + offset; - size_t identSize = i2p::data::GetIdentityBufferLen (ident, len - offset); - if (identSize) - { - offset += identSize; - uint32_t payloadLen = bufbe32toh (buf + offset); - if (payloadLen + offset <= len) - { - offset += 4; - uint32_t nonce = bufbe32toh (buf + offset + payloadLen); - if (m_Destination->IsReady ()) - { - if (m_IsSendAccepted) - SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted - std::shared_ptr remoteSession; - { - std::lock_guard l(m_RoutingSessionsMutex); - auto it = m_RoutingSessions.find (ident + i2p::data::DEFAULT_IDENTITY_SIZE - 35); // 32 bytes signing key - if (it != m_RoutingSessions.end ()) - { - if (!it->second->IsTerminated ()) - remoteSession = it->second; - else - m_RoutingSessions.erase (it); - } - } - if (!remoteSession || !m_Destination->SendMsg (buf + offset, payloadLen, remoteSession, nonce)) - { - i2p::data::IdentHash identHash; - SHA256(ident, identSize, identHash); // calculate ident hash, because we don't need full identity - m_Destination->SendMsgTo (buf + offset, payloadLen, identHash, nonce); - } - } - else - { - LogPrint(eLogInfo, "I2CP: Destination is not ready"); - SendMessageStatusMessage (nonce, eI2CPMessageStatusNoLocalTunnels); - } - } - else - LogPrint(eLogError, "I2CP: Cannot send message, too big"); - } - else - LogPrint(eLogError, "I2CP: Invalid identity"); - } - } - else - LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); - } - - void I2CPSession::SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len) - { - SendMessageMessageHandler (buf, len - 8); // ignore flags(2) and expiration(6) - } - - void I2CPSession::HostLookupMessageHandler (const uint8_t * buf, size_t len) - { - uint16_t sessionID = bufbe16toh (buf); - if (sessionID == m_SessionID || sessionID == 0xFFFF) // -1 means without session - { - uint32_t requestID = bufbe32toh (buf + 2); - //uint32_t timeout = bufbe32toh (buf + 6); - i2p::data::IdentHash ident; - switch (buf[10]) - { - case 0: // hash - ident = i2p::data::IdentHash (buf + 11); - break; - case 1: // address - { - auto name = ExtractString (buf + 11, len - 11); - auto addr = i2p::client::context.GetAddressBook ().GetAddress (name); - if (!addr || !addr->IsIdentHash ()) - { - // TODO: handle blinded addresses - LogPrint (eLogError, "I2CP: Address ", name, " not found"); - SendHostReplyMessage (requestID, nullptr); - return; - } - else - ident = addr->identHash; - break; - } - default: - LogPrint (eLogError, "I2CP: Request type ", (int)buf[10], " is not supported"); - SendHostReplyMessage (requestID, nullptr); - return; - } - - std::shared_ptr destination = m_Destination; - if(!destination) destination = i2p::client::context.GetSharedLocalDestination (); - if (destination) - { - auto ls = destination->FindLeaseSet (ident); - if (ls) - SendHostReplyMessage (requestID, ls->GetIdentity ()); - else - { - auto s = shared_from_this (); - destination->RequestDestination (ident, - [s, requestID](std::shared_ptr leaseSet) - { - s->SendHostReplyMessage (requestID, leaseSet ? leaseSet->GetIdentity () : nullptr); - }); - } - } - else - SendHostReplyMessage (requestID, nullptr); - } - else - LogPrint (eLogError, "I2CP: Unexpected sessionID ", sessionID); - } - - void I2CPSession::SendHostReplyMessage (uint32_t requestID, std::shared_ptr identity) - { - if (identity) - { - size_t l = identity->GetFullLen () + 7; - uint8_t * buf = new uint8_t[l]; - htobe16buf (buf, m_SessionID); - htobe32buf (buf + 2, requestID); - buf[6] = 0; // result code - identity->ToBuffer (buf + 7, l - 7); - SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, l); - delete[] buf; - } - else - { - uint8_t buf[7]; - htobe16buf (buf, m_SessionID); - htobe32buf (buf + 2, requestID); - buf[6] = 1; // result code - SendI2CPMessage (I2CP_HOST_REPLY_MESSAGE, buf, 7); - } - } - - void I2CPSession::DestLookupMessageHandler (const uint8_t * buf, size_t len) - { - if (m_Destination) - { - auto ls = m_Destination->FindLeaseSet (buf); - if (ls) - { - auto l = ls->GetIdentity ()->GetFullLen (); - uint8_t * identBuf = new uint8_t[l]; - ls->GetIdentity ()->ToBuffer (identBuf, l); - SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l); - delete[] identBuf; - } - else - { - auto s = shared_from_this (); - i2p::data::IdentHash ident (buf); - m_Destination->RequestDestination (ident, - [s, ident](std::shared_ptr leaseSet) - { - if (leaseSet) // found - { - auto l = leaseSet->GetIdentity ()->GetFullLen (); - uint8_t * identBuf = new uint8_t[l]; - leaseSet->GetIdentity ()->ToBuffer (identBuf, l); - s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, identBuf, l); - delete[] identBuf; - } - else - s->SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, ident, 32); // not found - }); - } - } - else - SendI2CPMessage (I2CP_DEST_REPLY_MESSAGE, buf, 32); - } - - void I2CPSession::GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len) - { - uint8_t limits[64]; - memset (limits, 0, 64); - uint32_t limit; i2p::config::GetOption("i2cp.inboundlimit", limit); - if (!limit) limit = i2p::context.GetBandwidthLimit (); - htobe32buf (limits, limit); // inbound - i2p::config::GetOption("i2cp.outboundlimit", limit); - if (!limit) limit = i2p::context.GetBandwidthLimit (); - htobe32buf (limits + 4, limit); // outbound - SendI2CPMessage (I2CP_BANDWIDTH_LIMITS_MESSAGE, limits, 64); - } - - void I2CPSession::SendMessagePayloadMessage (const uint8_t * payload, size_t len) - { - // we don't use SendI2CPMessage to eliminate additional copy - auto l = len + 10 + I2CP_HEADER_SIZE; - if (l > I2CP_MAX_MESSAGE_LENGTH) - { - LogPrint (eLogError, "I2CP: Message to send is too long ", l); - return; - } - auto sendBuf = m_IsSending ? std::make_shared (l) : nullptr; - uint8_t * buf = sendBuf ? sendBuf->buf : m_SendBuffer; - htobe32buf (buf + I2CP_HEADER_LENGTH_OFFSET, len + 10); - buf[I2CP_HEADER_TYPE_OFFSET] = I2CP_MESSAGE_PAYLOAD_MESSAGE; - htobe16buf (buf + I2CP_HEADER_SIZE, m_SessionID); - htobe32buf (buf + I2CP_HEADER_SIZE + 2, m_MessageID++); - htobe32buf (buf + I2CP_HEADER_SIZE + 6, len); - memcpy (buf + I2CP_HEADER_SIZE + 10, payload, len); - if (sendBuf) - { - if (m_SendQueue.GetSize () < I2CP_MAX_SEND_QUEUE_SIZE) - m_SendQueue.Add (std::move(sendBuf)); - else - { - LogPrint (eLogWarning, "I2CP: Send queue size exceeds ", I2CP_MAX_SEND_QUEUE_SIZE); - return; - } - } - else - { - auto socket = m_Socket; - if (socket) - { - m_IsSending = true; - boost::asio::async_write (*socket, boost::asio::buffer (m_SendBuffer, l), - boost::asio::transfer_all (), std::bind(&I2CPSession::HandleI2CPMessageSent, - shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - } - } - - I2CPServer::I2CPServer (const std::string& interface, uint16_t port, bool isSingleThread): - RunnableService ("I2CP"), m_IsSingleThread (isSingleThread), - m_Acceptor (GetIOService (), - boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(interface), port)) - { - memset (m_MessagesHandlers, 0, sizeof (m_MessagesHandlers)); - m_MessagesHandlers[I2CP_GET_DATE_MESSAGE] = &I2CPSession::GetDateMessageHandler; - m_MessagesHandlers[I2CP_CREATE_SESSION_MESSAGE] = &I2CPSession::CreateSessionMessageHandler; - m_MessagesHandlers[I2CP_DESTROY_SESSION_MESSAGE] = &I2CPSession::DestroySessionMessageHandler; - m_MessagesHandlers[I2CP_RECONFIGURE_SESSION_MESSAGE] = &I2CPSession::ReconfigureSessionMessageHandler; - m_MessagesHandlers[I2CP_CREATE_LEASESET_MESSAGE] = &I2CPSession::CreateLeaseSetMessageHandler; - m_MessagesHandlers[I2CP_CREATE_LEASESET2_MESSAGE] = &I2CPSession::CreateLeaseSet2MessageHandler; - m_MessagesHandlers[I2CP_SEND_MESSAGE_MESSAGE] = &I2CPSession::SendMessageMessageHandler; - m_MessagesHandlers[I2CP_SEND_MESSAGE_EXPIRES_MESSAGE] = &I2CPSession::SendMessageExpiresMessageHandler; - m_MessagesHandlers[I2CP_HOST_LOOKUP_MESSAGE] = &I2CPSession::HostLookupMessageHandler; - m_MessagesHandlers[I2CP_DEST_LOOKUP_MESSAGE] = &I2CPSession::DestLookupMessageHandler; - m_MessagesHandlers[I2CP_GET_BANDWIDTH_LIMITS_MESSAGE] = &I2CPSession::GetBandwidthLimitsMessageHandler; - } - - I2CPServer::~I2CPServer () - { - if (IsRunning ()) - Stop (); - } - - void I2CPServer::Start () - { - Accept (); - StartIOService (); - } - - void I2CPServer::Stop () - { - m_Acceptor.cancel (); - - decltype(m_Sessions) sessions; - m_Sessions.swap (sessions); - for (auto& it: sessions) - it.second->Stop (); - - StopIOService (); - } - - void I2CPServer::Accept () - { - auto newSocket = std::make_shared (GetIOService ()); - m_Acceptor.async_accept (*newSocket, std::bind (&I2CPServer::HandleAccept, this, - std::placeholders::_1, newSocket)); - } - - void I2CPServer::HandleAccept(const boost::system::error_code& ecode, - std::shared_ptr socket) - { - if (!ecode && socket) - { - boost::system::error_code ec; - auto ep = socket->remote_endpoint (ec); - if (!ec) - { - LogPrint (eLogDebug, "I2CP: New connection from ", ep); - auto session = std::make_shared(*this, socket); - session->Start (); - } - else - LogPrint (eLogError, "I2CP: Incoming connection error ", ec.message ()); - } - else - LogPrint (eLogError, "I2CP: Accept error: ", ecode.message ()); - - if (ecode != boost::asio::error::operation_aborted) - Accept (); - } - - bool I2CPServer::InsertSession (std::shared_ptr session) - { - if (!session) return false; - if (!m_Sessions.insert({session->GetSessionID (), session}).second) - { - LogPrint (eLogError, "I2CP: Duplicate session id ", session->GetSessionID ()); - return false; - } - return true; - } - - void I2CPServer::RemoveSession (uint16_t sessionID) - { - m_Sessions.erase (sessionID); - } - - std::shared_ptr I2CPServer::FindSessionByIdentHash (const i2p::data::IdentHash& ident) const - { - for (const auto& it: m_Sessions) - { - if (it.second) - { - auto dest = it.second->GetDestination (); - if (dest && dest->GetIdentHash () == ident) - return it.second; - } - } - return nullptr; - } -} -} diff --git a/libi2pd_client/I2CP.h b/libi2pd_client/I2CP.h deleted file mode 100644 index 37c14dbb..00000000 --- a/libi2pd_client/I2CP.h +++ /dev/null @@ -1,272 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 I2CP_H__ -#define I2CP_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "util.h" -#include "Destination.h" -#include "Streaming.h" -#include "CryptoKey.h" - -namespace i2p -{ -namespace client -{ - const uint8_t I2CP_PROTOCOL_BYTE = 0x2A; - const size_t I2CP_SESSION_BUFFER_SIZE = 4096; - const size_t I2CP_MAX_MESSAGE_LENGTH = 65535; - const size_t I2CP_MAX_SEND_QUEUE_SIZE = 1024*1024; // in bytes, 1M - const int I2CP_LEASESET_CREATION_TIMEOUT = 10; // in seconds - const int I2CP_DESTINATION_READINESS_CHECK_INTERVAL = 5; // in seconds - const int I2CP_SESSION_ACK_REQUEST_INTERVAL = 12100; // in milliseconds - - const size_t I2CP_HEADER_LENGTH_OFFSET = 0; - const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4; - const size_t I2CP_HEADER_SIZE = I2CP_HEADER_TYPE_OFFSET + 1; - - const uint8_t I2CP_GET_DATE_MESSAGE = 32; - const uint8_t I2CP_SET_DATE_MESSAGE = 33; - const uint8_t I2CP_CREATE_SESSION_MESSAGE = 1; - const uint8_t I2CP_RECONFIGURE_SESSION_MESSAGE = 2; - const uint8_t I2CP_SESSION_STATUS_MESSAGE = 20; - const uint8_t I2CP_DESTROY_SESSION_MESSAGE = 3; - const uint8_t I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE = 37; - const uint8_t I2CP_CREATE_LEASESET_MESSAGE = 4; - const uint8_t I2CP_CREATE_LEASESET2_MESSAGE = 41; - const uint8_t I2CP_SEND_MESSAGE_MESSAGE = 5; - const uint8_t I2CP_SEND_MESSAGE_EXPIRES_MESSAGE = 36; - const uint8_t I2CP_MESSAGE_PAYLOAD_MESSAGE = 31; - const uint8_t I2CP_MESSAGE_STATUS_MESSAGE = 22; - const uint8_t I2CP_HOST_LOOKUP_MESSAGE = 38; - const uint8_t I2CP_HOST_REPLY_MESSAGE = 39; - const uint8_t I2CP_DEST_LOOKUP_MESSAGE = 34; - const uint8_t I2CP_DEST_REPLY_MESSAGE = 35; - const uint8_t I2CP_GET_BANDWIDTH_LIMITS_MESSAGE = 8; - const uint8_t I2CP_BANDWIDTH_LIMITS_MESSAGE = 23; - - enum I2CPMessageStatus - { - eI2CPMessageStatusAccepted = 1, - eI2CPMessageStatusGuaranteedSuccess = 4, - eI2CPMessageStatusGuaranteedFailure = 5, - eI2CPMessageStatusNoLocalTunnels = 16, - eI2CPMessageStatusNoLeaseSet = 21 - }; - - enum I2CPSessionStatus - { - eI2CPSessionStatusDestroyed = 0, - eI2CPSessionStatusCreated = 1, - eI2CPSessionStatusUpdated = 2, - eI2CPSessionStatusInvalid = 3, - eI2CPSessionStatusRefused = 4 - }; - - // params - const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability"; - - class I2CPSession; - class I2CPDestination: public LeaseSetDestination - { - public: - - I2CPDestination (boost::asio::io_context& service, std::shared_ptr owner, - std::shared_ptr identity, bool isPublic, bool isSameThread, - const std::map& params); - ~I2CPDestination () {}; - - void Stop () override; - - void SetEncryptionPrivateKey (const uint8_t * key); - void SetEncryptionType (i2p::data::CryptoKeyType keyType) { m_EncryptionKeyType = keyType; }; - void SetECIESx25519EncryptionPrivateKey (const uint8_t * key); - void LeaseSetCreated (const uint8_t * buf, size_t len); // called from I2CPSession - void LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len); // called from I2CPSession - void SendMsgTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint32_t nonce); // called from I2CPSession - bool SendMsg (const uint8_t * payload, size_t len, std::shared_ptr remoteSession, uint32_t nonce); - - // implements LocalDestination - bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override; - bool SupportsEncryptionType (i2p::data::CryptoKeyType keyType) const override; - const uint8_t * GetEncryptionPublicKey (i2p::data::CryptoKeyType keyType) const override; // for 4 only - std::shared_ptr GetIdentity () const override { return m_Identity; }; - - protected: - - // GarlicDestination - i2p::data::CryptoKeyType GetRatchetsHighestCryptoType () const override - { - return m_ECIESx25519Decryptor ? i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD : 0; - } - // LeaseSetDestination - void CleanupDestination () override; - i2p::data::CryptoKeyType GetPreferredCryptoType () const override; - // I2CP - void HandleDataMessage (const uint8_t * buf, size_t len) override; - void CreateNewLeaseSet (const std::vector >& tunnels) override; - - private: - - std::shared_ptr GetSharedFromThis () - { return std::static_pointer_cast(shared_from_this ()); } - bool SendMsg (std::shared_ptr msg, std::shared_ptr remote); - bool SendMsg (std::shared_ptr garlic, - std::shared_ptr outboundTunnel, std::shared_ptr remoteLease); - - void PostCreateNewLeaseSet (std::vector > tunnels); - - private: - - std::shared_ptr m_Owner; - std::shared_ptr m_Identity; - i2p::data::CryptoKeyType m_EncryptionKeyType; - std::shared_ptr m_Decryptor; // standard - std::shared_ptr m_ECIESx25519Decryptor; - uint8_t m_ECIESx25519PrivateKey[32]; - uint64_t m_LeaseSetExpirationTime; - bool m_IsCreatingLeaseSet, m_IsSameThread; - boost::asio::deadline_timer m_LeaseSetCreationTimer, m_ReadinessCheckTimer; - i2p::util::MemoryPoolMt > m_I2NPMsgsPool; - }; - - class RunnableI2CPDestination: private i2p::util::RunnableService, public I2CPDestination - { - public: - - RunnableI2CPDestination (std::shared_ptr owner, std::shared_ptr identity, - bool isPublic, const std::map& params); - ~RunnableI2CPDestination (); - - void Start (); - void Stop (); - }; - - class I2CPServer; - class I2CPSession: public std::enable_shared_from_this - { - public: - - I2CPSession (I2CPServer& owner, std::shared_ptr socket); - - ~I2CPSession (); - - void Start (); - void Stop (); - uint16_t GetSessionID () const { return m_SessionID; }; - std::shared_ptr GetDestination () const { return m_Destination; }; - - // called from I2CPDestination - void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len); - void SendMessagePayloadMessage (const uint8_t * payload, size_t len); - void SendMessageStatusMessage (uint32_t nonce, I2CPMessageStatus status); - void AddRoutingSession (const i2p::data::IdentHash& signingKey, std::shared_ptr remoteSession); - void CleanupRoutingSessions (); - - // message handlers - void GetDateMessageHandler (const uint8_t * buf, size_t len); - void CreateSessionMessageHandler (const uint8_t * buf, size_t len); - void DestroySessionMessageHandler (const uint8_t * buf, size_t len); - void ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len); - void CreateLeaseSetMessageHandler (const uint8_t * buf, size_t len); - void CreateLeaseSet2MessageHandler (const uint8_t * buf, size_t len); - void SendMessageMessageHandler (const uint8_t * buf, size_t len); - void SendMessageExpiresMessageHandler (const uint8_t * buf, size_t len); - void HostLookupMessageHandler (const uint8_t * buf, size_t len); - void DestLookupMessageHandler (const uint8_t * buf, size_t len); - void GetBandwidthLimitsMessageHandler (const uint8_t * buf, size_t len); - - private: - - void ReadProtocolByte (); - void ReceiveHeader (); - void HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void ReceivePayload (); - void HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleMessage (); - void Terminate (); - - void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - - std::string_view ExtractString (const uint8_t * buf, size_t len) const; - size_t PutString (uint8_t * buf, size_t len, std::string_view str); - void ExtractMapping (const uint8_t * buf, size_t len, std::map& mapping) const; - void SendSessionStatusMessage (I2CPSessionStatus status); - void SendHostReplyMessage (uint32_t requestID, std::shared_ptr identity); - - private: - - I2CPServer& m_Owner; - std::shared_ptr m_Socket; - uint8_t m_Header[I2CP_HEADER_SIZE], m_Payload[I2CP_MAX_MESSAGE_LENGTH]; - size_t m_PayloadLen; - - std::shared_ptr m_Destination; - std::mutex m_RoutingSessionsMutex; - std::unordered_map > m_RoutingSessions; // signing key->session - uint16_t m_SessionID; - uint32_t m_MessageID; - bool m_IsSendAccepted; - - // to client - bool m_IsSending; - uint8_t m_SendBuffer[I2CP_MAX_MESSAGE_LENGTH]; - i2p::stream::SendBufferQueue m_SendQueue; - }; - typedef void (I2CPSession::*I2CPMessageHandler)(const uint8_t * buf, size_t len); - - class I2CPServer: private i2p::util::RunnableService - { - public: - - I2CPServer (const std::string& interface, uint16_t port, bool isSingleThread); - ~I2CPServer (); - - void Start (); - void Stop (); - auto& GetService () { return GetIOService (); }; - bool IsSingleThread () const { return m_IsSingleThread; }; - - bool InsertSession (std::shared_ptr session); - void RemoveSession (uint16_t sessionID); - std::shared_ptr FindSessionByIdentHash (const i2p::data::IdentHash& ident) const; - - private: - - void Accept (); - - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); - - private: - - bool m_IsSingleThread; - I2CPMessageHandler m_MessagesHandlers[256]; - std::map > m_Sessions; - - boost::asio::ip::tcp::acceptor m_Acceptor; - - public: - - const decltype(m_MessagesHandlers)& GetMessagesHandlers () const { return m_MessagesHandlers; }; - - // for HTTP - const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; - }; -} -} - -#endif diff --git a/libi2pd_client/I2PService.cpp b/libi2pd_client/I2PService.cpp deleted file mode 100644 index 4ec2648a..00000000 --- a/libi2pd_client/I2PService.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 "Destination.h" -#include "Identity.h" -#include "ClientContext.h" -#include "I2PService.h" -#include - -namespace i2p -{ -namespace client -{ - static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519; - - I2PService::I2PService (std::shared_ptr localDestination): - m_LocalDestination (localDestination ? localDestination : - i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE)), - m_ReadyTimer(m_LocalDestination->GetService()), - m_ReadyTimerTriggered(false), - m_ConnectTimeout(0), - isUpdated (true) - { - m_LocalDestination->Acquire (); - } - - I2PService::I2PService (i2p::data::SigningKeyType kt): - m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt)), - m_ReadyTimer(m_LocalDestination->GetService()), - m_ConnectTimeout(0), - isUpdated (true) - { - m_LocalDestination->Acquire (); - } - - I2PService::~I2PService () - { - ClearHandlers (); - if (m_LocalDestination) m_LocalDestination->Release (); - } - - void I2PService::ClearHandlers () - { - if(m_ConnectTimeout) - m_ReadyTimer.cancel(); - std::unique_lock l(m_HandlersMutex); - for (auto it: m_Handlers) - it->Terminate (); - m_Handlers.clear(); - } - - void I2PService::SetConnectTimeout(uint32_t timeout) - { - m_ConnectTimeout = timeout; - } - - void I2PService::AddReadyCallback(ReadyCallback cb) - { - uint32_t now = i2p::util::GetSecondsSinceEpoch(); - uint32_t tm = (m_ConnectTimeout) ? now + m_ConnectTimeout : NEVER_TIMES_OUT; - - LogPrint(eLogDebug, "I2PService::AddReadyCallback() ", tm, " ", now); - m_ReadyCallbacks.push_back({cb, tm}); - if (!m_ReadyTimerTriggered) TriggerReadyCheckTimer(); - } - - void I2PService::TriggerReadyCheckTimer() - { - m_ReadyTimer.expires_from_now(boost::posix_time::seconds (1)); - m_ReadyTimer.async_wait(std::bind(&I2PService::HandleReadyCheckTimer, shared_from_this (), std::placeholders::_1)); - m_ReadyTimerTriggered = true; - - } - - void I2PService::HandleReadyCheckTimer(const boost::system::error_code &ec) - { - if(ec || m_LocalDestination->IsReady()) - { - for(auto & itr : m_ReadyCallbacks) - itr.first(ec); - m_ReadyCallbacks.clear(); - } - else if(!m_LocalDestination->IsReady()) - { - // expire timed out requests - uint32_t now = i2p::util::GetSecondsSinceEpoch (); - auto itr = m_ReadyCallbacks.begin(); - while(itr != m_ReadyCallbacks.end()) - { - if(itr->second != NEVER_TIMES_OUT && now >= itr->second) - { - itr->first(boost::asio::error::timed_out); - itr = m_ReadyCallbacks.erase(itr); - } - else - ++itr; - } - } - if(!ec && m_ReadyCallbacks.size()) - TriggerReadyCheckTimer(); - else - m_ReadyTimerTriggered = false; - } - - void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, std::string_view dest, uint16_t port) { - assert(streamRequestComplete); - auto address = i2p::client::context.GetAddressBook ().GetAddress (dest); - if (address) - CreateStream(streamRequestComplete, address, port); - else - { - LogPrint (eLogWarning, "I2PService: Remote destination not found: ", dest); - streamRequestComplete (nullptr); - } - } - - void I2PService::CreateStream(StreamRequestComplete streamRequestComplete, std::shared_ptr address, uint16_t port) - { - if(m_ConnectTimeout && !m_LocalDestination->IsReady()) - { - AddReadyCallback([this, streamRequestComplete, address, port] (const boost::system::error_code & ec) - { - if(ec) - { - LogPrint(eLogWarning, "I2PService::CreateStream() ", ec.message()); - streamRequestComplete(nullptr); - } - else - { - if (address->IsIdentHash ()) - this->m_LocalDestination->CreateStream(streamRequestComplete, address->identHash, port); - else - this->m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port); - } - }); - } - else - { - if (address->IsIdentHash ()) - m_LocalDestination->CreateStream (streamRequestComplete, address->identHash, port); - else - m_LocalDestination->CreateStream (streamRequestComplete, address->blindedPublicKey, port); - } - } -} -} diff --git a/libi2pd_client/I2PService.h b/libi2pd_client/I2PService.h deleted file mode 100644 index d19c28e2..00000000 --- a/libi2pd_client/I2PService.h +++ /dev/null @@ -1,291 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 I2PSERVICE_H__ -#define I2PSERVICE_H__ - -#include -#include -#include -#include -#include -#include "Destination.h" -#include "Identity.h" -#include "AddressBook.h" - -namespace i2p -{ -namespace client -{ - class I2PServiceHandler; - class I2PService : public std::enable_shared_from_this - { - public: - - typedef std::function ReadyCallback; - - public: - - I2PService (std::shared_ptr localDestination = nullptr); - I2PService (i2p::data::SigningKeyType kt); - virtual ~I2PService (); - - inline void AddHandler (std::shared_ptr conn) - { - std::unique_lock l(m_HandlersMutex); - m_Handlers.insert(conn); - } - inline void RemoveHandler (std::shared_ptr conn) - { - std::unique_lock l(m_HandlersMutex); - m_Handlers.erase(conn); - } - void ClearHandlers (); - - void SetConnectTimeout(uint32_t timeout); - - void AddReadyCallback(ReadyCallback cb); - - inline std::shared_ptr GetLocalDestination () { return m_LocalDestination; } - inline std::shared_ptr GetLocalDestination () const { return m_LocalDestination; } - inline void SetLocalDestination (std::shared_ptr dest) - { - if (m_LocalDestination) m_LocalDestination->Release (); - if (dest) dest->Acquire (); - m_LocalDestination = dest; - } - void CreateStream (StreamRequestComplete streamRequestComplete, std::string_view dest, uint16_t port = 0); - void CreateStream(StreamRequestComplete complete, std::shared_ptr address, uint16_t port); - auto& GetService () { return m_LocalDestination->GetService (); } - - virtual void Start () = 0; - virtual void Stop () = 0; - - virtual const char* GetName() { return "Generic I2P Service"; } - - private: - - void TriggerReadyCheckTimer(); - void HandleReadyCheckTimer(const boost::system::error_code & ec); - - private: - - std::shared_ptr m_LocalDestination; - std::unordered_set > m_Handlers; - std::mutex m_HandlersMutex; - std::vector > m_ReadyCallbacks; - boost::asio::deadline_timer m_ReadyTimer; - bool m_ReadyTimerTriggered; - uint32_t m_ConnectTimeout; - - const size_t NEVER_TIMES_OUT = 0; - - public: - - bool isUpdated; // transient, used during reload only - }; - - /*Simple interface for I2PHandlers, allows detection of finalization amongst other things */ - class I2PServiceHandler - { - public: - - I2PServiceHandler(I2PService * parent) : m_Service(parent), m_Dead(false) { } - virtual ~I2PServiceHandler() { } - //If you override this make sure you call it from the children - virtual void Handle() {}; //Start handling the socket - virtual void Start () {}; - - void Terminate () { Kill (); }; - - protected: - - // Call when terminating or handing over to avoid race conditions - inline bool Kill () { return m_Dead.exchange(true); } - // Call to know if the handler is dead - inline bool Dead () { return m_Dead; } - // Call when done to clean up (make sure Kill is called first) - inline void Done (std::shared_ptr me) { if(m_Service) m_Service->RemoveHandler(me); } - // Call to talk with the owner - inline I2PService * GetOwner() { return m_Service; } - - private: - - I2PService *m_Service; - std::atomic m_Dead; //To avoid cleaning up multiple times - }; - - const size_t SOCKETS_PIPE_BUFFER_SIZE = 8192 * 8; - - // bidirectional pipe for 2 stream sockets - template - class SocketsPipe: public I2PServiceHandler, - public std::enable_shared_from_this > - { - public: - - SocketsPipe(I2PService * owner, std::shared_ptr upstream, std::shared_ptr downstream): - I2PServiceHandler(owner), m_up(upstream), m_down(downstream) - { - boost::asio::socket_base::receive_buffer_size option(SOCKETS_PIPE_BUFFER_SIZE); - upstream->set_option(option); - downstream->set_option(option); - } - ~SocketsPipe() { Terminate(); } - - void Start() override - { - Transfer (m_up, m_down, m_upstream_to_down_buf, SOCKETS_PIPE_BUFFER_SIZE); // receive from upstream - Transfer (m_down, m_up, m_downstream_to_up_buf, SOCKETS_PIPE_BUFFER_SIZE); // receive from upstream - } - - private: - - void Terminate() - { - if(Kill()) return; - if (m_up) - { - if (m_up->is_open()) - m_up->close(); - m_up = nullptr; - } - if (m_down) - { - if (m_down->is_open()) - m_down->close(); - m_down = nullptr; - } - Done(SocketsPipe::shared_from_this()); - } - - template - void Transfer (std::shared_ptr from, std::shared_ptr to, uint8_t * buf, size_t len) - { - if (!from || !to || !buf) return; - auto s = SocketsPipe::shared_from_this (); - from->async_read_some(boost::asio::buffer(buf, len), - [from, to, s, buf, len](const boost::system::error_code& ecode, std::size_t transferred) - { - if (ecode == boost::asio::error::operation_aborted) return; - if (!ecode) - { - boost::asio::async_write(*to, boost::asio::buffer(buf, transferred), boost::asio::transfer_all(), - [from, to, s, buf, len](const boost::system::error_code& ecode, std::size_t transferred) - { - (void) transferred; - if (ecode == boost::asio::error::operation_aborted) return; - if (!ecode) - s->Transfer (from, to, buf, len); - else - { - LogPrint(eLogWarning, "SocketsPipe: Write error:" , ecode.message()); - s->Terminate(); - } - }); - } - else - { - LogPrint(eLogWarning, "SocketsPipe: Read error:" , ecode.message()); - s->Terminate(); - } - }); - } - - private: - - uint8_t m_upstream_to_down_buf[SOCKETS_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[SOCKETS_PIPE_BUFFER_SIZE]; - std::shared_ptr m_up; - std::shared_ptr m_down; - }; - - template - std::shared_ptr CreateSocketsPipe (I2PService * owner, std::shared_ptr upstream, std::shared_ptr downstream) - { - return std::make_shared >(owner, upstream, downstream); - } - - //This is a service that listens for connections on the IP network or local socket and interacts with I2P - template - class ServiceAcceptor: public I2PService - { - public: - - ServiceAcceptor (const typename Protocol::endpoint& localEndpoint, std::shared_ptr localDestination = nullptr) : - I2PService(localDestination), m_LocalEndpoint (localEndpoint) {} - - virtual ~ServiceAcceptor () { Stop(); } - void Start () override - { - m_Acceptor.reset (new typename Protocol::acceptor (GetService (), m_LocalEndpoint)); - // update the local end point in case port has been set zero and got updated now - m_LocalEndpoint = m_Acceptor->local_endpoint(); - m_Acceptor->listen (); - Accept (); - } - void Stop () override - { - if (m_Acceptor) - { - m_Acceptor->close(); - m_Acceptor.reset (nullptr); - } - ClearHandlers(); - } - const typename Protocol::endpoint& GetLocalEndpoint () const { return m_LocalEndpoint; }; - - const char* GetName() override { return "Generic TCP/IP accepting daemon"; } - - protected: - - virtual std::shared_ptr CreateHandler(std::shared_ptr socket) = 0; - - private: - - void Accept() - { - auto newSocket = std::make_shared (GetService ()); - m_Acceptor->async_accept (*newSocket, - [newSocket, this](const boost::system::error_code& ecode) - { - if (ecode == boost::asio::error::operation_aborted) return; - if (!ecode) - { - LogPrint(eLogDebug, "ServiceAcceptor: ", GetName(), " accepted"); - auto handler = CreateHandler(newSocket); - if (handler) - { - AddHandler(handler); - handler->Handle(); - } - else - newSocket->close(); - Accept(); - } - else - LogPrint (eLogError, "ServiceAcceptor: ", GetName(), " closing socket on accept because: ", ecode.message ()); - }); - } - - private: - - typename Protocol::endpoint m_LocalEndpoint; - std::unique_ptr m_Acceptor; - }; - - class TCPIPAcceptor: public ServiceAcceptor - { - public: - - TCPIPAcceptor (const std::string& address, uint16_t port, std::shared_ptr localDestination = nullptr) : - ServiceAcceptor (boost::asio::ip::tcp::endpoint (boost::asio::ip::make_address(address), port), localDestination) {} - }; -} -} - -#endif diff --git a/libi2pd_client/I2PTunnel.cpp b/libi2pd_client/I2PTunnel.cpp deleted file mode 100644 index d6436c78..00000000 --- a/libi2pd_client/I2PTunnel.cpp +++ /dev/null @@ -1,888 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include "Base.h" -#include "Log.h" -#include "Destination.h" -#include "ClientContext.h" -#include "I2PTunnel.h" -#include "util.h" - -namespace i2p -{ -namespace client -{ - - /** set standard socket options */ - static void I2PTunnelSetSocketOptions (std::shared_ptr socket) - { - if (socket && socket->is_open()) - { - boost::asio::socket_base::receive_buffer_size option(I2P_TUNNEL_CONNECTION_BUFFER_SIZE); - socket->set_option(option); - } - } - - I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr leaseSet, uint16_t port): - I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()) - { - m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port); - } - - I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, - std::shared_ptr socket, std::shared_ptr stream): - I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), - m_RemoteEndpoint (socket->remote_endpoint ()) - { - } - - I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, - const boost::asio::ip::tcp::endpoint& target,std::shared_ptr sslCtx): - I2PServiceHandler(owner), m_Stream (stream), m_RemoteEndpoint (target) - { - m_Socket = std::make_shared (owner->GetService ()); - if (sslCtx) - m_SSL = std::make_shared > (*m_Socket, *sslCtx); - } - - I2PTunnelConnection::~I2PTunnelConnection () - { - } - - void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len) - { - if (m_Stream) - { - if (msg) - m_Stream->Send (msg, len); // connect and send - else - m_Stream->Send (m_Buffer, 0); // connect - } - StreamReceive (); - Receive (); - } - - boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr) - { - boost::asio::ip::address_v4::bytes_type bytes; - const uint8_t * ident = addr; - bytes[0] = 127; - memcpy (bytes.data ()+1, ident, 3); - boost::asio::ip::address ourIP = boost::asio::ip::address_v4 (bytes); - return ourIP; - } - -#ifdef __linux__ - static void MapToLoopback(std::shared_ptr sock, const i2p::data::IdentHash & addr) - { - if (sock) - { - // bind to 127.x.x.x address - // where x.x.x are first three bytes from ident - auto ourIP = GetLoopbackAddressFor(addr); - boost::system::error_code ec; - sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec); - if (ec) - LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ()); - } - } -#endif - - void I2PTunnelConnection::Connect (bool isUniqueLocal) - { - if (m_Socket) - { - I2PTunnelSetSocketOptions (m_Socket); -#ifdef __linux__ - if (isUniqueLocal && m_RemoteEndpoint.address ().is_v4 () && - m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127) - { - m_Socket->open (boost::asio::ip::tcp::v4 ()); - auto ident = m_Stream->GetRemoteIdentity()->GetIdentHash(); - MapToLoopback(m_Socket, ident); - } -#endif - m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect, - shared_from_this (), std::placeholders::_1)); - } - } - - void I2PTunnelConnection::Connect (const boost::asio::ip::address& localAddress) - { - if (m_Socket) - { - if (m_RemoteEndpoint.address().is_v6 ()) - m_Socket->open (boost::asio::ip::tcp::v6 ()); - else - m_Socket->open (boost::asio::ip::tcp::v4 ()); - boost::system::error_code ec; - m_Socket->bind (boost::asio::ip::tcp::endpoint (localAddress, 0), ec); - if (ec) - LogPrint (eLogError, "I2PTunnel: Can't bind to ", localAddress.to_string (), ": ", ec.message ()); - } - Connect (false); - } - - void I2PTunnelConnection::Terminate () - { - if (Kill()) return; - if (m_SSL) m_SSL = nullptr; - if (m_Stream) - { - m_Stream->Close (); - m_Stream.reset (); - } - boost::system::error_code ec; - m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); // avoid RST - m_Socket->close (); - - Done(shared_from_this ()); - } - - void I2PTunnelConnection::Receive () - { - if (m_SSL) - m_SSL->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), - std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - else - m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), - std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void I2PTunnelConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogError, "I2PTunnel: Read error: ", ecode.message ()); - Terminate (); - } - } - else - WriteToStream (m_Buffer, bytes_transferred); - } - - void I2PTunnelConnection::WriteToStream (const uint8_t * buf, size_t len) - { - if (m_Stream) - { - auto s = shared_from_this (); - m_Stream->AsyncSend (buf, len, - [s](const boost::system::error_code& ecode) - { - if (!ecode) - s->Receive (); - else - s->Terminate (); - }); - } - } - - void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint (eLogError, "I2PTunnel: Write error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - StreamReceive (); - } - - void I2PTunnelConnection::StreamReceive () - { - if (m_Stream) - { - if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew || - m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular - { - m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), - std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2), - I2P_TUNNEL_CONNECTION_MAX_IDLE); - } - else // closed by peer - { - // get remaining data - auto len = m_Stream->ReadSome (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE); - if (len > 0) // still some data - Write (m_StreamBuffer, len); - else // no more data - Terminate (); - } - } - } - - void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogError, "I2PTunnel: Stream read error: ", ecode.message ()); - if (bytes_transferred > 0) - Write (m_StreamBuffer, bytes_transferred); // postpone termination - else if (ecode == boost::asio::error::timed_out && m_Stream && m_Stream->IsOpen ()) - StreamReceive (); - else - Terminate (); - } - else - Terminate (); - } - else - Write (m_StreamBuffer, bytes_transferred); - } - - void I2PTunnelConnection::Write (const uint8_t * buf, size_t len) - { - if (m_SSL) - boost::asio::async_write (*m_SSL, boost::asio::buffer (buf, len), boost::asio::transfer_all (), - std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); - else - boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (), - std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); - } - - void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint (eLogError, "I2PTunnel: Connect error: ", ecode.message ()); - Terminate (); - } - else - { - LogPrint (eLogDebug, "I2PTunnel: Connected"); - if (m_SSL) - m_SSL->async_handshake (boost::asio::ssl::stream_base::client, - std::bind (&I2PTunnelConnection::HandleHandshake, shared_from_this (), std::placeholders::_1)); - else - Established (); - } - } - - void I2PTunnelConnection::HandleHandshake (const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint (eLogError, "I2PTunnel: Handshake error: ", ecode.message ()); - Terminate (); - } - else - { - LogPrint (eLogDebug, "I2PTunnel: SSL connected"); - Established (); - } - } - - void I2PTunnelConnection::Established () - { - StreamReceive (); - Receive (); - } - - void I2PClientTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) - { - if (m_HeaderSent) - I2PTunnelConnection::Write (buf, len); - else - { - m_InHeader.clear (); - 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 - { - if (!m_ConnectionSent && !line.compare(0, 10, "Connection")) - { - /* close connection, if not Connection: (U|u)pgrade (for websocket) */ - auto x = line.find("pgrade"); - if (x != std::string::npos && std::tolower(line[x - 1]) == 'u') - m_OutHeader << line << "\r\n"; - else - m_OutHeader << "Connection: close\r\n"; - - m_ConnectionSent = true; - } - else if (!m_ProxyConnectionSent && !line.compare(0, 16, "Proxy-Connection")) - { - m_OutHeader << "Proxy-Connection: close\r\n"; - m_ProxyConnectionSent = true; - } - else - m_OutHeader << line << "\n"; - } - } - else - { - // insert incomplete line back - m_InHeader.clear (); - m_InHeader << line; - break; - } - } - - if (endOfHeader) - { - if (!m_ConnectionSent) m_OutHeader << "Connection: close\r\n"; - if (!m_ProxyConnectionSent) m_OutHeader << "Proxy-Connection: close\r\n"; - m_OutHeader << "\r\n"; // end of header - m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header - m_InHeader.str (""); - m_HeaderSent = true; - I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); - } - else if (m_OutHeader.tellp () < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE) - StreamReceive (); // read more header - else - { - LogPrint (eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE); - Terminate (); - } - } - } - - I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, - const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P, - std::shared_ptr sslCtx): - I2PTunnelConnection (owner, stream, target, sslCtx), m_Host (host), m_XI2P (XI2P), - m_HeaderSent (false), m_ResponseHeaderSent (false) - { - if (sslCtx) - SSL_set_tlsext_host_name(GetSSL ()->native_handle(), host.c_str ()); - } - - void I2PServerTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) - { - if (m_HeaderSent) - I2PTunnelConnection::Write (buf, len); - else - { - m_InHeader.clear (); - m_InHeader.write ((const char *)buf, len); - std::string line; - bool endOfHeader = false, connection = false; - while (!endOfHeader) - { - std::getline(m_InHeader, line); - if (m_InHeader.fail ()) break; - if (!m_InHeader.eof ()) - { - if (line == "\r") endOfHeader = true; - else - { - // strip up some headers - static const std::array excluded // list of excluded headers - { - "Keep-Alive:", "X-I2P" - }; - bool matched = false; - for (const auto& it: excluded) - if (boost::iequals (line.substr (0, it.length ()), it)) - { - matched = true; - break; - } - if (matched) continue; - - // replace some headers - if (!m_Host.empty () && boost::iequals (line.substr (0, 5), "Host:")) - m_OutHeader << "Host: " << m_Host << "\r\n"; // override host - else if (boost::iequals (line.substr (0, 11), "Connection:")) - { - auto x = line.find("pgrade"); - if (x != std::string::npos && x && std::tolower(line[x - 1]) != 'u') // upgrade or Upgrade - m_OutHeader << line << "\n"; - else - m_OutHeader << "Connection: close\r\n"; - connection = true; - } - else // forward as is - m_OutHeader << line << "\n"; - } - } - else - { - // insert incomplete line back - m_InHeader.clear (); - m_InHeader << line; - break; - } - } - - if (endOfHeader) - { - // add Connection if not presented - if (!connection) - m_OutHeader << "Connection: close\r\n"; - // add X-I2P fields - m_OutHeader << m_XI2P; - // end of header - m_OutHeader << "\r\n"; - - m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header - m_InHeader.str (""); - m_HeaderSent = true; - I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); - } - else if (m_OutHeader.tellp () < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE) - StreamReceive (); // read more header - else - { - LogPrint (eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE); - Terminate (); - } - } - } - - 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 ()) break; - if (!m_InHeader.eof ()) - { - if (line == "\r") endOfHeader = true; - else - { - static const std::array 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 - { - // insert incomplete line back - m_InHeader.clear (); - m_InHeader << line; - 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 stream, - const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass, - std::shared_ptr sslCtx): - I2PTunnelConnection (owner, stream, target, sslCtx), m_From (stream->GetRemoteIdentity ()), - m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass) - { - } - - void I2PTunnelConnectionIRC::Write (const uint8_t * buf, size_t len) - { - m_OutPacket.str (""); - if (m_NeedsWebIrc) - { - m_NeedsWebIrc = false; - m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) - << " " << GetSocket ()->local_endpoint ().address () << std::endl; - } - - m_InPacket.clear (); - m_InPacket.write ((const char *)buf, len); - - while (!m_InPacket.eof () && !m_InPacket.fail ()) - { - std::string line; - std::getline (m_InPacket, line); - if (line.length () == 0 && m_InPacket.eof ()) - m_InPacket.str (""); - auto pos = line.find ("USER"); - if (!pos) // start of line - { - pos = line.find (" "); - pos++; - pos = line.find (" ", pos); - pos++; - auto nextpos = line.find (" ", pos); - m_OutPacket << line.substr (0, pos); - m_OutPacket << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()); - m_OutPacket << line.substr (nextpos) << '\n'; - } - else - m_OutPacket << line << '\n'; - } - I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ()); - } - - - /* This handler tries to establish a connection with the desired server and dies if it fails to do so */ - class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this - { - public: - I2PClientTunnelHandler (I2PClientTunnel * parent, std::shared_ptr address, - uint16_t destinationPort, std::shared_ptr socket): - I2PServiceHandler(parent), m_Address(address), - m_DestinationPort (destinationPort), m_Socket(socket) {}; - void Handle(); - void Terminate(); - private: - void HandleStreamRequestComplete (std::shared_ptr stream); - std::shared_ptr m_Address; - uint16_t m_DestinationPort; - std::shared_ptr m_Socket; - }; - - void I2PClientTunnelHandler::Handle() - { - GetOwner()->CreateStream ( - std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1), - m_Address, m_DestinationPort); - } - - void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr stream) - { - if (stream) - { - if (Kill()) return; - LogPrint (eLogDebug, "I2PTunnel: New connection"); - auto connection = std::make_shared(GetOwner(), m_Socket, stream); - GetOwner()->AddHandler (connection); - connection->I2PConnect (); - Done(shared_from_this()); - } - else - { - LogPrint (eLogError, "I2PTunnel: Client Tunnel Issue when creating the stream, check the previous warnings for more info."); - Terminate(); - } - } - - void I2PClientTunnelHandler::Terminate() - { - if (Kill()) return; - if (m_Socket) - { - m_Socket->close(); - m_Socket = nullptr; - } - Done(shared_from_this()); - } - - I2PClientTunnel::I2PClientTunnel (const std::string& name, const std::string& destination, - const std::string& address, uint16_t port, std::shared_ptr localDestination, uint16_t destinationPort): - TCPIPAcceptor (address, port, localDestination), m_Name (name), m_Destination (destination), - m_DestinationPort (destinationPort), m_KeepAliveInterval (0) - { - } - - void I2PClientTunnel::Start () - { - TCPIPAcceptor::Start (); - GetAddress (); - if (m_KeepAliveInterval) - ScheduleKeepAliveTimer (); - } - - void I2PClientTunnel::Stop () - { - TCPIPAcceptor::Stop(); - m_Address = nullptr; - if (m_KeepAliveTimer) m_KeepAliveTimer->cancel (); - } - - void I2PClientTunnel::SetKeepAliveInterval (uint32_t keepAliveInterval) - { - m_KeepAliveInterval = keepAliveInterval; - if (m_KeepAliveInterval) - m_KeepAliveTimer.reset (new boost::asio::deadline_timer (GetLocalDestination ()->GetService ())); - } - - /* HACK: maybe we should create a caching IdentHash provider in AddressBook */ - std::shared_ptr I2PClientTunnel::GetAddress () - { - if (!m_Address) - { - m_Address = i2p::client::context.GetAddressBook ().GetAddress (m_Destination); - if (!m_Address) - LogPrint (eLogWarning, "I2PTunnel: Remote destination ", m_Destination, " not found"); - } - return m_Address; - } - - std::shared_ptr I2PClientTunnel::CreateHandler(std::shared_ptr socket) - { - auto address = GetAddress (); - if (address) - return std::make_shared(this, address, m_DestinationPort, socket); - else - return nullptr; - } - - void I2PClientTunnel::ScheduleKeepAliveTimer () - { - if (m_KeepAliveTimer) - { - m_KeepAliveTimer->expires_from_now (boost::posix_time::seconds (m_KeepAliveInterval)); - m_KeepAliveTimer->async_wait (std::bind (&I2PClientTunnel::HandleKeepAliveTimer, - this, std::placeholders::_1)); - } - } - - void I2PClientTunnel::HandleKeepAliveTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - if (m_Address && m_Address->IsValid ()) - { - if (m_Address->IsIdentHash ()) - GetLocalDestination ()->SendPing (m_Address->identHash); - else - GetLocalDestination ()->SendPing (m_Address->blindedPublicKey); - } - ScheduleKeepAliveTimer (); - } - } - - I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address, - uint16_t port, std::shared_ptr localDestination, uint16_t inport, bool gzip): - I2PService (localDestination), m_IsUniqueLocal(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false) - { - m_PortDestination = localDestination->GetStreamingDestination (inport); - if (!m_PortDestination) // default destination - m_PortDestination = localDestination->CreateStreamingDestination (inport, gzip); - } - - void I2PServerTunnel::Start () - { - m_Endpoint.port (m_Port); - boost::system::error_code ec; - auto addr = boost::asio::ip::make_address (m_Address, ec); - if (!ec) - { - m_Endpoint.address (addr); - Accept (); - } - else - { - auto resolver = std::make_shared(GetService ()); - resolver->async_resolve (m_Address, "", - std::bind (&I2PServerTunnel::HandleResolve, this, - std::placeholders::_1, std::placeholders::_2, resolver)); - } - } - - void I2PServerTunnel::Stop () - { - if (m_PortDestination) - m_PortDestination->ResetAcceptor (); - auto localDestination = GetLocalDestination (); - if (localDestination) - localDestination->StopAcceptingStreams (); - - ClearHandlers (); - } - - void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::results_type endpoints, - std::shared_ptr resolver) - { - if (!ecode) - { - bool found = false; - boost::asio::ip::tcp::endpoint ep; - if (m_LocalAddress) - { - for (const auto& it: endpoints) - { - ep = it; - if (!ep.address ().is_unspecified ()) - { - if (ep.address ().is_v4 ()) - { - if (m_LocalAddress->is_v4 ()) found = true; - } - else if (ep.address ().is_v6 ()) - { - if (i2p::util::net::IsYggdrasilAddress (ep.address ())) - { - if (i2p::util::net::IsYggdrasilAddress (*m_LocalAddress)) - found = true; - } - else if (m_LocalAddress->is_v6 ()) - found = true; - } - } - if (found) break; - } - } - else - { - found = true; - ep = *endpoints.begin (); // first available - } - if (!found) - { - LogPrint (eLogError, "I2PTunnel: Unable to resolve ", m_Address, " to compatible address"); - return; - } - - auto addr = ep.address (); - LogPrint (eLogInfo, "I2PTunnel: Server tunnel ", (*endpoints.begin ()).host_name (), " has been resolved to ", addr); - m_Endpoint.address (addr); - Accept (); - } - else - LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address ", m_Address, ": ", ecode.message ()); - } - - void I2PServerTunnel::SetAccessList (const std::set& accessList) - { - m_AccessList = accessList; - m_IsAccessList = true; - } - - void I2PServerTunnel::SetLocalAddress (const std::string& localAddress) - { - boost::system::error_code ec; - auto addr = boost::asio::ip::make_address(localAddress, ec); - if (!ec) - m_LocalAddress.reset (new boost::asio::ip::address (addr)); - else - LogPrint (eLogError, "I2PTunnel: Can't set local address ", localAddress); - } - - void I2PServerTunnel::SetSSL (bool ssl) - { - if (ssl) - { - m_SSLCtx = std::make_shared (boost::asio::ssl::context::sslv23); - m_SSLCtx->set_verify_mode(boost::asio::ssl::context::verify_none); - } - else - m_SSLCtx = nullptr; - } - - void I2PServerTunnel::Accept () - { - if (m_PortDestination) - m_PortDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); - - auto localDestination = GetLocalDestination (); - if (localDestination) - { - if (!localDestination->IsAcceptingStreams ()) // set it as default if not set yet - localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1)); - } - else - LogPrint (eLogError, "I2PTunnel: Local destination not set for server tunnel"); - } - - void I2PServerTunnel::HandleAccept (std::shared_ptr stream) - { - if (stream) - { - if (m_IsAccessList) - { - if (!m_AccessList.count (stream->GetRemoteIdentity ()->GetIdentHash ())) - { - LogPrint (eLogWarning, "I2PTunnel: Address ", stream->GetRemoteIdentity ()->GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped"); - stream->Close (); - return; - } - } - // new connection - auto conn = CreateI2PConnection (stream); - AddHandler (conn); - if (m_LocalAddress) - conn->Connect (*m_LocalAddress); - else - conn->Connect (m_IsUniqueLocal); - } - } - - std::shared_ptr I2PServerTunnel::CreateI2PConnection (std::shared_ptr stream) - { - return std::make_shared (this, stream, GetEndpoint (), m_SSLCtx); - - } - - I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& name, const std::string& address, - uint16_t port, std::shared_ptr localDestination, - const std::string& host, uint16_t inport, bool gzip): - I2PServerTunnel (name, address, port, localDestination, inport, gzip), - m_Host (host) - { - } - - std::shared_ptr I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr stream) - { - if (m_XI2P.empty () || stream->GetRemoteIdentity () != m_From.lock ()) - { - auto from = stream->GetRemoteIdentity (); - m_From = from; - std::stringstream ss; - ss << X_I2P_DEST_B32 << ": " << context.GetAddressBook ().ToAddress(from->GetIdentHash ()) << "\r\n"; - ss << X_I2P_DEST_HASH << ": " << from->GetIdentHash ().ToBase64 () << "\r\n"; - ss << X_I2P_DEST_B64 << ": " << from->ToBase64 () << "\r\n"; - m_XI2P = ss.str (); - } - return std::make_shared (this, stream, GetEndpoint (), m_Host, m_XI2P, GetSSLCtx ()); - } - - I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address, - uint16_t port, std::shared_ptr localDestination, - const std::string& webircpass, uint16_t inport, bool gzip): - I2PServerTunnel (name, address, port, localDestination, inport, gzip), - m_WebircPass (webircpass) - { - } - - std::shared_ptr I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr stream) - { - return std::make_shared (this, stream, GetEndpoint (), m_WebircPass, GetSSLCtx ()); - } -} -} - diff --git a/libi2pd_client/I2PTunnel.h b/libi2pd_client/I2PTunnel.h deleted file mode 100644 index 7d4c3400..00000000 --- a/libi2pd_client/I2PTunnel.h +++ /dev/null @@ -1,270 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 I2PTUNNEL_H__ -#define I2PTUNNEL_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "Identity.h" -#include "Destination.h" -#include "Streaming.h" -#include "I2PService.h" -#include "AddressBook.h" - -namespace i2p -{ -namespace client -{ - const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 16384; - const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds - const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds - // for HTTP tunnels - constexpr char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64 - constexpr char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64 - constexpr char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address - const int I2P_TUNNEL_HTTP_MAX_HEADER_SIZE = 8192; - - class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this - { - public: - - I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr leaseSet, uint16_t port = 0); // to I2P - I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, - std::shared_ptr stream); // to I2P using simplified API - I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, - const boost::asio::ip::tcp::endpoint& target, - std::shared_ptr sslCtx = nullptr); // from I2P - ~I2PTunnelConnection (); - void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); - void Connect (bool isUniqueLocal = true); - void Connect (const boost::asio::ip::address& localAddress); - - protected: - - virtual void Established (); - void Terminate (); - - void Receive (); - void StreamReceive (); - void HandleStreamReceive (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 WriteToStream (const uint8_t * buf, size_t len); // can be overloaded - - std::shared_ptr GetSocket () const { return m_Socket; }; - std::shared_ptr GetStream () const { return m_Stream; }; - std::shared_ptr > GetSSL () const { return m_SSL; }; - uint8_t * GetStreamBuffer () { return m_StreamBuffer; }; - - private: - - void HandleConnect (const boost::system::error_code& ecode); - void HandleHandshake (const boost::system::error_code& ecode); - void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleWrite (const boost::system::error_code& ecode); - - private: - - uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE]; - std::shared_ptr m_Socket; - std::shared_ptr > m_SSL; - std::shared_ptr m_Stream; - boost::asio::ip::tcp::endpoint m_RemoteEndpoint; - }; - - class I2PClientTunnelConnectionHTTP: public I2PTunnelConnection - { - public: - - I2PClientTunnelConnectionHTTP (I2PService * owner, std::shared_ptr socket, - std::shared_ptr stream): - I2PTunnelConnection (owner, socket, stream), m_HeaderSent (false), - m_ConnectionSent (false), m_ProxyConnectionSent (false) {}; - - protected: - - void Write (const uint8_t * buf, size_t len) override; - - private: - - std::stringstream m_InHeader, m_OutHeader; - bool m_HeaderSent, m_ConnectionSent, m_ProxyConnectionSent; - }; - - class I2PServerTunnelConnectionHTTP: public I2PTunnelConnection - { - public: - - I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, - const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P, - std::shared_ptr sslCtx = nullptr); - - protected: - - void Write (const uint8_t * buf, size_t len) override; - void WriteToStream (const uint8_t * buf, size_t len) override; - - private: - - std::string m_Host, m_XI2P; - std::stringstream m_InHeader, m_OutHeader; - bool m_HeaderSent, m_ResponseHeaderSent; - }; - - class I2PTunnelConnectionIRC: public I2PTunnelConnection - { - public: - - I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr stream, - const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass, - std::shared_ptr sslCtx = nullptr); - - protected: - - void Write (const uint8_t * buf, size_t len) override; - - private: - - std::shared_ptr m_From; - std::stringstream m_OutPacket, m_InPacket; - bool m_NeedsWebIrc; - std::string m_WebircPass; - }; - - - class I2PClientTunnel: public TCPIPAcceptor - { - protected: - - // Implements TCPIPAcceptor - std::shared_ptr CreateHandler(std::shared_ptr socket); - - public: - - I2PClientTunnel (const std::string& name, const std::string& destination, - const std::string& address, uint16_t port, std::shared_ptr localDestination, uint16_t destinationPort = 0); - ~I2PClientTunnel () {} - - void Start (); - void Stop (); - - const char* GetName() { return m_Name.c_str (); } - void SetKeepAliveInterval (uint32_t keepAliveInterval); - - private: - - std::shared_ptr GetAddress (); - - void ScheduleKeepAliveTimer (); - void HandleKeepAliveTimer (const boost::system::error_code& ecode); - - private: - - std::string m_Name, m_Destination; - std::shared_ptr m_Address; - uint16_t m_DestinationPort; - uint32_t m_KeepAliveInterval; - std::unique_ptr m_KeepAliveTimer; - }; - - class I2PServerTunnel: public I2PService - { - public: - - I2PServerTunnel (const std::string& name, const std::string& address, uint16_t port, - std::shared_ptr localDestination, uint16_t inport = 0, bool gzip = true); - - void Start (); - void Stop (); - - void SetAccessList (const std::set& accessList); - - void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; } - bool IsUniqueLocal () const { return m_IsUniqueLocal; } - - void SetSSL (bool ssl); - std::shared_ptr GetSSLCtx () const { return m_SSLCtx; }; - - void SetLocalAddress (const std::string& localAddress); - - const std::string& GetAddress() const { return m_Address; } - uint16_t GetPort () const { return m_Port; }; - uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); }; - const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; } - - const char* GetName() { return m_Name.c_str (); } - - private: - - void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::results_type endpoints, - std::shared_ptr resolver); - - void Accept (); - void HandleAccept (std::shared_ptr stream); - virtual std::shared_ptr CreateI2PConnection (std::shared_ptr stream); - - private: - - bool m_IsUniqueLocal; - std::string m_Name, m_Address; - uint16_t m_Port; - boost::asio::ip::tcp::endpoint m_Endpoint; - std::shared_ptr m_PortDestination; - std::set m_AccessList; - bool m_IsAccessList; - std::unique_ptr m_LocalAddress; - std::shared_ptr m_SSLCtx; - }; - - class I2PServerTunnelHTTP: public I2PServerTunnel - { - public: - - I2PServerTunnelHTTP (const std::string& name, const std::string& address, uint16_t port, - std::shared_ptr localDestination, const std::string& host, - uint16_t inport = 0, bool gzip = true); - - private: - - std::shared_ptr CreateI2PConnection (std::shared_ptr stream); - - private: - - std::string m_Host, m_XI2P; - std::weak_ptr m_From; - }; - - class I2PServerTunnelIRC: public I2PServerTunnel - { - public: - - I2PServerTunnelIRC (const std::string& name, const std::string& address, uint16_t port, - std::shared_ptr localDestination, const std::string& webircpass, - uint16_t inport = 0, bool gzip = true); - - private: - - std::shared_ptr CreateI2PConnection (std::shared_ptr stream); - - private: - - std::string m_WebircPass; - }; - - boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr); -} -} - -#endif diff --git a/libi2pd_client/MatchedDestination.cpp b/libi2pd_client/MatchedDestination.cpp deleted file mode 100644 index 40752f5b..00000000 --- a/libi2pd_client/MatchedDestination.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* -* Copyright (c) 2013-2023, 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 "MatchedDestination.h" -#include "Log.h" -#include "ClientContext.h" - - -namespace i2p -{ -namespace client -{ - MatchedTunnelDestination::MatchedTunnelDestination(const i2p::data::PrivateKeys & keys, const std::string & remoteName, const std::map * params) - : RunnableClientDestination(keys, false, params), - m_RemoteName(remoteName) {} - - - void MatchedTunnelDestination::ResolveCurrentLeaseSet() - { - auto addr = i2p::client::context.GetAddressBook().GetAddress (m_RemoteName); - if(addr && addr->IsIdentHash ()) - { - m_RemoteIdent = addr->identHash; - auto ls = FindLeaseSet(m_RemoteIdent); - if(ls) - HandleFoundCurrentLeaseSet(ls); - else - RequestDestination(m_RemoteIdent, std::bind(&MatchedTunnelDestination::HandleFoundCurrentLeaseSet, this, std::placeholders::_1)); - } - else - LogPrint(eLogWarning, "Destination: Failed to resolve ", m_RemoteName); - } - - void MatchedTunnelDestination::HandleFoundCurrentLeaseSet(std::shared_ptr ls) - { - if(ls) - { - LogPrint(eLogDebug, "Destination: Resolved remote lease set for ", m_RemoteName); - m_RemoteLeaseSet = ls; - } - else - { - m_ResolveTimer->expires_from_now(boost::posix_time::seconds(1)); - m_ResolveTimer->async_wait([&](const boost::system::error_code & ec) { - if(!ec) ResolveCurrentLeaseSet(); - }); - } - } - - - void MatchedTunnelDestination::Start() - { - ClientDestination::Start(); - m_ResolveTimer = std::make_shared(GetService()); - GetTunnelPool()->SetCustomPeerSelector(this); - ResolveCurrentLeaseSet(); - } - - void MatchedTunnelDestination::Stop() - { - ClientDestination::Stop(); - if(m_ResolveTimer) - m_ResolveTimer->cancel(); - } - - - bool MatchedTunnelDestination::SelectPeers(i2p::tunnel::Path & path, int hops, bool inbound) - { - auto pool = GetTunnelPool(); - if(!pool || !pool->StandardSelectPeers(path, hops, inbound, - std::bind(&i2p::tunnel::TunnelPool::SelectNextHop, pool, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3))) - return false; - // more here for outbound tunnels - if(!inbound && m_RemoteLeaseSet) - { - if(m_RemoteLeaseSet->IsExpired()) - ResolveCurrentLeaseSet(); - if(m_RemoteLeaseSet && !m_RemoteLeaseSet->IsExpired()) - { - // remote lease set is good - auto leases = m_RemoteLeaseSet->GetNonExpiredLeases(); - // pick lease - std::shared_ptr obep; - while(!obep && leases.size() > 0) - { - auto idx = rand() % leases.size(); - auto lease = leases[idx]; - obep = i2p::data::netdb.FindRouter(lease->tunnelGateway); - leases.erase(leases.begin()+idx); - } - if(obep) - { - path.Add (obep); - LogPrint(eLogDebug, "Destination: Found OBEP matching IBGW"); - } else - LogPrint(eLogWarning, "Destination: Could not find proper IBGW for matched outbound tunnel"); - } - } - return true; - } -} -} diff --git a/libi2pd_client/MatchedDestination.h b/libi2pd_client/MatchedDestination.h deleted file mode 100644 index 30ad8942..00000000 --- a/libi2pd_client/MatchedDestination.h +++ /dev/null @@ -1,47 +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 MATCHED_DESTINATION_H_ -#define MATCHED_DESTINATION_H_ -#include "Destination.h" -#include - -namespace i2p -{ -namespace client -{ - /** - * client tunnel that uses same OBEP as IBGW of each remote lease for a remote destination - */ - class MatchedTunnelDestination : public RunnableClientDestination, public i2p::tunnel::ITunnelPeerSelector - { - public: - - MatchedTunnelDestination(const i2p::data::PrivateKeys& keys, const std::string & remoteName, - const std::map * params = nullptr); - void Start(); - void Stop(); - - bool SelectPeers(i2p::tunnel::Path & peers, int hops, bool inbound); - - private: - - void ResolveCurrentLeaseSet(); - void HandleFoundCurrentLeaseSet(std::shared_ptr ls); - - private: - - std::string m_RemoteName; - i2p::data::IdentHash m_RemoteIdent; - std::shared_ptr m_RemoteLeaseSet; - std::shared_ptr m_ResolveTimer; - }; -} -} - -#endif diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp deleted file mode 100644 index 2c0f8d92..00000000 --- a/libi2pd_client/SAM.cpp +++ /dev/null @@ -1,1625 +0,0 @@ -/* -* Copyright (c) 2013-2025, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#ifdef _MSC_VER -#include -#endif -#include "Base.h" -#include "Identity.h" -#include "Log.h" -#include "Destination.h" -#include "ClientContext.h" -#include "util.h" -#include "SAM.h" - -namespace i2p -{ -namespace client -{ - SAMSocket::SAMSocket (SAMBridge& owner): - m_Owner (owner), m_Socket(owner.GetService()), m_Timer (m_Owner.GetService ()), - m_BufferOffset (0), - m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), - m_IsAccepting (false), m_Stream (nullptr) - { - } - - SAMSocket::~SAMSocket () - { - m_Stream = nullptr; - } - - void SAMSocket::Terminate (const char* reason) - { - if(m_Stream) - { - m_Stream->AsyncClose (); - m_Stream = nullptr; - } - switch (m_SocketType) - { - case eSAMSocketTypeSession: - m_Owner.CloseSession (m_ID); - break; - case eSAMSocketTypeStream: - break; - case eSAMSocketTypeAcceptor: - case eSAMSocketTypeForward: - { - auto session = m_Owner.FindSession(m_ID); - if (session) - { - if (m_IsAccepting && session->GetLocalDestination ()) - session->GetLocalDestination ()->StopAcceptingStreams (); - } - break; - } - default: ; - } - m_SocketType = eSAMSocketTypeTerminated; - if (m_Socket.is_open ()) - { - boost::system::error_code ec; - m_Socket.shutdown (boost::asio::ip::tcp::socket::shutdown_both, ec); - m_Socket.close (); - } - m_Owner.RemoveSocket(shared_from_this()); - } - - void SAMSocket::ReceiveHandshake () - { - m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), - std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - static bool SAMVersionAcceptable(const std::string & ver) - { - return ver == "3.0" || ver == "3.1"; - } - - static bool SAMVersionTooLow(const std::string & ver) - { - return ver.size() && ver[0] < '3'; - } - - static bool SAMVersionTooHigh(const std::string & ver) - { - return ver.size() && ver > "3.1"; - } - - void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Handshake read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("SAM: handshake read error"); - } - else - { - m_Buffer[bytes_transferred] = 0; - char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred); - if (eol) - *eol = 0; - LogPrint (eLogDebug, "SAM: Handshake ", m_Buffer); - char * separator = strchr (m_Buffer, ' '); - if (separator) - { - separator = strchr (separator + 1, ' '); - if (separator) - *separator = 0; - } - - if (!strcmp (m_Buffer, SAM_HANDSHAKE)) - { - std::string maxver("3.1"); - std::string minver("3.0"); - // try to find MIN and MAX, 3.0 if not found - if (separator) - { - separator++; - std::map params; - ExtractParams (separator, params); - auto it = params.find (SAM_PARAM_MAX); - if (it != params.end ()) - maxver = it->second; - it = params.find(SAM_PARAM_MIN); - if (it != params.end ()) - minver = it->second; - } - // version negotiation - std::string version; - if (SAMVersionAcceptable(maxver)) - { - version = maxver; - } - else if (SAMVersionAcceptable(minver)) - { - version = minver; - } - else if (SAMVersionTooLow(minver) && SAMVersionTooHigh(maxver)) - { - version = "3.0"; - } - - if (SAMVersionAcceptable(version)) - { -#ifdef _MSC_VER - size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); -#else - size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); -#endif - boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), - std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - else - SendMessageReply (SAM_HANDSHAKE_NOVERSION, strlen (SAM_HANDSHAKE_NOVERSION), true); - } - else - { - LogPrint (eLogError, "SAM: Handshake mismatch"); - Terminate ("SAM: handshake mismatch"); - } - } - } - - bool SAMSocket::IsSession(const std::string & id) const - { - return id == m_ID; - } - - void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Handshake reply send error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("SAM: handshake reply send error"); - } - else - { - m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), - std::bind(&SAMSocket::HandleMessage, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - } - - void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close) - { - LogPrint (eLogDebug, "SAMSocket::SendMessageReply, close=",close?"true":"false", " reason: ", msg); - - if (!m_IsSilent || m_SocketType == eSAMSocketTypeForward) - boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), - std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, close)); - else - { - if (close) - Terminate ("SAMSocket::SendMessageReply(close=true)"); - else - Receive (); - } - } - - void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Reply send error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("SAM: reply send error"); - } - else - { - if (close) - Terminate ("SAMSocket::HandleMessageReplySent(close=true)"); - else - Receive (); - } - } - - void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("SAM: read error"); - } - else if (m_SocketType == eSAMSocketTypeStream) - HandleReceived (ecode, bytes_transferred); - else - { - bytes_transferred += m_BufferOffset; - m_BufferOffset = 0; - m_Buffer[bytes_transferred] = 0; - char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred); - if (eol) - { - if (eol > m_Buffer && eol[-1] == '\r') eol--; - *eol = 0; - char * separator = strchr (m_Buffer, ' '); - if (separator) - { - separator = strchr (separator + 1, ' '); - if (separator) - *separator = 0; - else - separator = eol; - - if (!strcmp (m_Buffer, SAM_SESSION_CREATE)) - ProcessSessionCreate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_STREAM_CONNECT)) - ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1, bytes_transferred - (eol - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT)) - ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_STREAM_FORWARD)) - ProcessStreamForward (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_DEST_GENERATE)) - ProcessDestGenerate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP)) - ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_SESSION_ADD)) - ProcessSessionAdd (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_SESSION_REMOVE)) - ProcessSessionRemove (separator + 1, bytes_transferred - (separator - m_Buffer) - 1); - else if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND) || !strcmp (m_Buffer, SAM_RAW_SEND)) - { - size_t len = bytes_transferred - (separator - m_Buffer) - 1; - size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1); - if (processed < len) - { - m_BufferOffset = len - processed; - if (processed > 0) - memmove (m_Buffer, separator + 1 + processed, m_BufferOffset); - else - { - // restore string back - *separator = ' '; - *eol = '\n'; - } - } - // since it's SAM v1 reply is not expected - Receive (); - } - else - { - LogPrint (eLogError, "SAM: Unexpected message ", m_Buffer); - Terminate ("SAM: unexpected message"); - } - } - else - { - LogPrint (eLogError, "SAM: Malformed message ", m_Buffer); - Terminate ("malformed message"); - } - } - - else - { - LogPrint (eLogWarning, "SAM: Incomplete message ", bytes_transferred); - m_BufferOffset = bytes_transferred; - // try to receive remaining message - Receive (); - } - } - } - - static bool IsAcceptableSessionName(const std::string & str) - { - auto itr = str.begin(); - while(itr != str.end()) - { - char ch = *itr; - ++itr; - if (ch == '<' || ch == '>' || ch == '"' || ch == '\'' || ch == '/') - return false; - } - return true; - } - - void SAMSocket::ProcessSessionCreate (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Session create: ", buf); - std::map params; - ExtractParams (buf, params); - std::string& style = params[SAM_PARAM_STYLE]; - std::string& id = params[SAM_PARAM_ID]; - std::string& destination = params[SAM_PARAM_DESTINATION]; - - if(!IsAcceptableSessionName(id)) - { - // invalid session id - SendMessageReply (SAM_SESSION_CREATE_INVALID_ID, strlen(SAM_SESSION_CREATE_INVALID_ID), true); - return; - } - m_ID = id; - if (m_Owner.FindSession (id)) - { - // session exists - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true); - return; - } - - SAMSessionType type = eSAMSessionTypeUnknown; - if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream; - else if (style == SAM_VALUE_DATAGRAM) type = eSAMSessionTypeDatagram; - else if (style == SAM_VALUE_RAW) type = eSAMSessionTypeRaw; - else if (style == SAM_VALUE_MASTER) type = eSAMSessionTypeMaster; - if (type == eSAMSessionTypeUnknown) - { - // unknown style - SendSessionI2PError("Unknown STYLE"); - return; - } - - std::shared_ptr forward = nullptr; - if ((type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) && - params.find(SAM_PARAM_HOST) != params.end() && params.find(SAM_PARAM_PORT) != params.end()) - { - // udp forward selected - boost::system::error_code e; - // TODO: support hostnames in udp forward - auto addr = boost::asio::ip::make_address(params[SAM_PARAM_HOST], e); - if (e) - { - // not an ip address - SendSessionI2PError("Invalid IP Address in HOST"); - return; - } - - auto port = std::stoi(params[SAM_PARAM_PORT]); - if (port == -1) - { - SendSessionI2PError("Invalid port"); - return; - } - forward = std::make_shared(addr, port); - } - - //ensure we actually received a destination - if (destination.empty()) - { - SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); - return; - } - - if (destination != SAM_VALUE_TRANSIENT) - { - //ensure it's a base64 string - i2p::data::PrivateKeys keys; - if (!keys.FromBase64(destination)) - { - SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); - return; - } - } - - // create destination - auto session = m_Owner.CreateSession (id, type, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms); - if (session) - { - m_SocketType = eSAMSocketTypeSession; - if (type == eSAMSessionTypeDatagram || type == eSAMSessionTypeRaw) - { - session->UDPEndpoint = forward; - auto dest = session->GetLocalDestination ()->CreateDatagramDestination (); - auto port = forward ? std::stoi(params[SAM_PARAM_PORT]) : 0; - if (type == eSAMSessionTypeDatagram) - dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), - port - ); - else // raw - dest->SetRawReceiver (std::bind (&SAMSocket::HandleI2PRawDatagramReceive, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), - port - ); - } - - if (session->GetLocalDestination ()->IsReady ()) - SendSessionCreateReplyOk (); - else - { - m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); - m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, - shared_from_this (), std::placeholders::_1)); - } - } - else - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true); - } - - void SAMSocket::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - if (m_Socket.is_open ()) - { - auto session = m_Owner.FindSession(m_ID); - if(session) - { - if (session->GetLocalDestination ()->IsReady ()) - SendSessionCreateReplyOk (); - else - { - m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); - m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, - shared_from_this (), std::placeholders::_1)); - } - } - } - else - Terminate ("SAM: session socket closed"); - } - } - - void SAMSocket::SendSessionCreateReplyOk () - { - auto session = m_Owner.FindSession(m_ID); - if (session) - { - std::string priv = session->GetLocalDestination ()->GetPrivateKeys ().ToBase64 (); -#ifdef _MSC_VER - size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv.c_str ()); -#else - size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv.c_str ()); -#endif - SendMessageReply (m_Buffer, l2, false); - } - } - - void SAMSocket::ProcessStreamConnect (char * buf, size_t len, size_t rem) - { - LogPrint (eLogDebug, "SAM: Stream connect: ", buf); - if ( m_SocketType != eSAMSocketTypeUnknown) - { - SendSessionI2PError ("Socket already in use"); - return; - } - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - std::string& destination = params[SAM_PARAM_DESTINATION]; - std::string& silent = params[SAM_PARAM_SILENT]; - if (silent == SAM_VALUE_TRUE) m_IsSilent = true; - m_ID = id; - auto session = m_Owner.FindSession (id); - if (session) - { - if (rem > 0) // handle follow on data - { - memmove (m_Buffer, buf + len + 1, rem); // buf is a pointer to m_Buffer's content - m_BufferOffset = rem; - } - else - m_BufferOffset = 0; - - std::shared_ptr addr; - if (destination.find(".i2p") != std::string::npos) - addr = context.GetAddressBook().GetAddress (destination); - else - { - auto dest = std::make_shared (); - size_t l = dest->FromBase64(destination); - if (l > 0) - { - context.GetAddressBook().InsertFullAddress(dest); - addr = std::make_shared
(dest->GetIdentHash ()); - } - } - - if (addr && addr->IsValid ()) - { - if (addr->IsIdentHash ()) - { - if (session->GetLocalDestination ()->GetIdentHash () != addr->identHash) - { - auto leaseSet = session->GetLocalDestination ()->FindLeaseSet(addr->identHash); - if (leaseSet) - Connect(leaseSet, session); - else - { - session->GetLocalDestination ()->RequestDestination(addr->identHash, - std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete, - shared_from_this(), std::placeholders::_1)); - } - } - else - SendStreamCantReachPeer ("Can't connect to myself"); - } - else // B33 - session->GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, - std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete, - shared_from_this(), std::placeholders::_1)); - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_KEY, strlen(SAM_STREAM_STATUS_INVALID_KEY), true); - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - } - - void SAMSocket::Connect (std::shared_ptr remote, std::shared_ptr session) - { - if (!session) session = m_Owner.FindSession(m_ID); - if (session) - { - if (session->GetLocalDestination ()->SupportsEncryptionType (remote->GetEncryptionType ())) - { - m_SocketType = eSAMSocketTypeStream; - m_Stream = session->GetLocalDestination ()->CreateStream (remote); - if (m_Stream) - { - m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send - m_BufferOffset = 0; - I2PReceive (); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - } - else - SendStreamCantReachPeer ("Incompatible crypto"); - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - } - - void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr leaseSet) - { - if (leaseSet) - Connect (leaseSet); - else - { - LogPrint (eLogError, "SAM: Destination to connect not found"); - SendStreamCantReachPeer ("LeaseSet not found"); - } - } - - void SAMSocket::ProcessStreamAccept (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Stream accept: ", buf); - if ( m_SocketType != eSAMSocketTypeUnknown) - { - SendSessionI2PError ("Socket already in use"); - return; - } - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - std::string& silent = params[SAM_PARAM_SILENT]; - if (silent == SAM_VALUE_TRUE) m_IsSilent = true; - m_ID = id; - auto session = m_Owner.FindSession (id); - if (session) - { - m_SocketType = eSAMSocketTypeAcceptor; - if (!session->GetLocalDestination ()->IsAcceptingStreams ()) - { - m_IsAccepting = true; - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); - } - else - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - while (!session->acceptQueue.empty () && session->acceptQueue.front ().second + SAM_SESSION_MAX_ACCEPT_INTERVAL > ts) - { - auto socket = session->acceptQueue.front ().first; - session->acceptQueue.pop_front (); - if (socket) - boost::asio::post (m_Owner.GetService (), std::bind(&SAMSocket::TerminateClose, socket)); - } - if (session->acceptQueue.size () < SAM_SESSION_MAX_ACCEPT_QUEUE_SIZE) - { - // already accepting, queue up - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - session->acceptQueue.push_back (std::make_pair(shared_from_this(), ts)); - } - else - { - LogPrint (eLogInfo, "SAM: Session ", m_ID, " accept queue is full ", session->acceptQueue.size ()); - SendStreamI2PError ("Already accepting"); - } - } - } - else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - } - - void SAMSocket::ProcessStreamForward (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Stream forward: ", buf); - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - auto session = m_Owner.FindSession (id); - if (!session) - { - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); - return; - } - if (session->GetLocalDestination ()->IsAcceptingStreams ()) - { - SendSessionI2PError ("Already accepting"); - return; - } - auto it = params.find (SAM_PARAM_PORT); - if (it == params.end ()) - { - SendSessionI2PError ("PORT is missing"); - return; - } - auto port = std::stoi (it->second); - if (port <= 0 || port >= 0xFFFF) - { - SendSessionI2PError ("Invalid PORT"); - return; - } - boost::system::error_code ec; - auto ep = m_Socket.remote_endpoint (ec); - if (ec) - { - SendSessionI2PError ("Socket error"); - return; - } - ep.port (port); - m_SocketType = eSAMSocketTypeForward; - m_ID = id; - m_IsAccepting = true; - std::string& silent = params[SAM_PARAM_SILENT]; - if (silent == SAM_VALUE_TRUE) m_IsSilent = true; - session->GetLocalDestination ()->AcceptStreams (std::bind (&SAMSocket::HandleI2PForward, - shared_from_this (), std::placeholders::_1, ep)); - SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } - - size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data) - { - LogPrint (eLogDebug, "SAM: Datagram send: ", buf, " ", len); - std::map params; - ExtractParams (buf, params); - size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf; - if (offset + size <= len) - { - auto session = m_Owner.FindSession(m_ID); - if (session) - { - auto d = session->GetLocalDestination ()->GetDatagramDestination (); - if (d) - { - i2p::data::IdentityEx dest; - dest.FromBase64 (params[SAM_PARAM_DESTINATION]); - if (session->Type == eSAMSessionTypeDatagram) - d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); - else // raw - d->SendRawDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ()); - } - else - LogPrint (eLogError, "SAM: Missing datagram destination"); - } - else - LogPrint (eLogError, "SAM: Session is not created from DATAGRAM SEND"); - } - else - { - LogPrint (eLogWarning, "SAM: Sent datagram size ", size, " exceeds buffer ", len - offset); - return 0; // try to receive more - } - return offset + size; - } - - void SAMSocket::ProcessDestGenerate (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Dest generate"); - std::map params; - ExtractParams (buf, params); - // extract signature type - i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; - auto it = params.find (SAM_PARAM_SIGNATURE_TYPE); - if (it != params.end ()) - { - if (!m_Owner.ResolveSignatureType (it->second, signatureType)) - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second); - } - it = params.find (SAM_PARAM_CRYPTO_TYPE); - if (it != params.end ()) - { - try - { - cryptoType = std::stoi(it->second); - } - catch (const std::exception& ex) - { - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ()); - } - } - auto keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType, true); -#ifdef _MSC_VER - size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, - keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); -#else - size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, - keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); -#endif - SendMessageReply (m_Buffer, l, false); - } - - void SAMSocket::ProcessNamingLookup (char * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Naming lookup: ", buf); - std::map params; - ExtractParams (buf, params); - std::string& name = params[SAM_PARAM_NAME]; - std::shared_ptr identity; - std::shared_ptr addr; - auto session = m_Owner.FindSession(m_ID); - auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->GetLocalDestination (); - if (name == "ME") - SendNamingLookupReply (name, dest->GetIdentity ()); - else if ((identity = context.GetAddressBook ().GetFullAddress (name)) != nullptr) - SendNamingLookupReply (name, identity); - else if ((addr = context.GetAddressBook ().GetAddress (name))) - { - if (addr->IsIdentHash ()) - { - auto leaseSet = dest->FindLeaseSet (addr->identHash); - if (leaseSet) - SendNamingLookupReply (name, leaseSet->GetIdentity ()); - else - dest->RequestDestination (addr->identHash, - std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, - shared_from_this (), std::placeholders::_1, name)); - } - else - dest->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, - std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, - shared_from_this (), std::placeholders::_1, name)); - } - else - { - LogPrint (eLogError, "SAM: Naming failed, unknown address ", name); -#ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); -#else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); -#endif - SendMessageReply (m_Buffer, len, false); - } - } - - void SAMSocket::ProcessSessionAdd (char * buf, size_t len) - { - auto session = m_Owner.FindSession(m_ID); - if (session && session->Type == eSAMSessionTypeMaster) - { - LogPrint (eLogDebug, "SAM: Subsession add: ", buf); - auto masterSession = std::static_pointer_cast(session); - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - if (masterSession->subsessions.count (id) > 1) - { - // session exists - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); - return; - } - std::string& style = params[SAM_PARAM_STYLE]; - SAMSessionType type = eSAMSessionTypeUnknown; - if (style == SAM_VALUE_STREAM) type = eSAMSessionTypeStream; - // TODO: implement other styles - if (type == eSAMSessionTypeUnknown) - { - // unknown style - SendSessionI2PError("Unsupported STYLE"); - return; - } - auto fromPort = std::stoi(params[SAM_PARAM_FROM_PORT]); - if (fromPort == -1) - { - SendSessionI2PError("Invalid from port"); - return; - } - auto subsession = std::make_shared(masterSession, id, type, fromPort); - if (m_Owner.AddSession (subsession)) - { - masterSession->subsessions.insert (id); - SendSessionCreateReplyOk (); - } - else - SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), false); - } - else - SendSessionI2PError ("Wrong session type"); - } - - void SAMSocket::ProcessSessionRemove (char * buf, size_t len) - { - auto session = m_Owner.FindSession(m_ID); - if (session && session->Type == eSAMSessionTypeMaster) - { - LogPrint (eLogDebug, "SAM: Subsession remove: ", buf); - auto masterSession = std::static_pointer_cast(session); - std::map params; - ExtractParams (buf, params); - std::string& id = params[SAM_PARAM_ID]; - if (!masterSession->subsessions.erase (id)) - { - SendMessageReply (SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), false); - return; - } - m_Owner.CloseSession (id); - SendSessionCreateReplyOk (); - } - else - SendSessionI2PError ("Wrong session type"); - } - - void SAMSocket::SendReplyWithMessage (const char * reply, const std::string & msg) - { -#ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, reply, msg.c_str()); -#else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, reply, msg.c_str()); -#endif - SendMessageReply (m_Buffer, len, true); - } - - void SAMSocket::SendSessionI2PError(const std::string & msg) - { - LogPrint (eLogError, "SAM: Session I2P error: ", msg); - SendReplyWithMessage (SAM_SESSION_STATUS_I2P_ERROR, msg); - } - - void SAMSocket::SendStreamI2PError(const std::string & msg) - { - LogPrint (eLogError, "SAM: Stream I2P error: ", msg); - SendReplyWithMessage (SAM_STREAM_STATUS_I2P_ERROR, msg); - } - - void SAMSocket::SendStreamCantReachPeer(const std::string & msg) - { - SendReplyWithMessage (SAM_STREAM_STATUS_CANT_REACH_PEER, msg); - } - - void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, std::string name) - { - if (leaseSet) - { - context.GetAddressBook ().InsertFullAddress (leaseSet->GetIdentity ()); - SendNamingLookupReply (name, leaseSet->GetIdentity ()); - } - else - { - LogPrint (eLogError, "SAM: Naming lookup failed. LeaseSet for ", name, " not found"); -#ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); -#else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); -#endif - SendMessageReply (m_Buffer, len, false); - } - } - - void SAMSocket::SendNamingLookupReply (const std::string& name, std::shared_ptr identity) - { - auto base64 = identity->ToBase64 (); -#ifdef _MSC_VER - size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ()); -#else - size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ()); -#endif - SendMessageReply (m_Buffer, l, false); - } - - void SAMSocket::ExtractParams (char * buf, std::map& params) - { - char * separator; - do - { - separator = strchr (buf, ' '); - if (separator) *separator = 0; - char * value = strchr (buf, '='); - if (value) - { - *value = 0; - value++; - params[buf] = value; - } - buf = separator + 1; - } - while (separator); - } - - void SAMSocket::Receive () - { - m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset), - std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage, - shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - - void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("read error"); - } - else - { - if (m_Stream) - { - bytes_transferred += m_BufferOffset; - m_BufferOffset = 0; - m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred, - std::bind(&SAMSocket::HandleStreamSend, shared_from_this(), std::placeholders::_1)); - } - else - { - Terminate("No Stream Remaining"); - } - } - } - - void SAMSocket::I2PReceive () - { - if (m_Stream) - { - if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew || - m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular - { - m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE), - std::bind (&SAMSocket::HandleI2PReceive, shared_from_this(), - std::placeholders::_1, std::placeholders::_2), - SAM_SOCKET_CONNECTION_MAX_IDLE); - } - else // closed by peer - { - uint8_t * buff = new uint8_t[SAM_SOCKET_BUFFER_SIZE]; - // get remaining data - auto len = m_Stream->ReadSome (buff, SAM_SOCKET_BUFFER_SIZE); - if (len > 0) // still some data - { - WriteI2PDataImmediate(buff, len); - } - else // no more data - { - delete [] buff; - Terminate ("no more data"); - } - } - } - } - - void SAMSocket::WriteI2PDataImmediate(uint8_t * buff, size_t sz) - { - boost::asio::async_write ( - m_Socket, - boost::asio::buffer (buff, sz), - boost::asio::transfer_all(), - std::bind (&SAMSocket::HandleWriteI2PDataImmediate, shared_from_this (), std::placeholders::_1, buff)); // postpone termination - } - - void SAMSocket::HandleWriteI2PDataImmediate(const boost::system::error_code & ec, uint8_t * buff) - { - delete [] buff; - } - - void SAMSocket::WriteI2PData(size_t sz) - { - boost::asio::async_write ( - m_Socket, - boost::asio::buffer (m_StreamBuffer, sz), - boost::asio::transfer_all(), - std::bind(&SAMSocket::HandleWriteI2PData, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); - } - - void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Stream read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - { - if (bytes_transferred > 0) - { - WriteI2PData(bytes_transferred); - } - else - { - auto s = shared_from_this (); - boost::asio::post (m_Owner.GetService (), [s] { s->Terminate ("stream read error"); }); - } - } - else - { - auto s = shared_from_this (); - boost::asio::post (m_Owner.GetService (), [s] { s->Terminate ("stream read error (op aborted)"); }); - } - } - else - { - if (m_SocketType != eSAMSocketTypeTerminated) - { - if (bytes_transferred > 0) - { - WriteI2PData(bytes_transferred); - } - else - I2PReceive(); - } - } - } - - void SAMSocket::HandleWriteI2PData (const boost::system::error_code& ecode, size_t bytes_transferred) - { - if (ecode) - { - LogPrint (eLogError, "SAM: Socket write error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate ("socket write error at HandleWriteI2PData"); - } - else - { - I2PReceive (); - } - } - - void SAMSocket::HandleI2PAccept (std::shared_ptr stream) - { - if (stream) - { - LogPrint (eLogDebug, "SAM: Incoming I2P connection for session ", m_ID); - m_SocketType = eSAMSocketTypeStream; - m_IsAccepting = false; - m_Stream = stream; - context.GetAddressBook ().InsertFullAddress (stream->GetRemoteIdentity ()); - auto session = m_Owner.FindSession (m_ID); - if (session && !session->acceptQueue.empty ()) - { - // pending acceptors - auto ts = i2p::util::GetSecondsSinceEpoch (); - while (!session->acceptQueue.empty () && session->acceptQueue.front ().second + SAM_SESSION_MAX_ACCEPT_INTERVAL > ts) - { - auto socket = session->acceptQueue.front ().first; - session->acceptQueue.pop_front (); - if (socket) - boost::asio::post (m_Owner.GetService (), std::bind(&SAMSocket::TerminateClose, socket)); - } - if (!session->acceptQueue.empty ()) - { - auto socket = session->acceptQueue.front ().first; - session->acceptQueue.pop_front (); - if (socket && socket->GetSocketType () == eSAMSocketTypeAcceptor) - { - socket->m_IsAccepting = true; - session->GetLocalDestination ()->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, socket, std::placeholders::_1)); - } - } - } - if (!m_IsSilent) - { - if (m_SocketType != eSAMSocketTypeTerminated) - { - // get remote peer address - auto ident = std::make_shared(stream->GetRemoteIdentity()->ToBase64 ()); // we need to keep it until sent - ident->push_back ('\n'); - // send remote peer address back to client like received from stream - boost::asio::async_write (m_Socket, boost::asio::buffer (ident->data (), ident->size ()), boost::asio::transfer_all(), - [ident, s = shared_from_this ()](const boost::system::error_code& ecode, size_t bytes_transferred) - { - s->HandleWriteI2PData (ecode, bytes_transferred); - }); - } - } - else - I2PReceive (); - } - else - LogPrint (eLogWarning, "SAM: I2P acceptor has been reset"); - } - - void SAMSocket::HandleI2PForward (std::shared_ptr stream, - boost::asio::ip::tcp::endpoint ep) - { - if (stream) - { - LogPrint (eLogDebug, "SAM: Incoming forward I2P connection for session ", m_ID); - auto newSocket = std::make_shared(m_Owner); - newSocket->SetSocketType (eSAMSocketTypeStream); - auto s = shared_from_this (); - newSocket->GetSocket ().async_connect (ep, - [s, newSocket, stream](const boost::system::error_code& ecode) - { - if (!ecode) - { - s->m_Owner.AddSocket (newSocket); - newSocket->Receive (); - newSocket->m_Stream = stream; - newSocket->m_ID = s->m_ID; - if (!s->m_IsSilent) - { - // get remote peer address - auto dest = stream->GetRemoteIdentity()->ToBase64 (); - memcpy (newSocket->m_StreamBuffer, dest.c_str (), dest.length ()); - newSocket->m_StreamBuffer[dest.length ()] = '\n'; - newSocket->HandleI2PReceive (boost::system::error_code (),dest.length () + 1); // we send identity like it has been received from stream - } - else - newSocket->I2PReceive (); - } - else - stream->AsyncClose (); - }); - } - else - LogPrint (eLogWarning, "SAM: I2P forward acceptor has been reset"); - } - - void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Datagram received ", len); - auto base64 = from.ToBase64 (); - auto session = m_Owner.FindSession(m_ID); - if(session) - { - auto ep = session->UDPEndpoint; - if (ep) - { - // udp forward enabled - const char lf = '\n'; - // send to remote endpoint, { destination, linefeed, payload } - m_Owner.SendTo({ {(const uint8_t *)base64.c_str(), base64.size()}, {(const uint8_t *)&lf, 1}, {buf, len} }, *ep); - } - else - { -#ifdef _MSC_VER - size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); -#else - size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); -#endif - if (len < SAM_SOCKET_BUFFER_SIZE - l) - { - memcpy (m_StreamBuffer + l, buf, len); - WriteI2PData(len + l); - } - else - LogPrint (eLogWarning, "SAM: Received datagram size ", len," exceeds buffer"); - } - } - } - - void SAMSocket::HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - LogPrint (eLogDebug, "SAM: Raw datagram received ", len); - auto session = m_Owner.FindSession(m_ID); - if(session) - { - auto ep = session->UDPEndpoint; - if (ep) - // udp forward enabled - m_Owner.SendTo({ {buf, len} }, *ep); - else - { -#ifdef _MSC_VER - size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len); -#else - size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len); -#endif - if (len < SAM_SOCKET_BUFFER_SIZE - l) - { - memcpy (m_StreamBuffer + l, buf, len); - WriteI2PData(len + l); - } - else - LogPrint (eLogWarning, "SAM: Received raw datagram size ", len," exceeds buffer"); - } - } - } - - void SAMSocket::HandleStreamSend(const boost::system::error_code & ec) - { - boost::asio::post (m_Owner.GetService (), std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this())); - } - - SAMSession::SAMSession (SAMBridge & parent, const std::string & id, SAMSessionType type): - m_Bridge(parent), Name(id), Type (type), UDPEndpoint(nullptr) - { - } - - void SAMSession::CloseStreams () - { - for(const auto & itr : m_Bridge.ListSockets(Name)) - { - itr->Terminate(nullptr); - } - } - - SAMSingleSession::SAMSingleSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr dest): - SAMSession (parent, name, type), - localDestination (dest) - { - } - - SAMSingleSession::~SAMSingleSession () - { - i2p::client::context.DeleteLocalDestination (localDestination); - } - - void SAMSingleSession::StopLocalDestination () - { - localDestination->Release (); - // stop accepting new streams - localDestination->StopAcceptingStreams (); - // terminate existing streams - auto s = localDestination->GetStreamingDestination (); // TODO: take care about datagrams - if (s) s->Stop (); - } - - void SAMMasterSession::Close () - { - SAMSingleSession::Close (); - for (const auto& it: subsessions) - m_Bridge.CloseSession (it); - subsessions.clear (); - } - - SAMSubSession::SAMSubSession (std::shared_ptr master, const std::string& name, SAMSessionType type, uint16_t port): - SAMSession (master->m_Bridge, name, type), masterSession (master), inPort (port) - { - if (Type == eSAMSessionTypeStream) - { - auto d = masterSession->GetLocalDestination ()->CreateStreamingDestination (inPort); - if (d) d->Start (); - } - // TODO: implement datagrams - } - - std::shared_ptr SAMSubSession::GetLocalDestination () - { - return masterSession ? masterSession->GetLocalDestination () : nullptr; - } - - void SAMSubSession::StopLocalDestination () - { - auto dest = GetLocalDestination (); - if (dest && Type == eSAMSessionTypeStream) - { - auto d = dest->RemoveStreamingDestination (inPort); - if (d) d->Stop (); - } - // TODO: implement datagrams - } - - SAMBridge::SAMBridge (const std::string& address, uint16_t portTCP, uint16_t portUDP, bool singleThread): - RunnableService ("SAM"), m_IsSingleThread (singleThread), - m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), portTCP)), - m_DatagramEndpoint (boost::asio::ip::make_address(address), (!portUDP) ? portTCP-1 : portUDP), m_DatagramSocket (GetIOService (), m_DatagramEndpoint), - m_SignatureTypes - { - {"DSA_SHA1", i2p::data::SIGNING_KEY_TYPE_DSA_SHA1}, - {"ECDSA_SHA256_P256", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256}, - {"ECDSA_SHA384_P384", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA384_P384}, - {"ECDSA_SHA512_P521", i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA512_P521}, - {"EdDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519}, - {"GOST_GOSTR3411256_GOSTR3410CRYPTOPROA", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256}, - {"GOST_GOSTR3411512_GOSTR3410TC26A512", i2p::data::SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512}, - {"RedDSA_SHA512_Ed25519", i2p::data::SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519}, - } - { - } - - SAMBridge::~SAMBridge () - { - if (IsRunning ()) - Stop (); - } - - void SAMBridge::Start () - { - Accept (); - ReceiveDatagram (); - StartIOService (); - } - - void SAMBridge::Stop () - { - try - { - m_Acceptor.cancel (); - } - catch (const std::exception& ex) - { - LogPrint (eLogError, "SAM: Runtime exception: ", ex.what ()); - } - - decltype(m_Sessions) sessions; - { - std::unique_lock l(m_SessionsMutex); - m_Sessions.swap (sessions); - } - for (auto& it: sessions) - it.second->Close (); - - StopIOService (); - } - - void SAMBridge::Accept () - { - auto newSocket = std::make_shared(*this); - m_Acceptor.async_accept (newSocket->GetSocket(), std::bind (&SAMBridge::HandleAccept, this, - std::placeholders::_1, newSocket)); - } - - void SAMBridge::AddSocket(std::shared_ptr socket) - { - std::unique_lock lock(m_OpenSocketsMutex); - m_OpenSockets.push_back(socket); - } - - void SAMBridge::RemoveSocket(const std::shared_ptr & socket) - { - std::unique_lock lock(m_OpenSocketsMutex); - m_OpenSockets.remove_if([socket](const std::shared_ptr & item) -> bool { return item == socket; }); - } - - void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) - { - if (!ecode) - { - boost::system::error_code ec; - auto ep = socket->GetSocket ().remote_endpoint (ec); - if (!ec) - { - LogPrint (eLogDebug, "SAM: New connection from ", ep); - AddSocket (socket); - socket->ReceiveHandshake (); - } - else - LogPrint (eLogError, "SAM: Incoming connection error: ", ec.message ()); - } - else - LogPrint (eLogError, "SAM: Accept error: ", ecode.message ()); - - if (ecode != boost::asio::error::operation_aborted) - Accept (); - } - - std::shared_ptr SAMBridge::CreateSession (const std::string& id, SAMSessionType type, - const std::string& destination, const std::map * params) - { - std::shared_ptr localDestination = nullptr; - if (destination != "") - { - i2p::data::PrivateKeys keys; - if (!keys.FromBase64 (destination)) return nullptr; - localDestination = m_IsSingleThread ? - i2p::client::context.CreateNewLocalDestination (GetIOService (), keys, true, params) : - i2p::client::context.CreateNewLocalDestination (keys, true, params); - } - else // transient - { - // extract signature type - i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; - i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL; - if (params) - { - auto it = params->find (SAM_PARAM_SIGNATURE_TYPE); - if (it != params->end ()) - { - if (!ResolveSignatureType (it->second, signatureType)) - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_SIGNATURE_TYPE, " is invalid ", it->second); - } - it = params->find (SAM_PARAM_CRYPTO_TYPE); - if (it != params->end ()) - { - try - { - cryptoType = std::stoi(it->second); - } - catch (const std::exception& ex) - { - LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ()); - } - } - } - localDestination = m_IsSingleThread ? - i2p::client::context.CreateNewLocalDestination (GetIOService (), true, signatureType, cryptoType, params) : - i2p::client::context.CreateNewLocalDestination (true, signatureType, cryptoType, params); - } - if (localDestination) - { - localDestination->Acquire (); - auto session = (type == eSAMSessionTypeMaster) ? std::make_shared(*this, id, localDestination) : - std::make_shared(*this, id, type, localDestination); - std::unique_lock l(m_SessionsMutex); - auto ret = m_Sessions.insert (std::make_pair(id, session)); - if (!ret.second) - LogPrint (eLogWarning, "SAM: Session ", id, " already exists"); - return ret.first->second; - } - return nullptr; - } - - bool SAMBridge::AddSession (std::shared_ptr session) - { - if (!session) return false; - auto ret = m_Sessions.emplace (session->Name, session); - return ret.second; - } - - void SAMBridge::CloseSession (const std::string& id) - { - std::shared_ptr session; - { - std::unique_lock l(m_SessionsMutex); - auto it = m_Sessions.find (id); - if (it != m_Sessions.end ()) - { - session = it->second; - m_Sessions.erase (it); - } - } - if (session) - { - session->StopLocalDestination (); - session->Close (); - if (m_IsSingleThread) - ScheduleSessionCleanupTimer (session); // let all session's streams close - } - } - - void SAMBridge::ScheduleSessionCleanupTimer (std::shared_ptr session) - { - auto timer = std::make_shared(GetService ()); - timer->expires_from_now (boost::posix_time::seconds(5)); // postpone destination clean for 5 seconds - timer->async_wait (std::bind (&SAMBridge::HandleSessionCleanupTimer, this, std::placeholders::_1, session, timer)); - } - - void SAMBridge::HandleSessionCleanupTimer (const boost::system::error_code& ecode, - std::shared_ptr session, std::shared_ptr timer) - { - if (ecode != boost::asio::error::operation_aborted && session) - { - auto dest = session->GetLocalDestination (); - if (dest) - { - auto streamingDest = dest->GetStreamingDestination (); - auto numStreams = streamingDest->GetNumStreams (); - if (numStreams > 0) - { - LogPrint (eLogInfo, "SAM: Session ", session->Name, " still has ", numStreams, " streams"); - ScheduleSessionCleanupTimer (session); - } - else - LogPrint (eLogDebug, "SAM: Session ", session->Name, " terminated"); - } - } - // session's destructor is called here unless rescheduled - } - - std::shared_ptr SAMBridge::FindSession (const std::string& id) const - { - std::unique_lock l(m_SessionsMutex); - auto it = m_Sessions.find (id); - if (it != m_Sessions.end ()) - return it->second; - return nullptr; - } - - std::list > SAMBridge::ListSockets(const std::string & id) const - { - std::list > list; - { - std::unique_lock l(m_OpenSocketsMutex); - for (const auto & itr : m_OpenSockets) - if (itr->IsSession(id)) - list.push_back(itr); - } - return list; - } - - void SAMBridge::SendTo (const std::vector& bufs, const boost::asio::ip::udp::endpoint& ep) - { - m_DatagramSocket.send_to (bufs, ep); - } - - void SAMBridge::ReceiveDatagram () - { - m_DatagramSocket.async_receive_from ( - boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE), - m_SenderEndpoint, - std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2)); - } - - void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (!ecode) - { - m_DatagramReceiveBuffer[bytes_transferred] = 0; - char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n'); - if(eol) - { - *eol = 0; eol++; - size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer); - LogPrint (eLogDebug, "SAM: Datagram received ", m_DatagramReceiveBuffer," size=", payloadLen); - char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' '); - if (sessionID) - { - sessionID++; - char * destination = strchr (sessionID, ' '); - if (destination) - { - *destination = 0; destination++; - auto session = FindSession (sessionID); - if (session) - { - auto localDest = session->GetLocalDestination (); - auto datagramDest = localDest ? localDest->GetDatagramDestination () : nullptr; - if (datagramDest) - { - i2p::data::IdentityEx dest; - dest.FromBase64 (destination); - if (session->Type == eSAMSessionTypeDatagram) - datagramDest->SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); - else if (session->Type == eSAMSessionTypeRaw) - datagramDest->SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); - else - LogPrint (eLogError, "SAM: Unexpected session type ", (int)session->Type, "for session ", sessionID); - } - else - LogPrint (eLogError, "SAM: Datagram destination is not set for session ", sessionID); - } - else - LogPrint (eLogError, "SAM: Session ", sessionID, " not found"); - } - else - LogPrint (eLogError, "SAM: Missing destination key"); - } - else - LogPrint (eLogError, "SAM: Missing sessionID"); - } - else - LogPrint(eLogError, "SAM: Invalid datagram"); - ReceiveDatagram (); - } - else - LogPrint (eLogError, "SAM: Datagram receive error: ", ecode.message ()); - } - - bool SAMBridge::ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const - { - try - { - type = std::stoi (name); - } - catch (const std::invalid_argument& ex) - { - // name is not numeric, resolving - auto it = m_SignatureTypes.find (name); - if (it != m_SignatureTypes.end ()) - type = it->second; - else - return false; - } - catch (const std::exception& ex) - { - return false; - } - // name has been resolved - return true; - } -} -} diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h deleted file mode 100644 index 1886324a..00000000 --- a/libi2pd_client/SAM.h +++ /dev/null @@ -1,299 +0,0 @@ -/* -* Copyright (c) 2013-2025, 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 SAM_H__ -#define SAM_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "util.h" -#include "Identity.h" -#include "LeaseSet.h" -#include "Streaming.h" -#include "Destination.h" - -namespace i2p -{ -namespace client -{ - const size_t SAM_SOCKET_BUFFER_SIZE = 8192; - const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds - const int SAM_SESSION_READINESS_CHECK_INTERVAL = 3; // in seconds - const size_t SAM_SESSION_MAX_ACCEPT_QUEUE_SIZE = 50; - const size_t SAM_SESSION_MAX_ACCEPT_INTERVAL = 3; // in seconds - - const char SAM_HANDSHAKE[] = "HELLO VERSION"; - const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; - const char SAM_HANDSHAKE_NOVERSION[] = "HELLO REPLY RESULT=NOVERSION\n"; - const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n"; - const char SAM_SESSION_CREATE[] = "SESSION CREATE"; - const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n"; - const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n"; - const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; - const char SAM_SESSION_CREATE_INVALID_ID[] = "SESSION STATUS RESULT=INVALID_ID\n"; - const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n"; - const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"%s\"\n"; - const char SAM_SESSION_ADD[] = "SESSION ADD"; - const char SAM_SESSION_REMOVE[] = "SESSION REMOVE"; - const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; - const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; - const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; - const char SAM_STREAM_STATUS_INVALID_KEY[] = "STREAM STATUS RESULT=INVALID_KEY\n"; - const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER MESSAGE=\"%s\"\n"; - const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"%s\"\n"; - const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; - const char SAM_STREAM_FORWARD[] = "STREAM FORWARD"; - const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND"; - const char SAM_RAW_SEND[] = "RAW SEND"; - const char SAM_DEST_GENERATE[] = "DEST GENERATE"; - const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n"; - const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n"; - const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP"; - const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=%s VALUE=%s\n"; - const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n"; - const char SAM_RAW_RECEIVED[] = "RAW RECEIVED SIZE=%lu\n"; - const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n"; - const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=KEY_NOT_FOUND NAME=%s\n"; - const char SAM_PARAM_MIN[] = "MIN"; - const char SAM_PARAM_MAX[] = "MAX"; - const char SAM_PARAM_STYLE[] = "STYLE"; - const char SAM_PARAM_ID[] = "ID"; - const char SAM_PARAM_SILENT[] = "SILENT"; - const char SAM_PARAM_DESTINATION[] = "DESTINATION"; - const char SAM_PARAM_NAME[] = "NAME"; - const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE"; - const char SAM_PARAM_CRYPTO_TYPE[] = "CRYPTO_TYPE"; - const char SAM_PARAM_SIZE[] = "SIZE"; - const char SAM_PARAM_HOST[] = "HOST"; - const char SAM_PARAM_PORT[] = "PORT"; - const char SAM_PARAM_FROM_PORT[] = "FROM_PORT"; - const char SAM_VALUE_TRANSIENT[] = "TRANSIENT"; - const char SAM_VALUE_STREAM[] = "STREAM"; - const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; - const char SAM_VALUE_RAW[] = "RAW"; - const char SAM_VALUE_MASTER[] = "MASTER"; - const char SAM_VALUE_TRUE[] = "true"; - const char SAM_VALUE_FALSE[] = "false"; - - enum SAMSocketType - { - eSAMSocketTypeUnknown, - eSAMSocketTypeSession, - eSAMSocketTypeStream, - eSAMSocketTypeAcceptor, - eSAMSocketTypeForward, - eSAMSocketTypeTerminated - }; - - class SAMBridge; - struct SAMSession; - class SAMSocket: public std::enable_shared_from_this - { - public: - - typedef boost::asio::ip::tcp::socket Socket_t; - SAMSocket (SAMBridge& owner); - ~SAMSocket (); - - Socket_t& GetSocket () { return m_Socket; }; - void ReceiveHandshake (); - void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; }; - SAMSocketType GetSocketType () const { return m_SocketType; }; - - void Terminate (const char* reason); - - bool IsSession(const std::string & id) const; - - private: - - void TerminateClose() { Terminate(nullptr); } - - void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void SendMessageReply (const char * msg, size_t len, bool close); - void HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close); - void Receive (); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - - void I2PReceive (); - void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleI2PAccept (std::shared_ptr stream); - void HandleI2PForward (std::shared_ptr stream, boost::asio::ip::tcp::endpoint ep); - void HandleWriteI2PData (const boost::system::error_code& ecode, size_t sz); - void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void HandleI2PRawDatagramReceive (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - - void ProcessSessionCreate (char * buf, size_t len); - void ProcessStreamConnect (char * buf, size_t len, size_t rem); - void ProcessStreamAccept (char * buf, size_t len); - void ProcessStreamForward (char * buf, size_t len); - void ProcessDestGenerate (char * buf, size_t len); - void ProcessNamingLookup (char * buf, size_t len); - void ProcessSessionAdd (char * buf, size_t len); - void ProcessSessionRemove (char * buf, size_t len); - void SendReplyWithMessage (const char * reply, const std::string & msg); - void SendSessionI2PError(const std::string & msg); - void SendStreamI2PError(const std::string & msg); - void SendStreamCantReachPeer(const std::string & msg); - size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0 - void ExtractParams (char * buf, std::map& params); - - void Connect (std::shared_ptr remote, std::shared_ptr session = nullptr); - void HandleConnectLeaseSetRequestComplete (std::shared_ptr leaseSet); - void SendNamingLookupReply (const std::string& name, std::shared_ptr identity); - void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, std::string name); - void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode); - void SendSessionCreateReplyOk (); - - void WriteI2PData(size_t sz); - void WriteI2PDataImmediate(uint8_t * ptr, size_t sz); - - void HandleWriteI2PDataImmediate(const boost::system::error_code & ec, uint8_t * buff); - void HandleStreamSend(const boost::system::error_code & ec); - - private: - - SAMBridge& m_Owner; - Socket_t m_Socket; - boost::asio::deadline_timer m_Timer; - char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1]; - size_t m_BufferOffset; - uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE]; - SAMSocketType m_SocketType; - std::string m_ID; // nickname - bool m_IsSilent; - bool m_IsAccepting; // for eSAMSocketTypeAcceptor only - std::shared_ptr m_Stream; - }; - - enum SAMSessionType - { - eSAMSessionTypeUnknown, - eSAMSessionTypeStream, - eSAMSessionTypeDatagram, - eSAMSessionTypeRaw, - eSAMSessionTypeMaster - }; - - struct SAMSession - { - SAMBridge & m_Bridge; - std::string Name; - SAMSessionType Type; - std::shared_ptr UDPEndpoint; // TODO: move - std::list, uint64_t> > acceptQueue; // socket, receive time in seconds - - SAMSession (SAMBridge & parent, const std::string & name, SAMSessionType type); - virtual ~SAMSession () {}; - - virtual std::shared_ptr GetLocalDestination () = 0; - virtual void StopLocalDestination () = 0; - virtual void Close () { CloseStreams (); }; - - void CloseStreams (); - }; - - struct SAMSingleSession: public SAMSession - { - std::shared_ptr localDestination; - - SAMSingleSession (SAMBridge & parent, const std::string & name, SAMSessionType type, std::shared_ptr dest); - ~SAMSingleSession (); - - std::shared_ptr GetLocalDestination () { return localDestination; }; - void StopLocalDestination (); - }; - - struct SAMMasterSession: public SAMSingleSession - { - std::set subsessions; - SAMMasterSession (SAMBridge & parent, const std::string & name, std::shared_ptr dest): - SAMSingleSession (parent, name, eSAMSessionTypeMaster, dest) {}; - void Close (); - }; - - struct SAMSubSession: public SAMSession - { - std::shared_ptr masterSession; - uint16_t inPort; - - SAMSubSession (std::shared_ptr master, const std::string& name, SAMSessionType type, uint16_t port); - // implements SAMSession - std::shared_ptr GetLocalDestination (); - void StopLocalDestination (); - }; - - class SAMBridge: private i2p::util::RunnableService - { - public: - - SAMBridge (const std::string& address, uint16_t portTCP, uint16_t portUDP, bool singleThread); - ~SAMBridge (); - - void Start (); - void Stop (); - - auto& GetService () { return GetIOService (); }; - std::shared_ptr CreateSession (const std::string& id, SAMSessionType type, const std::string& destination, // empty string means transient - const std::map * params); - bool AddSession (std::shared_ptr session); - void CloseSession (const std::string& id); - std::shared_ptr FindSession (const std::string& id) const; - - std::list > ListSockets(const std::string & id) const; - - /** send raw data to remote endpoint from our UDP Socket */ - void SendTo (const std::vector& bufs, const boost::asio::ip::udp::endpoint& ep); - - void AddSocket(std::shared_ptr socket); - void RemoveSocket(const std::shared_ptr & socket); - - bool ResolveSignatureType (const std::string& name, i2p::data::SigningKeyType& type) const; - - private: - - void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); - - void ReceiveDatagram (); - void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred); - - void ScheduleSessionCleanupTimer (std::shared_ptr session); - void HandleSessionCleanupTimer (const boost::system::error_code& ecode, - std::shared_ptr session, std::shared_ptr timer); - - private: - - bool m_IsSingleThread; - boost::asio::ip::tcp::acceptor m_Acceptor; - boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint; - boost::asio::ip::udp::socket m_DatagramSocket; - mutable std::mutex m_SessionsMutex; - std::map > m_Sessions; - mutable std::mutex m_OpenSocketsMutex; - std::list > m_OpenSockets; - uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1]; - std::map m_SignatureTypes; - - public: - - // for HTTP - const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; - }; -} -} - -#endif diff --git a/libi2pd_client/SOCKS.cpp b/libi2pd_client/SOCKS.cpp deleted file mode 100644 index 27df33c8..00000000 --- a/libi2pd_client/SOCKS.cpp +++ /dev/null @@ -1,828 +0,0 @@ -/* -* Copyright (c) 2013-2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include -#include -#include "SOCKS.h" -#include "Identity.h" -#include "Streaming.h" -#include "Destination.h" -#include "ClientContext.h" -#include "I2PEndian.h" -#include "I2PTunnel.h" -#include "I2PService.h" -#include "util.h" -#include "Socks5.h" - -namespace i2p -{ -namespace proxy -{ - static const size_t socks_buffer_size = 8192; - static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse - - struct SOCKSDnsAddress - { - uint8_t size; - char value[max_socks_hostname_size]; - void FromString (const std::string& str) - { - size = str.length(); - if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size; - memcpy(value,str.c_str(),size); - } - std::string ToString() { return std::string(value, size); } - void push_back (char c) { value[size++] = c; } - }; - - class SOCKSServer; - class SOCKSHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this - { - private: - - enum state - { - GET_SOCKSV, - GET_COMMAND, - GET_PORT, - GET_IPV4, - GET4_IDENT, - GET4A_HOST, - GET5_AUTHNUM, - GET5_AUTH, - GET5_REQUESTV, - GET5_GETRSV, - GET5_GETADDRTYPE, - GET5_IPV6, - GET5_HOST_SIZE, - GET5_HOST, - GET5_USERPASSWD, - GET5_USER_SIZE, - GET5_USER, - GET5_PASSWD_SIZE, - GET5_PASSWD, - READY, - UPSTREAM_RESOLVE, - UPSTREAM_CONNECT, - UPSTREAM_HANDSHAKE - }; - enum authMethods - { - AUTH_NONE = 0, //No authentication, skip to next step - AUTH_GSSAPI = 1, //GSSAPI authentication - AUTH_USERPASSWD = 2, //Username and password - AUTH_UNACCEPTABLE = 0xff //No acceptable method found - }; - enum addrTypes - { - ADDR_IPV4 = 1, //IPv4 address (4 octets) - ADDR_DNS = 3, // DNS name (up to 255 octets) - ADDR_IPV6 = 4 //IPV6 address (16 octets) - }; - enum errTypes - { - SOCKS5_OK = 0, // No error for SOCKS5 - SOCKS5_GEN_FAIL = 1, // General server failure - SOCKS5_RULE_DENIED = 2, // Connection disallowed by ruleset - SOCKS5_NET_UNREACH = 3, // Network unreachable - SOCKS5_HOST_UNREACH = 4, // Host unreachable - SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer - SOCKS5_TTL_EXPIRED = 6, // TTL Expired - SOCKS5_CMD_UNSUP = 7, // Command unsupported - SOCKS5_ADDR_UNSUP = 8, // Address type unsupported - SOCKS4_OK = 90, // No error for SOCKS4 - SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed - SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server - SOCKS4_IDENTD_DIFFER = 93 // The ID reported by the application and by identd differ - }; - enum cmdTypes - { - CMD_CONNECT = 1, // TCP Connect - CMD_BIND = 2, // TCP Bind - CMD_UDP = 3 // UDP associate - }; - enum socksVersions - { - SOCKS4 = 4, // SOCKS4 - SOCKS5 = 5 // SOCKS5 - }; - union address - { - uint32_t ip; - SOCKSDnsAddress dns; - uint8_t ipv6[16]; - }; - - void EnterState(state nstate, uint8_t parseleft = 1); - bool HandleData(uint8_t *sock_buff, std::size_t len); - bool ValidateSOCKSRequest(); - void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered); - void Terminate(); - void AsyncSockRead(); - boost::asio::const_buffer GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port); - boost::asio::const_buffer GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port); - bool Socks5ChooseAuth(); - void Socks5UserPasswdResponse (); - void SocksRequestFailed(errTypes error); - void SocksRequestSuccess(); - void SentSocksFailed(const boost::system::error_code & ecode); - void SentSocksDone(const boost::system::error_code & ecode); - void SentSocksResponse(const boost::system::error_code & ecode); - void HandleStreamRequestComplete (std::shared_ptr stream); - void ForwardSOCKS(); - - template - void SocksUpstreamSuccess(std::shared_ptr& upstreamSock); - void AsyncUpstreamSockRead(); - template - void SendUpstreamRequest(std::shared_ptr& upstreamSock); - void HandleUpstreamConnected(const boost::system::error_code & ecode, - const boost::asio::ip::tcp::endpoint& ep); - void HandleUpstreamResolved(const boost::system::error_code & ecode, - boost::asio::ip::tcp::resolver::results_type endpoints); - - boost::asio::ip::tcp::resolver m_proxy_resolver; - uint8_t m_sock_buff[socks_buffer_size]; - std::shared_ptr m_sock, m_upstreamSock; -#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) - std::shared_ptr m_upstreamLocalSock; -#endif - std::shared_ptr m_stream; - uint8_t *m_remaining_data; //Data left to be sent - uint8_t *m_remaining_upstream_data; //upstream data left to be forwarded - uint8_t m_response[7+max_socks_hostname_size]; - address m_address; //Address - std::size_t m_remaining_data_len; //Size of the data left to be sent - uint32_t m_4aip; //Used in 4a requests - uint16_t m_port; - uint8_t m_command; - uint8_t m_parseleft; //Octets left to parse - authMethods m_authchosen; //Authentication chosen - addrTypes m_addrtype; //Address type chosen - socksVersions m_socksv; //Socks version - cmdTypes m_cmd; // Command requested - state m_state; - const bool m_UseUpstreamProxy; // do we want to use the upstream proxy for non i2p addresses? - const std::string m_UpstreamProxyAddress; - const uint16_t m_UpstreamProxyPort; - - public: - - SOCKSHandler(SOCKSServer * parent, std::shared_ptr sock, const std::string & upstreamAddr, const uint16_t upstreamPort, const bool useUpstream) : - I2PServiceHandler(parent), - m_proxy_resolver(parent->GetService()), - m_sock(sock), m_stream(nullptr), - m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4), - m_UseUpstreamProxy(useUpstream), - m_UpstreamProxyAddress(upstreamAddr), - m_UpstreamProxyPort(upstreamPort) - { m_address.ip = 0; EnterState(GET_SOCKSV); } - - ~SOCKSHandler() { Terminate(); } - void Handle() { AsyncSockRead(); } - }; - - void SOCKSHandler::AsyncSockRead() - { - LogPrint(eLogDebug, "SOCKS: Async sock read"); - if (m_sock) { - m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size), - std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(), - std::placeholders::_1, std::placeholders::_2)); - } else { - LogPrint(eLogError,"SOCKS: No socket for read"); - } - } - - void SOCKSHandler::Terminate() - { - if (Kill()) return; - if (m_sock) - { - LogPrint(eLogDebug, "SOCKS: Closing socket"); - m_sock->close(); - m_sock = nullptr; - } - if (m_upstreamSock) - { - LogPrint(eLogDebug, "SOCKS: Closing upstream socket"); - m_upstreamSock->close(); - m_upstreamSock = nullptr; - } -#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) - if (m_upstreamLocalSock) - { - LogPrint(eLogDebug, "SOCKS: Closing upstream local socket"); - m_upstreamLocalSock->close(); - m_upstreamLocalSock = nullptr; - } -#endif - if (m_stream) - { - LogPrint(eLogDebug, "SOCKS: Closing stream"); - m_stream.reset (); - } - Done(shared_from_this()); - } - - boost::asio::const_buffer SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port) - { - assert(error >= SOCKS4_OK); - m_response[0] = '\x00'; // version - m_response[1] = error; // response code - htobe16buf(m_response + 2, port); // port - htobe32buf(m_response + 4, ip); // IP - return boost::asio::const_buffer (m_response,8); - } - - boost::asio::const_buffer SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port) - { - size_t size = 6; // header + port - assert(error <= SOCKS5_ADDR_UNSUP); - m_response[0] = '\x05'; // version - m_response[1] = error; // response code - m_response[2] = '\x00'; // reserved - m_response[3] = type; // address type - switch (type) - { - case ADDR_IPV4: - size += 4; - htobe32buf(m_response + 4, addr.ip); - htobe16buf(m_response + size - 2, port); - break; - case ADDR_IPV6: - size += 16; - memcpy(m_response + 4, addr.ipv6, 16); - htobe16buf(m_response + size - 2, port); - break; - case ADDR_DNS: - std::string address(addr.dns.value, addr.dns.size); - if(address.substr(addr.dns.size - 4, 4) == ".i2p") // overwrite if requested address inside I2P - { - m_response[3] = ADDR_IPV4; - size += 4; - memset(m_response + 4, 0, 6); // six HEX zeros - } - else - { - size += (1 + addr.dns.size); /* name length + resolved address */ - m_response[4] = addr.dns.size; - memcpy(m_response + 5, addr.dns.value, addr.dns.size); - htobe16buf(m_response + size - 2, port); - } - break; - } - return boost::asio::const_buffer (m_response, size); - } - - bool SOCKSHandler::Socks5ChooseAuth() - { - m_response[0] = '\x05'; // Version - m_response[1] = m_authchosen; // Response code - boost::asio::const_buffer response(m_response, 2); - if (m_authchosen == AUTH_UNACCEPTABLE) - { - LogPrint(eLogWarning, "SOCKS: v5 authentication negotiation failed"); - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, shared_from_this(), std::placeholders::_1)); - return false; - } - else - { - LogPrint(eLogDebug, "SOCKS: v5 choosing authentication method: ", m_authchosen); - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse, shared_from_this(), std::placeholders::_1)); - return true; - } - } - - void SOCKSHandler::Socks5UserPasswdResponse () - { - m_response[0] = 1; // Version of the subnegotiation - m_response[1] = 0; // Response code - LogPrint(eLogDebug, "SOCKS: v5 user/password response"); - boost::asio::async_write(*m_sock, boost::asio::const_buffer(m_response, 2), - std::bind(&SOCKSHandler::SentSocksResponse, shared_from_this(), std::placeholders::_1)); - } - - /* All hope is lost beyond this point */ - void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error) - { - boost::asio::const_buffer response(nullptr,0); - assert(error != SOCKS4_OK && error != SOCKS5_OK); - switch (m_socksv) - { - case SOCKS4: - LogPrint(eLogWarning, "SOCKS: v4 request failed: ", error); - if (error < SOCKS4_OK) error = SOCKS4_FAIL; // Transparently map SOCKS5 errors - response = GenerateSOCKS4Response(error, m_4aip, m_port); - break; - case SOCKS5: - LogPrint(eLogWarning, "SOCKS: v5 request failed: ", error); - response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port); - break; - } - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed, - shared_from_this(), std::placeholders::_1)); - } - - void SOCKSHandler::SocksRequestSuccess() - { - boost::asio::const_buffer response(nullptr,0); - // TODO: this should depend on things like the command type and callbacks may change - switch (m_socksv) - { - case SOCKS4: - LogPrint(eLogInfo, "SOCKS: v4 connection success"); - response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port); - break; - case SOCKS5: - LogPrint(eLogInfo, "SOCKS: v5 connection success"); - auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash()); - address ad; ad.dns.FromString(s); - // HACK only 16 bits passed in port as SOCKS5 doesn't allow for more - response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID()); - break; - } - boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone, shared_from_this(), std::placeholders::_1)); - } - - void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) { - switch (nstate) - { - case GET_PORT: parseleft = 2; break; - case GET_IPV4: m_addrtype = ADDR_IPV4; m_address.ip = 0; parseleft = 4; break; - case GET4_IDENT: m_4aip = m_address.ip; break; - case GET4A_HOST: - case GET5_HOST: m_addrtype = ADDR_DNS; m_address.dns.size = 0; break; - case GET5_IPV6: m_addrtype = ADDR_IPV6; parseleft = 16; break; - default:; - } - m_parseleft = parseleft; - m_state = nstate; - } - - bool SOCKSHandler::ValidateSOCKSRequest() - { - if ( m_cmd != CMD_CONNECT ) - { - // TODO: we need to support binds and other shit! - LogPrint(eLogError, "SOCKS: Unsupported command: ", m_cmd); - SocksRequestFailed(SOCKS5_CMD_UNSUP); - return false; - } - // TODO: we may want to support other address types! - if ( m_addrtype != ADDR_DNS ) - { - switch (m_socksv) - { - case SOCKS5: - LogPrint(eLogError, "SOCKS: v5 unsupported address type: ", m_addrtype); - break; - case SOCKS4: - LogPrint(eLogError, "SOCKS: Request with v4a rejected because it's actually SOCKS4"); - break; - } - SocksRequestFailed(SOCKS5_ADDR_UNSUP); - return false; - } - return true; - } - - bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len) - { - assert(len); // This should always be called with a least a byte left to parse - while (len > 0) - { - switch (m_state) - { - case GET_SOCKSV: - m_socksv = (SOCKSHandler::socksVersions) *sock_buff; - switch (*sock_buff) - { - case SOCKS4: - EnterState(GET_COMMAND); //Initialize the parser at the right position - break; - case SOCKS5: - EnterState(GET5_AUTHNUM); //Initialize the parser at the right position - break; - default: - LogPrint(eLogError, "SOCKS: Rejected invalid version: ", ((int)*sock_buff)); - Terminate(); - return false; - } - break; - case GET5_AUTHNUM: - EnterState(GET5_AUTH, *sock_buff); - break; - case GET5_AUTH: - m_parseleft --; - if (*sock_buff == AUTH_NONE) - m_authchosen = AUTH_NONE; - else if (*sock_buff == AUTH_USERPASSWD) - m_authchosen = AUTH_USERPASSWD; - if ( m_parseleft == 0 ) - { - if (!Socks5ChooseAuth()) return false; - if (m_authchosen == AUTH_USERPASSWD) - EnterState(GET5_USERPASSWD); - else - EnterState(GET5_REQUESTV); - } - break; - case GET_COMMAND: - switch (*sock_buff) - { - case CMD_CONNECT: - case CMD_BIND: - break; - case CMD_UDP: - if (m_socksv == SOCKS5) break; - [[fallthrough]]; - default: - LogPrint(eLogError, "SOCKS: Invalid command: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - m_cmd = (SOCKSHandler::cmdTypes)*sock_buff; - switch (m_socksv) - { - case SOCKS5: EnterState(GET5_GETRSV); break; - case SOCKS4: EnterState(GET_PORT); break; - } - break; - case GET_PORT: - m_port = (m_port << 8)|((uint16_t)*sock_buff); - m_parseleft--; - if (m_parseleft == 0) - { - switch (m_socksv) - { - case SOCKS5: EnterState(READY); break; - case SOCKS4: EnterState(GET_IPV4); break; - } - } - break; - case GET_IPV4: - m_address.ip = (m_address.ip << 8)|((uint32_t)*sock_buff); - m_parseleft--; - if (m_parseleft == 0) - { - switch (m_socksv) - { - case SOCKS5: EnterState(GET_PORT); break; - case SOCKS4: EnterState(GET4_IDENT); m_4aip = m_address.ip; break; - } - } - break; - case GET4_IDENT: - if (!*sock_buff) - { - if( m_4aip == 0 || m_4aip > 255 ) - EnterState(READY); - else - EnterState(GET4A_HOST); - } - break; - case GET4A_HOST: - if (!*sock_buff) - { - EnterState(READY); - break; - } - if (m_address.dns.size >= max_socks_hostname_size) - { - LogPrint(eLogError, "SOCKS: v4a req failed: destination is too large"); - SocksRequestFailed(SOCKS4_FAIL); - return false; - } - m_address.dns.push_back(*sock_buff); - break; - case GET5_REQUESTV: - if (*sock_buff != SOCKS5) - { - LogPrint(eLogError,"SOCKS: v5 rejected unknown request version: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - EnterState(GET_COMMAND); - break; - case GET5_GETRSV: - if ( *sock_buff != 0 ) - { - LogPrint(eLogError, "SOCKS: v5 unknown reserved field: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - EnterState(GET5_GETADDRTYPE); - break; - case GET5_GETADDRTYPE: - switch (*sock_buff) - { - case ADDR_IPV4: EnterState(GET_IPV4); break; - case ADDR_IPV6: EnterState(GET5_IPV6); break; - case ADDR_DNS : EnterState(GET5_HOST_SIZE); break; - default: - LogPrint(eLogError, "SOCKS: v5 unknown address type: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - break; - case GET5_IPV6: - m_address.ipv6[16-m_parseleft] = *sock_buff; - m_parseleft--; - if (m_parseleft == 0) EnterState(GET_PORT); - break; - case GET5_HOST_SIZE: - EnterState(GET5_HOST, *sock_buff); - break; - case GET5_HOST: - m_address.dns.push_back(*sock_buff); - m_parseleft--; - if (m_parseleft == 0) EnterState(GET_PORT); - break; - case GET5_USERPASSWD: - if (*sock_buff != 1) - { - LogPrint(eLogError,"SOCKS: v5 rejected invalid username/password subnegotiation: ", ((int)*sock_buff)); - SocksRequestFailed(SOCKS5_GEN_FAIL); - return false; - } - EnterState(GET5_USER_SIZE); - break; - case GET5_USER_SIZE: - if (*sock_buff) - EnterState(GET5_USER, *sock_buff); - else // empty user - EnterState(GET5_PASSWD_SIZE); - break; - case GET5_USER: - // skip user for now - m_parseleft--; - if (m_parseleft == 0) EnterState(GET5_PASSWD_SIZE); - break; - case GET5_PASSWD_SIZE: - if (*sock_buff) - EnterState(GET5_PASSWD, *sock_buff); - else // empty password - { - Socks5UserPasswdResponse (); - EnterState(GET5_REQUESTV); - } - break; - case GET5_PASSWD: - // skip passwd for now - m_parseleft--; - if (m_parseleft == 0) - { - Socks5UserPasswdResponse (); - EnterState(GET5_REQUESTV); - } - break; - default: - LogPrint(eLogError, "SOCKS: Parse state?? ", m_state); - Terminate(); - return false; - } - sock_buff++; - len--; - if (m_state == READY) - { - m_remaining_data_len = len; - m_remaining_data = sock_buff; - return ValidateSOCKSRequest(); - } - } - return true; - } - - void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len) - { - LogPrint(eLogDebug, "SOCKS: Received ", len, " bytes"); - if(ecode) - { - LogPrint(eLogWarning, "SOCKS: Recv got error: ", ecode); - Terminate(); - return; - } - - if (HandleData(m_sock_buff, len)) - { - if (m_state == READY) - { - const std::string addr = m_address.dns.ToString(); - LogPrint(eLogInfo, "SOCKS: Requested ", addr, ":" , m_port); - const size_t addrlen = addr.size(); - // does it end with .i2p? - if ( addr.rfind(".i2p") == addrlen - 4) { - // yes it does, make an i2p session - GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete, - shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port); - } else if (m_UseUpstreamProxy) { - // forward it to upstream proxy - ForwardSOCKS(); - } else { - // no upstream proxy - SocksRequestFailed(SOCKS5_ADDR_UNSUP); - } - } - else - AsyncSockRead(); - } - } - - void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode) - { - if (ecode) - LogPrint (eLogError, "SOCKS: Closing socket after sending failure because: ", ecode.message ()); - Terminate(); - } - - void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode) - { - if (!ecode) - { - if (Kill()) return; - LogPrint (eLogInfo, "SOCKS: New I2PTunnel connection"); - auto connection = std::make_shared(GetOwner(), m_sock, m_stream); - GetOwner()->AddHandler (connection); - connection->I2PConnect (m_remaining_data,m_remaining_data_len); - Done(shared_from_this()); - } - else - { - LogPrint (eLogError, "SOCKS: Closing socket after completion reply because: ", ecode.message ()); - Terminate(); - } - } - - void SOCKSHandler::SentSocksResponse(const boost::system::error_code & ecode) - { - if (ecode) - { - LogPrint (eLogError, "SOCKS: Closing socket after sending reply because: ", ecode.message ()); - Terminate(); - } - } - - void SOCKSHandler::HandleStreamRequestComplete (std::shared_ptr stream) - { - if (stream) - { - m_stream = stream; - SocksRequestSuccess(); - } - else - { - LogPrint (eLogError, "SOCKS: Error when creating the stream, check the previous warnings for more info"); - SocksRequestFailed(SOCKS5_HOST_UNREACH); - } - } - - void SOCKSHandler::ForwardSOCKS() - { - LogPrint(eLogInfo, "SOCKS: Forwarding to upstream"); - if (m_UpstreamProxyPort) // TCP - { - EnterState(UPSTREAM_RESOLVE); - m_proxy_resolver.async_resolve(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort), - std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); - } - else if (!m_UpstreamProxyAddress.empty ())// local - { -#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) - EnterState(UPSTREAM_CONNECT); - m_upstreamLocalSock = std::make_shared(GetOwner()->GetService()); - auto s = shared_from_this (); - m_upstreamLocalSock->async_connect(m_UpstreamProxyAddress, - [s](const boost::system::error_code& ecode) - { - if (ecode) - { - LogPrint(eLogWarning, "SOCKS: Could not connect to local upstream proxy: ", ecode.message()); - s->SocksRequestFailed(SOCKS5_NET_UNREACH); - return; - } - LogPrint(eLogInfo, "SOCKS: Connected to local upstream proxy"); - s->SendUpstreamRequest(s->m_upstreamLocalSock); - }); -#else - LogPrint(eLogError, "SOCKS: Local sockets for upstream proxy not supported"); - SocksRequestFailed(SOCKS5_ADDR_UNSUP); -#endif - } - else - { - LogPrint(eLogError, "SOCKS: Incorrect upstream proxy address"); - SocksRequestFailed(SOCKS5_ADDR_UNSUP); - } - } - - template - void SOCKSHandler::SocksUpstreamSuccess(std::shared_ptr& upstreamSock) - { - LogPrint(eLogInfo, "SOCKS: Upstream success"); - boost::asio::const_buffer response(nullptr, 0); - switch (m_socksv) - { - case SOCKS4: - LogPrint(eLogInfo, "SOCKS: v4 connection success"); - response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port); - break; - case SOCKS5: - LogPrint(eLogInfo, "SOCKS: v5 connection success"); - //HACK only 16 bits passed in port as SOCKS5 doesn't allow for more - response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, m_address, m_port); - break; - } - m_sock->send(response); - auto forwarder = CreateSocketsPipe (GetOwner(), m_sock, upstreamSock); - upstreamSock = nullptr; - m_sock = nullptr; - GetOwner()->AddHandler(forwarder); - forwarder->Start(); - Terminate(); - } - - template - void SOCKSHandler::SendUpstreamRequest(std::shared_ptr& upstreamSock) - { - LogPrint(eLogInfo, "SOCKS: Negotiating with upstream proxy"); - EnterState(UPSTREAM_HANDSHAKE); - if (upstreamSock) - { - auto s = shared_from_this (); - i2p::transport::Socks5Handshake (*upstreamSock, std::make_pair(m_address.dns.ToString (), m_port), - [s, &upstreamSock](const boost::system::error_code& ec) - { - if (!ec) - s->SocksUpstreamSuccess(upstreamSock); - else - { - s->SocksRequestFailed(SOCKS5_NET_UNREACH); - LogPrint(eLogError, "SOCKS: Upstream proxy failure: ", ec.message ()); - } - }); - } - else - LogPrint(eLogError, "SOCKS: No upstream socket to send handshake to"); - } - - void SOCKSHandler::HandleUpstreamConnected(const boost::system::error_code & ecode, - const boost::asio::ip::tcp::endpoint& ep) - { - if (ecode) { - LogPrint(eLogWarning, "SOCKS: Could not connect to upstream proxy: ", ecode.message()); - SocksRequestFailed(SOCKS5_NET_UNREACH); - return; - } - LogPrint(eLogInfo, "SOCKS: Connected to upstream proxy"); - SendUpstreamRequest(m_upstreamSock); - } - - void SOCKSHandler::HandleUpstreamResolved(const boost::system::error_code & ecode, - boost::asio::ip::tcp::resolver::results_type endpoints) - { - if (ecode) { - // error resolving - LogPrint(eLogWarning, "SOCKS: Upstream proxy", m_UpstreamProxyAddress, " not resolved: ", ecode.message()); - SocksRequestFailed(SOCKS5_NET_UNREACH); - return; - } - LogPrint(eLogInfo, "SOCKS: Upstream proxy resolved"); - EnterState(UPSTREAM_CONNECT); - auto & service = GetOwner()->GetService(); - m_upstreamSock = std::make_shared(service); - boost::asio::async_connect(*m_upstreamSock, endpoints, - std::bind(&SOCKSHandler::HandleUpstreamConnected, - shared_from_this(), std::placeholders::_1, std::placeholders::_2)); - } - - SOCKSServer::SOCKSServer(const std::string& name, const std::string& address, uint16_t port, - bool outEnable, const std::string& outAddress, uint16_t outPort, - std::shared_ptr localDestination) : - TCPIPAcceptor (address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ()), m_Name (name) - { - m_UseUpstreamProxy = false; - if (outAddress.length() > 0 && outEnable) - SetUpstreamProxy(outAddress, outPort); - } - - std::shared_ptr SOCKSServer::CreateHandler(std::shared_ptr socket) - { - return std::make_shared (this, socket, m_UpstreamProxyAddress, m_UpstreamProxyPort, m_UseUpstreamProxy); - } - - void SOCKSServer::SetUpstreamProxy(const std::string & addr, const uint16_t port) - { - m_UpstreamProxyAddress = addr; - m_UpstreamProxyPort = port; - m_UseUpstreamProxy = true; - } -} -} diff --git a/libi2pd_client/SOCKS.h b/libi2pd_client/SOCKS.h deleted file mode 100644 index bd88d6e6..00000000 --- a/libi2pd_client/SOCKS.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -* Copyright (c) 2013-2023, 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 SOCKS_H__ -#define SOCKS_H__ - -#include -#include -#include -#include -#include "I2PService.h" - -namespace i2p -{ -namespace proxy -{ - class SOCKSServer: public i2p::client::TCPIPAcceptor - { - public: - - SOCKSServer(const std::string& name, const std::string& address, uint16_t port, bool outEnable, const std::string& outAddress, uint16_t outPort, - std::shared_ptr localDestination = nullptr); - ~SOCKSServer() {}; - - void SetUpstreamProxy(const std::string & addr, const uint16_t port); - - protected: - - // Implements TCPIPAcceptor - std::shared_ptr CreateHandler(std::shared_ptr socket); - const char* GetName() { return m_Name.c_str (); } - - private: - - std::string m_Name; - std::string m_UpstreamProxyAddress; - uint16_t m_UpstreamProxyPort; - bool m_UseUpstreamProxy; - }; - - typedef SOCKSServer SOCKSProxy; -} -} -#endif diff --git a/libi2pd_client/UDPTunnel.cpp b/libi2pd_client/UDPTunnel.cpp deleted file mode 100644 index b173fc0f..00000000 --- a/libi2pd_client/UDPTunnel.cpp +++ /dev/null @@ -1,413 +0,0 @@ -/* -* Copyright (c) 2013-2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include "Log.h" -#include "util.h" -#include "ClientContext.h" -#include "I2PTunnel.h" // for GetLoopbackAddressFor -#include "UDPTunnel.h" - -namespace i2p -{ -namespace client -{ - void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - if (!m_LastSession || m_LastSession->Identity.GetLL()[0] != from.GetIdentHash ().GetLL()[0] || fromPort != m_LastSession->RemotePort) - m_LastSession = ObtainUDPSession(from, toPort, fromPort); - m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); - m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); - } - - void I2PUDPServerTunnel::HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - if (m_LastSession && (fromPort != m_LastSession->RemotePort || toPort != m_LastSession->LocalPort)) - { - std::lock_guard lock(m_SessionsMutex); - auto it = m_Sessions.find (GetSessionIndex (fromPort, toPort)); - if (it != m_Sessions.end ()) - m_LastSession = it->second; - else - m_LastSession = nullptr; - } - if (m_LastSession) - { - m_LastSession->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint); - m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); - } - } - - void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) - { - std::lock_guard lock(m_SessionsMutex); - uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); - auto itr = m_Sessions.begin(); - while(itr != m_Sessions.end()) - { - if(now - itr->second->LastActivity >= delta ) - itr = m_Sessions.erase(itr); - else - itr++; - } - } - - void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) - { - std::lock_guard lock(m_SessionsMutex); - uint64_t now = i2p::util::GetMillisecondsSinceEpoch(); - std::vector removePorts; - for (const auto & s : m_Sessions) { - if (now - s.second->second >= delta) - removePorts.push_back(s.first); - } - for(auto port : removePorts) { - m_Sessions.erase(port); - } - } - - UDPSessionPtr I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort) - { - auto ih = from.GetIdentHash(); - auto idx = GetSessionIndex (remotePort, localPort); - { - std::lock_guard lock(m_SessionsMutex); - auto it = m_Sessions.find (idx); - if (it != m_Sessions.end ()) - { - if (it->second->Identity.GetLL()[0] == ih.GetLL()[0]) - { - LogPrint(eLogDebug, "UDPServer: Found session ", it->second->IPSocket.local_endpoint(), " ", ih.ToBase32()); - return it->second; - } - else - { - LogPrint(eLogWarning, "UDPServer: Session with from ", remotePort, " and to ", localPort, " ports already exists. But from different address. Removed"); - m_Sessions.erase (it); - } - } - } - - boost::asio::ip::address addr; - /** create new udp session */ - if(m_IsUniqueLocal && m_LocalAddress.is_loopback()) - { - auto ident = from.GetIdentHash(); - addr = GetLoopbackAddressFor(ident); - } - else - addr = m_LocalAddress; - - auto s = std::make_shared(boost::asio::ip::udp::endpoint(addr, 0), - m_LocalDest, m_RemoteEndpoint, ih, localPort, remotePort); - std::lock_guard lock(m_SessionsMutex); - m_Sessions.emplace (idx, s); - return s; - } - - UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint, - const std::shared_ptr & localDestination, - const boost::asio::ip::udp::endpoint& endpoint, const i2p::data::IdentHash& to, - uint16_t ourPort, uint16_t theirPort) : - m_Destination(localDestination->GetDatagramDestination()), - IPSocket(localDestination->GetService(), localEndpoint), - Identity (to), SendEndpoint(endpoint), - LastActivity(i2p::util::GetMillisecondsSinceEpoch()), - LocalPort(ourPort), - RemotePort(theirPort) - { - IPSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU )); - Receive(); - } - - void UDPSession::Receive() - { - LogPrint(eLogDebug, "UDPSession: Receive"); - IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU), - FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2)); - } - - void UDPSession::HandleReceived(const boost::system::error_code & ecode, std::size_t len) - { - if(!ecode) - { - LogPrint(eLogDebug, "UDPSession: Forward ", len, "B from ", FromEndpoint); - auto ts = i2p::util::GetMillisecondsSinceEpoch(); - auto session = m_Destination->GetSession (Identity); - if (ts > LastActivity + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL) - m_Destination->SendDatagram(session, m_Buffer, len, LocalPort, RemotePort); - else - m_Destination->SendRawDatagram(session, m_Buffer, len, LocalPort, RemotePort); - size_t numPackets = 0; - while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE) - { - boost::system::error_code ec; - size_t moreBytes = IPSocket.available(ec); - if (ec || !moreBytes) break; - len = IPSocket.receive_from (boost::asio::buffer (m_Buffer, I2P_UDP_MAX_MTU), FromEndpoint, 0, ec); - m_Destination->SendRawDatagram (session, m_Buffer, len, LocalPort, RemotePort); - numPackets++; - } - if (numPackets > 0) - LogPrint(eLogDebug, "UDPSession: Forward more ", numPackets, "packets B from ", FromEndpoint); - m_Destination->FlushSendQueue (session); - LastActivity = ts; - Receive(); - } - else - LogPrint(eLogError, "UDPSession: ", ecode.message()); - } - - I2PUDPServerTunnel::I2PUDPServerTunnel (const std::string & name, std::shared_ptr localDestination, - const boost::asio::ip::address& localAddress, const boost::asio::ip::udp::endpoint& forwardTo, uint16_t inPort, bool gzip) : - m_IsUniqueLocal (true), m_Name (name), m_LocalAddress (localAddress), - m_RemoteEndpoint (forwardTo), m_LocalDest (localDestination), m_inPort(inPort), m_Gzip (gzip) - { - } - - I2PUDPServerTunnel::~I2PUDPServerTunnel () - { - Stop (); - } - - void I2PUDPServerTunnel::Start () - { - m_LocalDest->Start (); - - auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip); - dgram->SetReceiver ( - std::bind (&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), - m_inPort - ); - dgram->SetRawReceiver ( - std::bind (&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), - m_inPort - ); - } - - void I2PUDPServerTunnel::Stop () - { - auto dgram = m_LocalDest->GetDatagramDestination (); - if (dgram) { - dgram->ResetReceiver (m_inPort); - dgram->ResetRawReceiver (m_inPort); - } - } - - std::vector > I2PUDPServerTunnel::GetSessions () - { - std::vector > sessions; - std::lock_guard lock (m_SessionsMutex); - - for (const auto &it: m_Sessions) - { - auto s = it.second; - if (!s->m_Destination) continue; - auto info = s->m_Destination->GetInfoForRemote (s->Identity); - if (!info) continue; - - auto sinfo = std::make_shared (); - sinfo->Name = m_Name; - sinfo->LocalIdent = std::make_shared (m_LocalDest->GetIdentHash ().data ()); - sinfo->RemoteIdent = std::make_shared (s->Identity.data ()); - sinfo->CurrentIBGW = info->IBGW; - sinfo->CurrentOBEP = info->OBEP; - sessions.push_back (sinfo); - } - return sessions; - } - - I2PUDPClientTunnel::I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest, - const boost::asio::ip::udp::endpoint& localEndpoint, - std::shared_ptr localDestination, - uint16_t remotePort, bool gzip) : - m_Name (name), m_RemoteDest (remoteDest), m_LocalDest (localDestination), m_LocalEndpoint (localEndpoint), - m_ResolveThread (nullptr), m_LocalSocket (nullptr), RemotePort (remotePort), - m_LastPort (0), m_cancel_resolve (false), m_Gzip (gzip) - { - } - - I2PUDPClientTunnel::~I2PUDPClientTunnel () - { - Stop (); - } - - void I2PUDPClientTunnel::Start () - { - // Reset flag in case of tunnel reload - if (m_cancel_resolve) m_cancel_resolve = false; - - m_LocalSocket.reset (new boost::asio::ip::udp::socket (m_LocalDest->GetService (), m_LocalEndpoint)); - m_LocalSocket->set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU)); - m_LocalSocket->set_option (boost::asio::socket_base::reuse_address (true)); - - auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip); - dgram->SetReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2P, this, - std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3, std::placeholders::_4, - std::placeholders::_5), - RemotePort - ); - dgram->SetRawReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), - RemotePort - ); - - m_LocalDest->Start (); - if (m_ResolveThread == nullptr) - m_ResolveThread = new std::thread (std::bind (&I2PUDPClientTunnel::TryResolving, this)); - RecvFromLocal (); - } - - void I2PUDPClientTunnel::Stop () - { - auto dgram = m_LocalDest->GetDatagramDestination (); - if (dgram) { - dgram->ResetReceiver (RemotePort); - dgram->ResetRawReceiver (RemotePort); - } - m_cancel_resolve = true; - - m_Sessions.clear(); - - if(m_LocalSocket && m_LocalSocket->is_open ()) - m_LocalSocket->close (); - - if(m_ResolveThread) - { - m_ResolveThread->join (); - delete m_ResolveThread; - m_ResolveThread = nullptr; - } - m_RemoteAddr = nullptr; - } - - void I2PUDPClientTunnel::RecvFromLocal () - { - m_LocalSocket->async_receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), - m_RecvEndpoint, std::bind (&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2)); - } - - void I2PUDPClientTunnel::HandleRecvFromLocal (const boost::system::error_code & ec, std::size_t transferred) - { - if (m_cancel_resolve) { - LogPrint (eLogDebug, "UDP Client: Ignoring incoming data: stopping"); - return; - } - if (ec) { - LogPrint (eLogError, "UDP Client: Reading from socket error: ", ec.message (), ". Restarting listener..."); - RecvFromLocal (); // Restart listener and continue work - return; - } - if (!m_RemoteAddr || !m_RemoteAddr->IsIdentHash ()) // TODO: handle B33 - { - LogPrint (eLogWarning, "UDP Client: Remote endpoint not resolved yet"); - RecvFromLocal (); - return; // drop, remote not resolved - } - auto remotePort = m_RecvEndpoint.port (); - if (!m_LastPort || m_LastPort != remotePort) - { - auto itr = m_Sessions.find (remotePort); - if (itr != m_Sessions.end ()) - m_LastSession = itr->second; - else - { - m_LastSession = std::make_shared (boost::asio::ip::udp::endpoint (m_RecvEndpoint), 0); - m_Sessions.emplace (remotePort, m_LastSession); - } - m_LastPort = remotePort; - } - // send off to remote i2p destination - auto ts = i2p::util::GetMillisecondsSinceEpoch (); - LogPrint (eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteAddr->identHash.ToBase32 (), ":", RemotePort); - auto session = m_LocalDest->GetDatagramDestination ()->GetSession (m_RemoteAddr->identHash); - if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL) - m_LocalDest->GetDatagramDestination ()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); - else - m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); - size_t numPackets = 0; - while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE) - { - boost::system::error_code ec; - size_t moreBytes = m_LocalSocket->available (ec); - if (ec || !moreBytes) break; - transferred = m_LocalSocket->receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), m_RecvEndpoint, 0, ec); - remotePort = m_RecvEndpoint.port (); - // TODO: check remotePort - m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); - numPackets++; - } - if (numPackets) - LogPrint (eLogDebug, "UDP Client: Sent ", numPackets, " more packets to ", m_RemoteAddr->identHash.ToBase32 ()); - m_LocalDest->GetDatagramDestination ()->FlushSendQueue (session); - - // mark convo as active - if (m_LastSession) - m_LastSession->second = ts; - RecvFromLocal (); - } - - std::vector > I2PUDPClientTunnel::GetSessions () - { - // TODO: implement - std::vector > infos; - return infos; - } - - void I2PUDPClientTunnel::TryResolving () - { - i2p::util::SetThreadName ("UDP Resolver"); - LogPrint (eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest); - - while (!(m_RemoteAddr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve) - { - LogPrint (eLogWarning, "UDP Tunnel: Failed to lookup ", m_RemoteDest); - std::this_thread::sleep_for (std::chrono::seconds (1)); - } - if (m_cancel_resolve) - { - LogPrint(eLogError, "UDP Tunnel: Lookup of ", m_RemoteDest, " was cancelled"); - return; - } - if (!m_RemoteAddr) - { - LogPrint (eLogError, "UDP Tunnel: ", m_RemoteDest, " not found"); - return; - } - LogPrint(eLogInfo, "UDP Tunnel: Resolved ", m_RemoteDest, " to ", m_RemoteAddr->identHash.ToBase32 ()); - } - - void I2PUDPClientTunnel::HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - if (m_RemoteAddr && from.GetIdentHash() == m_RemoteAddr->identHash) - HandleRecvFromI2PRaw (fromPort, toPort, buf, len); - else - LogPrint(eLogWarning, "UDP Client: Unwarranted traffic from ", from.GetIdentHash().ToBase32 ()); - } - - void I2PUDPClientTunnel::HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) - { - auto itr = m_Sessions.find (toPort); - // found convo ? - if (itr != m_Sessions.end ()) - { - // found convo - if (len > 0) - { - LogPrint (eLogDebug, "UDP Client: Got ", len, "B from ", m_RemoteAddr ? m_RemoteAddr->identHash.ToBase32 () : ""); - m_LocalSocket->send_to (boost::asio::buffer (buf, len), itr->second->first); - // mark convo as active - itr->second->second = i2p::util::GetMillisecondsSinceEpoch (); - } - } - else - LogPrint (eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort); - } - -} -} diff --git a/libi2pd_client/UDPTunnel.h b/libi2pd_client/UDPTunnel.h deleted file mode 100644 index 5650124c..00000000 --- a/libi2pd_client/UDPTunnel.h +++ /dev/null @@ -1,189 +0,0 @@ -/* -* Copyright (c) 2013-2024, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef UDPTUNNEL_H__ -#define UDPTUNNEL_H__ - -#include -#include -#include -#include -#include -#include -#include -#include "Identity.h" -#include "Destination.h" -#include "Datagram.h" -#include "AddressBook.h" - -namespace i2p -{ -namespace client -{ - /** 2 minute timeout for udp sessions */ - const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2; - const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds - - /** max size for i2p udp */ - const size_t I2P_UDP_MAX_MTU = 64*1024; - - struct UDPSession - { - i2p::datagram::DatagramDestination * m_Destination; - boost::asio::ip::udp::socket IPSocket; - i2p::data::IdentHash Identity; - boost::asio::ip::udp::endpoint FromEndpoint; - boost::asio::ip::udp::endpoint SendEndpoint; - uint64_t LastActivity; - - uint16_t LocalPort; - uint16_t RemotePort; - - uint8_t m_Buffer[I2P_UDP_MAX_MTU]; - - UDPSession(boost::asio::ip::udp::endpoint localEndpoint, - const std::shared_ptr & localDestination, - const boost::asio::ip::udp::endpoint& remote, const i2p::data::IdentHash& ident, - uint16_t ourPort, uint16_t theirPort); - void HandleReceived(const boost::system::error_code & ecode, std::size_t len); - void Receive(); - }; - - - /** read only info about a datagram session */ - struct DatagramSessionInfo - { - /** the name of this forward */ - std::string Name; - /** ident hash of local destination */ - std::shared_ptr LocalIdent; - /** ident hash of remote destination */ - std::shared_ptr RemoteIdent; - /** ident hash of IBGW in use currently in this session or nullptr if none is set */ - std::shared_ptr CurrentIBGW; - /** ident hash of OBEP in use for this session or nullptr if none is set */ - std::shared_ptr CurrentOBEP; - /** i2p router's udp endpoint */ - boost::asio::ip::udp::endpoint LocalEndpoint; - /** client's udp endpoint */ - boost::asio::ip::udp::endpoint RemoteEndpoint; - /** how long has this conversation been idle in ms */ - uint64_t idle; - }; - - typedef std::shared_ptr UDPSessionPtr; - - /** server side udp tunnel, many i2p inbound to 1 ip outbound */ - class I2PUDPServerTunnel - { - public: - - I2PUDPServerTunnel (const std::string & name, - std::shared_ptr localDestination, - const boost::asio::ip::address& localAddress, - const boost::asio::ip::udp::endpoint& forwardTo, uint16_t port, bool gzip); - ~I2PUDPServerTunnel (); - - /** expire stale udp conversations */ - void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); - void Start (); - void Stop (); - const char * GetName () const { return m_Name.c_str(); } - std::vector > GetSessions (); - std::shared_ptr GetLocalDestination () const { return m_LocalDest; } - - void SetUniqueLocal (bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; } - - private: - - void HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - UDPSessionPtr ObtainUDPSession (const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort); - uint32_t GetSessionIndex (uint16_t fromPort, uint16_t toPort) const { return ((uint32_t)fromPort << 16) + toPort; } - - private: - - bool m_IsUniqueLocal; - const std::string m_Name; - boost::asio::ip::address m_LocalAddress; - boost::asio::ip::udp::endpoint m_RemoteEndpoint; - std::mutex m_SessionsMutex; - std::unordered_map m_Sessions; // (from port, to port)->session - std::shared_ptr m_LocalDest; - UDPSessionPtr m_LastSession; - uint16_t m_inPort; - bool m_Gzip; - - public: - - bool isUpdated; // transient, used during reload only - }; - - class I2PUDPClientTunnel - { - public: - - I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest, - const boost::asio::ip::udp::endpoint& localEndpoint, std::shared_ptr localDestination, - uint16_t remotePort, bool gzip); - ~I2PUDPClientTunnel (); - - void Start (); - void Stop (); - const char * GetName () const { return m_Name.c_str(); } - std::vector > GetSessions (); - - bool IsLocalDestination (const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); } - - std::shared_ptr GetLocalDestination () const { return m_LocalDest; } - inline void SetLocalDestination (std::shared_ptr dest) - { - if (m_LocalDest) m_LocalDest->Release (); - if (dest) dest->Acquire (); - m_LocalDest = dest; - } - - void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); - - private: - - typedef std::pair UDPConvo; - void RecvFromLocal (); - void HandleRecvFromLocal (const boost::system::error_code & e, std::size_t transferred); - void HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); - void TryResolving (); - - private: - - const std::string m_Name; - std::mutex m_SessionsMutex; - std::unordered_map > m_Sessions; // maps i2p port -> local udp convo - const std::string m_RemoteDest; - std::shared_ptr m_LocalDest; - const boost::asio::ip::udp::endpoint m_LocalEndpoint; - std::shared_ptr m_RemoteAddr; - std::thread * m_ResolveThread; - std::unique_ptr m_LocalSocket; - boost::asio::ip::udp::endpoint m_RecvEndpoint; - uint8_t m_RecvBuff[I2P_UDP_MAX_MTU]; - uint16_t RemotePort, m_LastPort; - bool m_cancel_resolve; - bool m_Gzip; - std::shared_ptr m_LastSession; - - public: - - bool isUpdated; // transient, used during reload only - }; - - -} -} - -#endif diff --git a/libi2pd_wrapper/api.go b/libi2pd_wrapper/api.go deleted file mode 100644 index 8c215f13..00000000 --- a/libi2pd_wrapper/api.go +++ /dev/null @@ -1,15 +0,0 @@ -package api - -/* -* Copyright (c) 2021-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree - */ - -/* -#cgo CXXFLAGS: -I${SRCDIR}/../i18n -I${SRCDIR}/../libi2pd_client -I${SRCDIR}/../libi2pd -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-psabi -fPIC -D__AES__ -maes -#cgo LDFLAGS: -L${SRCDIR}/ -l:../libi2pdwrapper.a -l:../libi2pd.a -l:../libi2pdlang.a -latomic -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lstdc++ -*/ -import "C" diff --git a/libi2pd_wrapper/api.swigcxx b/libi2pd_wrapper/api.swigcxx deleted file mode 100644 index e1d18eef..00000000 --- a/libi2pd_wrapper/api.swigcxx +++ /dev/null @@ -1,8 +0,0 @@ -// See swig.org for more interface options, -// e.g. map std::string to Go string - -%{ -#include "capi.h" -%} - -%include "capi.h" diff --git a/libi2pd_wrapper/capi.cpp b/libi2pd_wrapper/capi.cpp deleted file mode 100644 index af4765da..00000000 --- a/libi2pd_wrapper/capi.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* -* Copyright (c) 2021-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include "../libi2pd/api.h" -#include "capi.h" -#include -#include -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif - -void C_InitI2P (int argc, char *argv[], const char * appName) -{ - std::cout << argv; - return i2p::api::InitI2P(argc, argv, appName); -} - -void C_TerminateI2P () -{ - return i2p::api::TerminateI2P(); -} - -void C_StartI2P () -{ - std::shared_ptr logStream; - return i2p::api::StartI2P(logStream); -} - -void C_StopI2P () -{ - return i2p::api::StopI2P(); -} - -void C_RunPeerTest () -{ - return i2p::api::RunPeerTest(); -} - -#ifdef __cplusplus -} -#endif diff --git a/libi2pd_wrapper/capi.h b/libi2pd_wrapper/capi.h deleted file mode 100644 index aefd89f3..00000000 --- a/libi2pd_wrapper/capi.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -* Copyright (c) 2021-2022, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef CAPI_H__ -#define CAPI_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -// initialization start and stop -void C_InitI2P (int argc, char *argv[], const char * appName); -//void C_InitI2P (int argc, char** argv, const char * appName); -void C_TerminateI2P (); -void C_StartI2P (); -// write system log to logStream, if not specified to .log in application's folder -void C_StopI2P (); -void C_RunPeerTest (); // should be called after UPnP - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/tests/.gitignore b/tests/.gitignore deleted file mode 100644 index 90457c23..00000000 --- a/tests/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -/test-http-merge_chunked -/test-http-req -/test-http-res -/test-http-url -/test-http-url_decode -/test-gost -/test-gost-sig -/test-base-64 -/test-x25519 -/test-aeadchacha20poly1305 -/test-blinding -/test-elligator diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index fb03d434..00000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,123 +0,0 @@ -enable_testing() -find_package(Check 0.9.10 REQUIRED) -include_directories(${CHECK_INCLUDE_DIRS}) - -# Compiler flags: -if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -Wl,-undefined,dynamic_lookup") -else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -Wl,--unresolved-symbols=ignore-in-object-files") -endif() - -set(TEST_PATH ${CMAKE_CURRENT_BINARY_DIR}) - -include_directories( - ../libi2pd - ${Boost_INCLUDE_DIRS} - ${OPENSSL_INCLUDE_DIR} -) - -set(test-http-merge_chunked_SRCS - test-http-merge_chunked.cpp -) - -set(test-http-req_SRCS - test-http-req.cpp -) - -set(test-http-res_SRCS - test-http-res.cpp -) - -set(test-http-url_decode_SRCS - test-http-url_decode.cpp -) - -set(test-http-url_SRCS - test-http-url.cpp -) - -set(test-base-64_SRCS - test-base-64.cpp -) - -set(test-gost_SRCS - test-gost.cpp -) - -set(test-gost-sig_SRCS - test-gost-sig.cpp -) - -set(test-aeadchacha20poly1305_SRCS - test-aeadchacha20poly1305.cpp -) - -set(test-blinding_SRCS - test-blinding.cpp -) - -SET(test-elligator_SRCS - test-elligator.cpp -) - -set(test-eddsa_SRCS - test-eddsa.cpp -) - -set(test-aes_SRCS - test-aes.cpp -) - -add_executable(test-http-merge_chunked ${test-http-merge_chunked_SRCS}) -add_executable(test-http-req ${test-http-req_SRCS}) -add_executable(test-http-res ${test-http-res_SRCS}) -add_executable(test-http-url_decode ${test-http-url_decode_SRCS}) -add_executable(test-http-url ${test-http-url_SRCS}) -add_executable(test-base-64 ${test-base-64_SRCS}) -add_executable(test-gost ${test-gost_SRCS}) -add_executable(test-gost-sig ${test-gost-sig_SRCS}) -add_executable(test-aeadchacha20poly1305 ${test-aeadchacha20poly1305_SRCS}) -add_executable(test-blinding ${test-blinding_SRCS}) -add_executable(test-elligator ${test-elligator_SRCS}) -add_executable(test-eddsa ${test-eddsa_SRCS}) -add_executable(test-aes ${test-aes_SRCS}) - -set(LIBS - libi2pd - ${Boost_LIBRARIES} - OpenSSL::SSL - OpenSSL::Crypto - ZLIB::ZLIB - Threads::Threads - ${CHECK_LDFLAGS} - ${CMAKE_REQUIRED_LIBRARIES} -) - -target_link_libraries(test-http-merge_chunked ${LIBS}) -target_link_libraries(test-http-req ${LIBS}) -target_link_libraries(test-http-res ${LIBS}) -target_link_libraries(test-http-url_decode ${LIBS}) -target_link_libraries(test-http-url ${LIBS}) -target_link_libraries(test-base-64 ${LIBS}) -target_link_libraries(test-gost ${LIBS}) -target_link_libraries(test-gost-sig ${LIBS}) -target_link_libraries(test-aeadchacha20poly1305 ${LIBS}) -target_link_libraries(test-blinding ${LIBS}) -target_link_libraries(test-elligator ${LIBS}) -target_link_libraries(test-eddsa ${LIBS}) -target_link_libraries(test-aes ${LIBS}) - -add_test(test-http-merge_chunked ${TEST_PATH}/test-http-merge_chunked) -add_test(test-http-req ${TEST_PATH}/test-http-req) -add_test(test-http-res ${TEST_PATH}/test-http-res) -add_test(test-http-url_decode ${TEST_PATH}/test-http-url_decode) -add_test(test-http-url ${TEST_PATH}/test-http-url) -add_test(test-base-64 ${TEST_PATH}/test-base-64) -add_test(test-gost ${TEST_PATH}/test-gost) -add_test(test-gost-sig ${TEST_PATH}/test-gost-sig) -add_test(test-aeadchacha20poly1305 ${TEST_PATH}/test-aeadchacha20poly1305) -add_test(test-blinding ${TEST_PATH}/test-blinding) -add_test(test-elligator ${TEST_PATH}/test-elligator) -add_test(test-eddsa ${TEST_PATH}/test-eddsa) -add_test(test-aes ${TEST_PATH}/test-aes) diff --git a/tests/Makefile b/tests/Makefile deleted file mode 100644 index b020427d..00000000 --- a/tests/Makefile +++ /dev/null @@ -1,66 +0,0 @@ -SYS := $(shell $(CXX) -dumpmachine) - -CXXFLAGS += -Wall -Wno-unused-parameter -Wextra -pedantic -O0 -g -std=c++17 -D_GLIBCXX_USE_NANOSLEEP=1 -DOPENSSL_SUPPRESS_DEPRECATED -pthread -Wl,--unresolved-symbols=ignore-in-object-files -INCFLAGS += -I../libi2pd - -LIBI2PD = ../libi2pd.a - -TESTS = \ - test-http-merge_chunked test-http-req test-http-res test-http-url test-http-url_decode \ - test-gost test-gost-sig test-base-64 test-aeadchacha20poly1305 test-blinding \ - test-elligator test-eddsa test-aes - -ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS))) - CXXFLAGS += -DWIN32_LEAN_AND_MEAN - LDFLAGS += -mwindows -static - BOOST_SUFFIX = -mt - NEEDED_LDLIBS = -lwsock32 -lws2_32 -lgdi32 -liphlpapi -lole32 -endif - -LDLIBS = \ - -lboost_system$(BOOST_SUFFIX) \ - -lboost_program_options$(BOOST_SUFFIX) \ - -lssl \ - -lcrypto \ - -lz \ - $(NEEDED_LDLIBS) \ - -lpthread - - -all: $(TESTS) run - -$(LIBI2PD): - @echo "Building libi2pd.a ..." && cd .. && $(MAKE) libi2pd.a - -test-http-%: test-http-%.cpp $(LIBI2PD) - $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) - -test-base-%: test-base-%.cpp $(LIBI2PD) - $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) - -test-gost: test-gost.cpp $(LIBI2PD) - $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) - -test-gost-sig: test-gost-sig.cpp $(LIBI2PD) - $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) - -test-aeadchacha20poly1305: test-aeadchacha20poly1305.cpp $(LIBI2PD) - $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) - -test-blinding: test-blinding.cpp $(LIBI2PD) - $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) - -test-elligator: test-elligator.cpp $(LIBI2PD) - $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) - -test-eddsa: test-eddsa.cpp $(LIBI2PD) - $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) - -test-aes: test-aes.cpp $(LIBI2PD) - $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) - -run: $(TESTS) - @for TEST in $(TESTS); do echo Running $$TEST; ./$$TEST ; done - -clean: - rm -f $(TESTS) diff --git a/tests/test-aeadchacha20poly1305.cpp b/tests/test-aeadchacha20poly1305.cpp deleted file mode 100644 index 2ba6a253..00000000 --- a/tests/test-aeadchacha20poly1305.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include - -#include "Crypto.h" - -char text[] = "Ladies and Gentlemen of the class of '99: If I could offer you " -"only one tip for the future, sunscreen would be it."; // 114 bytes - -uint8_t key[32] = -{ - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f -}; - -uint8_t ad[12] = -{ - 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 -}; - -uint8_t nonce[12] = -{ - 0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 -}; - -uint8_t tag[16] = -{ - 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91 -}; - -uint8_t encrypted[114] = -{ - 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2, - 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, - 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, - 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36, - 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, - 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, - 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b, - 0x61, 0x16 -}; - -int main () -{ - uint8_t buf[114+16]; - i2p::crypto::AEADChaCha20Poly1305Encryptor encryptor; - // test encryption - encryptor.Encrypt ((uint8_t *)text, 114, ad, 12, key, nonce, buf, 114 + 16); - assert (memcmp (buf, encrypted, 114) == 0); - assert (memcmp (buf + 114, tag, 16) == 0); - // test decryption - uint8_t buf1[114]; - i2p::crypto::AEADChaCha20Poly1305Decryptor decryptor; - assert (decryptor.Decrypt (buf, 114, ad, 12, key, nonce, buf1, 114)); - assert (memcmp (buf1, text, 114) == 0); - // test encryption of multiple buffers - memcpy (buf, text, 114); - std::vector > bufs{ std::make_pair (buf, 20), std::make_pair (buf + 20, 10), std::make_pair (buf + 30, 70), std::make_pair (buf + 100, 14) }; - encryptor.Encrypt (bufs, key, nonce, buf + 114); - decryptor.Decrypt (buf, 114, nullptr, 0, key, nonce, buf1, 114); - assert (memcmp (buf1, text, 114) == 0); -} diff --git a/tests/test-aes.cpp b/tests/test-aes.cpp deleted file mode 100644 index 15f4de1e..00000000 --- a/tests/test-aes.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include - -#include "Crypto.h" - -uint8_t ecb_key1[32] = -{ - 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, - 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 -}; - -uint8_t ecb_plain1[16] = -{ - 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a -}; - -uint8_t ecb_cipher1[16] = -{ - 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8 -}; - -uint8_t cbc_key1[32] = -{ - 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, - 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 -}; - -uint8_t cbc_iv1[16] = -{ - 0xF5, 0x8C, 0x4C, 0x04, 0xD6, 0xE5, 0xF1, 0xBA, 0x77, 0x9E, 0xAB, 0xFB, 0x5F, 0x7B, 0xFB, 0xD6 -}; - -uint8_t cbc_plain1[16] = -{ - 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51 -}; - -uint8_t cbc_cipher1[16] = -{ - 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d -}; - -int main () -{ - // ECB encrypt test1 - i2p::crypto::ECBEncryption ecbencryption; - ecbencryption.SetKey (ecb_key1); - uint8_t out[16]; - ecbencryption.Encrypt (ecb_plain1, out); - assert (memcmp (ecb_cipher1, out, 16) == 0); - - // ECB decrypt test1 - i2p::crypto::ECBDecryption ecbdecryption; - ecbdecryption.SetKey (ecb_key1); - ecbdecryption.Decrypt (ecb_cipher1, out); - assert (memcmp (ecb_plain1, out, 16) == 0); - // CBC encrypt test - i2p::crypto::CBCEncryption cbcencryption; - cbcencryption.SetKey (cbc_key1); - cbcencryption.Encrypt (cbc_plain1, 16, cbc_iv1, out); - assert (memcmp (cbc_cipher1, out, 16) == 0); - // CBC decrypt test - i2p::crypto::CBCDecryption cbcdecryption; - cbcdecryption.SetKey (cbc_key1); - cbcdecryption.Decrypt (cbc_cipher1, 16, cbc_iv1, out); - assert (memcmp (cbc_plain1, out, 16) == 0); -} - diff --git a/tests/test-base-64.cpp b/tests/test-base-64.cpp deleted file mode 100644 index 0ab46c06..00000000 --- a/tests/test-base-64.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include - -#include "Base.h" - -using namespace i2p::data; - -int main() { - const char *in = "test"; - size_t in_len = strlen(in); - char out[16]; - - /* bytes -> b64 */ - assert(ByteStreamToBase64(NULL, 0, NULL, 0) == 0); - assert(ByteStreamToBase64(NULL, 0, out, sizeof(out)) == 0); - - assert(Base64EncodingBufferSize(2) == 4); - assert(Base64EncodingBufferSize(4) == 8); - assert(Base64EncodingBufferSize(6) == 8); - assert(Base64EncodingBufferSize(7) == 12); - assert(Base64EncodingBufferSize(9) == 12); - assert(Base64EncodingBufferSize(10) == 16); - assert(Base64EncodingBufferSize(12) == 16); - assert(Base64EncodingBufferSize(13) == 20); - - assert(ByteStreamToBase64((uint8_t *) in, in_len, out, sizeof(out)) == 8); - assert(memcmp(out, "dGVzdA==", 8) == 0); - - /* b64 -> bytes */ - assert(Base64ToByteStream(NULL, 0, NULL, 0) == 0); - assert(Base64ToByteStream(NULL, 0, (uint8_t *) out, sizeof(out)) == 0); - - in = "dGVzdA=="; /* valid b64 */ - assert(Base64ToByteStream(in, strlen(in), (uint8_t *) out, sizeof(out)) == 4); - assert(memcmp(out, "test", 4) == 0); - - in = "dGVzdA="; /* invalid b64 : not padded */ - assert(Base64ToByteStream(in, strlen(in), (uint8_t *) out, sizeof(out)) == 0); - - in = "dG/z.A=="; /* invalid b64 : char not from alphabet */ -// assert(Base64ToByteStream(in, strlen(in), (uint8_t *) out, sizeof(out)) == 0); -// ^^^ fails, current implementation not checks acceptable symbols - - return 0; -} diff --git a/tests/test-blinding.cpp b/tests/test-blinding.cpp deleted file mode 100644 index 10b72e4f..00000000 --- a/tests/test-blinding.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include -#include "Blinding.h" -#include "Identity.h" -#include "Timestamp.h" - -using namespace i2p::data; -using namespace i2p::util; -using namespace i2p::crypto; - -void BlindTest (SigningKeyType sigType) -{ - auto keys = PrivateKeys::CreateRandomKeys (sigType); - BlindedPublicKey blindedKey (keys.GetPublic ()); - auto timestamp = GetSecondsSinceEpoch (); - char date[9]; - GetDateString (timestamp, date); - uint8_t blindedPriv[32], blindedPub[32]; - auto publicKeyLen = blindedKey.BlindPrivateKey (keys.GetSigningPrivateKey (), date, blindedPriv, blindedPub); - uint8_t blindedPub1[32]; - blindedKey.GetBlindedKey (date, blindedPub1); - // check if public key produced from private blinded key matches blided public key - assert (!memcmp (blindedPub, blindedPub1, publicKeyLen)); - // try to sign and verify - std::unique_ptr blindedSigner (PrivateKeys::CreateSigner (blindedKey.GetBlindedSigType (), blindedPriv)); - uint8_t buf[100], signature[64]; - memset (buf, 1, 100); - blindedSigner->Sign (buf, 100, signature); - std::unique_ptr blindedVerifier (IdentityEx::CreateVerifier (blindedKey.GetBlindedSigType ())); - blindedVerifier->SetPublicKey (blindedPub); - assert (blindedVerifier->Verify (buf, 100, signature)); -} - -int main () -{ - // EdDSA test - BlindTest (SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); - // RedDSA test - BlindTest (SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519); -} diff --git a/tests/test-eddsa.cpp b/tests/test-eddsa.cpp deleted file mode 100644 index b3895e2b..00000000 --- a/tests/test-eddsa.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include -#include - -#include "Signature.h" - -// TEST 1024 from RFC-8032 - -int main () -{ - uint8_t key[32], pub[32], msg[1024], sig[64]; - BIGNUM * input = BN_new(); - BN_hex2bn(&input, "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5"); - BN_bn2bin(input, key); - BN_hex2bn(&input, - "08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98" - "fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d8" - "79de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d" - "658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc" - "1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4fe" - "ba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e" - "06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbef" - "efd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7" - "aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed1" - "85ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2" - "d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24" - "554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f270" - "88d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc" - "2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b07" - "07e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128ba" - "b27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51a" - "ddd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429e" - "c96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb7" - "51fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c" - "42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8" - "ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34df" - "f7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08" - "d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649" - "de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e4" - "88acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a3" - "2ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e" - "6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5f" - "b93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b5" - "0d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1" - "369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380d" - "b2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c" - "0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0" - ); - BN_bn2bin(input, msg); - BN_hex2bn(&input, - "0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350" - "aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03"); - BN_bn2bin(input, sig); - BN_hex2bn(&input, - "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e"); - BN_bn2bin(input, pub); - - uint8_t s[64]; - i2p::crypto::EDDSA25519Signer signer (key); - signer.Sign (msg, 1023, s); -#if OPENSSL_EDDSA - assert(memcmp (s, sig, 64) == 0); -#endif - - i2p::crypto::EDDSA25519Verifier verifier; - verifier.SetPublicKey (pub); - assert(verifier.Verify (msg, 1023, s)); -} diff --git a/tests/test-elligator.cpp b/tests/test-elligator.cpp deleted file mode 100644 index 359c71c5..00000000 --- a/tests/test-elligator.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include - -#include "Elligator.h" - -const uint8_t key[32] = -{ - 0x33, 0x95, 0x19, 0x64, 0x00, 0x3c, 0x94, 0x08, 0x78, 0x06, 0x3c, 0xcf, 0xd0, 0x34, 0x8a, 0xf4, - 0x21, 0x50, 0xca, 0x16, 0xd2, 0x64, 0x6f, 0x2c, 0x58, 0x56, 0xe8, 0x33, 0x83, 0x77, 0xd8, 0x80 -}; - -const uint8_t encoded_key[32] = -{ - 0x28, 0x20, 0xb6, 0xb2, 0x41, 0xe0, 0xf6, 0x8a, 0x6c, 0x4a, 0x7f, 0xee, 0x3d, 0x97, 0x82, 0x28, - 0xef, 0x3a, 0xe4, 0x55, 0x33, 0xcd, 0x41, 0x0a, 0xa9, 0x1a, 0x41, 0x53, 0x31, 0xd8, 0x61, 0x2d -}; - -const uint8_t encoded_key_high_y[32] = -{ - 0x3c, 0xfb, 0x87, 0xc4, 0x6c, 0x0b, 0x45, 0x75, 0xca, 0x81, 0x75, 0xe0, 0xed, 0x1c, 0x0a, 0xe9, - 0xda, 0xe7, 0x9d, 0xb7, 0x8d, 0xf8, 0x69, 0x97, 0xc4, 0x84, 0x7b, 0x9f, 0x20, 0xb2, 0x77, 0x18 -}; - -const uint8_t encoded1[32] = -{ - 0xe7, 0x35, 0x07, 0xd3, 0x8b, 0xae, 0x63, 0x99, 0x2b, 0x3f, 0x57, 0xaa, 0xc4, 0x8c, 0x0a, 0xbc, - 0x14, 0x50, 0x95, 0x89, 0x28, 0x84, 0x57, 0x99, 0x5a, 0x2b, 0x4c, 0xa3, 0x49, 0x0a, 0xa2, 0x07 -}; - -const uint8_t key1[32] = -{ - 0x1e, 0x8a, 0xff, 0xfe, 0xd6, 0xbf, 0x53, 0xfe, 0x27, 0x1a, 0xd5, 0x72, 0x47, 0x32, 0x62, 0xde, - 0xd8, 0xfa, 0xec, 0x68, 0xe5, 0xe6, 0x7e, 0xf4, 0x5e, 0xbb, 0x82, 0xee, 0xba, 0x52, 0x60, 0x4f -}; - -const uint8_t encoded2[32] = -{ - 0x95, 0xa1, 0x60, 0x19, 0x04, 0x1d, 0xbe, 0xfe, 0xd9, 0x83, 0x20, 0x48, 0xed, 0xe1, 0x19, 0x28, - 0xd9, 0x03, 0x65, 0xf2, 0x4a, 0x38, 0xaa, 0x7a, 0xef, 0x1b, 0x97, 0xe2, 0x39, 0x54, 0x10, 0x1b -}; - -const uint8_t key2[32] = -{ - 0x79, 0x4f, 0x05, 0xba, 0x3e, 0x3a, 0x72, 0x95, 0x80, 0x22, 0x46, 0x8c, 0x88, 0x98, 0x1e, 0x0b, - 0xe5, 0x78, 0x2b, 0xe1, 0xe1, 0x14, 0x5c, 0xe2, 0xc3, 0xc6, 0xfd, 0xe1, 0x6d, 0xed, 0x53, 0x63 -}; - -const uint8_t encoded3[32] = -{ - 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f -}; - -const uint8_t key3[32] = -{ - 0x9c, 0xdb, 0x52, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 -}; - -const uint8_t failed_key[32] = -{ - 0xe6, 0xf6, 0x6f, 0xdf, 0x6e, 0x23, 0x0c, 0x60, 0x3c, 0x5e, 0x6e, 0x59, 0xa2, 0x54, 0xea, 0x14, - 0x76, 0xa1, 0x3e, 0xb9, 0x51, 0x1b, 0x95, 0x49, 0x84, 0x67, 0x81, 0xe1, 0x2e, 0x52, 0x23, 0x0a -}; - -int main () -{ - uint8_t buf[32]; - i2p::crypto::Elligator2 el; - // encoding tests - el.Encode (key, buf, false, false); - assert(memcmp (buf, encoded_key, 32) == 0); - el.Encode (key, buf, true, false); // with highY - assert(memcmp (buf, encoded_key_high_y, 32) == 0); - // decoding tests - el.Decode (encoded1, buf); - assert(memcmp (buf, key1, 32) == 0); - el.Decode (encoded2, buf); - assert(memcmp (buf, key2, 32) == 0); - el.Decode (encoded3, buf); - assert(memcmp (buf, key3, 32) == 0); - // encoding fails - assert (!el.Encode (failed_key, buf)); -} diff --git a/tests/test-gost-sig.cpp b/tests/test-gost-sig.cpp deleted file mode 100644 index 1348af54..00000000 --- a/tests/test-gost-sig.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include - -#include "Gost.h" -#include "Signature.h" - -const uint8_t example2[72] = -{ - 0xfb,0xe2,0xe5,0xf0,0xee,0xe3,0xc8,0x20,0xfb,0xea,0xfa,0xeb,0xef,0x20,0xff,0xfb, - 0xf0,0xe1,0xe0,0xf0,0xf5,0x20,0xe0,0xed,0x20,0xe8,0xec,0xe0,0xeb,0xe5,0xf0,0xf2, - 0xf1,0x20,0xff,0xf0,0xee,0xec,0x20,0xf1,0x20,0xfa,0xf2,0xfe,0xe5,0xe2,0x20,0x2c, - 0xe8,0xf6,0xf3,0xed,0xe2,0x20,0xe8,0xe6,0xee,0xe1,0xe8,0xf0,0xf2,0xd1,0x20,0x2c, - 0xe8,0xf0,0xf2,0xe5,0xe2,0x20,0xe5,0xd1 -}; - - -int main () -{ - uint8_t priv[64], pub[128], signature[128]; - i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410TC26A512, priv, pub); - i2p::crypto::GOSTR3410_512_Signer signer (i2p::crypto::eGOSTR3410TC26A512, priv); - signer.Sign (example2, 72, signature); - i2p::crypto::GOSTR3410_512_Verifier verifier (i2p::crypto::eGOSTR3410TC26A512); - verifier.SetPublicKey (pub); - assert (verifier.Verify (example2, 72, signature)); - - i2p::crypto::CreateGOSTR3410RandomKeys (i2p::crypto::eGOSTR3410CryptoProA, priv, pub); - i2p::crypto::GOSTR3410_256_Signer signer1 (i2p::crypto::eGOSTR3410CryptoProA, priv); - signer1.Sign (example2, 72, signature); - i2p::crypto::GOSTR3410_256_Verifier verifier1 (i2p::crypto::eGOSTR3410CryptoProA); - verifier1.SetPublicKey (pub); - assert (verifier1.Verify (example2, 72, signature)); -} diff --git a/tests/test-gost.cpp b/tests/test-gost.cpp deleted file mode 100644 index 658e87d4..00000000 --- a/tests/test-gost.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include -#include - -#include "Gost.h" - -const uint8_t example1[63] = -{ - 0x32,0x31,0x30,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x39,0x38,0x37, - 0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31, - 0x30,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x39,0x38,0x37,0x36,0x35, - 0x34,0x33,0x32,0x31,0x30,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30 -}; - -const uint8_t example2[72] = -{ - 0xfb,0xe2,0xe5,0xf0,0xee,0xe3,0xc8,0x20,0xfb,0xea,0xfa,0xeb,0xef,0x20,0xff,0xfb, - 0xf0,0xe1,0xe0,0xf0,0xf5,0x20,0xe0,0xed,0x20,0xe8,0xec,0xe0,0xeb,0xe5,0xf0,0xf2, - 0xf1,0x20,0xff,0xf0,0xee,0xec,0x20,0xf1,0x20,0xfa,0xf2,0xfe,0xe5,0xe2,0x20,0x2c, - 0xe8,0xf6,0xf3,0xed,0xe2,0x20,0xe8,0xe6,0xee,0xe1,0xe8,0xf0,0xf2,0xd1,0x20,0x2c, - 0xe8,0xf0,0xf2,0xe5,0xe2,0x20,0xe5,0xd1 -}; - -const uint8_t example1_hash_512[64] = -{ - 0x48,0x6f,0x64,0xc1,0x91,0x78,0x79,0x41,0x7f,0xef,0x08,0x2b,0x33,0x81,0xa4,0xe2, - 0x11,0xc3,0x24,0xf0,0x74,0x65,0x4c,0x38,0x82,0x3a,0x7b,0x76,0xf8,0x30,0xad,0x00, - 0xfa,0x1f,0xba,0xe4,0x2b,0x12,0x85,0xc0,0x35,0x2f,0x22,0x75,0x24,0xbc,0x9a,0xb1, - 0x62,0x54,0x28,0x8d,0xd6,0x86,0x3d,0xcc,0xd5,0xb9,0xf5,0x4a,0x1a,0xd0,0x54,0x1b -}; - -const uint8_t example1_hash_256[32] = -{ - 0x00,0x55,0x7b,0xe5,0xe5,0x84,0xfd,0x52,0xa4,0x49,0xb1,0x6b,0x02,0x51,0xd0,0x5d, - 0x27,0xf9,0x4a,0xb7,0x6c,0xba,0xa6,0xda,0x89,0x0b,0x59,0xd8,0xef,0x1e,0x15,0x9d -}; - -const uint8_t example2_hash_512[64] = -{ - 0x28,0xfb,0xc9,0xba,0xda,0x03,0x3b,0x14,0x60,0x64,0x2b,0xdc,0xdd,0xb9,0x0c,0x3f, - 0xb3,0xe5,0x6c,0x49,0x7c,0xcd,0x0f,0x62,0xb8,0xa2,0xad,0x49,0x35,0xe8,0x5f,0x03, - 0x76,0x13,0x96,0x6d,0xe4,0xee,0x00,0x53,0x1a,0xe6,0x0f,0x3b,0x5a,0x47,0xf8,0xda, - 0xe0,0x69,0x15,0xd5,0xf2,0xf1,0x94,0x99,0x6f,0xca,0xbf,0x26,0x22,0xe6,0x88,0x1e -}; - -const uint8_t example2_hash_256[32] = -{ - 0x50,0x8f,0x7e,0x55,0x3c,0x06,0x50,0x1d,0x74,0x9a,0x66,0xfc,0x28,0xc6,0xca,0xc0, - 0xb0,0x05,0x74,0x6d,0x97,0x53,0x7f,0xa8,0x5d,0x9e,0x40,0x90,0x4e,0xfe,0xd2,0x9d -}; - -int main () -{ - uint8_t digest[64]; - i2p::crypto::GOSTR3411_2012_512 (example1, 63, digest); - assert(memcmp (digest, example1_hash_512, 64) == 0); - - i2p::crypto::GOSTR3411_2012_256 (example1, 63, digest); - assert(memcmp (digest, example1_hash_256, 32) == 0); - - i2p::crypto::GOSTR3411_2012_512 (example2, 72, digest); - assert(memcmp (digest, example2_hash_512, 64) == 0); - - i2p::crypto::GOSTR3411_2012_256 (example2, 72, digest); - assert(memcmp (digest, example2_hash_256, 32) == 0); -} diff --git a/tests/test-http-merge_chunked.cpp b/tests/test-http-merge_chunked.cpp deleted file mode 100644 index 31b6a298..00000000 --- a/tests/test-http-merge_chunked.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include "HTTP.h" - -using namespace i2p::http; - -int main() { - const char *buf = - "4\r\n" - "HTTP\r\n" - "A\r\n" - " response \r\n" - "E\r\n" - "with \r\n" - "chunks.\r\n" - "0\r\n" - "\r\n" - ; - std::stringstream in(buf); - std::stringstream out; - - assert(MergeChunkedResponse(in, out) == true); - assert(out.str() == "HTTP response with \r\nchunks."); - - return 0; -} diff --git a/tests/test-http-req.cpp b/tests/test-http-req.cpp deleted file mode 100644 index db973a2e..00000000 --- a/tests/test-http-req.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include "HTTP.h" - -using namespace i2p::http; - -int main() { - HTTPReq *req; - int ret = 0, len = 0; - const char *buf; - - /* test: parsing request with body */ - buf = - "GET / HTTP/1.0\r\n" - "User-Agent: curl/7.26.0\r\n" - "Host: inr.i2p\r\n" - "Accept: */*\r\n" - "\r\n" - "test"; - len = strlen(buf); - req = new HTTPReq; - assert((ret = req->parse(buf, len)) == len - 4); - assert(req->version == "HTTP/1.0"); - assert(req->method == "GET"); - assert(req->uri == "/"); - assert(req->GetNumHeaders () == 3); - assert(req->GetNumHeaders("Host") == 1); - assert(req->GetNumHeaders("Accept") == 1); - assert(req->GetNumHeaders("User-Agent") == 1); - assert(req->GetHeader("Host") == "inr.i2p"); - assert(req->GetHeader("Accept") == "*/*"); - assert(req->GetHeader("User-Agent") == "curl/7.26.0"); - delete req; - - /* test: parsing request without body */ - buf = - "GET / HTTP/1.0\r\n" - "\r\n"; - len = strlen(buf); - req = new HTTPReq; - assert((ret = req->parse(buf, len)) == len); - assert(req->version == "HTTP/1.0"); - assert(req->method == "GET"); - assert(req->uri == "/"); - assert(req->GetNumHeaders () == 0); - delete req; - - /* test: parsing request without body */ - buf = - "GET / HTTP/1.1\r\n" - "\r\n"; - len = strlen(buf); - req = new HTTPReq; - assert((ret = req->parse(buf, len)) > 0); - delete req; - - /* test: parsing incomplete request */ - buf = - "GET / HTTP/1.0\r\n" - ""; - len = strlen(buf); - req = new HTTPReq; - assert((ret = req->parse(buf, len)) == 0); /* request not completed */ - delete req; - - /* test: parsing slightly malformed request */ - buf = - "GET http://inr.i2p HTTP/1.1\r\n" - "Host: stats.i2p\r\n" - "Accept-Encoding: \r\n" - "Accept: */*\r\n" - "\r\n"; - len = strlen(buf); - req = new HTTPReq; - assert((ret = req->parse(buf, len)) == len); /* no host header */ - assert(req->method == "GET"); - assert(req->uri == "http://inr.i2p"); - assert(req->GetNumHeaders () == 3); - assert(req->GetNumHeaders("Host") == 1); - assert(req->GetNumHeaders("Accept") == 1); - assert(req->GetNumHeaders("Accept-Encoding") == 1); - assert(req->GetHeader("Host") == "stats.i2p"); - assert(req->GetHeader("Accept") == "*/*"); - assert(req->GetHeader("Accept-Encoding") == ""); - delete req; - - return 0; -} - -/* vim: expandtab:ts=2 */ diff --git a/tests/test-http-res.cpp b/tests/test-http-res.cpp deleted file mode 100644 index 270f32a3..00000000 --- a/tests/test-http-res.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include "HTTP.h" - -using namespace i2p::http; - -int main() { - HTTPRes *res; - int ret = 0, len = 0; - const char *buf; - - /* test: parsing valid response without body */ - buf = - "HTTP/1.1 304 Not Modified\r\n" - "Date: Thu, 14 Apr 2016 00:00:00 GMT\r\n" - "Server: nginx/1.2.1\r\n" - "Content-Length: 536\r\n" - "\r\n"; - len = strlen(buf); - res = new HTTPRes; - assert((ret = res->parse(buf, len)) == len); - assert(res->version == "HTTP/1.1"); - assert(res->status == "Not Modified"); - assert(res->code == 304); - assert(res->headers.size() == 3); - assert(res->headers.count("Date") == 1); - assert(res->headers.count("Server") == 1); - assert(res->headers.count("Content-Length") == 1); - assert(res->headers.find("Date")->second == "Thu, 14 Apr 2016 00:00:00 GMT"); - assert(res->headers.find("Server")->second == "nginx/1.2.1"); - assert(res->headers.find("Content-Length")->second == "536"); - assert(res->is_chunked() == false); - assert(res->content_length() == 536); - delete res; - - /* test: building request */ - buf = - "HTTP/1.0 304 Not Modified\r\n" - "Content-Length: 0\r\n" - "\r\n"; - res = new HTTPRes; - res->version = "HTTP/1.0"; - res->code = 304; - res->status = "Not Modified"; - res->add_header("Content-Length", "0"); - assert(res->to_string() == buf); - - return 0; -} - -/* vim: expandtab:ts=2 */ diff --git a/tests/test-http-url.cpp b/tests/test-http-url.cpp deleted file mode 100644 index a5021c43..00000000 --- a/tests/test-http-url.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include "HTTP.h" - -using namespace i2p::http; - -int main() { - std::map params; - URL *url; - - url = new URL; - assert(url->parse("https://127.0.0.1:7070/asdasd?12345") == true); - assert(url->schema == "https"); - assert(url->user == ""); - assert(url->pass == ""); - assert(url->host == "127.0.0.1"); - assert(url->port == 7070); - assert(url->path == "/asdasd"); - assert(url->hasquery == true); - assert(url->query == "12345"); - assert(url->to_string() == "https://127.0.0.1:7070/asdasd?12345"); - delete url; - - url = new URL; - assert(url->parse("http://user:password@site.com:8080/asdasd?123456") == true); - assert(url->schema == "http"); - assert(url->user == "user"); - assert(url->pass == "password"); - assert(url->host == "site.com"); - assert(url->port == 8080); - assert(url->path == "/asdasd"); - assert(url->hasquery == true); - assert(url->query == "123456"); - delete url; - - url = new URL; - assert(url->parse("http://user:password@site.com/asdasd?name=value") == true); - assert(url->schema == "http"); - assert(url->user == "user"); - assert(url->pass == "password"); - assert(url->host == "site.com"); - assert(url->port == 0); - assert(url->path == "/asdasd"); - assert(url->hasquery == true); - assert(url->query == "name=value"); - delete url; - - url = new URL; - assert(url->parse("http://user:@site.com/asdasd?name=value1&name=value2") == true); - assert(url->schema == "http"); - assert(url->user == "user"); - assert(url->pass == ""); - assert(url->host == "site.com"); - assert(url->port == 0); - assert(url->path == "/asdasd"); - assert(url->hasquery == true); - assert(url->query == "name=value1&name=value2"); - delete url; - - url = new URL; - assert(url->parse("http://user@site.com/asdasd?name1=value1&name2&name3=value2") == true); - assert(url->schema == "http"); - assert(url->user == "user"); - assert(url->pass == ""); - assert(url->host == "site.com"); - assert(url->port == 0); - assert(url->path == "/asdasd"); - assert(url->hasquery == true); - assert(url->query == "name1=value1&name2&name3=value2"); - assert(url->parse_query(params)); - assert(params.size() == 3); - assert(params.count("name1") == 1); - assert(params.count("name2") == 1); - assert(params.count("name3") == 1); - assert(params.find("name1")->second == "value1"); - assert(params.find("name2")->second == ""); - assert(params.find("name3")->second == "value2"); - delete url; - - url = new URL; - assert(url->parse("http://@site.com:800/asdasd?") == true); - assert(url->schema == "http"); - assert(url->user == ""); - assert(url->pass == ""); - assert(url->host == "site.com"); - assert(url->port == 800); - assert(url->path == "/asdasd"); - assert(url->hasquery == true); - assert(url->query == ""); - delete url; - - url = new URL; - assert(url->parse("http://@site.com:17") == true); - assert(url->schema == "http"); - assert(url->user == ""); - assert(url->pass == ""); - assert(url->host == "site.com"); - assert(url->port == 17); - assert(url->path == ""); - assert(url->hasquery == false); - assert(url->query == ""); - delete url; - - url = new URL; - assert(url->parse("http://user:password@site.com:err_port/asdasd") == false); - assert(url->schema == "http"); - assert(url->user == "user"); - assert(url->pass == "password"); - assert(url->host == "site.com"); - assert(url->port == 0); - assert(url->path == ""); - assert(url->hasquery == false); - assert(url->query == ""); - delete url; - - url = new URL; - assert(url->parse("http://user:password@site.com:84/asdasd/@17#frag") == true); - assert(url->schema == "http"); - assert(url->user == "user"); - assert(url->pass == "password"); - assert(url->host == "site.com"); - assert(url->port == 84); - assert(url->path == "/asdasd/@17"); - assert(url->hasquery == false); - assert(url->query == ""); - assert(url->frag == "frag"); - delete url; - - return 0; -} - -/* vim: expandtab:ts=2 */ diff --git a/tests/test-http-url_decode.cpp b/tests/test-http-url_decode.cpp deleted file mode 100644 index 7f08bbc6..00000000 --- a/tests/test-http-url_decode.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include "HTTP.h" - -using namespace i2p::http; - -int main() { - std::string in("/%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0/"); - std::string out = UrlDecode(in); - - assert(strcmp(out.c_str(), "/страница/") == 0); - - in = "/%00/"; - out = UrlDecode(in, false); - assert(strcmp(out.c_str(), "/%00/") == 0); - out = UrlDecode(in, true); - assert(strcmp(out.c_str(), "/\0/") == 0); - - return 0; -} diff --git a/util.cpp b/util.cpp new file mode 100644 index 00000000..cf5628f3 --- /dev/null +++ b/util.cpp @@ -0,0 +1,508 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "Log.h" + +#if defined(__linux__) || defined(__FreeBSD_kernel__) +#include +#include +#endif + +#ifdef WIN32 +#include +#include +#endif + +namespace i2p +{ +namespace util +{ + +namespace config +{ + std::map mapArgs; + std::map > mapMultiArgs; + + void OptionParser(int argc, const char* const argv[]) + { + mapArgs.clear(); + mapMultiArgs.clear(); + for (int i = 1; i < argc; i++) + { + std::string strKey (argv[i]); + std::string strValue; + size_t has_data = strKey.find('='); + if (has_data != std::string::npos) + { + strValue = strKey.substr(has_data+1); + strKey = strKey.substr(0, has_data); + } + +#ifdef WIN32 + boost::to_lower(strKey); + if (boost::algorithm::starts_with(strKey, "/")) + strKey = "-" + strKey.substr(1); +#endif + if (strKey[0] != '-') + break; + + mapArgs[strKey] = strValue; + mapMultiArgs[strKey].push_back(strValue); + } + + BOOST_FOREACH(PAIRTYPE(const std::string,std::string)& entry, mapArgs) + { + std::string name = entry.first; + + // interpret --foo as -foo (as long as both are not set) + if (name.find("--") == 0) + { + std::string singleDash(name.begin()+1, name.end()); + if (mapArgs.count(singleDash) == 0) + mapArgs[singleDash] = entry.second; + name = singleDash; + } + } + } + + const char* GetCharArg(const std::string& strArg, const std::string& nDefault) + { + if (mapArgs.count(strArg)) + return mapArgs[strArg].c_str(); + return nDefault.c_str(); + } + + std::string GetArg(const std::string& strArg, const std::string& strDefault) + { + if (mapArgs.count(strArg)) + return mapArgs[strArg]; + return strDefault; + } + + int GetArg(const std::string& strArg, int nDefault) + { + if (mapArgs.count(strArg)) + return atoi(mapArgs[strArg].c_str()); + return nDefault; + } +} + +namespace filesystem +{ + std::string appName ("i2pd"); + + void SetAppName (const std::string& name) + { + appName = name; + } + + std::string GetAppName () + { + return appName; + } + + const boost::filesystem::path &GetDataDir() + { + static boost::filesystem::path path; + + // TODO: datadir parameter is useless because GetDataDir is called before OptionParser + // and mapArgs is not initialized yet + /*if (i2p::util::config::mapArgs.count("-datadir")) + path = boost::filesystem::system_complete(i2p::util::config::mapArgs["-datadir"]); + else */ + path = GetDefaultDataDir(); + + if (!boost::filesystem::exists( path )) + { + // Create data directory + if (!boost::filesystem::create_directory( path )) + { + LogPrint("Failed to create data directory!"); + path = ""; + return path; + } + } + if (!boost::filesystem::is_directory(path)) + path = GetDefaultDataDir(); + return path; + } + + std::string GetFullPath (const std::string& filename) + { + std::string fullPath = GetDataDir ().string (); +#ifndef _WIN32 + fullPath.append ("/"); +#else + fullPath.append ("\\"); +#endif + fullPath.append (filename); + return fullPath; + } + + boost::filesystem::path GetConfigFile() + { + boost::filesystem::path pathConfigFile(i2p::util::config::GetArg("-conf", "i2p.conf")); + if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir() / pathConfigFile; + return pathConfigFile; + } + + void ReadConfigFile(std::map& mapSettingsRet, + std::map >& mapMultiSettingsRet) + { + boost::filesystem::ifstream streamConfig(GetConfigFile()); + if (!streamConfig.good()) + return; // No i2pd.conf file is OK + + std::set setOptions; + setOptions.insert("*"); + + for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) + { + // Don't overwrite existing settings so command line settings override i2pd.conf + std::string strKey = std::string("-") + it->string_key; + if (mapSettingsRet.count(strKey) == 0) + { + mapSettingsRet[strKey] = it->value[0]; + } + mapMultiSettingsRet[strKey].push_back(it->value[0]); + } + } + + boost::filesystem::path GetDefaultDataDir() + { + // Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd + // Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd + // Mac: ~/Library/Application Support/i2pd + // Unix: ~/.i2pd or /var/lib/i2pd is system=1 +#ifdef WIN32 + // Windows + char localAppData[MAX_PATH]; + SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData); + return boost::filesystem::path(std::string(localAppData) + "\\" + appName); +#else + if (i2p::util::config::GetArg("-service", 0)) // use system folder + return boost::filesystem::path(std::string ("/var/lib/") + appName); + boost::filesystem::path pathRet; + char* pszHome = getenv("HOME"); + if (pszHome == NULL || strlen(pszHome) == 0) + pathRet = boost::filesystem::path("/"); + else + pathRet = boost::filesystem::path(pszHome); +#ifdef MAC_OSX + // Mac + pathRet /= "Library/Application Support"; + boost::filesystem::create_directory(pathRet); + return pathRet / appName; +#else + // Unix + return pathRet / (std::string (".") + appName); +#endif +#endif + } + + boost::filesystem::path GetCertificatesDir() + { + return GetDataDir () / "certificates"; + } +} + +namespace http +{ + std::string httpRequest(const std::string& address) + { + try + { + i2p::util::http::url u(address); + boost::asio::ip::tcp::iostream site; + // please don't uncomment following line because it's not compatible with boost 1.46 + // 1.46 is default boost for Ubuntu 12.04 LTS + //site.expires_from_now (boost::posix_time::seconds(30)); + if (u.port_ == 80) + site.connect(u.host_, "http"); + else + { + std::stringstream ss; ss << u.port_; + site.connect(u.host_, ss.str()); + } + if (site) + { + // User-Agent is needed to get the server list routerInfo files. + site << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_ + << "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n"; + // read response + std::string version, statusMessage; + site >> version; // HTTP version + int status; + site >> status; // status + std::getline (site, statusMessage); + if (status == 200) // OK + { + bool isChunked = false; + std::string header; + while (!site.eof () && header != "\r") + { + std::getline(site, header); + auto colon = header.find (':'); + if (colon != std::string::npos) + { + std::string field = header.substr (0, colon); + if (field == i2p::util::http::TRANSFER_ENCODING) + isChunked = (header.find ("chunked", colon + 1) != std::string::npos); + } + } + + std::stringstream ss; + if (isChunked) + MergeChunkedResponse (site, ss); + else + ss << site.rdbuf(); + return ss.str(); + } + else + { + LogPrint ("HTTP response ", status); + return ""; + } + } + else + { + LogPrint ("Can't connect to ", address); + return ""; + } + } + catch (std::exception& ex) + { + LogPrint ("Failed to download ", address, " : ", ex.what ()); + return ""; + } + } + + void MergeChunkedResponse (std::istream& response, std::ostream& merged) + { + while (!response.eof ()) + { + std::string hexLen; + int len; + std::getline (response, hexLen); + std::istringstream iss (hexLen); + iss >> std::hex >> len; + if (!len) break; + char * buf = new char[len]; + response.read (buf, len); + merged.write (buf, len); + delete[] buf; + std::getline (response, hexLen); // read \r\n after chunk + } + } + + int httpRequestViaI2pProxy(const std::string& address, std::string &content) + { + content = ""; + try + { + boost::asio::ip::tcp::iostream site; + // please don't uncomment following line because it's not compatible with boost 1.46 + // 1.46 is default boost for Ubuntu 12.04 LTS + //site.expires_from_now (boost::posix_time::seconds(30)); + { + std::stringstream ss; ss << i2p::util::config::GetArg("-httpproxyport", 4446); + site.connect("127.0.0.1", ss.str()); + } + if (site) + { + i2p::util::http::url u(address); + std::stringstream ss; + ss << "GET " << address << " HTTP/1.0" << std::endl; + ss << "Host: " << u.host_ << std::endl; + ss << "Accept: */*" << std::endl; + ss << "User - Agent: Wget / 1.11.4" << std::endl; + ss << "Connection: close" << std::endl; + ss << std::endl; + site << ss.str(); + + // read response + std::string version, statusMessage; + site >> version; // HTTP version + int status; + site >> status; // status + std::getline(site, statusMessage); + if (status == 200) // OK + { + std::string header; + while (std::getline(site, header) && header != "\r"){} + std::stringstream ss; + ss << site.rdbuf(); + content = ss.str(); + return status; + } + else + { + LogPrint("HTTP response ", status); + return status; + } + } + else + { + LogPrint("Can't connect to proxy"); + return 408; + } + } + catch (std::exception& ex) + { + LogPrint("Failed to download ", address, " : ", ex.what()); + return 408; + } + } + + url::url(const std::string& url_s) + { + portstr_ = "80"; + port_ = 80; + user_ = ""; + pass_ = ""; + + parse(url_s); + } + + + // code for parser tests + //{ + // i2p::util::http::url u_0("http://127.0.0.1:7070/asdasd?qqqqqqqqqqqq"); + // i2p::util::http::url u_1("http://user:password@site.com:8080/asdasd?qqqqqqqqqqqqq"); + // i2p::util::http::url u_2("http://user:password@site.com/asdasd?qqqqqqqqqqqqqq"); + // i2p::util::http::url u_3("http://user:@site.com/asdasd?qqqqqqqqqqqqq"); + // i2p::util::http::url u_4("http://user@site.com/asdasd?qqqqqqqqqqqq"); + // i2p::util::http::url u_5("http://@site.com:800/asdasd?qqqqqqqqqqqq"); + // i2p::util::http::url u_6("http://@site.com:err_port/asdasd?qqqqqqqqqqqq"); + // i2p::util::http::url u_7("http://user:password@site.com:err_port/asdasd?qqqqqqqqqqqq"); + //} + void url::parse(const std::string& url_s) + { + const std::string prot_end("://"); + std::string::const_iterator prot_i = search(url_s.begin(), url_s.end(), + prot_end.begin(), prot_end.end()); + protocol_.reserve(distance(url_s.begin(), prot_i)); + transform(url_s.begin(), prot_i, + back_inserter(protocol_), + std::ptr_fun(tolower)); // protocol is icase + if( prot_i == url_s.end() ) + return; + advance(prot_i, prot_end.length()); + std::string::const_iterator path_i = find(prot_i, url_s.end(), '/'); + host_.reserve(distance(prot_i, path_i)); + transform(prot_i, path_i, + back_inserter(host_), + std::ptr_fun(tolower)); // host is icase + + // parse user/password + auto user_pass_i = find(host_.begin(), host_.end(), '@'); + if (user_pass_i != host_.end()) + { + std::string user_pass = std::string(host_.begin(), user_pass_i); + auto pass_i = find(user_pass.begin(), user_pass.end(), ':'); + if (pass_i != user_pass.end()) + { + user_ = std::string(user_pass.begin(), pass_i); + pass_ = std::string(pass_i + 1, user_pass.end()); + } + else + user_ = user_pass; + + host_.assign(user_pass_i + 1, host_.end()); + } + + // parse port + auto port_i = find(host_.begin(), host_.end(), ':'); + if (port_i != host_.end()) + { + portstr_ = std::string(port_i + 1, host_.end()); + host_.assign(host_.begin(), port_i); + try{ + port_ = boost::lexical_cast(portstr_); + } + catch (std::exception e) { + port_ = 80; + } + } + + std::string::const_iterator query_i = find(path_i, url_s.end(), '?'); + path_.assign(path_i, query_i); + if( query_i != url_s.end() ) + ++query_i; + query_.assign(query_i, url_s.end()); + } + +} + +namespace net +{ + int GetMTU (const boost::asio::ip::address& localAddress) + { +#if defined(__linux__) || defined(__FreeBSD_kernel__) + ifaddrs * ifaddr, * ifa = nullptr; + if (getifaddrs(&ifaddr) == -1) + { + LogPrint (eLogError, "Can't excute getifaddrs"); + return 0; + } + int family = 0; + // loook for interface matching local address + for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) + { + if (!ifa->ifa_addr) continue; + family = ifa->ifa_addr->sa_family; + if (family == AF_INET && localAddress.is_v4 ()) + { + sockaddr_in * sa = (sockaddr_in *)ifa->ifa_addr; + if (!memcmp (&sa->sin_addr, localAddress.to_v4 ().to_bytes ().data (), 4)) + break; // address matches + } + else if (family == AF_INET6 && localAddress.is_v6 ()) + { + sockaddr_in6 * sa = (sockaddr_in6 *)ifa->ifa_addr; + if (!memcmp (&sa->sin6_addr, localAddress.to_v6 ().to_bytes ().data (), 16)) + break; // address matches + } + } + int mtu = 0; + if (ifa && family) // interface found? + { + int fd = socket (family, SOCK_DGRAM, 0); + if (fd > 0) + { + ifreq ifr; + strncpy (ifr.ifr_name, ifa->ifa_name, IFNAMSIZ); // set interface for query + if (ioctl (fd, SIOCGIFMTU, &ifr) >= 0) + mtu = ifr.ifr_mtu; // MTU + else + LogPrint (eLogError, "Failed to run ioctl"); + close (fd); + } + else + LogPrint (eLogError, "Failed to create datagram socket"); + } + else + LogPrint (eLogWarning, "Interface for local address", localAddress.to_string (), " not found"); + + freeifaddrs (ifaddr); + return mtu; +#else + return 0; +#endif + } +} + +} // util +} // i2p diff --git a/util.h b/util.h new file mode 100644 index 00000000..e07b746e --- /dev/null +++ b/util.h @@ -0,0 +1,74 @@ +#ifndef UTIL_H +#define UTIL_H + +#include +#include +#include +#include +#include +#include + +#define PAIRTYPE(t1, t2) std::pair + +namespace i2p +{ +namespace util +{ + namespace config + { + extern std::map mapArgs; + extern std::map > mapMultiArgs; + void OptionParser(int argc, const char* const argv[]); + int GetArg(const std::string& strArg, int nDefault); + std::string GetArg(const std::string& strArg, const std::string& strDefault); + const char* GetCharArg(const std::string& strArg, const std::string& nDefault); + } + + namespace filesystem + { + void SetAppName (const std::string& name); + std::string GetAppName (); + + const boost::filesystem::path &GetDataDir(); + std::string GetFullPath (const std::string& filename); + boost::filesystem::path GetDefaultDataDir(); + boost::filesystem::path GetConfigFile(); + void ReadConfigFile(std::map& mapSettingsRet, + std::map >& mapMultiSettingsRet); + boost::filesystem::path GetCertificatesDir(); + } + + namespace http + { + const char ETAG[] = "ETag"; + const char IF_NONE_MATCH[] = "If-None-Match"; + const char IF_MODIFIED_SINCE[] = "If-Modified-Since"; + const char LAST_MODIFIED[] = "Last-Modified"; + const char TRANSFER_ENCODING[] = "Transfer-Encoding"; + + std::string httpRequest(const std::string& address); + void MergeChunkedResponse (std::istream& response, std::ostream& merged); + int httpRequestViaI2pProxy(const std::string& address, std::string &content); // return http code + + struct url { + url(const std::string& url_s); // omitted copy, ==, accessors, ... + private: + void parse(const std::string& url_s); + public: + std::string protocol_, host_, path_, query_; + std::string portstr_; + unsigned int port_; + std::string user_; + std::string pass_; + }; + } + + namespace net + { + int GetMTU (const boost::asio::ip::address& localAddress); + } +} +} + + +#endif diff --git a/version.h b/version.h new file mode 100644 index 00000000..668b5798 --- /dev/null +++ b/version.h @@ -0,0 +1,8 @@ +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#define CODENAME "Purple" +#define VERSION "0.6.0" +#define I2P_VERSION "0.9.17" + +#endif