Merge pull request #1805 from PurpleI2P/openssl

2.44.0
This commit is contained in:
R4SAS 2022-11-22 20:57:22 +00:00 committed by GitHub
commit 74b2ba7ae2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 5688 additions and 2510 deletions

32
.github/workflows/build-deb.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: Build Debian packages
on: [push, pull_request]
jobs:
build:
name: ${{ matrix.dist }}
runs-on: ubuntu-latest
strategy:
matrix:
dist: ['buster', 'bullseye', 'bookworm']
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: change debian changelog
run: |
sudo apt-get update
sudo apt-get install devscripts
debchange -v "`git describe --tags`-${{ matrix.dist }}" -b -M --distribution ${{ matrix.dist }} "trunk build"
- uses: jtdor/build-deb-action@v1
with:
docker-image: debian:${{ matrix.dist }}-slim
buildpackage-opts: --build=binary --no-sign
- uses: actions/upload-artifact@v3
with:
name: i2pd_${{ matrix.dist }}
path: debian/artifacts/i2pd_*.deb
- uses: actions/upload-artifact@v3
with:
name: i2pd-dbgsym_${{ matrix.dist }}
path: debian/artifacts/i2pd-dbgsym_*.deb

View File

@ -4,18 +4,25 @@ on: [push, pull_request]
jobs: jobs:
build: build:
runs-on: macos-10.15 runs-on: macos-12
name: with UPnP name: with UPnP
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Test in FreeBSD - name: Test in FreeBSD
id: test id: test
uses: vmactions/freebsd-vm@v0.1.5 uses: vmactions/freebsd-vm@v0.3.0
with: with:
usesh: true usesh: true
mem: 2048 mem: 2048
sync: rsync
copyback: true
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
run: | run: |
cd build cd build
cmake -DWITH_UPNP=ON -DCMAKE_BUILD_TYPE=Release . cmake -DWITH_UPNP=ON -DCMAKE_BUILD_TYPE=Release .
gmake -j2 gmake -j2
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: i2pd-freebsd
path: build/i2pd

View File

@ -38,51 +38,3 @@ jobs:
cd build cd build
cmake -DWITH_UPNP=${{ matrix.with_upnp }} . cmake -DWITH_UPNP=${{ matrix.with_upnp }} .
make -j3 make -j3
build-deb-stretch:
name: Build package for stretch
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: change debian changelog
run: |
sudo apt-get update
sudo apt-get install devscripts
debchange -v "`git describe --tags`-stretch" -b -M --distribution stretch "trunk build"
- uses: singingwolfboy/build-dpkg-stretch@v1
id: build
with:
args: --unsigned-source --unsigned-changes -b
- uses: actions/upload-artifact@v1
with:
name: ${{ steps.build.outputs.filename }}
path: ${{ steps.build.outputs.filename }}
- uses: actions/upload-artifact@v1
with:
name: ${{ steps.build.outputs.filename-dbgsym }}
path: ${{ steps.build.outputs.filename-dbgsym }}
build-deb-buster:
name: Build package for buster
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: change debian changelog
run: |
sudo apt-get update
sudo apt-get install devscripts
debchange -v "`git describe --tags`-buster" -b -M --distribution buster "trunk build"
- uses: singingwolfboy/build-dpkg-buster@v1
id: build
with:
args: --unsigned-source --unsigned-changes -b
- uses: actions/upload-artifact@v1
with:
name: ${{ steps.build.outputs.filename }}
path: ${{ steps.build.outputs.filename }}
- uses: actions/upload-artifact@v1
with:
name: ${{ steps.build.outputs.filename-dbgsym }}
path: ${{ steps.build.outputs.filename-dbgsym }}

View File

@ -4,67 +4,137 @@ on:
push: push:
branches: branches:
- openssl - openssl
- docker
tags: tags:
- '*' - '*'
jobs: jobs:
docker: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
packages: write packages: write
contents: read 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: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v2
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v1 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry - name: Login to GitHub Container registry
uses: docker/login-action@v1 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push trunk container - name: Build container for ${{ matrix.archname }}
if: ${{ !startsWith(github.ref, 'refs/tags/') }} uses: docker/build-push-action@v3
uses: docker/build-push-action@v2
with: with:
context: ./contrib/docker context: ./contrib/docker
file: ./contrib/docker/Dockerfile file: ./contrib/docker/Dockerfile
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7 platforms: ${{ matrix.platform }}
push: true push: true
tags: | tags: |
purplei2p/i2pd:latest purplei2p/i2pd:latest-${{ matrix.archname }}
ghcr.io/purplei2p/i2pd:latest ghcr.io/purplei2p/i2pd:latest-${{ matrix.archname }}
- name: Set env push:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
needs: build
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push latest manifest image to Docker Hub
uses: Noelware/docker-manifest-action@master
with:
base-image: purplei2p/i2pd:latest
extra-images: 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
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/purplei2p/i2pd:latest
extra-images: 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/') }} if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
- name: Build and push release container - name: Create and push release manifest image to Docker Hub
if: ${{ startsWith(github.ref, 'refs/tags/') }} if: ${{ startsWith(github.ref, 'refs/tags/') }}
uses: docker/build-push-action@v2 uses: Noelware/docker-manifest-action@master
with: with:
context: ./contrib/docker base-image: purplei2p/i2pd:latest-release
file: ./contrib/docker/Dockerfile extra-images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7 push: true
- name: Create and push release manifest image to GHCR
if: ${{ startsWith(github.ref, 'refs/tags/') }}
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/purplei2p/i2pd:latest-release
extra-images: 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: Create and push versioned manifest image to Docker Hub
if: ${{ startsWith(github.ref, 'refs/tags/') }}
uses: Noelware/docker-manifest-action@master
with:
base-image: purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
extra-images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
push: true
- name: Create and push versioned manifest image to GHCR
if: ${{ startsWith(github.ref, 'refs/tags/') }}
uses: Noelware/docker-manifest-action@master
with:
base-image: ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
extra-images: 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 push: true
tags: |
purplei2p/i2pd:latest
purplei2p/i2pd:latest-release
purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
ghcr.io/purplei2p/i2pd:latest
ghcr.io/purplei2p/i2pd:latest-release
ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}

View File

@ -1,6 +1,66 @@
# for this file format description, # for this file format description,
# see https://github.com/olivierlacan/keep-a-changelog # see https://github.com/olivierlacan/keep-a-changelog
## [2.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 enbaled 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 ## [2.42.1] - 2022-05-24
### Fixed ### Fixed
- Incorrect jump link in HTTP Proxy - Incorrect jump link in HTTP Proxy

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -154,9 +154,10 @@ namespace win32
case eRouterStatusUnknown: s << "Unk"; break; case eRouterStatusUnknown: s << "Unk"; break;
case eRouterStatusProxy: s << "Proxy"; break; case eRouterStatusProxy: s << "Proxy"; break;
case eRouterStatusMesh: s << "Mesh"; break; case eRouterStatusMesh: s << "Mesh"; break;
case eRouterStatusError: default: s << "Unk";
};
if (i2p::context.GetError () != eRouterErrorNone)
{ {
s << "Err";
switch (i2p::context.GetError ()) switch (i2p::context.GetError ())
{ {
case eRouterErrorClockSkew: case eRouterErrorClockSkew:
@ -170,9 +171,6 @@ namespace win32
break; break;
default: ; default: ;
} }
break;
}
default: s << "Unk";
} }
} }

View File

@ -85,6 +85,7 @@ set(DAEMON_SRC
"${DAEMON_SRC_DIR}/Daemon.cpp" "${DAEMON_SRC_DIR}/Daemon.cpp"
"${DAEMON_SRC_DIR}/HTTPServer.cpp" "${DAEMON_SRC_DIR}/HTTPServer.cpp"
"${DAEMON_SRC_DIR}/I2PControl.cpp" "${DAEMON_SRC_DIR}/I2PControl.cpp"
"${DAEMON_SRC_DIR}/I2PControlHandlers.cpp"
"${DAEMON_SRC_DIR}/i2pd.cpp" "${DAEMON_SRC_DIR}/i2pd.cpp"
"${DAEMON_SRC_DIR}/UPnP.cpp" "${DAEMON_SRC_DIR}/UPnP.cpp"
) )
@ -171,6 +172,13 @@ if(WITH_THREADSANITIZER)
endif() endif()
# Enable usage of STD's Atomic instead of Boost's on PowerPC
# For more information refer to https://github.com/PurpleI2P/i2pd/issues/1726#issuecomment-1306335111
if(ARCHITECTURE MATCHES "ppc")
add_definitions(-DBOOST_SP_USE_STD_ATOMIC)
endif()
# libraries # libraries
set(THREADS_PREFER_PTHREAD_FLAG ON) set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)

View File

@ -61,7 +61,7 @@ set(archdetect_c_code "
#else #else
#error cmake_ARCH mips #error cmake_ARCH mips
#endif #endif
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \\ #elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) || defined(__POWERPC__) \\
|| defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \\ || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \\
|| defined(_M_MPPC) || defined(_M_PPC) || defined(_M_MPPC) || defined(_M_PPC)
#if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__) #if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)

View File

@ -1,5 +1,18 @@
#
# 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 FROM alpine:latest
LABEL authors "Mikal Villa <mikal@sigterm.no>, Darknet Villain <supervillain@riseup.net>" LABEL authors="Mikal Villa <mikal@sigterm.no>, Darknet Villain <supervillain@riseup.net>"
LABEL maintainer="R4SAS <r4sas@i2pmail.org>"
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 # Expose git branch, tag and URL variables as arguments
ARG GIT_BRANCH="openssl" ARG GIT_BRANCH="openssl"
@ -11,27 +24,28 @@ ENV REPO_URL=${REPO_URL}
ENV I2PD_HOME="/home/i2pd" ENV I2PD_HOME="/home/i2pd"
ENV DATA_DIR="${I2PD_HOME}/data" ENV DATA_DIR="${I2PD_HOME}/data"
ENV DEFAULT_ARGS=" --datadir=$DATA_DIR --reseed.verify=true --upnp.enabled=false --http.enabled=true --http.address=0.0.0.0 --httpproxy.enabled=true --httpproxy.address=0.0.0.0 --socksproxy.enabled=true --socksproxy.address=0.0.0.0 --sam.enabled=true --sam.address=0.0.0.0" ENV DEFAULT_ARGS=" --datadir=$DATA_DIR"
RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \ RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \
&& adduser -S -h "$I2PD_HOME" i2pd \ && adduser -S -h "$I2PD_HOME" i2pd \
&& chown -R i2pd:nobody "$I2PD_HOME" && 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 # 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. # image under 20mb we need to remove all the build dependencies in the same "RUN" / layer.
# #
# 1. install deps, clone and build. # 1. install deps, clone and build.
# 2. strip binaries. # 2. strip binaries.
# 3. Purge all dependencies and other unrelated packages, including build directory. # 3. Purge all dependencies and other unrelated packages, including build directory.
RUN apk update \ 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 \ && 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 \ && mkdir -p /tmp/build \
&& cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \ && cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \
&& cd i2pd \ && cd i2pd \
&& if [ -n "${GIT_TAG}" ]; then git checkout tags/${GIT_TAG}; fi \ && if [ -n "${GIT_TAG}" ]; then git checkout tags/${GIT_TAG}; fi \
&& make USE_UPNP=yes \ && make -j$(nproc) USE_UPNP=yes \
&& cp -R contrib/certificates /i2pd_certificates \ && cp -R contrib/certificates /i2pd_certificates \
&& mkdir -p /usr/local/bin \ && mkdir -p /usr/local/bin \
&& mv i2pd /usr/local/bin \ && mv i2pd /usr/local/bin \
@ -45,6 +59,9 @@ RUN apk update \
# 2. Adding required libraries to run i2pd to ensure it will run. # 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++ 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 "$I2PD_HOME/i2pd.conf"
RUN chown i2pd:nobody "$I2PD_HOME/i2pd.conf"
COPY entrypoint.sh /entrypoint.sh COPY entrypoint.sh /entrypoint.sh
RUN chmod a+x /entrypoint.sh RUN chmod a+x /entrypoint.sh

View File

@ -0,0 +1,52 @@
## 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

View File

@ -1,13 +1,13 @@
# i2pd # i2pd
# Copyright (C) 2021 PurpleI2P team # Copyright (C) 2021-2022 PurpleI2P team
# This file is distributed under the same license as the i2pd package. # This file is distributed under the same license as the i2pd package.
# R4SAS <r4sas@i2pmail.org>, 2021. # R4SAS <r4sas@i2pmail.org>, 2021-2022.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: i2pd\n" "Project-Id-Version: i2pd\n"
"Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n" "Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n"
"POT-Creation-Date: 2021-08-06 17:12\n" "POT-Creation-Date: 2022-07-26 21:22\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@ -18,706 +18,712 @@ msgstr ""
"X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n" "X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n"
"X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n" "X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n"
#: daemon/HTTPServer.cpp:177 #: daemon/HTTPServer.cpp:108
msgid "day" msgid "day"
msgid_plural "days" msgid_plural "days"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: daemon/HTTPServer.cpp:181 #: daemon/HTTPServer.cpp:112
msgid "hour" msgid "hour"
msgid_plural "hours" msgid_plural "hours"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: daemon/HTTPServer.cpp:185 #: daemon/HTTPServer.cpp:116
msgid "minute" msgid "minute"
msgid_plural "minutes" msgid_plural "minutes"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: daemon/HTTPServer.cpp:188 #: daemon/HTTPServer.cpp:119
msgid "second" msgid "second"
msgid_plural "seconds" msgid_plural "seconds"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#. tr: Kibibit #. tr: Kibibit
#: daemon/HTTPServer.cpp:196 daemon/HTTPServer.cpp:224 #: daemon/HTTPServer.cpp:127 daemon/HTTPServer.cpp:155
msgid "KiB" msgid "KiB"
msgstr "" msgstr ""
#. tr: Mebibit #. tr: Mebibit
#: daemon/HTTPServer.cpp:198 #: daemon/HTTPServer.cpp:129
msgid "MiB" msgid "MiB"
msgstr "" msgstr ""
#. tr: Gibibit #. tr: Gibibit
#: daemon/HTTPServer.cpp:200 #: daemon/HTTPServer.cpp:131
msgid "GiB" msgid "GiB"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:217 #: daemon/HTTPServer.cpp:148
msgid "building" msgid "building"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:218 #: daemon/HTTPServer.cpp:149
msgid "failed" msgid "failed"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:219 #: daemon/HTTPServer.cpp:150
msgid "expiring" msgid "expiring"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:220 #: daemon/HTTPServer.cpp:151
msgid "established" msgid "established"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:221 #: daemon/HTTPServer.cpp:152
msgid "unknown" msgid "unknown"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:223 #: daemon/HTTPServer.cpp:154
msgid "exploratory" msgid "exploratory"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:259 #. tr: Webconsole page title
#: daemon/HTTPServer.cpp:185
msgid "Purple I2P Webconsole"
msgstr ""
#: daemon/HTTPServer.cpp:190
msgid "<b>i2pd</b> webconsole" msgid "<b>i2pd</b> webconsole"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:262 #: daemon/HTTPServer.cpp:193
msgid "Main page" msgid "Main page"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:725 #: daemon/HTTPServer.cpp:194 daemon/HTTPServer.cpp:700
msgid "Router commands" msgid "Router commands"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:448 #: daemon/HTTPServer.cpp:195 daemon/HTTPServer.cpp:382
#: daemon/HTTPServer.cpp:460 #: daemon/HTTPServer.cpp:394
msgid "Local Destinations" msgid "Local Destinations"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:266 daemon/HTTPServer.cpp:418 #: daemon/HTTPServer.cpp:197 daemon/HTTPServer.cpp:352
#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:510 #: daemon/HTTPServer.cpp:438 daemon/HTTPServer.cpp:444
#: daemon/HTTPServer.cpp:641 daemon/HTTPServer.cpp:684 #: daemon/HTTPServer.cpp:597 daemon/HTTPServer.cpp:640
#: daemon/HTTPServer.cpp:688 #: daemon/HTTPServer.cpp:644
msgid "LeaseSets" msgid "LeaseSets"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:268 daemon/HTTPServer.cpp:694 #: daemon/HTTPServer.cpp:199 daemon/HTTPServer.cpp:650
msgid "Tunnels" msgid "Tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:269 daemon/HTTPServer.cpp:425 #: daemon/HTTPServer.cpp:201 daemon/HTTPServer.cpp:359
#: daemon/HTTPServer.cpp:787 daemon/HTTPServer.cpp:803 #: daemon/HTTPServer.cpp:770 daemon/HTTPServer.cpp:786
msgid "Transit Tunnels" msgid "Transit Tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:270 daemon/HTTPServer.cpp:852 #: daemon/HTTPServer.cpp:203 daemon/HTTPServer.cpp:839
msgid "Transports" msgid "Transports"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:271 #: daemon/HTTPServer.cpp:204
msgid "I2P tunnels" msgid "I2P tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:914 #: daemon/HTTPServer.cpp:206 daemon/HTTPServer.cpp:908
#: daemon/HTTPServer.cpp:924 #: daemon/HTTPServer.cpp:918
msgid "SAM sessions" msgid "SAM sessions"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:289 daemon/HTTPServer.cpp:1306 #: daemon/HTTPServer.cpp:222 daemon/HTTPServer.cpp:1302
#: daemon/HTTPServer.cpp:1309 daemon/HTTPServer.cpp:1312 #: daemon/HTTPServer.cpp:1305 daemon/HTTPServer.cpp:1308
#: daemon/HTTPServer.cpp:1326 daemon/HTTPServer.cpp:1371 #: daemon/HTTPServer.cpp:1322 daemon/HTTPServer.cpp:1367
#: daemon/HTTPServer.cpp:1374 daemon/HTTPServer.cpp:1377 #: daemon/HTTPServer.cpp:1370 daemon/HTTPServer.cpp:1373
msgid "ERROR" msgid "ERROR"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:296 #: daemon/HTTPServer.cpp:229
msgid "OK" msgid "OK"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:297 #: daemon/HTTPServer.cpp:230
msgid "Testing" msgid "Testing"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:298 #: daemon/HTTPServer.cpp:231
msgid "Firewalled" msgid "Firewalled"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:299 daemon/HTTPServer.cpp:320 #: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:253
#: daemon/HTTPServer.cpp:406 #: daemon/HTTPServer.cpp:325
msgid "Unknown" msgid "Unknown"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:300 daemon/HTTPServer.cpp:435 #: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:369
#: daemon/HTTPServer.cpp:436 daemon/HTTPServer.cpp:982 #: daemon/HTTPServer.cpp:370 daemon/HTTPServer.cpp:976
#: daemon/HTTPServer.cpp:991 #: daemon/HTTPServer.cpp:985
msgid "Proxy" msgid "Proxy"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:301 #: daemon/HTTPServer.cpp:234
msgid "Mesh" msgid "Mesh"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:304 #: daemon/HTTPServer.cpp:237
msgid "Error" msgid "Error"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:308 #: daemon/HTTPServer.cpp:241
msgid "Clock skew" msgid "Clock skew"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:311 #: daemon/HTTPServer.cpp:244
msgid "Offline" msgid "Offline"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:314 #: daemon/HTTPServer.cpp:247
msgid "Symmetric NAT" msgid "Symmetric NAT"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:326 #: daemon/HTTPServer.cpp:259
msgid "Uptime" msgid "Uptime"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:329 #: daemon/HTTPServer.cpp:262
msgid "Network status" msgid "Network status"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:334 #: daemon/HTTPServer.cpp:267
msgid "Network status v6" msgid "Network status v6"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:340 daemon/HTTPServer.cpp:347 #: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:280
msgid "Stopping in" msgid "Stopping in"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:354 #: daemon/HTTPServer.cpp:287
msgid "Family" msgid "Family"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:355 #: daemon/HTTPServer.cpp:288
msgid "Tunnel creation success rate" msgid "Tunnel creation success rate"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:356 #: daemon/HTTPServer.cpp:289
msgid "Received" msgid "Received"
msgstr "" msgstr ""
#. tr: Kibibit/s #. tr: Kibibit/s
#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:361 #: daemon/HTTPServer.cpp:291 daemon/HTTPServer.cpp:294
#: daemon/HTTPServer.cpp:364 #: daemon/HTTPServer.cpp:297
msgid "KiB/s" msgid "KiB/s"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:359 #: daemon/HTTPServer.cpp:292
msgid "Sent" msgid "Sent"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:362 #: daemon/HTTPServer.cpp:295
msgid "Transit" msgid "Transit"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:365 #: daemon/HTTPServer.cpp:298
msgid "Data path" msgid "Data path"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:368 #: daemon/HTTPServer.cpp:301
msgid "Hidden content. Press on text to see." msgid "Hidden content. Press on text to see."
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:371 #: daemon/HTTPServer.cpp:304
msgid "Router Ident" msgid "Router Ident"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:373 #: daemon/HTTPServer.cpp:306
msgid "Router Family" msgid "Router Family"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:374 #: daemon/HTTPServer.cpp:307
msgid "Router Caps" msgid "Router Caps"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:375 #: daemon/HTTPServer.cpp:308
msgid "Version" msgid "Version"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:376 #: daemon/HTTPServer.cpp:309
msgid "Our external address" msgid "Our external address"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:384 #: daemon/HTTPServer.cpp:337
msgid "supported" msgid "supported"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:416 #: daemon/HTTPServer.cpp:350
msgid "Routers" msgid "Routers"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:417 #: daemon/HTTPServer.cpp:351
msgid "Floodfills" msgid "Floodfills"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:424 daemon/HTTPServer.cpp:968 #: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:962
msgid "Client Tunnels" msgid "Client Tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:434 #: daemon/HTTPServer.cpp:368
msgid "Services" msgid "Services"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436 #: daemon/HTTPServer.cpp:369 daemon/HTTPServer.cpp:370
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438 #: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:372
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440 #: daemon/HTTPServer.cpp:373 daemon/HTTPServer.cpp:374
msgid "Enabled" msgid "Enabled"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436 #: daemon/HTTPServer.cpp:369 daemon/HTTPServer.cpp:370
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438 #: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:372
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440 #: daemon/HTTPServer.cpp:373 daemon/HTTPServer.cpp:374
msgid "Disabled" msgid "Disabled"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:483 #: daemon/HTTPServer.cpp:417
msgid "Encrypted B33 address" msgid "Encrypted B33 address"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:492 #: daemon/HTTPServer.cpp:426
msgid "Address registration line" msgid "Address registration line"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:497 #: daemon/HTTPServer.cpp:431
msgid "Domain" msgid "Domain"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:498 #: daemon/HTTPServer.cpp:432
msgid "Generate" msgid "Generate"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:499 #: daemon/HTTPServer.cpp:433
msgid "" msgid ""
"<b>Note:</b> result string can be used only for registering 2LD domains " "<b>Note:</b> result string can be used only for registering 2LD domains "
"(example.i2p). For registering subdomains please use i2pd-tools." "(example.i2p). For registering subdomains please use i2pd-tools."
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:505 #: daemon/HTTPServer.cpp:439
msgid "Address" msgid "Address"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:505 #: daemon/HTTPServer.cpp:439
msgid "Type" msgid "Type"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:505 #: daemon/HTTPServer.cpp:439
msgid "EncType" msgid "EncType"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:515 daemon/HTTPServer.cpp:699 #: daemon/HTTPServer.cpp:449 daemon/HTTPServer.cpp:655
msgid "Inbound tunnels" msgid "Inbound tunnels"
msgstr "" msgstr ""
#. tr: Milliseconds #. tr: Milliseconds
#: daemon/HTTPServer.cpp:520 daemon/HTTPServer.cpp:530 #: daemon/HTTPServer.cpp:464 daemon/HTTPServer.cpp:484
#: daemon/HTTPServer.cpp:704 daemon/HTTPServer.cpp:714 #: daemon/HTTPServer.cpp:669 daemon/HTTPServer.cpp:689
msgid "ms" msgid "ms"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:525 daemon/HTTPServer.cpp:709 #: daemon/HTTPServer.cpp:469 daemon/HTTPServer.cpp:674
msgid "Outbound tunnels" msgid "Outbound tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:537 #: daemon/HTTPServer.cpp:491
msgid "Tags" msgid "Tags"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:537 #: daemon/HTTPServer.cpp:491
msgid "Incoming" msgid "Incoming"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:544 daemon/HTTPServer.cpp:547 #: daemon/HTTPServer.cpp:498 daemon/HTTPServer.cpp:501
msgid "Outgoing" msgid "Outgoing"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:545 daemon/HTTPServer.cpp:561 #: daemon/HTTPServer.cpp:499 daemon/HTTPServer.cpp:515
msgid "Destination" msgid "Destination"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:545 #: daemon/HTTPServer.cpp:499
msgid "Amount" msgid "Amount"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:552 #: daemon/HTTPServer.cpp:506
msgid "Incoming Tags" msgid "Incoming Tags"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:560 daemon/HTTPServer.cpp:563 #: daemon/HTTPServer.cpp:514 daemon/HTTPServer.cpp:517
msgid "Tags sessions" msgid "Tags sessions"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:561 #: daemon/HTTPServer.cpp:515
msgid "Status" msgid "Status"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:570 daemon/HTTPServer.cpp:626 #: daemon/HTTPServer.cpp:524 daemon/HTTPServer.cpp:582
msgid "Local Destination" msgid "Local Destination"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:580 daemon/HTTPServer.cpp:947 #: daemon/HTTPServer.cpp:535 daemon/HTTPServer.cpp:941
msgid "Streams" msgid "Streams"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:602 #: daemon/HTTPServer.cpp:558
msgid "Close stream" msgid "Close stream"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:631 #: daemon/HTTPServer.cpp:587
msgid "I2CP session not found" msgid "I2CP session not found"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:634 #: daemon/HTTPServer.cpp:590
msgid "I2CP is not enabled" msgid "I2CP is not enabled"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:660 #: daemon/HTTPServer.cpp:616
msgid "Invalid" msgid "Invalid"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:663 #: daemon/HTTPServer.cpp:619
msgid "Store type" msgid "Store type"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:664 #: daemon/HTTPServer.cpp:620
msgid "Expires" msgid "Expires"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:669 #: daemon/HTTPServer.cpp:625
msgid "Non Expired Leases" msgid "Non Expired Leases"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:672 #: daemon/HTTPServer.cpp:628
msgid "Gateway" msgid "Gateway"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:673 #: daemon/HTTPServer.cpp:629
msgid "TunnelID" msgid "TunnelID"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:674 #: daemon/HTTPServer.cpp:630
msgid "EndDate" msgid "EndDate"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:684 #: daemon/HTTPServer.cpp:640
msgid "not floodfill" msgid "not floodfill"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:695 #: daemon/HTTPServer.cpp:651
msgid "Queue size" msgid "Queue size"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:726 #: daemon/HTTPServer.cpp:701
msgid "Run peer test" msgid "Run peer test"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:731 #: daemon/HTTPServer.cpp:706
msgid "Decline transit tunnels" msgid "Decline transit tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:733 #: daemon/HTTPServer.cpp:708
msgid "Accept transit tunnels" msgid "Accept transit tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:737 daemon/HTTPServer.cpp:742 #: daemon/HTTPServer.cpp:712 daemon/HTTPServer.cpp:717
msgid "Cancel graceful shutdown" msgid "Cancel graceful shutdown"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:739 daemon/HTTPServer.cpp:744 #: daemon/HTTPServer.cpp:714 daemon/HTTPServer.cpp:719
msgid "Start graceful shutdown" msgid "Start graceful shutdown"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:747 #: daemon/HTTPServer.cpp:722
msgid "Force shutdown" msgid "Force shutdown"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:748 #: daemon/HTTPServer.cpp:723
msgid "Reload external CSS styles" msgid "Reload external CSS styles"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:751 #: daemon/HTTPServer.cpp:726
msgid "" msgid ""
"<b>Note:</b> any action done here are not persistent and not changes your " "<b>Note:</b> any action done here are not persistent and not changes your "
"config files." "config files."
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:753 #: daemon/HTTPServer.cpp:728
msgid "Logging level" msgid "Logging level"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:761 #: daemon/HTTPServer.cpp:736
msgid "Transit tunnels limit" msgid "Transit tunnels limit"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:766 daemon/HTTPServer.cpp:778 #: daemon/HTTPServer.cpp:741 daemon/HTTPServer.cpp:760
msgid "Change" msgid "Change"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:770 #: daemon/HTTPServer.cpp:748
msgid "Change language" msgid "Change language"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:803 #: daemon/HTTPServer.cpp:786
msgid "no transit tunnels currently built" msgid "no transit tunnels currently built"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:908 daemon/HTTPServer.cpp:931 #: daemon/HTTPServer.cpp:902 daemon/HTTPServer.cpp:925
msgid "SAM disabled" msgid "SAM disabled"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:924 #: daemon/HTTPServer.cpp:918
msgid "no sessions currently running" msgid "no sessions currently running"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:937 #: daemon/HTTPServer.cpp:931
msgid "SAM session not found" msgid "SAM session not found"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:942 #: daemon/HTTPServer.cpp:936
msgid "SAM Session" msgid "SAM Session"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:999 #: daemon/HTTPServer.cpp:993
msgid "Server Tunnels" msgid "Server Tunnels"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1015 #: daemon/HTTPServer.cpp:1009
msgid "Client Forwards" msgid "Client Forwards"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1029 #: daemon/HTTPServer.cpp:1023
msgid "Server Forwards" msgid "Server Forwards"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1227 #: daemon/HTTPServer.cpp:1223
msgid "Unknown page" msgid "Unknown page"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1246 #: daemon/HTTPServer.cpp:1242
msgid "Invalid token" msgid "Invalid token"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1304 daemon/HTTPServer.cpp:1361 #: daemon/HTTPServer.cpp:1300 daemon/HTTPServer.cpp:1357
#: daemon/HTTPServer.cpp:1401 #: daemon/HTTPServer.cpp:1397
msgid "SUCCESS" msgid "SUCCESS"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1304 #: daemon/HTTPServer.cpp:1300
msgid "Stream closed" msgid "Stream closed"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1306 #: daemon/HTTPServer.cpp:1302
msgid "Stream not found or already was closed" msgid "Stream not found or already was closed"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1309 #: daemon/HTTPServer.cpp:1305
msgid "Destination not found" msgid "Destination not found"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1312 #: daemon/HTTPServer.cpp:1308
msgid "StreamID can't be null" msgid "StreamID can't be null"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1314 daemon/HTTPServer.cpp:1379 #: daemon/HTTPServer.cpp:1310 daemon/HTTPServer.cpp:1375
msgid "Return to destination page" msgid "Return to destination page"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1315 daemon/HTTPServer.cpp:1328 #: daemon/HTTPServer.cpp:1311 daemon/HTTPServer.cpp:1324
#: daemon/HTTPServer.cpp:1403 #: daemon/HTTPServer.cpp:1399
msgid "You will be redirected in 5 seconds" msgid "You will be redirected in 5 seconds"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1326 #: daemon/HTTPServer.cpp:1322
msgid "Transit tunnels count must not exceed 65535" msgid "Transit tunnels count must not exceed 65535"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1402 #: daemon/HTTPServer.cpp:1323 daemon/HTTPServer.cpp:1398
msgid "Back to commands list" msgid "Back to commands list"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1363 #: daemon/HTTPServer.cpp:1359
msgid "Register at reg.i2p" msgid "Register at reg.i2p"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1364 #: daemon/HTTPServer.cpp:1360
msgid "Description" msgid "Description"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1364 #: daemon/HTTPServer.cpp:1360
msgid "A bit information about service on domain" msgid "A bit information about service on domain"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1365 #: daemon/HTTPServer.cpp:1361
msgid "Submit" msgid "Submit"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1371 #: daemon/HTTPServer.cpp:1367
msgid "Domain can't end with .b32.i2p" msgid "Domain can't end with .b32.i2p"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1374 #: daemon/HTTPServer.cpp:1370
msgid "Domain must end with .i2p" msgid "Domain must end with .i2p"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1377 #: daemon/HTTPServer.cpp:1373
msgid "Such destination is not found" msgid "Such destination is not found"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1397 #: daemon/HTTPServer.cpp:1393
msgid "Unknown command" msgid "Unknown command"
msgstr "" msgstr ""
#: daemon/HTTPServer.cpp:1401 #: daemon/HTTPServer.cpp:1397
msgid "Command accepted" msgid "Command accepted"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:157 #: libi2pd_client/HTTPProxy.cpp:163
msgid "Proxy error" msgid "Proxy error"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:165 #: libi2pd_client/HTTPProxy.cpp:171
msgid "Proxy info" msgid "Proxy info"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:173 #: libi2pd_client/HTTPProxy.cpp:179
msgid "Proxy error: Host not found" msgid "Proxy error: Host not found"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:174 #: libi2pd_client/HTTPProxy.cpp:180
msgid "Remote host not found in router's addressbook" msgid "Remote host not found in router's addressbook"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:175 #: libi2pd_client/HTTPProxy.cpp:181
msgid "You may try to find this host on jump services below" msgid "You may try to find this host on jump services below"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:273 libi2pd_client/HTTPProxy.cpp:288 #: libi2pd_client/HTTPProxy.cpp:282 libi2pd_client/HTTPProxy.cpp:297
#: libi2pd_client/HTTPProxy.cpp:322 libi2pd_client/HTTPProxy.cpp:365 #: libi2pd_client/HTTPProxy.cpp:331 libi2pd_client/HTTPProxy.cpp:372
msgid "Invalid request" msgid "Invalid request"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:273 #: libi2pd_client/HTTPProxy.cpp:282
msgid "Proxy unable to parse your request" msgid "Proxy unable to parse your request"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:288 #: libi2pd_client/HTTPProxy.cpp:297
msgid "addresshelper is not supported" msgid "addresshelper is not supported"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:297 libi2pd_client/HTTPProxy.cpp:306 #: libi2pd_client/HTTPProxy.cpp:306 libi2pd_client/HTTPProxy.cpp:315
#: libi2pd_client/HTTPProxy.cpp:385 #: libi2pd_client/HTTPProxy.cpp:392
msgid "Host" msgid "Host"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:297 #: libi2pd_client/HTTPProxy.cpp:306
msgid "added to router's addressbook from helper" msgid "added to router's addressbook from helper"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:298 #: libi2pd_client/HTTPProxy.cpp:307
msgid "Click here to proceed:" msgid "Click here to proceed:"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308 #: libi2pd_client/HTTPProxy.cpp:307 libi2pd_client/HTTPProxy.cpp:317
msgid "Continue" msgid "Continue"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309 #: libi2pd_client/HTTPProxy.cpp:308 libi2pd_client/HTTPProxy.cpp:318
msgid "Addresshelper found" msgid "Addresshelper found"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:306 #: libi2pd_client/HTTPProxy.cpp:315
msgid "already in router's addressbook" msgid "already in router's addressbook"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:307 #. tr: The "record" means addressbook's record. That message appears when domain was already added to addressbook, but helper link is opened for it.
#: libi2pd_client/HTTPProxy.cpp:316
msgid "Click here to update record:" msgid "Click here to update record:"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:322 #: libi2pd_client/HTTPProxy.cpp:331
msgid "invalid request uri" msgid "invalid request uri"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:365 #: libi2pd_client/HTTPProxy.cpp:372
msgid "Can't detect destination host from request" msgid "Can't detect destination host from request"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:382 libi2pd_client/HTTPProxy.cpp:386 #: libi2pd_client/HTTPProxy.cpp:389 libi2pd_client/HTTPProxy.cpp:393
msgid "Outproxy failure" msgid "Outproxy failure"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:382 #: libi2pd_client/HTTPProxy.cpp:389
msgid "bad outproxy settings" msgid "bad outproxy settings"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:385 #: libi2pd_client/HTTPProxy.cpp:392
msgid "not inside I2P network, but outproxy is not enabled" msgid "not inside I2P network, but outproxy is not enabled"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:474 #: libi2pd_client/HTTPProxy.cpp:482
msgid "unknown outproxy url" msgid "unknown outproxy url"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:480 #: libi2pd_client/HTTPProxy.cpp:490
msgid "cannot resolve upstream proxy" msgid "cannot resolve upstream proxy"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:488 #: libi2pd_client/HTTPProxy.cpp:498
msgid "hostname too long" msgid "hostname too long"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:515 #: libi2pd_client/HTTPProxy.cpp:525
msgid "cannot connect to upstream socks proxy" msgid "cannot connect to upstream socks proxy"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:521 #: libi2pd_client/HTTPProxy.cpp:531
msgid "Cannot negotiate with socks proxy" msgid "Cannot negotiate with socks proxy"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:563 #: libi2pd_client/HTTPProxy.cpp:573
msgid "CONNECT error" msgid "CONNECT error"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:563 #: libi2pd_client/HTTPProxy.cpp:573
msgid "Failed to Connect" msgid "Failed to Connect"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:574 libi2pd_client/HTTPProxy.cpp:600 #: libi2pd_client/HTTPProxy.cpp:584 libi2pd_client/HTTPProxy.cpp:610
msgid "socks proxy error" msgid "socks proxy error"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:582 #: libi2pd_client/HTTPProxy.cpp:592
msgid "failed to send request to upstream" msgid "failed to send request to upstream"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:603 #: libi2pd_client/HTTPProxy.cpp:613
msgid "No Reply From socks proxy" msgid "No Reply From socks proxy"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:610 #: libi2pd_client/HTTPProxy.cpp:620
msgid "cannot connect" msgid "cannot connect"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:610 #: libi2pd_client/HTTPProxy.cpp:620
msgid "http out proxy not implemented" msgid "http out proxy not implemented"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:611 #: libi2pd_client/HTTPProxy.cpp:621
msgid "cannot connect to upstream http proxy" msgid "cannot connect to upstream http proxy"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:644 #: libi2pd_client/HTTPProxy.cpp:654
msgid "Host is down" msgid "Host is down"
msgstr "" msgstr ""
#: libi2pd_client/HTTPProxy.cpp:644 #: libi2pd_client/HTTPProxy.cpp:654
msgid "" msgid ""
"Can't create connection to requested host, it may be down. Please try again " "Can't create connection to requested host, it may be down. Please try again "
"later." "later."

View File

@ -9,8 +9,8 @@ Regex for transforming gettext translations to our format:
--- ---
``` ```
in: msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\nmsgstr\[1\]\ \"(.*)\"\n(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)? in: msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\n(msgstr\[1\]\ \"(.*)\"\n)?(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)?
out: #{"$2", {"$3", "$4", "$6", "$8", "$10"}},\n out: #{"$2", {"$3", "$5", "$7", "$9", "$11"}},\n
``` ```
``` ```

View File

@ -75,8 +75,8 @@ ipv4 = true
## Enable communication through ipv6 ## Enable communication through ipv6
ipv6 = false ipv6 = false
## Enable SSU transport (default = true) ## Enable SSU transport
# ssu = true ssu = false
## Bandwidth configuration ## Bandwidth configuration
## L limit bandwidth to 32KBs/sec, O - to 256KBs/sec, P - to 2048KBs/sec, ## L limit bandwidth to 32KBs/sec, O - to 256KBs/sec, P - to 2048KBs/sec,
@ -96,6 +96,22 @@ ipv6 = false
## Note: that mode uses much more network connections and CPU! ## Note: that mode uses much more network connections and CPU!
# floodfill = true # 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
# enabled = true
## Publish address in RouterInfo
# published = true
## Port for incoming connections (default is global port option value or port + 1 if SSU is enabled)
# port = 4567
[http] [http]
## Web Console settings ## Web Console settings
## Uncomment and set to 'false' to disable Web Console ## Uncomment and set to 'false' to disable Web Console
@ -110,8 +126,8 @@ port = 7070
# user = i2pd # user = i2pd
# pass = changeme # pass = changeme
## Select webconsole language ## Select webconsole language
## Currently supported english (default), afrikaans, armenian, french, german, ## Currently supported english (default), afrikaans, armenian, chinese, french,
## russian, turkmen, ukrainian and uzbek languages ## german, italian, russian, spanish, turkmen, ukrainian and uzbek languages
# lang = english # lang = english
[httpproxy] [httpproxy]

View File

@ -1,7 +1,7 @@
%define git_hash %(git rev-parse HEAD | cut -c -7) %define git_hash %(git rev-parse HEAD | cut -c -7)
Name: i2pd-git Name: i2pd-git
Version: 2.42.1 Version: 2.44.0
Release: git%{git_hash}%{?dist} Release: git%{git_hash}%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd Conflicts: i2pd
@ -62,9 +62,7 @@ pushd redhat-linux-build
%endif %endif
%if 0%{?fedora} >= 35 %if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build pushd redhat-linux-build
%endif
%else %else
%if 0%{?fedora} >= 33 %if 0%{?fedora} >= 33
pushd %{_target_platform} pushd %{_target_platform}
@ -82,10 +80,8 @@ popd
%endif %endif
%if 0%{?fedora} >= 33 %if 0%{?fedora} >= 33
%if 0%{?fedora} < 37
popd popd
%endif %endif
%endif
%if 0%{?mageia} > 7 %if 0%{?mageia} > 7
popd popd
@ -99,9 +95,7 @@ pushd redhat-linux-build
%endif %endif
%if 0%{?fedora} >= 35 %if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build pushd redhat-linux-build
%endif
%else %else
%if 0%{?fedora} >= 33 %if 0%{?fedora} >= 33
pushd %{_target_platform} pushd %{_target_platform}
@ -164,6 +158,12 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Sun Nov 20 2022 orignal <orignal@i2pmail.org> - 2.44.0
- update to 2.44.0
* Mon Aug 22 2022 orignal <orignal@i2pmail.org> - 2.43.0
- update to 2.43.0
* Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1 * Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1
- update to 2.42.1 - update to 2.42.1

View File

@ -1,5 +1,5 @@
Name: i2pd Name: i2pd
Version: 2.42.1 Version: 2.44.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: I2P router written in C++ Summary: I2P router written in C++
Conflicts: i2pd-git Conflicts: i2pd-git
@ -59,9 +59,7 @@ pushd redhat-linux-build
%endif %endif
%if 0%{?fedora} >= 35 %if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build pushd redhat-linux-build
%endif
%else %else
%if 0%{?fedora} >= 33 %if 0%{?fedora} >= 33
pushd %{_target_platform} pushd %{_target_platform}
@ -79,10 +77,8 @@ popd
%endif %endif
%if 0%{?fedora} >= 33 %if 0%{?fedora} >= 33
%if 0%{?fedora} < 37
popd popd
%endif %endif
%endif
%if 0%{?mageia} > 7 %if 0%{?mageia} > 7
popd popd
@ -96,9 +92,7 @@ pushd redhat-linux-build
%endif %endif
%if 0%{?fedora} >= 35 %if 0%{?fedora} >= 35
%if 0%{?fedora} < 37
pushd redhat-linux-build pushd redhat-linux-build
%endif
%else %else
%if 0%{?fedora} >= 33 %if 0%{?fedora} >= 33
pushd %{_target_platform} pushd %{_target_platform}
@ -161,6 +155,12 @@ getent passwd i2pd >/dev/null || \
%changelog %changelog
* Sun Nov 20 2022 orignal <orignal@i2pmail.org> - 2.44.0
- update to 2.44.0
* Mon Aug 22 2022 orignal <orignal@i2pmail.org> - 2.43.0
- update to 2.43.0
* Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1 * Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1
- update to 2.42.1 - update to 2.42.1

View File

@ -31,7 +31,6 @@
#include "Crypto.h" #include "Crypto.h"
#include "UPnP.h" #include "UPnP.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "util.h"
#include "I18N.h" #include "I18N.h"
namespace i2p namespace i2p
@ -153,115 +152,18 @@ namespace util
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni); bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
bool avx; i2p::config::GetOption("cpuext.avx", avx); bool avx; i2p::config::GetOption("cpuext.avx", avx);
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt); bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
bool ssu; i2p::config::GetOption("ssu", ssu);
if (!ssu && i2p::config::IsDefault ("precomputation.elgamal"))
precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly
i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt); i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt);
i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces
int netID; i2p::config::GetOption("netid", netID); int netID; i2p::config::GetOption("netid", netID);
i2p::context.SetNetID (netID); i2p::context.SetNetID (netID);
i2p::context.Init (); i2p::context.Init ();
bool ipv6; i2p::config::GetOption("ipv6", ipv6); i2p::transport::InitTransports ();
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
}
bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
boost::asio::ip::address_v6 yggaddr;
if (ygg)
{
std::string yggaddress; i2p::config::GetOption ("meshnets.yggaddress", yggaddress);
if (!yggaddress.empty ())
{
yggaddr = boost::asio::ip::address_v6::from_string (yggaddress);
if (yggaddr.is_unspecified () || !i2p::util::net::IsYggdrasilAddress (yggaddr) ||
!i2p::util::net::IsLocalAddress (yggaddr))
{
LogPrint(eLogWarning, "Daemon: Can't find Yggdrasil address ", yggaddress);
ygg = false;
}
}
else
{
yggaddr = i2p::util::net::GetYggdrasilAddress ();
if (yggaddr.is_unspecified ())
{
LogPrint(eLogWarning, "Daemon: Yggdrasil is not running. Disabled");
ygg = false;
}
}
}
uint16_t port; i2p::config::GetOption("port", port);
if (!i2p::config::IsDefault("port"))
{
LogPrint(eLogInfo, "Daemon: Accepting incoming connections at port ", port);
i2p::context.UpdatePort (port);
}
i2p::context.SetSupportsV6 (ipv6);
i2p::context.SetSupportsV4 (ipv4);
i2p::context.SetSupportsMesh (ygg, yggaddr);
i2p::context.RemoveNTCPAddress (!ipv6); // TODO: remove later
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::address_v6::from_string (ipv6Addr);
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)
{
bool published; i2p::config::GetOption("ssu2.published", published);
if (published)
{
uint16_t ssu2port; i2p::config::GetOption("ssu2.port", ssu2port);
i2p::context.PublishSSU2Address (ssu2port, true, ipv4, ipv6); // publish
}
else
i2p::context.PublishSSU2Address (0, false, ipv4, ipv6); // unpublish
}
bool transit; i2p::config::GetOption("notransit", transit); bool transit; i2p::config::GetOption("notransit", transit);
i2p::context.SetAcceptsTunnels (!transit); i2p::context.SetAcceptsTunnels (!transit);
@ -404,7 +306,7 @@ namespace util
i2p::transport::transports.SetCheckReserved(checkInReserved); i2p::transport::transports.SetCheckReserved(checkInReserved);
i2p::transport::transports.Start(ntcp2, ssu, ssu2); i2p::transport::transports.Start(ntcp2, ssu, ssu2);
if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2()) if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundSSU2() || i2p::transport::transports.IsBoundNTCP2())
LogPrint(eLogInfo, "Daemon: Transports started"); LogPrint(eLogInfo, "Daemon: Transports started");
else else
{ {

View File

@ -80,7 +80,7 @@ namespace http {
const char HTTP_COMMAND_SHUTDOWN_CANCEL[] = "shutdown_cancel"; const char HTTP_COMMAND_SHUTDOWN_CANCEL[] = "shutdown_cancel";
const char HTTP_COMMAND_SHUTDOWN_NOW[] = "terminate"; const char HTTP_COMMAND_SHUTDOWN_NOW[] = "terminate";
const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test"; const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config"; const char HTTP_COMMAND_RELOAD_TUNNELS_CONFIG[] = "reload_tunnels_config";
const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel"; const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel";
const char HTTP_COMMAND_KILLSTREAM[] = "closestream"; const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit"; const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit";
@ -182,7 +182,7 @@ namespace http {
" <meta charset=\"UTF-8\">\r\n" " <meta charset=\"UTF-8\">\r\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n" " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n"
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n" " <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
" <title>Purple I2P Webconsole</title>\r\n"; " <title>" << tr(/* tr: Webconsole page title */ "Purple I2P Webconsole") << "</title>\r\n";
GetStyles(s); GetStyles(s);
s << s <<
"</head>\r\n" "</head>\r\n"
@ -222,7 +222,7 @@ namespace http {
s << "<b>" << tr("ERROR") << ":</b>&nbsp;" << string << "<br>\r\n"; s << "<b>" << tr("ERROR") << ":</b>&nbsp;" << string << "<br>\r\n";
} }
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status) static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, RouterError error)
{ {
switch (status) switch (status)
{ {
@ -232,10 +232,12 @@ namespace http {
case eRouterStatusUnknown: s << tr("Unknown"); break; case eRouterStatusUnknown: s << tr("Unknown"); break;
case eRouterStatusProxy: s << tr("Proxy"); break; case eRouterStatusProxy: s << tr("Proxy"); break;
case eRouterStatusMesh: s << tr("Mesh"); break; case eRouterStatusMesh: s << tr("Mesh"); break;
case eRouterStatusError: default: s << tr("Unknown");
}
if (error != eRouterErrorNone)
{ {
s << tr("Error"); s << "<br>";
switch (i2p::context.GetError ()) switch (error)
{ {
case eRouterErrorClockSkew: case eRouterErrorClockSkew:
s << " - " << tr("Clock skew"); s << " - " << tr("Clock skew");
@ -248,9 +250,6 @@ namespace http {
break; break;
default: ; default: ;
} }
break;
}
default: s << tr("Unknown");
} }
} }
@ -260,12 +259,12 @@ namespace http {
ShowUptime(s, i2p::context.GetUptime ()); ShowUptime(s, i2p::context.GetUptime ());
s << "<br>\r\n"; s << "<br>\r\n";
s << "<b>" << tr("Network status") << ":</b> "; s << "<b>" << tr("Network status") << ":</b> ";
ShowNetworkStatus (s, i2p::context.GetStatus ()); ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetError ());
s << "<br>\r\n"; s << "<br>\r\n";
if (i2p::context.SupportsV6 ()) if (i2p::context.SupportsV6 ())
{ {
s << "<b>" << tr("Network status v6") << ":</b> "; s << "<b>" << tr("Network status v6") << ":</b> ";
ShowNetworkStatus (s, i2p::context.GetStatusV6 ()); ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetErrorV6 ());
s << "<br>\r\n"; s << "<br>\r\n";
} }
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
@ -531,19 +530,21 @@ namespace http {
ShowLeaseSetDestination (s, dest, token); ShowLeaseSetDestination (s, dest, token);
// Print table with streams information // Print table with streams information
s << "<table>\r\n<caption>" << tr("Streams") << "</caption>\r\n<thead>\r\n<tr>"; s << "<table>\r\n<caption>"
s << "<th style=\"width:25px;\">StreamID</th>"; << tr("Streams")
s << "<th style=\"width:5px;\" \\>"; // Stream closing button column << "</caption>\r\n<thead>\r\n<tr>"
s << "<th class=\"streamdest\">Destination</th>"; << "<th style=\"width:25px;\">StreamID</th>"
s << "<th>Sent</th>"; << "<th style=\"width:5px;\" \\>" // Stream closing button column
s << "<th>Received</th>"; << "<th class=\"streamdest\">Destination</th>"
s << "<th>Out</th>"; << "<th>Sent</th>"
s << "<th>In</th>"; << "<th>Received</th>"
s << "<th>Buf</th>"; << "<th>Out</th>"
s << "<th>RTT</th>"; << "<th>In</th>"
s << "<th>Window</th>"; << "<th>Buf</th>"
s << "<th>Status</th>"; << "<th>RTT</th>"
s << "</tr>\r\n</thead>\r\n<tbody class=\"tableitem\">\r\n"; << "<th>Window</th>"
<< "<th>Status</th>"
<< "</tr>\r\n</thead>\r\n<tbody class=\"tableitem\">\r\n";
for (const auto& it: dest->GetAllStreams ()) for (const auto& it: dest->GetAllStreams ())
{ {
@ -697,8 +698,7 @@ namespace http {
s << "<b>" << tr("Router commands") << "</b><br>\r\n<br>\r\n<div class=\"commands\">\r\n"; s << "<b>" << tr("Router commands") << "</b><br>\r\n<br>\r\n<div class=\"commands\">\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a><br>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a><br>\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RELOAD_TUNNELS_CONFIG << "&token=" << token << "\">" << tr("Reload tunnels configuration") << "</a><br>\r\n";
// s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
if (i2p::context.AcceptsTunnels ()) if (i2p::context.AcceptsTunnels ())
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">" << tr("Decline transit tunnels") << "</a><br>\r\n"; s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">" << tr("Decline transit tunnels") << "</a><br>\r\n";
@ -739,17 +739,25 @@ namespace http {
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n"; s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n"; s << "</form>\r\n<br>\r\n";
std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language // get current used language
s << "<b>" << tr("Change language") << "</b><br>\r\n"; std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage();
s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_SETLANGUAGE << "\">\r\n"; s << "<b>"
s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n"; << tr("Change language")
s << " <select name=\"lang\" id=\"lang\">\r\n"; << "</b><br>\r\n"
<< "<form method=\"get\" action=\"" << webroot << "\">\r\n"
<< " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_SETLANGUAGE << "\">\r\n"
<< " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n"
<< " <select name=\"lang\" id=\"lang\">\r\n";
for (const auto& it: i2p::i18n::languages) for (const auto& it: i2p::i18n::languages)
s << " <option value=\"" << it.first << "\"" << ((it.first.compare(currLang) == 0) ? " selected" : "") << ">" << it.second.LocaleName << "</option>\r\n"; s << " <option value=\"" << it.first << "\"" << ((it.first.compare(currLang) == 0) ? " selected" : "") << ">" << it.second.LocaleName << "</option>\r\n";
s << " </select>\r\n";
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n"; s << " </select>\r\n"
s << "</form>\r\n<br>\r\n"; << " <button type=\"submit\">"
<< tr("Change")
<< "</button>\r\n"
<< "</form>\r\n<br>\r\n";
} }
@ -783,12 +791,13 @@ namespace http {
std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0; std::stringstream tmp_s, tmp_s6; uint16_t cnt = 0, cnt6 = 0;
for (const auto& it: sessions ) for (const auto& it: sessions )
{ {
if (it.second && it.second->IsEstablished () && !it.second->GetRemoteEndpoint ().address ().is_v6 ()) auto endpoint = it.second->GetRemoteEndpoint ();
if (it.second && it.second->IsEstablished () && endpoint.address ().is_v4 ())
{ {
tmp_s << "<div class=\"listitem\">\r\n"; tmp_s << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s << " &#8658; "; if (it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< it.second->GetRemoteEndpoint ().address ().to_string (); << endpoint.address ().to_string () << ":" << endpoint.port ();
if (!it.second->IsOutgoing ()) tmp_s << " &#8658; "; if (!it.second->IsOutgoing ()) tmp_s << " &#8658; ";
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ()) if (it.second->GetRelayTag ())
@ -796,12 +805,12 @@ namespace http {
tmp_s << "</div>\r\n" << std::endl; tmp_s << "</div>\r\n" << std::endl;
cnt++; cnt++;
} }
if (it.second && it.second->IsEstablished () && it.second->GetRemoteEndpoint ().address ().is_v6 ()) if (it.second && it.second->IsEstablished () && endpoint.address ().is_v6 ())
{ {
tmp_s6 << "<div class=\"listitem\">\r\n"; tmp_s6 << "<div class=\"listitem\">\r\n";
if (it.second->IsOutgoing ()) tmp_s6 << " &#8658; "; if (it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
<< "[" << it.second->GetRemoteEndpoint ().address ().to_string () << "]"; << "[" << endpoint.address ().to_string () << "]:" << endpoint.port ();
if (!it.second->IsOutgoing ()) tmp_s6 << " &#8658; "; if (!it.second->IsOutgoing ()) tmp_s6 << " &#8658; ";
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
if (it.second->GetRelayTag ()) if (it.second->GetRelayTag ())
@ -1236,7 +1245,7 @@ namespace http {
std::string cmd = params["cmd"]; std::string cmd = params["cmd"];
if (cmd == HTTP_COMMAND_RUN_PEER_TEST) if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
i2p::transport::transports.PeerTest (); i2p::transport::transports.PeerTest ();
else if (cmd == HTTP_COMMAND_RELOAD_CONFIG) else if (cmd == HTTP_COMMAND_RELOAD_TUNNELS_CONFIG)
i2p::client::context.ReloadConfig (); i2p::client::context.ReloadConfig ();
else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT) else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT)
i2p::context.SetAcceptsTunnels (true); i2p::context.SetAcceptsTunnels (true);

View File

@ -14,25 +14,17 @@
// Use global placeholders from boost introduced when local_time.hpp is loaded // Use global placeholders from boost introduced when local_time.hpp is loaded
#define BOOST_BIND_GLOBAL_PLACEHOLDERS #define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/lexical_cast.hpp>
#include <boost/date_time/local_time/local_time.hpp> #include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <boost/lexical_cast.hpp>
#include "Crypto.h"
#include "FS.h" #include "FS.h"
#include "Log.h" #include "Log.h"
#include "Config.h" #include "Config.h"
#include "NetDb.hpp" #include "NetDb.hpp"
#include "RouterContext.h"
#include "Daemon.h"
#include "Tunnel.h" #include "Tunnel.h"
#include "Timestamp.h" #include "Daemon.h"
#include "Transports.h"
#include "version.h"
#include "util.h"
#include "ClientContext.h"
#include "I2PControl.h" #include "I2PControl.h"
namespace i2p namespace i2p
@ -69,44 +61,18 @@ namespace client
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler; m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler; m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler;
m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler; m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler;
m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler; m_MethodHandlers["RouterInfo"] = &I2PControlHandlers::RouterInfoHandler;
m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler; m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler;
m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler; m_MethodHandlers["NetworkSetting"] = &I2PControlHandlers::NetworkSettingHandler;
m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler; m_MethodHandlers["ClientServicesInfo"] = &I2PControlHandlers::ClientServicesInfoHandler;
// I2PControl // I2PControl
m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler; m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler;
// RouterInfo
m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler;
m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler;
m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler;
m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler;
m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler;
m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S;
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler;
m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = &I2PControlService::TunnelsSuccessRateHandler;
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
// RouterManager // RouterManager
m_RouterManagerHandlers["Reseed"] = &I2PControlService::ReseedHandler; m_RouterManagerHandlers["Reseed"] = &I2PControlService::ReseedHandler;
m_RouterManagerHandlers["Shutdown"] = &I2PControlService::ShutdownHandler; m_RouterManagerHandlers["Shutdown"] = &I2PControlService::ShutdownHandler;
m_RouterManagerHandlers["ShutdownGraceful"] = &I2PControlService::ShutdownGracefulHandler; m_RouterManagerHandlers["ShutdownGraceful"] = &I2PControlService::ShutdownGracefulHandler;
// NetworkSetting
m_NetworkSettingHandlers["i2p.router.net.bw.in"] = &I2PControlService::InboundBandwidthLimit;
m_NetworkSettingHandlers["i2p.router.net.bw.out"] = &I2PControlService::OutboundBandwidthLimit;
// ClientServicesInfo
m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlService::I2PTunnelInfoHandler;
m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlService::HTTPProxyInfoHandler;
m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler;
m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler;
m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler;
m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler;
} }
I2PControlService::~I2PControlService () I2PControlService::~I2PControlService ()
@ -280,37 +246,6 @@ namespace client
} }
} }
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
{
ss << "\"" << name << "\":" << value;
}
void I2PControlService::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 I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const
{
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
}
void I2PControlService::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();
}
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket, void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml) std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
{ {
@ -396,91 +331,6 @@ namespace client
m_Tokens.clear (); m_Tokens.clear ();
} }
// RouterInfo
void I2PControlService::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 I2PControlService::UptimeHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL), false);
}
void I2PControlService::VersionHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.version", VERSION);
}
void I2PControlService::StatusHandler (std::ostringstream& results)
{
auto dest = i2p::client::context.GetSharedLocalDestination ();
InsertParam (results, "i2p.router.status", (dest && dest->IsReady ()) ? "1" : "0");
}
void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.netdb.knownpeers", i2p::data::netdb.GetNumRouters ());
}
void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.netdb.activepeers", (int)i2p::transport::transports.GetPeers ().size ());
}
void I2PControlService::NetStatusHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.status", (int)i2p::context.GetStatus ());
}
void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results)
{
int transit = i2p::tunnel::tunnels.GetTransitTunnels ().size ();
InsertParam (results, "i2p.router.net.tunnels.participating", transit);
}
void I2PControlService::TunnelsSuccessRateHandler (std::ostringstream& results)
{
int rate = i2p::tunnel::tunnels.GetTunnelCreationSuccessRate ();
InsertParam (results, "i2p.router.net.tunnels.successrate", rate);
}
void I2PControlService::InboundBandwidth1S (std::ostringstream& results)
{
double bw = i2p::transport::transports.GetInBandwidth ();
InsertParam (results, "i2p.router.net.bw.inbound.1s", bw);
}
void I2PControlService::OutboundBandwidth1S (std::ostringstream& results)
{
double bw = i2p::transport::transports.GetOutBandwidth ();
InsertParam (results, "i2p.router.net.bw.outbound.1s", bw);
}
void I2PControlService::NetTotalReceivedBytes (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.total.received.bytes", (double)i2p::transport::transports.GetTotalReceivedBytes ());
}
void I2PControlService::NetTotalSentBytes (std::ostringstream& results)
{
InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ());
}
// RouterManager // RouterManager
@ -532,37 +382,6 @@ namespace client
i2p::data::netdb.Reseed (); i2p::data::netdb.Reseed ();
} }
// network setting
void I2PControlService::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 I2PControlService::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 I2PControlService::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);
}
// certificate // certificate
void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path) void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path)
{ {
@ -611,178 +430,5 @@ namespace client
} }
EVP_PKEY_free (pkey); EVP_PKEY_free (pkey);
} }
// ClientServicesInfo
void I2PControlService::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 I2PControlService::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 I2PControlService::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 I2PControlService::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 I2PControlService::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 I2PControlService::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 I2PControlService::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);
}
} }
} }

View File

@ -20,6 +20,7 @@
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/asio/ssl.hpp> #include <boost/asio/ssl.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include "I2PControlHandlers.h"
namespace i2p namespace i2p
{ {
@ -32,7 +33,7 @@ namespace client
const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol"; const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol";
const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P"; const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P";
class I2PControlService class I2PControlService: public I2PControlHandlers
{ {
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket; typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
@ -63,61 +64,24 @@ namespace client
private: private:
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;
// methods // methods
typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results); 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 AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void EchoHandler (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 I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results); void RouterManagerHandler (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);
// I2PControl // I2PControl
typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value); typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value);
void PasswordHandler (const std::string& value); void PasswordHandler (const std::string& value);
// RouterInfo
typedef void (I2PControlService::*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 OutboundBandwidth1S (std::ostringstream& results);
void NetTotalReceivedBytes (std::ostringstream& results);
void NetTotalSentBytes (std::ostringstream& results);
// RouterManager // RouterManager
typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results); typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results);
void ShutdownHandler (std::ostringstream& results); void ShutdownHandler (std::ostringstream& results);
void ShutdownGracefulHandler (std::ostringstream& results); void ShutdownGracefulHandler (std::ostringstream& results);
void ReseedHandler (std::ostringstream& results); void ReseedHandler (std::ostringstream& results);
// NetworkSetting
typedef void (I2PControlService::*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 (I2PControlService::*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: private:
std::string m_Password; std::string m_Password;
@ -132,10 +96,7 @@ namespace client
std::map<std::string, MethodHandler> m_MethodHandlers; std::map<std::string, MethodHandler> m_MethodHandlers;
std::map<std::string, I2PControlRequestHandler> m_I2PControlHandlers; std::map<std::string, I2PControlRequestHandler> m_I2PControlHandlers;
std::map<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;
std::map<std::string, RouterManagerRequestHandler> m_RouterManagerHandlers; std::map<std::string, RouterManagerRequestHandler> m_RouterManagerHandlers;
std::map<std::string, NetworkSettingRequestHandler> m_NetworkSettingHandlers;
std::map<std::string, ClientServicesInfoRequestHandler> m_ClientServicesInfoHandlers;
}; };
} }
} }

View File

@ -0,0 +1,376 @@
/*
* 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 <iomanip>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/lexical_cast.hpp>
#include <boost/property_tree/json_parser.hpp>
#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.outbound.1s"] = &I2PControlHandlers::OutboundBandwidth1S;
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::OutboundBandwidth1S (std::ostringstream& results)
{
double bw = i2p::transport::transports.GetOutBandwidth ();
InsertParam (results, "i2p.router.net.bw.outbound.1s", 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);
}
}
}

View File

@ -0,0 +1,80 @@
/*
* 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 <sstream>
#include <map>
#include <string>
#include <boost/property_tree/ptree.hpp>
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 OutboundBandwidth1S (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<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;
std::map<std::string, NetworkSettingRequestHandler> m_NetworkSettingHandlers;
std::map<std::string, ClientServicesInfoRequestHandler> m_ClientServicesInfoHandlers;
};
}
}
#endif

12
debian/changelog vendored
View File

@ -1,3 +1,15 @@
i2pd (2.44.0-1) unstable; urgency=medium
* updated to version 2.44.0/0.9.56
-- orignal <orignal@i2pmail.org> 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 <orignal@i2pmail.org> Mon, 22 Aug 2022 16:00:00 +0000
i2pd (2.42.1-1) unstable; urgency=medium i2pd (2.42.1-1) unstable; urgency=medium
* updated to version 2.42.1/0.9.54 * updated to version 2.42.1/0.9.54

217
i18n/Chinese.cpp Normal file
View File

@ -0,0 +1,217 @@
/*
* Copyright (c) 2022, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// Simplified Chinese localization file
// This is an example translation file without strings in it.
namespace i2p
{
namespace i18n
{
namespace chinese // language namespace
{
// language name in lowercase
static std::string language = "chinese";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
return 0;
}
static std::map<std::string, std::string> strings
{
{"KiB", "KiB"},
{"MiB", "MiB"},
{"GiB", "GiB"},
{"building", "正在构建"},
{"failed", "连接失败"},
{"expiring", "即将过期"},
{"established", "连接成功"},
{"unknown", "未知"},
{"exploratory", "探索"},
{"Purple I2P Webconsole", "Purple I2P 网页控制台"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> 网页控制台"},
{"Main page", "主页"},
{"Router commands", "路由命令"},
{"Local Destinations", "本地目标"},
{"LeaseSets", "租契集"},
{"Tunnels", "隧道"},
{"Transit Tunnels", "中转隧道"},
{"Transports", "传输"},
{"I2P tunnels", "I2P 隧道"},
{"SAM sessions", "SAM 会话"},
{"ERROR", "错误"},
{"OK", "良好"},
{"Testing", "测试中"},
{"Firewalled", "受到防火墙限制"},
{"Unknown", "未知"},
{"Proxy", "代理"},
{"Mesh", "Mesh组网"},
{"Error", "错误"},
{"Clock skew", "时钟偏移"},
{"Offline", "离线"},
{"Symmetric NAT", "对称 NAT"},
{"Uptime", "运行时间"},
{"Network status", "IPv4 网络状态"},
{"Network status v6", "IPv6 网络状态"},
{"Stopping in", "距停止还有:"},
{"Family", "家族"},
{"Tunnel creation success rate", "隧道创建成功率"},
{"Received", "已接收"},
{"KiB/s", "KiB/s"},
{"Sent", "已发送"},
{"Transit", "中转"},
{"Data path", "数据文件路径"},
{"Hidden content. Press on text to see.", "隐藏内容 请点击此处查看。"},
{"Router Ident", "路由身份"},
{"Router Family", "路由器家族"},
{"Router Caps", "路由器类型"},
{"Version", "版本"},
{"Our external address", "外部地址"},
{"supported", "支持"},
{"Routers", "路由节点"},
{"Floodfills", "洪泛节点"},
{"Client Tunnels", "客户端隧道"},
{"Services", "服务"},
{"Enabled", "启用"},
{"Disabled", "禁用"},
{"Encrypted B33 address", "加密的 B33 地址"},
{"Address registration line", "地址域名注册"},
{"Domain", "域名"},
{"Generate", "生成"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>注意:</b> 结果字符串只能用于注册次级域名(例如example.i2p)。若需注册子域名,请使用 i2pd-tools。"},
{"Address", "地址"},
{"Type", "类型"},
{"EncType", "加密类型"},
{"Inbound tunnels", "入站隧道"},
{"ms", "毫秒"},
{"Outbound tunnels", "出站隧道"},
{"Tags", "标签"},
{"Incoming", "传入"},
{"Outgoing", "传出"},
{"Destination", "目标"},
{"Amount", "数量"},
{"Incoming Tags", "传入标签"},
{"Tags sessions", "标签会话"},
{"Status", "状态"},
{"Local Destination", "本地目标"},
{"Streams", ""},
{"Close stream", "断开流"},
{"I2CP session not found", "未找到 I2CP 会话"},
{"I2CP is not enabled", "I2CP 未启用"},
{"Invalid", "无效"},
{"Store type", "存储类型"},
{"Expires", "过期时间"},
{"Non Expired Leases", "未到期的租约"},
{"Gateway", "网关"},
{"TunnelID", "隧道 ID"},
{"EndDate", "结束日期"},
{"not floodfill", "非洪泛"},
{"Queue size", "队列大小"},
{"Run peer test", "运行节点测试"},
{"Decline transit tunnels", "拒绝中转隧道"},
{"Accept transit tunnels", "允许中转隧道"},
{"Cancel graceful shutdown", "取消平滑关闭"},
{"Start graceful shutdown", "平滑关闭"},
{"Force shutdown", "强制停止"},
{"Reload external CSS styles", "重载外部 CSS 样式"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>注意:</b> 此处完成的任何操作都不是永久的,不会更改您的配置文件。"},
{"Logging level", "日志记录级别"},
{"Transit tunnels limit", "中转隧道限制"},
{"Change", "修改"},
{"Change language", "更改语言"},
{"no transit tunnels currently built", "目前未构建中转隧道"},
{"SAM disabled", "SAM 已禁用"},
{"no sessions currently running", "没有正在运行的会话"},
{"SAM session not found", "未找到 SAM 会话"},
{"SAM Session", "SAM 会话"},
{"Server Tunnels", "服务器隧道"},
{"Client Forwards", "客户端转发"},
{"Server Forwards", "服务器转发"},
{"Unknown page", "未知页面"},
{"Invalid token", "无效令牌"},
{"SUCCESS", "成功"},
{"Stream closed", "流已关闭"},
{"Stream not found or already was closed", "流未找到或已关闭"},
{"Destination not found", "找不到目标"},
{"StreamID can't be null", "StreamID 不能为空"},
{"Return to destination page", "返回目标页面"},
{"You will be redirected in 5 seconds", "您将在5秒内被重定向"},
{"Transit tunnels count must not exceed 65535", "中转隧道数量不能超过 65535"},
{"Back to commands list", "返回命令列表"},
{"Register at reg.i2p", "在 reg.i2p 注册域名"},
{"Description", "描述"},
{"A bit information about service on domain", "在此域名上运行的服务的一些信息"},
{"Submit", "提交"},
{"Domain can't end with .b32.i2p", "域名不能以 .b32.i2p 结尾"},
{"Domain must end with .i2p", "域名必须以 .i2p 结尾"},
{"Such destination is not found", "找不到此目标"},
{"Unknown command", "未知指令"},
{"Command accepted", "已接受指令"},
{"Proxy error", "代理错误"},
{"Proxy info", "代理信息"},
{"Proxy error: Host not found", "代理错误:未找到主机"},
{"Remote host not found in router's addressbook", "在路由地址簿中未找到远程主机"},
{"You may try to find this host on jump services below", "您可以尝试在下方的跳转服务中找到该主机"},
{"Invalid request", "无效请求"},
{"Proxy unable to parse your request", "代理无法解析您的请求"},
{"addresshelper is not supported", "不支持地址助手"},
{"Host", "主机"},
{"added to router's addressbook from helper", "将此地址从地址助手添加到路由地址簿"},
{"Click here to proceed:", "点击此处继续:"},
{"Continue", "继续"},
{"Addresshelper found", "已找到地址助手"},
{"already in router's addressbook", "已在路由地址簿中"},
{"Click here to update record:", "点击此处更新地址簿记录"},
{"invalid request uri", "无效的 URL 请求"},
{"Can't detect destination host from request", "无法从请求中检测到目标主机"},
{"Outproxy failure", "出口代理故障"},
{"bad outproxy settings", "错误的出口代理设置"},
{"not inside I2P network, but outproxy is not enabled", "该地址不在 I2P 网络内,但未启用出口代理"},
{"unknown outproxy url", "未知的出口代理地址"},
{"cannot resolve upstream proxy", "无法解析上游代理"},
{"hostname too long", "主机名过长"},
{"cannot connect to upstream socks proxy", "无法连接到上游 socks 代理"},
{"Cannot negotiate with socks proxy", "无法与 socks 代理协商"},
{"CONNECT error", "连接错误"},
{"Failed to Connect", "连接失败"},
{"socks proxy error", "socks 代理错误"},
{"failed to send request to upstream", "向上游发送请求失败"},
{"No Reply From socks proxy", "没有来自 socks 代理的回复"},
{"cannot connect", "无法连接"},
{"http out proxy not implemented", "http 出口代理未实现"},
{"cannot connect to upstream http proxy", "无法连接到上游 http 代理"},
{"Host is down", "主机已关闭"},
{"Can't create connection to requested host, it may be down. Please try again later.", "无法创建到目标主机的连接。主机可能已下线,请稍后再试。"},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"days", {""}},
{"hours", {""}},
{"minutes", {""}},
{"seconds", {""}},
{"", {""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

View File

@ -35,24 +35,33 @@ namespace french // language namespace
{"MiB", "Mio"}, {"MiB", "Mio"},
{"GiB", "Gio"}, {"GiB", "Gio"},
{"building", "En construction"}, {"building", "En construction"},
{"failed", "echoué"}, {"failed", "échoué"},
{"expiring", "expiré"}, {"expiring", "expiré"},
{"established", "établi"}, {"established", "établi"},
{"unknown", "inconnu"}, {"unknown", "inconnu"},
{"exploratory", "exploratoire"}, {"exploratory", "exploratoire"},
{"Purple I2P Webconsole", "Console web Purple I2P"},
{"<b>i2pd</b> webconsole", "Console web <b>i2pd</b>"}, {"<b>i2pd</b> webconsole", "Console web <b>i2pd</b>"},
{"Main page", "Page principale"}, {"Main page", "Page principale"},
{"Router commands", "Commandes du routeur"}, {"Router commands", "Commandes du routeur"},
{"Local Destinations", "Destinations locales"}, {"Local Destinations", "Destinations locales"},
{"LeaseSets", "Jeu de baux"},
{"Tunnels", "Tunnels"}, {"Tunnels", "Tunnels"},
{"Transit Tunnels", "Tunnels transitoires"}, {"Transit Tunnels", "Tunnels transitoires"},
{"Transports", "Transports"},
{"I2P tunnels", "Tunnels I2P"}, {"I2P tunnels", "Tunnels I2P"},
{"SAM sessions", "Sessions SAM"}, {"SAM sessions", "Sessions SAM"},
{"ERROR", "ERREUR"}, {"ERROR", "ERREUR"},
{"OK", "OK"}, {"OK", "OK"},
{"Testing", "Test en cours"},
{"Firewalled", "Derrière un pare-feu"}, {"Firewalled", "Derrière un pare-feu"},
{"Unknown", "Inconnu"},
{"Proxy", "Proxy"},
{"Mesh", "Maillé"},
{"Error", "Erreur"}, {"Error", "Erreur"},
{"Clock skew", "Horloge décalée"},
{"Offline", "Hors ligne"}, {"Offline", "Hors ligne"},
{"Symmetric NAT", "NAT symétrique"},
{"Uptime", "Temps de fonctionnement"}, {"Uptime", "Temps de fonctionnement"},
{"Network status", "État du réseau"}, {"Network status", "État du réseau"},
{"Network status v6", "État du réseau v6"}, {"Network status v6", "État du réseau v6"},
@ -62,24 +71,124 @@ namespace french // language namespace
{"Received", "Reçu"}, {"Received", "Reçu"},
{"KiB/s", "kio/s"}, {"KiB/s", "kio/s"},
{"Sent", "Envoyé"}, {"Sent", "Envoyé"},
{"Transit", "Transit"}, {"Transit", "Transité"},
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour regarder."}, {"Data path", "Emplacement des données"},
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour afficher."},
{"Router Ident", "Identifiant du routeur"}, {"Router Ident", "Identifiant du routeur"},
{"Router Family", "Famille du routeur"}, {"Router Family", "Famille du routeur"},
{"Router Caps", "Limiteurs du routeur"},
{"Version", "Version"}, {"Version", "Version"},
{"Our external address", "Notre adresse externe"}, {"Our external address", "Notre adresse externe"},
{"supported", "supporté"},
{"Routers", "Routeurs"},
{"Client Tunnels", "Tunnels clients"}, {"Client Tunnels", "Tunnels clients"},
{"Services", "Services"}, {"Services", "Services"},
{"Enabled", "Activé"}, {"Enabled", "Activé"},
{"Disabled", "Désactivé"}, {"Disabled", "Désactivé"},
{"Encrypted B33 address", "Adresse B33 chiffrée"}, {"Encrypted B33 address", "Adresse B33 chiffrée"},
{"Address registration line", "Ligne d'inscription de l'adresse"},
{"Domain", "Domaine"}, {"Domain", "Domaine"},
{"Generate", "Générer"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Note:</b> La chaîne résultante peut seulement être utilisée pour enregistrer les domaines 2LD (exemple.i2p). Pour enregistrer des sous-domaines, veuillez utiliser i2pd-tools."}, {"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Note:</b> La chaîne résultante peut seulement être utilisée pour enregistrer les domaines 2LD (exemple.i2p). Pour enregistrer des sous-domaines, veuillez utiliser i2pd-tools."},
{"Address", "Adresse"}, {"Address", "Adresse"},
{"Type", "Type"},
{"Inbound tunnels", "Tunnels entrants"},
{"ms", "ms"}, {"ms", "ms"},
{"Outbound tunnels", "Tunnels sortants"}, {"Outbound tunnels", "Tunnels sortants"},
{"Tags", "Balises"},
{"Incoming", "Entrant"},
{"Outgoing", "Sortant"},
{"Destination", "Destination"}, {"Destination", "Destination"},
{"Amount", "Quantité"},
{"Incoming Tags", "Balises entrantes"},
{"Tags sessions", "Sessions des balises"},
{"Status", "Statut"},
{"Local Destination", "Destination locale"}, {"Local Destination", "Destination locale"},
{"Streams", "Flux"},
{"Close stream", "Fermer le flux"},
{"I2CP session not found", "Session I2CP introuvable"},
{"I2CP is not enabled", "I2CP est désactivé"},
{"Invalid", "Invalide"},
{"Store type", "Type de stockage"},
{"Expires", "Expire"},
{"Non Expired Leases", "Baux non expirés"},
{"Gateway", "Passerelle"},
{"TunnelID", "ID du tunnel"},
{"EndDate", "Date de fin"},
{"Queue size", "Longueur de la file"},
{"Run peer test", "Lancer test des pairs"},
{"Decline transit tunnels", "Refuser les tunnels transitoires"},
{"Accept transit tunnels", "Accepter les tunnels transitoires"},
{"Cancel graceful shutdown", "Annuler l'arrêt gracieux"},
{"Start graceful shutdown", "Démarrer l'arrêt gracieux"},
{"Force shutdown", "Forcer l'arrêt"},
{"Reload external CSS styles", "Rafraîchir les styles CSS externes"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Note:</b> Toute action effectuée ici n'est pas permanente et ne modifie pas vos fichiers de configuration."},
{"Logging level", "Niveau de journalisation"},
{"Transit tunnels limit", "Limite sur les tunnels transitoires"},
{"Change", "Changer"},
{"Change language", "Changer la langue"},
{"no transit tunnels currently built", "aucun tunnel transitoire présentement établi"},
{"SAM disabled", "SAM désactivé"},
{"no sessions currently running", "aucune session présentement en cours"},
{"SAM session not found", "session SAM introuvable"},
{"SAM Session", "Session SAM"},
{"Server Tunnels", "Tunnels serveurs"},
{"Unknown page", "Page inconnue"},
{"Invalid token", "Jeton invalide"},
{"SUCCESS", "SUCCÈS"},
{"Stream closed", "Flux fermé"},
{"Stream not found or already was closed", "Flux introuvable ou déjà fermé"},
{"Destination not found", "Destination introuvable"},
{"StreamID can't be null", "StreamID ne peut pas être vide"},
{"Return to destination page", "Retourner à la page de destination"},
{"You will be redirected in 5 seconds", "Vous allez être redirigé dans cinq secondes"},
{"Transit tunnels count must not exceed 65535", "Le nombre de tunnels transitoires ne doit pas dépasser 65535"},
{"Back to commands list", "Retour à la liste des commandes"},
{"Register at reg.i2p", "Inscription à reg.i2p"},
{"Description", "Description"},
{"A bit information about service on domain", "Un peu d'information à propos des services disponibles dans le domaine"},
{"Submit", "Soumettre"},
{"Domain can't end with .b32.i2p", "Le domaine ne peut pas terminer par .b32.i2p"},
{"Domain must end with .i2p", "Le domaine doit terminer par .i2p"},
{"Such destination is not found", "Cette destination est introuvable"},
{"Unknown command", "Commande inconnue"},
{"Command accepted", "Commande acceptée"},
{"Proxy error", "Erreur de proxy"},
{"Proxy info", "Information sur le proxy"},
{"Proxy error: Host not found", "Erreur de proxy: Hôte introuvable"},
{"Remote host not found in router's addressbook", "Hôte distant introuvable dans le carnet d'adresse du routeur"},
{"You may try to find this host on jump services below", "Vous pouvez essayer de trouver cet hôte sur des services de redirection ci-dessous"},
{"Invalid request", "Requête invalide"},
{"Proxy unable to parse your request", "Proxy incapable de comprendre votre requête"},
{"addresshelper is not supported", "Assistant d'adresse non supporté"},
{"Host", "Hôte"},
{"added to router's addressbook from helper", "Ajouté au carnet d'adresse du routeur par l'assistant"},
{"Click here to proceed:", "Cliquez ici pour continuer:"},
{"Continue", "Continuer"},
{"Addresshelper found", "Assistant d'adresse trouvé"},
{"already in router's addressbook", "déjà dans le carnet d'adresses du routeur"},
{"Click here to update record:", "Cliquez ici pour mettre à jour le carnet d'adresse:"},
{"invalid request uri", "uri de la requête invalide"},
{"Can't detect destination host from request", "Impossible de détecter l'hôte de destination à partir de la requête"},
{"Outproxy failure", "Échec de proxy de sortie"},
{"bad outproxy settings", "Mauvaise configuration du proxy de sortie"},
{"not inside I2P network, but outproxy is not enabled", "pas dans le réseau I2P, mais le proxy de sortie n'est pas activé"},
{"unknown outproxy url", "URL du proxy de sortie inconnu"},
{"cannot resolve upstream proxy", "impossible de résoudre l'adresse du proxy en amont"},
{"hostname too long", "nom d'hôte trop long"},
{"cannot connect to upstream socks proxy", "impossible de se connecter au proxy socks en amont"},
{"Cannot negotiate with socks proxy", "Impossible de négocier avec le proxy socks"},
{"CONNECT error", "Erreur de connexion"},
{"Failed to Connect", "Échec de connexion"},
{"socks proxy error", "Erreur de proxy socks"},
{"failed to send request to upstream", "Erreur lors de l'envoie de la requête en amont"},
{"No Reply From socks proxy", "Pas de réponse du proxy socks"},
{"cannot connect", "impossible de connecter"},
{"http out proxy not implemented", "Proxy de sortie HTTP non implémenté"},
{"cannot connect to upstream http proxy", "impossible de se connecter au proxy HTTP en amont"},
{"Host is down", "Hôte hors service"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Impossible d'établir une connexion avec l'hôte, il est peut-être hors service. Veuillez réessayer plus tard."},
{"", ""}, {"", ""},
}; };

View File

@ -36,14 +36,15 @@ namespace german // language namespace
{"GiB", "GiB"}, {"GiB", "GiB"},
{"building", "In Bau"}, {"building", "In Bau"},
{"failed", "fehlgeschlagen"}, {"failed", "fehlgeschlagen"},
{"expiring", "läuft ab in"}, {"expiring", "läuft ab"},
{"established", "hergestellt"}, {"established", "hergestellt"},
{"unknown", "Unbekannt"}, {"unknown", "Unbekannt"},
{"exploratory", "erforschende"}, {"exploratory", "erforschend"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b> Webkonsole"}, {"Purple I2P Webconsole", "Purple I2P-Webkonsole"},
{"<b>i2pd</b> webconsole", "<b>i2pd</b>-Webkonsole"},
{"Main page", "Startseite"}, {"Main page", "Startseite"},
{"Router commands", "Router Befehle"}, {"Router commands", "Routerbefehle"},
{"Local Destinations", "Lokale Destination"}, {"Local Destinations", "Lokale Ziele"},
{"LeaseSets", "LeaseSets"}, {"LeaseSets", "LeaseSets"},
{"Tunnels", "Tunnel"}, {"Tunnels", "Tunnel"},
{"Transit Tunnels", "Transittunnel"}, {"Transit Tunnels", "Transittunnel"},
@ -53,7 +54,7 @@ namespace german // language namespace
{"ERROR", "FEHLER"}, {"ERROR", "FEHLER"},
{"OK", "OK"}, {"OK", "OK"},
{"Testing", "Testen"}, {"Testing", "Testen"},
{"Firewalled", "Hinter eine Firewall"}, {"Firewalled", "Hinter einer Firewall"},
{"Unknown", "Unbekannt"}, {"Unknown", "Unbekannt"},
{"Proxy", "Proxy"}, {"Proxy", "Proxy"},
{"Mesh", "Mesh"}, {"Mesh", "Mesh"},
@ -81,15 +82,15 @@ namespace german // language namespace
{"supported", "unterstützt"}, {"supported", "unterstützt"},
{"Routers", "Router"}, {"Routers", "Router"},
{"Floodfills", "Floodfills"}, {"Floodfills", "Floodfills"},
{"Client Tunnels", "Klienttunnel"}, {"Client Tunnels", "Clienttunnel"},
{"Services", "Services"}, {"Services", "Services"},
{"Enabled", "Aktiviert"}, {"Enabled", "Aktiviert"},
{"Disabled", "Deaktiviert"}, {"Disabled", "Deaktiviert"},
{"Encrypted B33 address", "Verschlüsselte B33 Adresse"}, {"Encrypted B33 address", "Verschlüsselte B33-Adresse"},
{"Address registration line", "Adresseregistrierungszeile"}, {"Address registration line", "Adressregistrierungszeile"},
{"Domain", "Domain"}, {"Domain", "Domain"},
{"Generate", "Generieren"}, {"Generate", "Generieren"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Hinweis:</b> Der resultierende String kann nur für die Registrierung einer 2LD Domain (beispiel.i2p) benutzt werden. Für die Registrierung von Subdomains kann i2pd-tools verwendet werden."}, {"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Hinweis:</b> Der resultierende String kann nur für die Registrierung einer 2LD-Domain (beispiel.i2p) benutzt werden. Für die Registrierung von Subdomains kann i2pd-tools verwendet werden."},
{"Address", "Adresse"}, {"Address", "Adresse"},
{"Type", "Typ"}, {"Type", "Typ"},
{"EncType", "Verschlüsselungstyp"}, {"EncType", "Verschlüsselungstyp"},
@ -99,15 +100,15 @@ namespace german // language namespace
{"Tags", "Tags"}, {"Tags", "Tags"},
{"Incoming", "Eingehend"}, {"Incoming", "Eingehend"},
{"Outgoing", "Ausgehend"}, {"Outgoing", "Ausgehend"},
{"Destination", "Destination"}, {"Destination", "Ziel"},
{"Amount", "Anzahl"}, {"Amount", "Anzahl"},
{"Incoming Tags", "Eingehende Tags"}, {"Incoming Tags", "Eingehende Tags"},
{"Tags sessions", "Tags Sitzungen"}, {"Tags sessions", "Tags-Sitzungen"},
{"Status", "Status"}, {"Status", "Status"},
{"Local Destination", "Lokale Destination"}, {"Local Destination", "Lokales Ziel"},
{"Streams", "Streams"}, {"Streams", "Streams"},
{"Close stream", "Stream schließen"}, {"Close stream", "Stream schließen"},
{"I2CP session not found", "I2CP Sitzung nicht gefunden"}, {"I2CP session not found", "I2CP-Sitzung nicht gefunden"},
{"I2CP is not enabled", "I2CP ist nicht aktiviert"}, {"I2CP is not enabled", "I2CP ist nicht aktiviert"},
{"Invalid", "Ungültig"}, {"Invalid", "Ungültig"},
{"Store type", "Speichertyp"}, {"Store type", "Speichertyp"},
@ -117,67 +118,67 @@ namespace german // language namespace
{"TunnelID", "TunnelID"}, {"TunnelID", "TunnelID"},
{"EndDate", "Enddatum"}, {"EndDate", "Enddatum"},
{"not floodfill", "kein Floodfill"}, {"not floodfill", "kein Floodfill"},
{"Queue size", "Warteschlangengröße"}, {"Queue size", "Größe der Warteschlange"},
{"Run peer test", "Peer-Test ausführen"}, {"Run peer test", "Peer-Test durchführen"},
{"Decline transit tunnels", "Transittunnel ablehnen"}, {"Decline transit tunnels", "Transittunnel ablehnen"},
{"Accept transit tunnels", "Transittunnel akzeptieren"}, {"Accept transit tunnels", "Transittunnel akzeptieren"},
{"Cancel graceful shutdown", "Beende das kontrollierte herunterfahren"}, {"Cancel graceful shutdown", "Beende das kontrollierte Herunterfahren"},
{"Start graceful shutdown", "Starte das kontrollierte Herunterfahren"}, {"Start graceful shutdown", "Starte das kontrollierte Herunterfahren"},
{"Force shutdown", "Herunterfahren erzwingen"}, {"Force shutdown", "Herunterfahren erzwingen"},
{"Reload external CSS styles", "Lade externe CSS-Styles neu"}, {"Reload external CSS styles", "Lade externe CSS-Stile neu"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Hinweis:</b> Alle hier durchgeführten Aktionen sind nicht dauerhaft und ändern die Konfigurationsdateien nicht."}, {"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Hinweis:</b> Alle hier durchgeführten Aktionen sind nicht dauerhaft und ändern die Konfigurationsdateien nicht."},
{"Logging level", "Protokollierungslevel"}, {"Logging level", "Protokollierungslevel"},
{"Transit tunnels limit", "Limit für Transittunnel"}, {"Transit tunnels limit", "Limit für Transittunnel"},
{"Change", "Verändern"}, {"Change", "Ändern"},
{"Change language", "Sprache ändern"}, {"Change language", "Sprache ändern"},
{"no transit tunnels currently built", "derzeit keine Transittunnel aufgebaut"}, {"no transit tunnels currently built", "derzeit keine Transittunnel aufgebaut"},
{"SAM disabled", "SAM deaktiviert"}, {"SAM disabled", "SAM deaktiviert"},
{"no sessions currently running", "Derzeit keine laufenden Sitzungen"}, {"no sessions currently running", "Derzeit keine laufenden Sitzungen"},
{"SAM session not found", "SAM Sitzung nicht gefunden"}, {"SAM session not found", "SAM-Sitzung nicht gefunden"},
{"SAM Session", "SAM Sitzung"}, {"SAM Session", "SAM-Sitzung"},
{"Server Tunnels", "Servertunnel"}, {"Server Tunnels", "Servertunnel"},
{"Client Forwards", "Klient-Weiterleitungen"}, {"Client Forwards", "Client-Weiterleitungen"},
{"Server Forwards", "Server-Weiterleitungen"}, {"Server Forwards", "Server-Weiterleitungen"},
{"Unknown page", "Unbekannte Seite"}, {"Unknown page", "Unbekannte Seite"},
{"Invalid token", "Ungültiger Token"}, {"Invalid token", "Ungültiger Token"},
{"SUCCESS", "ERFOLGREICH"}, {"SUCCESS", "ERFOLGREICH"},
{"Stream closed", "Stream geschlossen"}, {"Stream closed", "Stream geschlossen"},
{"Stream not found or already was closed", "Stream nicht gefunden oder bereits geschlossen"}, {"Stream not found or already was closed", "Stream nicht gefunden oder bereits geschlossen"},
{"Destination not found", "Destination nicht gefunden"}, {"Destination not found", "Ziel nicht gefunden"},
{"StreamID can't be null", "StreamID kann nicht null sein"}, {"StreamID can't be null", "StreamID kann nicht null sein"},
{"Return to destination page", "Zurück zur Destination-Seite"}, {"Return to destination page", "Zurück zur Ziel-Seite"},
{"You will be redirected in 5 seconds", "Du wirst in 5 Sekunden weitergeleitet"}, {"You will be redirected in 5 seconds", "Du wirst in 5 Sekunden weitergeleitet"},
{"Transit tunnels count must not exceed 65535", "Es darf maximal 65535 Transittunnel geben"}, {"Transit tunnels count must not exceed 65535", "Es darf maximal 65535 Transittunnel geben"},
{"Back to commands list", "Zurück zur Kommandoliste"}, {"Back to commands list", "Zurück zur Befehlsliste"},
{"Register at reg.i2p", "Auf reg.i2p registrieren"}, {"Register at reg.i2p", "Auf reg.i2p registrieren"},
{"Description", "Beschreibung"}, {"Description", "Beschreibung"},
{"A bit information about service on domain", "Ein bisschen Informationen über den Service auf der Domain"}, {"A bit information about service on domain", "Ein paar Informationen über den Service auf der Domain"},
{"Submit", "Einreichen"}, {"Submit", "Absenden"},
{"Domain can't end with .b32.i2p", "Domain kann nicht mit .b32.i2p enden"}, {"Domain can't end with .b32.i2p", "Domain kann nicht auf .b32.i2p enden"},
{"Domain must end with .i2p", "Domain muss mit .i2p enden"}, {"Domain must end with .i2p", "Domain muss auf .i2p enden"},
{"Such destination is not found", "Eine solche Destination konnte nicht gefunden werden"}, {"Such destination is not found", "Ein solches Ziel konnte nicht gefunden werden"},
{"Unknown command", "Unbekannter Befehl"}, {"Unknown command", "Unbekannter Befehl"},
{"Command accepted", "Befehl akzeptiert"}, {"Command accepted", "Befehl akzeptiert"},
{"Proxy error", "Proxy-Fehler"}, {"Proxy error", "Proxy-Fehler"},
{"Proxy info", "Proxy-Info"}, {"Proxy info", "Proxy-Info"},
{"Proxy error: Host not found", "Proxy-Fehler: Host nicht gefunden"}, {"Proxy error: Host not found", "Proxy-Fehler: Host nicht gefunden"},
{"Remote host not found in router's addressbook", "Remote-Host nicht im Router Adressbuch gefunden"}, {"Remote host not found in router's addressbook", "Remote-Host nicht im Router-Adressbuch gefunden"},
{"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einen der Jump-Services unten finden"}, {"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einem der nachfolgenden Jump-Services finden"},
{"Invalid request", "Ungültige Anfrage"}, {"Invalid request", "Ungültige Anfrage"},
{"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht interpretieren"}, {"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht verarbeiten"},
{"addresshelper is not supported", "addresshelper wird nicht unterstützt"}, {"addresshelper is not supported", "Addresshelfer wird nicht unterstützt"},
{"Host", "Host"}, {"Host", "Host"},
{"added to router's addressbook from helper", "vom Helfer zum Router Adressbuch hinzugefügt"}, {"added to router's addressbook from helper", "vom Helfer zum Router-Adressbuch hinzugefügt"},
{"Click here to proceed:", "Klicke hier um fortzufahren:"}, {"Click here to proceed:", "Klicke hier um fortzufahren:"},
{"Continue", "Fortsetzen"}, {"Continue", "Fortsetzen"},
{"Addresshelper found", "Adresshelfer gefunden"}, {"Addresshelper found", "Adresshelfer gefunden"},
{"already in router's addressbook", "bereits im Adressbuch des Routers"}, {"already in router's addressbook", "bereits im Adressbuch des Routers"},
{"Click here to update record:", "Klicke hier, um den Eintrag zu aktualisieren:"}, {"Click here to update record:", "Klicke hier, um den Eintrag zu aktualisieren:"},
{"invalid request uri", "ungültige Anfrage-URI"}, {"invalid request uri", "ungültige Anfrage-URI"},
{"Can't detect destination host from request", "Kann Anhand der Anfrage den Destination-Host nicht erkennen"}, {"Can't detect destination host from request", "Kann den Ziel-Host von der Anfrage nicht erkennen"},
{"Outproxy failure", "Outproxy-Fehler"}, {"Outproxy failure", "Outproxy-Fehler"},
{"bad outproxy settings", "ungültige Outproxy-Einstellungen"}, {"bad outproxy settings", "ungültige Outproxy-Einstellungen"},
{"not inside I2P network, but outproxy is not enabled", "nicht innerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"}, {"not inside I2P network, but outproxy is not enabled", "außerhalb des I2P-Netzwerks, aber Outproxy ist nicht aktiviert"},
{"unknown outproxy url", "unbekannte Outproxy-URL"}, {"unknown outproxy url", "unbekannte Outproxy-URL"},
{"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"}, {"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"},
{"hostname too long", "Hostname zu lang"}, {"hostname too long", "Hostname zu lang"},
@ -192,7 +193,7 @@ namespace german // language namespace
{"http out proxy not implemented", "HTTP-Outproxy nicht implementiert"}, {"http out proxy not implemented", "HTTP-Outproxy nicht implementiert"},
{"cannot connect to upstream http proxy", "Kann nicht zu Upstream-HTTP-Proxy verbinden"}, {"cannot connect to upstream http proxy", "Kann nicht zu Upstream-HTTP-Proxy verbinden"},
{"Host is down", "Host ist offline"}, {"Host is down", "Host ist offline"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Konnte keine Verbindung zum angefragten Host aufbaunen, vielleicht ist es offline. Versuche es später noch einmal."}, {"Can't create connection to requested host, it may be down. Please try again later.", "Konnte keine Verbindung zum angefragten Host aufbauen, vielleicht ist er offline. Versuche es später noch mal."},
{"", ""}, {"", ""},
}; };

View File

@ -73,10 +73,13 @@ namespace i18n
// Add localization here with language name as namespace // Add localization here with language name as namespace
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace chinese { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace italian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace spanish { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
@ -87,13 +90,16 @@ namespace i18n
static std::map<std::string, langData> languages static std::map<std::string, langData> languages
{ {
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} }, { "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} }, { "armenian", {"hայերէն", "hy", i2p::i18n::armenian::GetLocale} },
{ "chinese", {"简体字", "zh-CN", i2p::i18n::chinese::GetLocale} },
{ "english", {"English", "en", i2p::i18n::english::GetLocale} }, { "english", {"English", "en", i2p::i18n::english::GetLocale} },
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} }, { "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} }, { "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} }, { "italian", {"Italiano", "it", i2p::i18n::italian::GetLocale} },
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} }, { "russian", {"Русский язык", "ru", i2p::i18n::russian::GetLocale} },
{ "ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} }, { "spanish", {"Español", "es", i2p::i18n::spanish::GetLocale} },
{ "turkmen", {"Türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
{ "ukrainian", {"Украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
{ "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} }, { "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} },
}; };

216
i18n/Italian.cpp Normal file
View File

@ -0,0 +1,216 @@
/*
* Copyright (c) 2022, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// Italian localization file
namespace i2p
{
namespace i18n
{
namespace italian // language namespace
{
// language name in lowercase
static std::string language = "italian";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
return n != 1 ? 1 : 0;
}
static std::map<std::string, std::string> strings
{
{"KiB", "KiB"},
{"MiB", "MiB"},
{"GiB", "GiB"},
{"building", "in costruzione"},
{"failed", "fallito"},
{"expiring", "in scadenza"},
{"established", "stabilita"},
{"unknown", "sconosciuto"},
{"exploratory", "esplorativo"},
{"Purple I2P Webconsole", "Terminale web Purple I2P"},
{"<b>i2pd</b> webconsole", "Terminal web <b>i2pd</b>"},
{"Main page", "Pagina principale"},
{"Router commands", "Comandi router"},
{"Local Destinations", "Destinazioni locali"},
{"LeaseSets", "LeaseSets"},
{"Tunnels", "Tunnel"},
{"Transit Tunnels", "Tunnel di transito"},
{"Transports", "Trasporti"},
{"I2P tunnels", "Tunnel I2P"},
{"SAM sessions", "Sessioni SAM"},
{"ERROR", "ERRORE"},
{"OK", "OK"},
{"Testing", "Testando"},
{"Firewalled", "Protetto da firewall"},
{"Unknown", "Sconosciuto"},
{"Proxy", "Proxy"},
{"Mesh", "Mesh"},
{"Error", "Errore"},
{"Clock skew", "Orologio disallineato"},
{"Offline", "Disconnesso"},
{"Symmetric NAT", "NAT simmetrico"},
{"Uptime", "In funzione da"},
{"Network status", "Stato della rete"},
{"Network status v6", "Stato della rete v6"},
{"Stopping in", "Arresto in"},
{"Family", "Famiglia"},
{"Tunnel creation success rate", "Percentuale di tunnel creati con successo"},
{"Received", "Ricevuti"},
{"KiB/s", "KiB/s"},
{"Sent", "Inviati"},
{"Transit", "Transitati"},
{"Data path", "Percorso dati"},
{"Hidden content. Press on text to see.", "Contenuto nascosto. Premi sul testo per vedere."},
{"Router Ident", "Identificativo del router"},
{"Router Family", "Famiglia del router"},
{"Router Caps", "Limiti del router"},
{"Version", "Versione"},
{"Our external address", "Il nostro indirizzo esterno"},
{"supported", "supportato"},
{"Routers", "Router"},
{"Floodfills", "Floodfill"},
{"Client Tunnels", "Tunnel client"},
{"Services", "Servizi"},
{"Enabled", "Abilitato"},
{"Disabled", "Disabilitato"},
{"Encrypted B33 address", "Indirizzo criptato B33"},
{"Address registration line", "Linea di registrazione indirizzo"},
{"Domain", "Dominio"},
{"Generate", "Genera"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Nota:</b> la stringa risultante può essere utilizzata solo per registrare domini 2LD (example.i2p). Per registrare i sottodomini, si prega di utilizzare i2pd-tools."},
{"Address", "Indirizzo"},
{"Type", "Tipologia"},
{"EncType", "Tipo di crittografia"},
{"Inbound tunnels", "Tunnel in entrata"},
{"ms", "ms"},
{"Outbound tunnels", "Tunnel in uscita"},
{"Tags", "Tag"},
{"Incoming", "In entrata"},
{"Outgoing", "In uscita"},
{"Destination", "Destinazione"},
{"Amount", "Quantità"},
{"Incoming Tags", "Tag in entrata"},
{"Tags sessions", "Sessioni dei tag"},
{"Status", "Stato"},
{"Local Destination", "Destinazione locale"},
{"Streams", "Flussi"},
{"Close stream", "Interrompi il flusso"},
{"I2CP session not found", "Sessione I2CP non trovata"},
{"I2CP is not enabled", "I2CP non è abilitato"},
{"Invalid", "Invalido"},
{"Store type", "Tipologia di archivio"},
{"Expires", "Scade"},
{"Non Expired Leases", "Lease non scaduti"},
{"Gateway", "Gateway"},
{"TunnelID", "TunnelID"},
{"EndDate", "Data di fine"},
{"not floodfill", "no floodfill"},
{"Queue size", "Dimensione della coda"},
{"Run peer test", "Esegui il test dei peer"},
{"Decline transit tunnels", "Rifiuta tunnel di transito"},
{"Accept transit tunnels", "Accetta tunnel di transito"},
{"Cancel graceful shutdown", "Annulla l'interruzione controllata"},
{"Start graceful shutdown", "Avvia l'interruzione controllata"},
{"Force shutdown", "Forza l'arresto"},
{"Reload external CSS styles", "Ricarica gli stili CSS esterni"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Nota:</b> qualsiasi azione effettuata qui non è persistente e non modifica i file di configurazione."},
{"Logging level", "Livello di log"},
{"Transit tunnels limit", "Limite di tunnel di transito"},
{"Change", "Modifica"},
{"Change language", "Modifica linguaggio"},
{"no transit tunnels currently built", "Attualmente non ci sono tunnel di transito instaurati"},
{"SAM disabled", "SAM disabilitato"},
{"no sessions currently running", "Attualmente non ci sono sessioni attive"},
{"SAM session not found", "Sessione SAM non trovata"},
{"SAM Session", "Sessione SAM"},
{"Server Tunnels", "Tunnel server"},
{"Client Forwards", "Client di inoltro"},
{"Server Forwards", "Server di inoltro"},
{"Unknown page", "Pagina sconosciuta"},
{"Invalid token", "Token non valido"},
{"SUCCESS", "SUCCESSO"},
{"Stream closed", "Flusso terminato"},
{"Stream not found or already was closed", "Il flusso non è stato trovato oppure è già stato terminato"},
{"Destination not found", "Destinazione non trovata"},
{"StreamID can't be null", "Lo StreamID non può essere null"},
{"Return to destination page", "Ritorna alla pagina di destinazione"},
{"You will be redirected in 5 seconds", "Verrai reindirizzato in 5 secondi"},
{"Transit tunnels count must not exceed 65535", "Il numero di tunnel di transito non può superare i 65535"},
{"Back to commands list", "Ritorna alla lista dei comandi"},
{"Register at reg.i2p", "Registra a reg.i2p"},
{"Description", "Descrizione"},
{"A bit information about service on domain", "Alcune informazioni riguardo il servizio sul dominio"},
{"Submit", "Invia"},
{"Domain can't end with .b32.i2p", "I domini non possono terminare con .b32.i2p"},
{"Domain must end with .i2p", "I domini devono terminare con .i2p"},
{"Such destination is not found", "Questa destinazione non è stata trovata"},
{"Unknown command", "Comando sconosciuto"},
{"Command accepted", "Comando accettato"},
{"Proxy error", "Errore del proxy"},
{"Proxy info", "Informazioni del proxy"},
{"Proxy error: Host not found", "Errore del proxy: Host non trovato"},
{"Remote host not found in router's addressbook", "L'host remoto non è stato trovato nella rubrica del router"},
{"You may try to find this host on jump services below", "Si può provare a trovare questo host sui servizi di salto qui sotto"},
{"Invalid request", "Richiesta non valida"},
{"Proxy unable to parse your request", "Il proxy non è in grado di elaborare la tua richiesta"},
{"addresshelper is not supported", "addresshelper non è supportato"},
{"Host", "Host"},
{"added to router's addressbook from helper", "aggiunto alla rubrica tramite l'helper"},
{"Click here to proceed:", "Clicca qui per procedere:"},
{"Continue", "Continua"},
{"Addresshelper found", "Addresshelper trovato"},
{"already in router's addressbook", "già presente nella rubrica del router"},
{"Click here to update record:", "Clicca qui per aggiornare l'elemento:"},
{"invalid request uri", "uri della richiesta non valido"},
{"Can't detect destination host from request", "Impossibile determinare l'host di destinazione dalla richiesta"},
{"Outproxy failure", "Fallimento del proxy di uscita"},
{"bad outproxy settings", "impostazioni errate del proxy di uscita"},
{"not inside I2P network, but outproxy is not enabled", "non all'interno della rete I2P, ma il proxy di uscita non è abilitato"},
{"unknown outproxy url", "url del proxy di uscita sconosciuto"},
{"cannot resolve upstream proxy", "impossibile identificare il flusso a monte del proxy"},
{"hostname too long", "il nome dell'host è troppo lungo"},
{"cannot connect to upstream socks proxy", "impossibile connettersi al flusso a monte del proxy socks"},
{"Cannot negotiate with socks proxy", "Impossibile negoziare con il proxy socks"},
{"CONNECT error", "Errore di connessione"},
{"Failed to Connect", "Connessione fallita"},
{"socks proxy error", "errore del proxy socks"},
{"failed to send request to upstream", "invio della richiesta a monte non riuscito"},
{"No Reply From socks proxy", "Nessuna risposta dal proxy socks"},
{"cannot connect", "impossibile connettersi"},
{"http out proxy not implemented", "proxy http di uscita non implementato"},
{"cannot connect to upstream http proxy", "impossibile connettersi al proxy http a monte"},
{"Host is down", "L'host è offline"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Impossibile creare la connessione all'host richiesto, probabilmente è offline. Riprova più tardi."},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"days", {"giorno", "giorni"}},
{"hours", {"ora", "ore"}},
{"minutes", {"minuto", "minuti"}},
{"seconds", {"secondo", "secondi"}},
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

216
i18n/Spanish.cpp Normal file
View File

@ -0,0 +1,216 @@
/*
* Copyright (c) 2022, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// Spanish localization file
namespace i2p
{
namespace i18n
{
namespace spanish // language namespace
{
// language name in lowercase
static std::string language = "spanish";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
return n != 1 ? 1 : 0;
}
static std::map<std::string, std::string> strings
{
{"KiB", "KiB"},
{"MiB", "MiB"},
{"GiB", "GiB"},
{"building", "pendiente"},
{"failed", "fallido"},
{"expiring", "expiró"},
{"established", "establecido"},
{"unknown", "desconocido"},
{"exploratory", "exploratorio"},
{"Purple I2P Webconsole", "Consola web de Purple I2P"},
{"<b>i2pd</b> webconsole", "Consola web de <b>i2pd</b>"},
{"Main page", "Inicio"},
{"Router commands", "Comandos de enrutador"},
{"Local Destinations", "Destinos locales"},
{"LeaseSets", "LeaseSets"},
{"Tunnels", "Túneles"},
{"Transit Tunnels", "Túneles de Tránsito"},
{"Transports", "Transportes"},
{"I2P tunnels", "Túneles I2P"},
{"SAM sessions", "Sesiones SAM"},
{"ERROR", "ERROR"},
{"OK", "VALE"},
{"Testing", "Probando"},
{"Firewalled", "Con cortafuegos"},
{"Unknown", "Desconocido"},
{"Proxy", "Proxy"},
{"Mesh", "Malla"},
{"Error", "Error"},
{"Clock skew", "Reloj desfasado"},
{"Offline", "Desconectado"},
{"Symmetric NAT", "NAT simétrico"},
{"Uptime", "Tiempo en línea"},
{"Network status", "Estado de red"},
{"Network status v6", "Estado de red v6"},
{"Stopping in", "Parando en"},
{"Family", "Familia"},
{"Tunnel creation success rate", "Tasa de éxito de creación de túneles"},
{"Received", "Recibido"},
{"KiB/s", "KiB/s"},
{"Sent", "Enviado"},
{"Transit", "Tránsito"},
{"Data path", "Ruta de datos"},
{"Hidden content. Press on text to see.", "Contenido oculto. Presione para ver."},
{"Router Ident", "Ident del Enrutador"},
{"Router Family", "Familia de enrutador"},
{"Router Caps", "Atributos del Enrutador"},
{"Version", "Versión"},
{"Our external address", "Nuestra dirección externa"},
{"supported", "soportado"},
{"Routers", "Enrutadores"},
{"Floodfills", "Inundaciones"},
{"Client Tunnels", "Túneles de cliente"},
{"Services", "Servicios"},
{"Enabled", "Activado"},
{"Disabled", "Desactivado"},
{"Encrypted B33 address", "Dirección encriptada B33"},
{"Address registration line", "Línea para registrar direcciones"},
{"Domain", "Dominio"},
{"Generate", "Generar"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Nota:</b> la cadena resultante solo se puede usar para registrar dominios 2LD (ejemplo.i2p). Para registrar subdominios, por favor utilice i2pd-tools."},
{"Address", "Dirección"},
{"Type", "Tipo"},
{"EncType", "TipoEncrip"},
{"Inbound tunnels", "Túneles entrantes"},
{"ms", "ms"},
{"Outbound tunnels", "Túneles salientes"},
{"Tags", "Etiquetas"},
{"Incoming", "Entrante"},
{"Outgoing", "Saliente"},
{"Destination", "Destino"},
{"Amount", "Cantidad"},
{"Incoming Tags", "Etiquetas entrantes"},
{"Tags sessions", "Sesiones de etiquetas"},
{"Status", "Estado"},
{"Local Destination", "Destino Local"},
{"Streams", "Flujos"},
{"Close stream", "Cerrar flujo"},
{"I2CP session not found", "Sesión I2CP no encontrada"},
{"I2CP is not enabled", "I2CP no está activado"},
{"Invalid", "Inválido"},
{"Store type", "Tipo de almacenamiento"},
{"Expires", "Caduca"},
{"Non Expired Leases", "Sesiones No Expiradas"},
{"Gateway", "Puerta de enlace"},
{"TunnelID", "TunnelID"},
{"EndDate", "FechaVenc"},
{"not floodfill", "no inundado"},
{"Queue size", "Tamaño de cola"},
{"Run peer test", "Ejecutar prueba de par"},
{"Decline transit tunnels", "Rechazar túneles de tránsito"},
{"Accept transit tunnels", "Aceptar túneles de tránsito"},
{"Cancel graceful shutdown", "Cancelar apagado con gracia"},
{"Start graceful shutdown", "Iniciar apagado con gracia"},
{"Force shutdown", "Forzar apagado"},
{"Reload external CSS styles", "Recargar estilos CSS externos"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Nota:</b> cualquier acción hecha aquí no es persistente y no cambia tus archivos de configuración."},
{"Logging level", "Nivel de registro de errores"},
{"Transit tunnels limit", "Límite de túneles de tránsito"},
{"Change", "Cambiar"},
{"Change language", "Cambiar idioma"},
{"no transit tunnels currently built", "no hay túneles de tránsito actualmente construidos"},
{"SAM disabled", "SAM desactivado"},
{"no sessions currently running", "no hay sesiones ejecutándose ahora"},
{"SAM session not found", "Sesión SAM no encontrada"},
{"SAM Session", "Sesión SAM"},
{"Server Tunnels", "Túneles de Servidor"},
{"Client Forwards", "Redirecciones de Cliente"},
{"Server Forwards", "Redirecciones de Servidor"},
{"Unknown page", "Página desconocida"},
{"Invalid token", "Token inválido"},
{"SUCCESS", "ÉXITO"},
{"Stream closed", "Transmisión cerrada"},
{"Stream not found or already was closed", "No se encontró la transmisión o ya se cerró"},
{"Destination not found", "Destino no encontrado"},
{"StreamID can't be null", "StreamID no puede ser nulo"},
{"Return to destination page", "Volver a la página de destino"},
{"You will be redirected in 5 seconds", "Serás redirigido en 5 segundos"},
{"Transit tunnels count must not exceed 65535", "La cantidad de túneles de tránsito no puede exceder 65535"},
{"Back to commands list", "Volver a lista de comandos"},
{"Register at reg.i2p", "Registrar en reg.i2p"},
{"Description", "Descripción"},
{"A bit information about service on domain", "Un poco de información sobre el servicio en el dominio"},
{"Submit", "Enviar"},
{"Domain can't end with .b32.i2p", "El dominio no puede terminar con .b32.i2p"},
{"Domain must end with .i2p", "El dominio debe terminar con .i2p"},
{"Such destination is not found", "No se encontró el destino"},
{"Unknown command", "Comando desconocido"},
{"Command accepted", "Comando aceptado"},
{"Proxy error", "Error de proxy"},
{"Proxy info", "Información del proxy"},
{"Proxy error: Host not found", "Error de proxy: Host no encontrado"},
{"Remote host not found in router's addressbook", "Servidor remoto no encontrado en la libreta de direcciones del enrutador"},
{"You may try to find this host on jump services below", "Puede intentar encontrar este dominio en los siguientes servicios de salto"},
{"Invalid request", "Solicitud inválida"},
{"Proxy unable to parse your request", "Proxy no puede procesar su solicitud"},
{"addresshelper is not supported", "ayudante de dirección no soportado"},
{"Host", "Dominio"},
{"added to router's addressbook from helper", "añadido a la libreta de direcciones desde el ayudante"},
{"Click here to proceed:", "Haga clic aquí para continuar:"},
{"Continue", "Continuar"},
{"Addresshelper found", "Se encontró ayudante de dirección"},
{"already in router's addressbook", "ya se encontró en libreta de direcciones"},
{"Click here to update record:", "Haga clic aquí para actualizar el registro:"},
{"invalid request uri", "uri de solicitud inválida"},
{"Can't detect destination host from request", "No se puede detectar el host de destino de la solicitud"},
{"Outproxy failure", "Fallo en el proxy saliente"},
{"bad outproxy settings", "configuración de outproxy incorrecta"},
{"not inside I2P network, but outproxy is not enabled", "no está dentro de la red I2P, pero el proxy de salida no está activado"},
{"unknown outproxy url", "url de proxy outproxy desconocido"},
{"cannot resolve upstream proxy", "no se puede resolver el proxy de upstream"},
{"hostname too long", "nombre de dominio muy largo"},
{"cannot connect to upstream socks proxy", "no se puede conectar al proxy socks principal"},
{"Cannot negotiate with socks proxy", "No se puede negociar con el proxy socks"},
{"CONNECT error", "Error de CONNECT"},
{"Failed to Connect", "Error al Conectar"},
{"socks proxy error", "error de proxy socks"},
{"failed to send request to upstream", "no se pudo enviar petición al principal"},
{"No Reply From socks proxy", "Sin respuesta del proxy socks"},
{"cannot connect", "no se puede conectar"},
{"http out proxy not implemented", "proxy externo http no implementado"},
{"cannot connect to upstream http proxy", "no se puede conectar al proxy http principal"},
{"Host is down", "Servidor caído"},
{"Can't create connection to requested host, it may be down. Please try again later.", "No se puede crear la conexión al servidor solicitado, puede estar caído. Intente de nuevo más tarde."},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"days", {"día", "días"}},
{"hours", {"hora", "horas"}},
{"minutes", {"minuto", "minutos"}},
{"seconds", {"segundo", "segundos"}},
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

View File

@ -64,7 +64,7 @@ namespace config {
("bandwidth", value<std::string>()->default_value(""), "Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") ("bandwidth", value<std::string>()->default_value(""), "Transit traffic bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)")
("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") ("share", value<int>()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)")
("ntcp", bool_switch()->default_value(false), "Ignored. Always false") ("ntcp", bool_switch()->default_value(false), "Ignored. Always false")
("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)") ("ssu", bool_switch()->default_value(false), "Enable SSU transport (default: disabled)")
("ntcpproxy", value<std::string>()->default_value(""), "Ignored") ("ntcpproxy", value<std::string>()->default_value(""), "Ignored")
#ifdef _WIN32 #ifdef _WIN32
("svcctl", value<std::string>()->default_value(""), "Ignored") ("svcctl", value<std::string>()->default_value(""), "Ignored")
@ -274,9 +274,12 @@ namespace config {
options_description ssu2("SSU2 Options"); options_description ssu2("SSU2 Options");
ssu2.add_options() ssu2.add_options()
("ssu2.enabled", value<bool>()->default_value(false), "Enable SSU2 (default: disabled)") ("ssu2.enabled", value<bool>()->default_value(true), "Enable SSU2 (default: enabled)")
("ssu2.published", value<bool>()->default_value(false), "Publish SSU2 (default: disabled)") ("ssu2.published", value<bool>()->default_value(true), "Publish SSU2 (default: enabled)")
("ssu2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming SSU2 packets (default: auto)") ("ssu2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming SSU2 packets (default: auto)")
("ssu2.mtu4", value<uint16_t>()->default_value(0), "MTU for ipv4 address (default: detect)")
("ssu2.mtu6", value<uint16_t>()->default_value(0), "MTU for ipv6 address (default: detect)")
("ssu2.proxy", value<std::string>()->default_value(""), "Socks5 proxy URL for SSU2 transport")
; ;
options_description nettime("Time sync options"); options_description nettime("Time sync options");

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -15,6 +15,7 @@
#include <map> #include <map>
#include <vector> #include <vector>
#include "Base.h" #include "Base.h"
#include "Gzip.h"
#include "Identity.h" #include "Identity.h"
#include "LeaseSet.h" #include "LeaseSet.h"
#include "I2NPProtocol.h" #include "I2NPProtocol.h"

View File

@ -1096,7 +1096,13 @@ namespace client
} }
auto leaseSet = FindLeaseSet (dest); auto leaseSet = FindLeaseSet (dest);
if (leaseSet) if (leaseSet)
streamRequestComplete(CreateStream (leaseSet, port)); {
auto stream = CreateStream (leaseSet, port);
GetService ().post ([streamRequestComplete, stream]()
{
streamRequestComplete(stream);
});
}
else else
{ {
auto s = GetSharedFromThis (); auto s = GetSharedFromThis ();
@ -1129,6 +1135,35 @@ namespace client
}); });
} }
template<typename Dest>
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStreamSync (const Dest& dest, int port)
{
std::shared_ptr<i2p::stream::Stream> stream;
std::condition_variable streamRequestComplete;
std::mutex streamRequestCompleteMutex;
std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
CreateStream (
[&streamRequestComplete, &streamRequestCompleteMutex, &stream](std::shared_ptr<i2p::stream::Stream> s)
{
stream = s;
std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
streamRequestComplete.notify_all ();
},
dest, port);
streamRequestComplete.wait (l);
return stream;
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (const i2p::data::IdentHash& dest, int port)
{
return CreateStreamSync (dest, port);
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port)
{
return CreateStreamSync (dest, port);
}
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port) std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
{ {
if (m_StreamingDestination) if (m_StreamingDestination)

View File

@ -247,6 +247,8 @@ namespace client
// following methods operate with default streaming destination // following methods operate with default streaming destination
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0); void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0); void CreateStream (StreamRequestComplete streamRequestComplete, std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0);
std::shared_ptr<i2p::stream::Stream> CreateStream (const i2p::data::IdentHash& dest, int port = 0); // sync
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, int port = 0); // sync
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0); std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
void SendPing (const i2p::data::IdentHash& to); void SendPing (const i2p::data::IdentHash& to);
void SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to); void SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to);
@ -282,6 +284,9 @@ namespace client
void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey); void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey);
void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params); void ReadAuthKey (const std::string& group, const std::map<std::string, std::string> * params);
template<typename Dest>
std::shared_ptr<i2p::stream::Stream> CreateStreamSync (const Dest& dest, int port);
private: private:
i2p::data::PrivateKeys m_Keys; i2p::data::PrivateKeys m_Keys;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -135,6 +135,7 @@ namespace http
? url.substr(pos_p, std::string::npos) ? url.substr(pos_p, std::string::npos)
: url.substr(pos_p, pos_c - pos_p); : url.substr(pos_p, pos_c - pos_p);
/* stoi throws exception on failure, we don't need it */ /* stoi throws exception on failure, we don't need it */
port = 0;
for (char c : port_str) { for (char c : port_str) {
if (c < '0' || c > '9') if (c < '0' || c > '9')
return false; return false;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -49,13 +49,30 @@ namespace data
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType) IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type, CryptoKeyType cryptoType)
{ {
/*uint8_t randomPaddingBlock[32];
RAND_bytes (randomPaddingBlock, 32);*/
if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) if (cryptoType == CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)
{
/*memcpy (m_StandardIdentity.publicKey, publicKey ? publicKey : randomPaddingBlock, 32);
for (int i = 0; i < 7; i++) // 224 bytes
memcpy (m_StandardIdentity.publicKey + 32*(i + 1), randomPaddingBlock, 32);*/
if (publicKey)
{ {
memcpy (m_StandardIdentity.publicKey, publicKey, 32); memcpy (m_StandardIdentity.publicKey, publicKey, 32);
RAND_bytes (m_StandardIdentity.publicKey + 32, 224); RAND_bytes (m_StandardIdentity.publicKey + 32, 224);
} }
else else
RAND_bytes (m_StandardIdentity.publicKey, 256);
}
else
{
if (publicKey)
memcpy (m_StandardIdentity.publicKey, publicKey, 256); memcpy (m_StandardIdentity.publicKey, publicKey, 256);
else
RAND_bytes (m_StandardIdentity.publicKey, 256);
/*for (int i = 0; i < 8; i++) // 256 bytes
memcpy (m_StandardIdentity.publicKey + 32*i, randomPaddingBlock, 32);*/
}
if (type != SIGNING_KEY_TYPE_DSA_SHA1) if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{ {
size_t excessLen = 0; size_t excessLen = 0;
@ -93,7 +110,9 @@ namespace data
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519: case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
{ {
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32 size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
RAND_bytes (m_StandardIdentity.signingKey, padding); /*for (int i = 0; i < 3; i++) // 96 bytes
memcpy (m_StandardIdentity.signingKey + 32*i, randomPaddingBlock, 32);*/
RAND_bytes (m_StandardIdentity.signingKey, 96);
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH); memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
break; break;
} }
@ -695,7 +714,7 @@ namespace data
return nullptr; return nullptr;
} }
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type, CryptoKeyType cryptoType) PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type, CryptoKeyType cryptoType, bool isDestination)
{ {
if (type != SIGNING_KEY_TYPE_DSA_SHA1) if (type != SIGNING_KEY_TYPE_DSA_SHA1)
{ {
@ -705,9 +724,12 @@ namespace data
GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, signingPublicKey); GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, signingPublicKey);
// encryption // encryption
uint8_t publicKey[256]; uint8_t publicKey[256];
if (isDestination)
RAND_bytes (keys.m_PrivateKey, 256);
else
GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey); GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey);
// identity // identity
keys.m_Public = std::make_shared<IdentityEx> (publicKey, signingPublicKey, type, cryptoType); keys.m_Public = std::make_shared<IdentityEx> (isDestination ? nullptr : publicKey, signingPublicKey, type, cryptoType);
keys.CreateSigner (); keys.CreateSigner ();
return keys; return keys;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -171,7 +171,7 @@ namespace data
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const; std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const;
static std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key); static std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (CryptoKeyType cryptoType, const uint8_t * key);
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL); static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1, CryptoKeyType cryptoType = CRYPTO_KEY_TYPE_ELGAMAL, bool isDestination = false);
static void GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub); static void GenerateSigningKeyPair (SigningKeyType type, uint8_t * priv, uint8_t * pub);
static void GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub); // priv and pub are 256 bytes long static void GenerateCryptoKeyPair (CryptoKeyType type, uint8_t * priv, uint8_t * pub); // priv and pub are 256 bytes long
static i2p::crypto::Signer * CreateSigner (SigningKeyType keyType, const uint8_t * priv); static i2p::crypto::Signer * CreateSigner (SigningKeyType keyType, const uint8_t * priv);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -37,14 +37,7 @@ namespace data
void LeaseSet::Update (const uint8_t * buf, size_t len, bool verifySignature) void LeaseSet::Update (const uint8_t * buf, size_t len, bool verifySignature)
{ {
if (len > m_BufferLen) SetBuffer (buf, len);
{
auto oldBuffer = m_Buffer;
m_Buffer = new uint8_t[len];
delete[] oldBuffer;
}
memcpy (m_Buffer, buf, len);
m_BufferLen = len;
ReadFromBuffer (false, verifySignature); ReadFromBuffer (false, verifySignature);
} }
@ -59,9 +52,9 @@ namespace data
if (readIdentity || !m_Identity) if (readIdentity || !m_Identity)
m_Identity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen); m_Identity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
size_t size = m_Identity->GetFullLen (); size_t size = m_Identity->GetFullLen ();
if (size > m_BufferLen) if (size + 256 > m_BufferLen)
{ {
LogPrint (eLogError, "LeaseSet: Identity length ", size, " exceeds buffer size ", m_BufferLen); LogPrint (eLogError, "LeaseSet: Identity length ", int(size), " exceeds buffer size ", int(m_BufferLen));
m_IsValid = false; m_IsValid = false;
return; return;
} }
@ -74,7 +67,7 @@ namespace data
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
if (size + 1 > m_BufferLen) if (size + 1 > m_BufferLen)
{ {
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen); LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen));
m_IsValid = false; m_IsValid = false;
return; return;
} }
@ -89,7 +82,7 @@ namespace data
} }
if (size + num*LEASE_SIZE > m_BufferLen) if (size + num*LEASE_SIZE > m_BufferLen)
{ {
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen); LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen));
m_IsValid = false; m_IsValid = false;
return; return;
} }
@ -125,7 +118,7 @@ namespace data
auto signedSize = leases - m_Buffer; auto signedSize = leases - m_Buffer;
if (signedSize + m_Identity->GetSignatureLen () > m_BufferLen) if (signedSize + m_Identity->GetSignatureLen () > m_BufferLen)
{ {
LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", m_BufferLen); LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", int(m_BufferLen));
m_IsValid = false; m_IsValid = false;
} }
else if (!m_Identity->Verify (m_Buffer, signedSize, leases)) else if (!m_Identity->Verify (m_Buffer, signedSize, leases))
@ -172,7 +165,7 @@ namespace data
m_ExpirationTime = lease.endDate; m_ExpirationTime = lease.endDate;
if (m_StoreLeases) if (m_StoreLeases)
{ {
auto ret = m_Leases.insert (std::make_shared<Lease>(lease)); auto ret = m_Leases.insert (i2p::data::netdb.NewLease (lease));
if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing
(*ret.first)->isUpdated = true; (*ret.first)->isUpdated = true;
} }
@ -264,7 +257,17 @@ namespace data
void LeaseSet::SetBuffer (const uint8_t * buf, size_t len) void LeaseSet::SetBuffer (const uint8_t * buf, size_t len)
{ {
if (m_Buffer) delete[] m_Buffer; if (len > MAX_LS_BUFFER_SIZE)
{
LogPrint (eLogError, "LeaseSet: Buffer is too long ", len);
len = MAX_LS_BUFFER_SIZE;
}
if (m_Buffer && len > m_BufferLen)
{
delete[] m_Buffer;
m_Buffer = nullptr;
}
if (!m_Buffer)
m_Buffer = new uint8_t[len]; m_Buffer = new uint8_t[len];
m_BufferLen = len; m_BufferLen = len;
memcpy (m_Buffer, buf, len); memcpy (m_Buffer, buf, len);
@ -274,7 +277,7 @@ namespace data
{ {
if (len <= m_BufferLen) m_BufferLen = len; if (len <= m_BufferLen) m_BufferLen = len;
else else
LogPrint (eLogError, "LeaseSet2: Actual buffer size ", len , " exceeds full buffer size ", m_BufferLen); LogPrint (eLogError, "LeaseSet2: Actual buffer size ", int(len) , " exceeds full buffer size ", int(m_BufferLen));
} }
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto): LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto):
@ -320,7 +323,7 @@ namespace data
else else
identity = GetIdentity (); identity = GetIdentity ();
size_t offset = identity->GetFullLen (); size_t offset = identity->GetFullLen ();
if (offset + 8 >= len) return; if (offset + 8 > len) return;
m_PublishedTimestamp = bufbe32toh (buf + offset); offset += 4; // published timestamp (seconds) m_PublishedTimestamp = bufbe32toh (buf + offset); offset += 4; // published timestamp (seconds)
uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds) uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds)
SetExpirationTime ((m_PublishedTimestamp + expires)*1000LL); // in milliseconds SetExpirationTime ((m_PublishedTimestamp + expires)*1000LL); // in milliseconds
@ -364,6 +367,10 @@ namespace data
SetIsValid (verified); SetIsValid (verified);
} }
offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen (); offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen ();
if (offset > len) {
LogPrint (eLogWarning, "LeaseSet2: short buffer: wanted ", int(offset), "bytes, have ", int(len));
return;
}
SetBufferLen (offset); SetBufferLen (offset);
} }
@ -388,17 +395,17 @@ namespace data
// properties // properties
uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2; uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2;
offset += propertiesLen; // skip for now. TODO: implement properties offset += propertiesLen; // skip for now. TODO: implement properties
if (offset + 1 >= len) return 0;
// key sections // key sections
CryptoKeyType preferredKeyType = m_EncryptionType; CryptoKeyType preferredKeyType = m_EncryptionType;
bool preferredKeyFound = false; bool preferredKeyFound = false;
if (offset + 1 > len) return 0;
int numKeySections = buf[offset]; offset++; int numKeySections = buf[offset]; offset++;
for (int i = 0; i < numKeySections; i++) for (int i = 0; i < numKeySections; i++)
{ {
if (offset + 4 > len) return 0;
uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption key type uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption key type
if (offset + 2 >= len) return 0;
uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2; uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2;
if (offset + encryptionKeyLen >= len) return 0; if (offset + encryptionKeyLen > len) return 0;
if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only
{ {
// we pick first valid key if preferred not found // we pick first valid key if preferred not found
@ -413,7 +420,7 @@ namespace data
offset += encryptionKeyLen; offset += encryptionKeyLen;
} }
// leases // leases
if (offset + 1 >= len) return 0; if (offset + 1 > len) return 0;
int numLeases = buf[offset]; offset++; int numLeases = buf[offset]; offset++;
auto ts = i2p::util::GetMillisecondsSinceEpoch (); auto ts = i2p::util::GetMillisecondsSinceEpoch ();
if (IsStoreLeases ()) if (IsStoreLeases ())
@ -432,7 +439,8 @@ namespace data
} }
else else
offset += numLeases*LEASE2_SIZE; // 40 bytes per lease offset += numLeases*LEASE2_SIZE; // 40 bytes per lease
return offset;
return (offset > len ? 0 : offset);
} }
size_t LeaseSet2::ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len) size_t LeaseSet2::ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len)
@ -442,18 +450,18 @@ namespace data
uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2; uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2;
offset += propertiesLen; // skip for now. TODO: implement properties offset += propertiesLen; // skip for now. TODO: implement properties
// entries // entries
if (offset + 1 >= len) return 0; if (offset + 1 > len) return 0;
int numEntries = buf[offset]; offset++; int numEntries = buf[offset]; offset++;
for (int i = 0; i < numEntries; i++) for (int i = 0; i < numEntries; i++)
{ {
if (offset + 40 >= len) return 0; if (offset + LEASE2_SIZE > len) return 0;
offset += 32; // hash offset += 32; // hash
offset += 3; // flags offset += 3; // flags
offset += 1; // cost offset += 1; // cost
offset += 4; // expires offset += 4; // expires
} }
// revocations // revocations
if (offset + 1 >= len) return 0; if (offset + 1 > len) return 0;
int numRevocations = buf[offset]; offset++; int numRevocations = buf[offset]; offset++;
for (int i = 0; i < numRevocations; i++) for (int i = 0; i < numRevocations; i++)
{ {

View File

@ -1558,7 +1558,7 @@ namespace transport
case eSocksProxy: case eSocksProxy:
{ {
// TODO: support username/password auth etc // TODO: support username/password auth etc
static const uint8_t buff[3] = {0x05, 0x01, 0x00}; static const uint8_t buff[3] = {SOCKS5_VER, 0x01, 0x00};
boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(), boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(),
[] (const boost::system::error_code & ec, std::size_t transferred) [] (const boost::system::error_code & ec, std::size_t transferred)
{ {
@ -1672,21 +1672,21 @@ namespace transport
size_t sz = 6; // header + port size_t sz = 6; // header + port
auto buff = std::make_shared<std::vector<int8_t> >(256); auto buff = std::make_shared<std::vector<int8_t> >(256);
auto readbuff = std::make_shared<std::vector<int8_t> >(256); auto readbuff = std::make_shared<std::vector<int8_t> >(256);
(*buff)[0] = 0x05; (*buff)[0] = SOCKS5_VER;
(*buff)[1] = 0x01; (*buff)[1] = SOCKS5_CMD_CONNECT;
(*buff)[2] = 0x00; (*buff)[2] = 0x00;
auto& ep = conn->GetRemoteEndpoint (); auto& ep = conn->GetRemoteEndpoint ();
if(ep.address ().is_v4 ()) if(ep.address ().is_v4 ())
{ {
(*buff)[3] = 0x01; (*buff)[3] = SOCKS5_ATYP_IPV4;
auto addrbytes = ep.address ().to_v4().to_bytes(); auto addrbytes = ep.address ().to_v4().to_bytes();
sz += 4; sz += 4;
memcpy(buff->data () + 4, addrbytes.data(), 4); memcpy(buff->data () + 4, addrbytes.data(), 4);
} }
else if (ep.address ().is_v6 ()) else if (ep.address ().is_v6 ())
{ {
(*buff)[3] = 0x04; (*buff)[3] = SOCKS5_ATYP_IPV6;
auto addrbytes = ep.address ().to_v6().to_bytes(); auto addrbytes = ep.address ().to_v6().to_bytes();
sz += 16; sz += 16;
memcpy(buff->data () + 4, addrbytes.data(), 16); memcpy(buff->data () + 4, addrbytes.data(), 16);
@ -1708,22 +1708,24 @@ namespace transport
} }
}); });
boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), 10), boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff->data (), SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE), // read min reply size
boost::asio::transfer_all(),
[timer, conn, sz, readbuff](const boost::system::error_code & e, std::size_t transferred) [timer, conn, sz, readbuff](const boost::system::error_code & e, std::size_t transferred)
{ {
if (e) if (e)
{
LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message()); LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message());
} else if (!(*readbuff)[1]) // succeeded
else if(transferred == sz)
{
if((*readbuff)[1] == 0x00)
{ {
boost::system::error_code ec;
size_t moreBytes = conn->GetSocket ().available(ec);
if (moreBytes) // read remaining portion of reply if ipv6 received
boost::asio::read (conn->GetSocket (), boost::asio::buffer(readbuff->data (), moreBytes), boost::asio::transfer_all (), ec);
timer->cancel(); timer->cancel();
conn->ClientLogin(); conn->ClientLogin();
return; return;
} }
} else
LogPrint(eLogError, "NTCP2: Proxy reply error ", (int)(*readbuff)[1]);
timer->cancel(); timer->cancel();
conn->Terminate(); conn->Terminate();
}); });

View File

@ -240,11 +240,10 @@ namespace data
m_HiddenMode = hide; m_HiddenMode = hide;
} }
bool NetDb::AddRouterInfo (const uint8_t * buf, int len) std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len)
{ {
bool updated; bool updated;
AddRouterInfo (buf, len, updated); return AddRouterInfo (buf, len, updated);
return updated;
} }
std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len, bool& updated) std::shared_ptr<const RouterInfo> NetDb::AddRouterInfo (const uint8_t * buf, int len, bool& updated)
@ -272,7 +271,10 @@ namespace data
if (r->IsNewer (buf, len)) if (r->IsNewer (buf, len))
{ {
bool wasFloodfill = r->IsFloodfill (); bool wasFloodfill = r->IsFloodfill ();
{
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
r->Update (buf, len); r->Update (buf, len);
}
LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64()); LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64());
if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated
{ {
@ -436,12 +438,15 @@ namespace data
// try reseeding from floodfill first if specified // try reseeding from floodfill first if specified
std::string riPath; std::string riPath;
if(i2p::config::GetOption("reseed.floodfill", riPath)) { if(i2p::config::GetOption("reseed.floodfill", riPath))
{
auto ri = std::make_shared<RouterInfo>(riPath); auto ri = std::make_shared<RouterInfo>(riPath);
if (ri->IsFloodfill()) { if (ri->IsFloodfill())
{
const uint8_t * riData = ri->GetBuffer(); const uint8_t * riData = ri->GetBuffer();
int riLen = ri->GetBufferLen(); int riLen = ri->GetBufferLen();
if(!i2p::data::netdb.AddRouterInfo(riData, riLen)) { if (!i2p::data::netdb.AddRouterInfo(riData, riLen))
{
// bad router info // bad router info
LogPrint(eLogError, "NetDb: Bad router info"); LogPrint(eLogError, "NetDb: Bad router info");
return; return;
@ -623,7 +628,8 @@ namespace data
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) (it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
it.second->SetUnreachable (false); it.second->SetUnreachable (false);
// find & mark expired routers // find & mark expired routers
if (!it.second->IsReachable () && it.second->IsSSU (false)) if (!it.second->IsReachable () && (it.second->GetCompatibleTransports (true) & (RouterInfo::eSSUV4 | RouterInfo::eSSU2V4)))
// non-reachable router, but reachable by ipv4 SSU or SSU2 means introducers
{ {
if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL) if (ts > it.second->GetTimestamp () + NETDB_INTRODUCEE_EXPIRATION_TIMEOUT*1000LL)
// RouterInfo expires after 1 hour if uses introducer // RouterInfo expires after 1 hour if uses introducer
@ -743,6 +749,11 @@ namespace data
{ {
const uint8_t * buf = m->GetPayload (); const uint8_t * buf = m->GetPayload ();
size_t len = m->GetSize (); size_t len = m->GetSize ();
if (len < DATABASE_STORE_HEADER_SIZE)
{
LogPrint (eLogError, "NetDb: Database store msg is too short ", len, ". Dropped");
return;
}
IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET); IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET);
if (ident.IsZero ()) if (ident.IsZero ())
{ {
@ -753,6 +764,11 @@ namespace data
size_t offset = DATABASE_STORE_HEADER_SIZE; size_t offset = DATABASE_STORE_HEADER_SIZE;
if (replyToken) if (replyToken)
{ {
if (len < offset + 36) // 32 + 4
{
LogPrint (eLogError, "NetDb: Database store msg with reply token is too short ", len, ". Dropped");
return;
}
auto deliveryStatus = CreateDeliveryStatusMsg (replyToken); auto deliveryStatus = CreateDeliveryStatusMsg (replyToken);
uint32_t tunnelID = bufbe32toh (buf + offset); uint32_t tunnelID = bufbe32toh (buf + offset);
offset += 4; offset += 4;
@ -940,9 +956,9 @@ namespace data
} }
uint16_t numExcluded = bufbe16toh (excluded); uint16_t numExcluded = bufbe16toh (excluded);
excluded += 2; excluded += 2;
if (numExcluded > 512) if (numExcluded > 512 || (excluded - buf) + numExcluded*32 > (int)msg->GetPayloadLength ())
{ {
LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " exceeds 512"); LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " is too much");
return; return;
} }
@ -951,10 +967,11 @@ namespace data
{ {
LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded"); LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded");
std::set<IdentHash> excludedRouters; std::set<IdentHash> excludedRouters;
const uint8_t * excluded_ident = excluded;
for (int i = 0; i < numExcluded; i++) for (int i = 0; i < numExcluded; i++)
{ {
excludedRouters.insert (excluded); excludedRouters.insert (excluded_ident);
excluded += 32; excluded_ident += 32;
} }
std::vector<IdentHash> routers; std::vector<IdentHash> routers;
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
@ -1231,6 +1248,16 @@ namespace data
}); });
} }
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const
{
return GetRandomRouter (
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
{
return !router->IsHidden () && router->IsSSU2Introducer (v4) &&
!excluded.count (router->GetIdentHash ());
});
}
std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const
{ {
return GetRandomRouter ( return GetRandomRouter (
@ -1431,6 +1458,7 @@ namespace data
else else
++it; ++it;
} }
m_LeasesPool.CleanUpMt ();
} }
void NetDb::PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r) void NetDb::PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r)

View File

@ -69,7 +69,7 @@ namespace data
void Start (); void Start ();
void Stop (); void Stop ();
bool AddRouterInfo (const uint8_t * buf, int len); std::shared_ptr<const RouterInfo> AddRouterInfo (const uint8_t * buf, int len);
bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len); bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len); bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len);
bool AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType); bool AddLeaseSet2 (const IdentHash& ident, const uint8_t * buf, int len, uint8_t storeType);
@ -93,6 +93,7 @@ namespace data
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const; std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later
std::shared_ptr<const RouterInfo> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const; std::shared_ptr<const RouterInfo> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetRandomSSU2Introducer (bool v4, const std::set<IdentHash>& excluded) const;
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const; std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num, std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const; std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
@ -125,6 +126,7 @@ namespace data
void ClearRouterInfos () { m_RouterInfos.clear (); }; void ClearRouterInfos () { m_RouterInfos.clear (); };
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); }; std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); };
void PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r); void PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r);
std::shared_ptr<Lease> NewLease (const Lease& lease) { return m_LeasesPool.AcquireSharedMt (lease); };
uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; }; uint32_t GetPublishReplyToken () const { return m_PublishReplyToken; };
@ -181,6 +183,7 @@ namespace data
uint32_t m_PublishReplyToken = 0; uint32_t m_PublishReplyToken = 0;
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool; i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
i2p::util::MemoryPoolMt<Lease> m_LeasesPool;
}; };
extern NetDb netdb; extern NetDb netdb;

View File

@ -29,7 +29,7 @@ namespace i2p
RouterContext::RouterContext (): RouterContext::RouterContext ():
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false), m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown), m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown),
m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID) m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone), m_NetID (I2PD_NET_ID)
{ {
} }
@ -60,11 +60,7 @@ namespace i2p
i2p::data::LocalRouterInfo routerInfo; i2p::data::LocalRouterInfo routerInfo;
routerInfo.SetRouterIdentity (GetIdentity ()); routerInfo.SetRouterIdentity (GetIdentity ());
uint16_t port; i2p::config::GetOption("port", port); uint16_t port; i2p::config::GetOption("port", port);
if (!port) if (!port) port = SelectRandomPort ();
{
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
}
bool ipv4; i2p::config::GetOption("ipv4", ipv4); bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6); bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool ssu; i2p::config::GetOption("ssu", ssu); bool ssu; i2p::config::GetOption("ssu", ssu);
@ -121,7 +117,11 @@ namespace i2p
if (ssu2) if (ssu2)
{ {
if (ssu2Published) if (ssu2Published)
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), port); {
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v4::from_string (host), ssu2Port);
}
else else
{ {
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4; addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4;
@ -166,7 +166,11 @@ namespace i2p
if (ssu2) if (ssu2)
{ {
if (ssu2Published) if (ssu2Published)
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), port); {
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
routerInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address_v6::from_string (host), ssu2Port);
}
else else
{ {
if (!ipv4) // no other ssu2 addresses yet if (!ipv4) // no other ssu2 addresses yet
@ -192,6 +196,13 @@ namespace i2p
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
} }
uint16_t RouterContext::SelectRandomPort () const
{
uint16_t port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
return port;
}
void RouterContext::UpdateRouterInfo () void RouterContext::UpdateRouterInfo ()
{ {
m_RouterInfo.CreateBuffer (m_Keys); m_RouterInfo.CreateBuffer (m_Keys);
@ -225,6 +236,13 @@ namespace i2p
fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys)); fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys));
} }
bool RouterContext::IsSSU2Only () const
{
auto transports = m_RouterInfo.GetCompatibleTransports (false);
return (transports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) &&
!(transports & (i2p::data::RouterInfo::eSSUV4 | i2p::data::RouterInfo::eSSUV6));
}
void RouterContext::SetStatus (RouterStatus status) void RouterContext::SetStatus (RouterStatus status)
{ {
if (status != m_Status) if (status != m_Status)
@ -245,11 +263,18 @@ namespace i2p
} }
} }
void RouterContext::SetStatusSSU2 (RouterStatus status)
{
if (IsSSU2Only ())
SetStatus (status);
}
void RouterContext::SetStatusV6 (RouterStatus status) void RouterContext::SetStatusV6 (RouterStatus status)
{ {
if (status != m_StatusV6) if (status != m_StatusV6)
{ {
m_StatusV6 = status; m_StatusV6 = status;
m_ErrorV6 = eRouterErrorNone;
switch (m_StatusV6) switch (m_StatusV6)
{ {
case eRouterStatusOK: case eRouterStatusOK:
@ -264,12 +289,18 @@ namespace i2p
} }
} }
void RouterContext::SetStatusV6SSU2 (RouterStatus status)
{
if (IsSSU2Only ())
SetStatusV6 (status);
}
void RouterContext::UpdatePort (int port) void RouterContext::UpdatePort (int port)
{ {
bool updated = false; bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ()) for (auto& address : m_RouterInfo.GetAddresses ())
{ {
if (!address->IsNTCP2 () && !address->IsSSU2 () && address->port != port) if (address->port != port && (address->transportStyle == i2p::data::RouterInfo::eTransportSSU || IsSSU2Only ()))
{ {
address->port = port; address->port = port;
updated = true; updated = true;
@ -297,12 +328,7 @@ namespace i2p
} }
if (isAddr) if (isAddr)
{ {
if (!port && !address->port) if (!port && !address->port) port = SelectRandomPort ();
{
// select random port only if address's port is not set
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
if (port == 9150) port = 9151; // Tor browser
}
if (port) address->port = port; if (port) address->port = port;
address->published = publish; address->published = publish;
memcpy (address->i, m_NTCP2Keys->iv, 16); memcpy (address->i, m_NTCP2Keys->iv, 16);
@ -318,18 +344,23 @@ namespace i2p
{ {
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
bool found = false, updated = false; bool found = false, updated = false;
for (auto it = addresses.begin (); it != addresses.end (); ++it) for (auto it = addresses.begin (); it != addresses.end ();)
{ {
if ((*it)->IsNTCP2 ()) if ((*it)->IsNTCP2 ())
{ {
found = true; found = true;
if (!enable) if (enable)
{ {
addresses.erase (it); (*it)->s = m_NTCP2Keys->staticPublicKey;
memcpy ((*it)->i, m_NTCP2Keys->iv, 16);
it++;
}
else
it = addresses.erase (it);
updated = true; updated = true;
} }
break; else
} it++;
} }
if (enable && !found) if (enable && !found)
{ {
@ -342,14 +373,26 @@ namespace i2p
void RouterContext::PublishSSU2Address (int port, bool publish, bool v4, bool v6) void RouterContext::PublishSSU2Address (int port, bool publish, bool v4, bool v6)
{ {
if (!m_SSU2Keys || (publish && !port)) return; if (!m_SSU2Keys) return;
int newPort = 0;
if (!port)
{
for (const auto& address : m_RouterInfo.GetAddresses ())
if (address->port)
{
newPort = address->port;
break;
}
if (!newPort) newPort = SelectRandomPort ();
}
bool updated = false; bool updated = false;
for (auto& address : m_RouterInfo.GetAddresses ()) for (auto& address : m_RouterInfo.GetAddresses ())
{ {
if (address->IsSSU2 () && (address->port != port || address->published != publish) && if (address->IsSSU2 () && (!address->port || address->port != port || address->published != publish) &&
((v4 && address->IsV4 ()) || (v6 && address->IsV6 ()))) ((v4 && address->IsV4 ()) || (v6 && address->IsV6 ())))
{ {
address->port = port; if (port) address->port = port;
else if (!address->port) address->port = newPort;
address->published = publish; address->published = publish;
if (publish) if (publish)
address->caps |= (i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting); address->caps |= (i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting);
@ -366,27 +409,41 @@ namespace i2p
{ {
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
bool found = false, updated = false; bool found = false, updated = false;
for (auto it = addresses.begin (); it != addresses.end (); ++it) for (auto it = addresses.begin (); it != addresses.end ();)
{ {
if ((*it)->IsSSU2 ()) if ((*it)->IsSSU2 ())
{ {
found = true; found = true;
if (!enable) if (enable)
{ {
addresses.erase (it); (*it)->s = m_SSU2Keys->staticPublicKey;
(*it)->i = m_SSU2Keys->intro;
it++;
}
else
it = addresses.erase (it);
updated = true; updated = true;
} }
break; else
} it++;
} }
if (enable && !found) if (enable && !found)
{ {
uint8_t addressCaps = 0;
bool ipv4; i2p::config::GetOption("ipv4", ipv4); bool ipv4; i2p::config::GetOption("ipv4", ipv4);
bool ipv6; i2p::config::GetOption("ipv6", ipv6); bool ipv6; i2p::config::GetOption("ipv6", ipv6);
bool published; i2p::config::GetOption("ntcp2.published", published);
if (published)
{
if (ipv4) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV4);
if (ipv6) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV6);
}
else
{
uint8_t addressCaps = 0;
if (ipv4) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4; if (ipv4) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4;
if (ipv6) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6; if (ipv6) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addressCaps); m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addressCaps);
}
updated = true; updated = true;
} }
if (updated) if (updated)
@ -401,24 +458,31 @@ namespace i2p
if (address->host != host && address->IsCompatible (host) && if (address->host != host && address->IsCompatible (host) &&
!i2p::util::net::IsYggdrasilAddress (address->host)) !i2p::util::net::IsYggdrasilAddress (address->host))
{ {
// update host
address->host = host; address->host = host;
if (host.is_v6 () && address->transportStyle == i2p::data::RouterInfo::eTransportSSU) updated = true;
}
if (host.is_v6 () && address->IsV6 () && address->ssu &&
(!address->ssu->mtu || updated) && m_StatusV6 != eRouterStatusProxy)
{ {
// update MTU // update MTU
auto mtu = i2p::util::net::GetMTU (host); auto mtu = i2p::util::net::GetMTU (host);
if (mtu) if (mtu)
{ {
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu); LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
if (mtu > 1472) { // TODO: magic constant int maxMTU = i2p::util::net::GetMaxMTU (host.to_v6 ());
mtu = 1472; if (mtu > maxMTU)
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of 1472 bytes"); {
} mtu = maxMTU;
if (address->ssu) address->ssu->mtu = mtu; LogPrint(eLogWarning, "Router: MTU dropped to upper limit of ", maxMTU, " bytes");
}
} }
if (mtu && !address->IsSSU2 ()) // SSU1
mtu = (mtu >> 4) << 4; // round to multiple of 16
address->ssu->mtu = mtu;
updated = true; updated = true;
} }
} }
}
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL) if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL)
UpdateRouterInfo (); UpdateRouterInfo ();
@ -438,6 +502,37 @@ namespace i2p
UpdateRouterInfo (); UpdateRouterInfo ();
} }
bool RouterContext::AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4)
{
if (!IsSSU2Only ()) return false;
bool ret = m_RouterInfo.AddSSU2Introducer (introducer, v4);
if (ret)
UpdateRouterInfo ();
return ret;
}
void RouterContext::RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4)
{
if (!IsSSU2Only ()) return;
if (m_RouterInfo.RemoveSSU2Introducer (h, v4))
UpdateRouterInfo ();
}
void RouterContext::ClearSSU2Introducers (bool v4)
{
bool updated = false;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses)
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())) &&
addr->ssu && !addr->ssu->introducers.empty ())
{
addr->ssu->introducers.clear ();
updated = true;
}
if (updated)
UpdateRouterInfo ();
}
void RouterContext::SetFloodfill (bool floodfill) void RouterContext::SetFloodfill (bool floodfill)
{ {
m_IsFloodfill = floodfill; m_IsFloodfill = floodfill;
@ -538,6 +633,7 @@ namespace i2p
void RouterContext::RemoveNTCPAddress (bool v4only) void RouterContext::RemoveNTCPAddress (bool v4only)
{ {
bool updated = false;
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
for (auto it = addresses.begin (); it != addresses.end ();) for (auto it = addresses.begin (); it != addresses.end ();)
{ {
@ -545,11 +641,38 @@ namespace i2p
(!v4only || (*it)->host.is_v4 ())) (!v4only || (*it)->host.is_v4 ()))
{ {
it = addresses.erase (it); it = addresses.erase (it);
updated = true;
if (v4only) break; // otherwise might be more than one address if (v4only) break; // otherwise might be more than one address
} }
else else
++it; ++it;
} }
if (updated)
m_RouterInfo.UpdateSupportedTransports ();
}
void RouterContext::RemoveSSUAddress ()
{
bool updated = false;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto it = addresses.begin (); it != addresses.end ();)
{
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportSSU)
{
it = addresses.erase (it);
updated = true;
}
else
++it;
}
if (updated)
m_RouterInfo.UpdateSupportedTransports ();
}
void RouterContext::SetUnreachableSSU2 (bool v4, bool v6)
{
if (IsSSU2Only ())
SetUnreachable (v4, v6);
} }
void RouterContext::SetUnreachable (bool v4, bool v6) void RouterContext::SetUnreachable (bool v4, bool v6)
@ -568,7 +691,8 @@ namespace i2p
// delete previous introducers // delete previous introducers
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses) for (auto& addr : addresses)
if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) if (addr->ssu && (!addr->IsSSU2 () || IsSSU2Only ()) &&
((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
{ {
addr->published = false; addr->published = false;
addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer addr->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
@ -598,13 +722,18 @@ namespace i2p
} }
uint16_t port = 0; uint16_t port = 0;
// delete previous introducers // delete previous introducers
bool isSSU2Published = IsSSU2Only (); // TODO
if (isSSU2Published)
i2p::config::GetOption ("ssu2.published", isSSU2Published);
auto& addresses = m_RouterInfo.GetAddresses (); auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr : addresses) for (auto& addr : addresses)
if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ()))) if (addr->ssu && (!addr->IsSSU2 () || isSSU2Published) &&
((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
{ {
addr->published = true; addr->published = true;
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer; addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
addr->ssu->introducers.clear (); addr->ssu->introducers.clear ();
if (addr->port && (!addr->IsSSU2 () || IsSSU2Only ()))
port = addr->port; port = addr->port;
} }
// publish NTCP2 // publish NTCP2
@ -652,17 +781,18 @@ namespace i2p
} }
port = addr->port; port = addr->port;
} }
if (!port) i2p::config::GetOption("port", port); if (!port)
// SSU
if (!foundSSU)
{ {
i2p::config::GetOption("port", port);
if (!port) port = SelectRandomPort ();
}
// SSU
bool ssu; i2p::config::GetOption("ssu", ssu); bool ssu; i2p::config::GetOption("ssu", ssu);
if (ssu) if (!foundSSU && ssu)
{ {
std::string host = "::1"; // TODO: read host std::string host = "::1"; // TODO: read host
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr); m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
} }
}
// NTCP2 // NTCP2
if (!foundNTCP2) if (!foundNTCP2)
{ {
@ -695,6 +825,7 @@ namespace i2p
if (ssu2Published) if (ssu2Published)
{ {
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("::1"), ssu2Port); m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("::1"), ssu2Port);
} }
else else
@ -740,14 +871,16 @@ namespace i2p
} }
if (addr->port) port = addr->port; if (addr->port) port = addr->port;
} }
if (!port) i2p::config::GetOption("port", port); if (!port)
// SSU
if (!foundSSU)
{ {
bool ssu; i2p::config::GetOption("ssu", ssu); i2p::config::GetOption("port", port);
if (ssu) if (!port) port = SelectRandomPort ();
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
} }
// SSU
bool ssu; i2p::config::GetOption("ssu", ssu);
if (!foundSSU && ssu)
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
// NTCP2 // NTCP2
if (!foundNTCP2) if (!foundNTCP2)
{ {
@ -775,10 +908,11 @@ namespace i2p
if (ssu2Published) if (ssu2Published)
{ {
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port); uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("127.0.0.1"), ssu2Port); m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("127.0.0.1"), ssu2Port);
} }
else else
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV6); m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV4);
} }
} }
m_RouterInfo.EnableV4 (); m_RouterInfo.EnableV4 ();
@ -815,6 +949,43 @@ namespace i2p
UpdateRouterInfo (); UpdateRouterInfo ();
} }
void RouterContext::SetMTU (int mtu, bool v4)
{
if (mtu < 1280 || mtu > 1500) return;
auto& addresses = m_RouterInfo.GetAddresses ();
for (auto& addr: addresses)
{
if (addr->ssu && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
{
if (!addr->IsSSU2 ()) // SSU1
{
// round to multiple of 16
if (v4)
{
if (mtu > 1484) mtu = 1484;
else
{
mtu -= 12;
mtu = (mtu >> 4) << 4;
mtu += 12;
}
}
else
{
if (mtu > 1488) mtu = 1488;
else
mtu = (mtu >> 4) << 4;
}
}
if (mtu)
{
addr->ssu->mtu = mtu;
LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu);
}
}
}
}
void RouterContext::UpdateNTCP2V6Address (const boost::asio::ip::address& host) void RouterContext::UpdateNTCP2V6Address (const boost::asio::ip::address& host)
{ {
bool isYgg = i2p::util::net::IsYggdrasilAddress (host); bool isYgg = i2p::util::net::IsYggdrasilAddress (host);

View File

@ -37,10 +37,9 @@ namespace garlic
eRouterStatusOK = 0, eRouterStatusOK = 0,
eRouterStatusTesting = 1, eRouterStatusTesting = 1,
eRouterStatusFirewalled = 2, eRouterStatusFirewalled = 2,
eRouterStatusError = 3, eRouterStatusUnknown = 3,
eRouterStatusUnknown = 4, eRouterStatusProxy = 4,
eRouterStatusProxy = 5, eRouterStatusMesh = 5
eRouterStatusMesh = 6
}; };
enum RouterError enum RouterError
@ -103,10 +102,14 @@ namespace garlic
uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; }; uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; };
RouterStatus GetStatus () const { return m_Status; }; RouterStatus GetStatus () const { return m_Status; };
void SetStatus (RouterStatus status); void SetStatus (RouterStatus status);
void SetStatusSSU2 (RouterStatus status);
RouterError GetError () const { return m_Error; }; RouterError GetError () const { return m_Error; };
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; }; void SetError (RouterError error) { m_Error = error; };
RouterStatus GetStatusV6 () const { return m_StatusV6; }; RouterStatus GetStatusV6 () const { return m_StatusV6; };
void SetStatusV6 (RouterStatus status); void SetStatusV6 (RouterStatus status);
void SetStatusV6SSU2 (RouterStatus status);
RouterError GetErrorV6 () const { return m_ErrorV6; };
void SetErrorV6 (RouterError error) { m_ErrorV6 = error; };
int GetNetID () const { return m_NetID; }; int GetNetID () const { return m_NetID; };
void SetNetID (int netID) { m_NetID = netID; }; void SetNetID (int netID) { m_NetID = netID; };
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data); bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
@ -119,10 +122,15 @@ namespace garlic
void PublishSSU2Address (int port, bool publish, bool v4, bool v6); void PublishSSU2Address (int port, bool publish, bool v4, bool v6);
void UpdateSSU2Address (bool enable); void UpdateSSU2Address (bool enable);
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
void RemoveSSUAddress (); // delete SSU address for older routers
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer); bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e); void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4);
void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4);
void ClearSSU2Introducers (bool v4);
bool IsUnreachable () const; bool IsUnreachable () const;
void SetUnreachable (bool v4, bool v6); void SetUnreachable (bool v4, bool v6);
void SetUnreachableSSU2 (bool v4, bool v6);
void SetReachable (bool v4, bool v6); void SetReachable (bool v4, bool v6);
bool IsFloodfill () const { return m_IsFloodfill; }; bool IsFloodfill () const { return m_IsFloodfill; };
void SetFloodfill (bool floodfill); void SetFloodfill (bool floodfill);
@ -139,6 +147,7 @@ namespace garlic
void SetSupportsV6 (bool supportsV6); void SetSupportsV6 (bool supportsV6);
void SetSupportsV4 (bool supportsV4); void SetSupportsV4 (bool supportsV4);
void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host); void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host);
void SetMTU (int mtu, bool v4);
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
@ -173,8 +182,10 @@ namespace garlic
void UpdateRouterInfo (); void UpdateRouterInfo ();
void NewNTCP2Keys (); void NewNTCP2Keys ();
void NewSSU2Keys (); void NewSSU2Keys ();
bool IsSSU2Only () const; // SSU2 and no SSU
bool Load (); bool Load ();
void SaveKeys (); void SaveKeys ();
uint16_t SelectRandomPort () const;
bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize); bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize);
@ -190,7 +201,7 @@ namespace garlic
uint64_t m_BandwidthLimit; // allowed bandwidth uint64_t m_BandwidthLimit; // allowed bandwidth
int m_ShareRatio; int m_ShareRatio;
RouterStatus m_Status, m_StatusV6; RouterStatus m_Status, m_StatusV6;
RouterError m_Error; RouterError m_Error, m_ErrorV6;
int m_NetID; int m_NetID;
std::mutex m_GarlicMutex; std::mutex m_GarlicMutex;
std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys; std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;

View File

@ -257,20 +257,38 @@ namespace data
if (!ecode && !address->host.is_unspecified ()) isHost = true; if (!ecode && !address->host.is_unspecified ()) isHost = true;
} }
else if (!strcmp (key, "port")) else if (!strcmp (key, "port"))
{
try
{
address->port = boost::lexical_cast<int>(value); address->port = boost::lexical_cast<int>(value);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'port' exception ", ex.what ());
}
}
else if (!strcmp (key, "mtu")) else if (!strcmp (key, "mtu"))
{ {
if (address->ssu) if (address->ssu)
{
try
{
address->ssu->mtu = boost::lexical_cast<int>(value); address->ssu->mtu = boost::lexical_cast<int>(value);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'mtu' exception ", ex.what ());
}
}
else else
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP"); LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
} }
else if (!strcmp (key, "key")) else if (!strcmp (key, "key"))
{ {
if (address->ssu) if (address->ssu)
isIntroKey = (Base64ToByteStream (value, strlen (value), address->i, 32) == 32); isIntroKey = (Base64ToByteStream (value, strlen (value), address->i, 32) == 32);
else else
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP"); LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP2");
} }
else if (!strcmp (key, "caps")) else if (!strcmp (key, "caps"))
address->caps = ExtractAddressCaps (value); address->caps = ExtractAddressCaps (value);
@ -327,14 +345,41 @@ namespace data
introducer.iHost = boost::asio::ip::address::from_string (value, ecode); introducer.iHost = boost::asio::ip::address::from_string (value, ecode);
} }
else if (!strcmp (key, "iport")) else if (!strcmp (key, "iport"))
{
try
{
introducer.iPort = boost::lexical_cast<int>(value); introducer.iPort = boost::lexical_cast<int>(value);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'iport' exception ", ex.what ());
}
}
else if (!strcmp (key, "itag")) else if (!strcmp (key, "itag"))
{
try
{
introducer.iTag = boost::lexical_cast<uint32_t>(value); introducer.iTag = boost::lexical_cast<uint32_t>(value);
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ());
}
}
else if (!strcmp (key, "ikey") || !strcmp (key, "ih")) else if (!strcmp (key, "ikey") || !strcmp (key, "ih"))
Base64ToByteStream (value, strlen (value), introducer.iKey, 32); Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
else if (!strcmp (key, "iexp")) else if (!strcmp (key, "iexp"))
{
try
{
introducer.iExp = boost::lexical_cast<uint32_t>(value); introducer.iExp = boost::lexical_cast<uint32_t>(value);
} }
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'iexp' exception ", ex.what ());
}
}
}
if (!s) return; if (!s) return;
} }
if (address->transportStyle == eTransportNTCP) if (address->transportStyle == eTransportNTCP)
@ -708,6 +753,7 @@ namespace data
{ {
auto addr = std::make_shared<Address>(); auto addr = std::make_shared<Address>();
addr->transportStyle = eTransportSSU2; addr->transportStyle = eTransportSSU2;
addr->port = 0;
addr->caps = caps; addr->caps = caps;
addr->date = 0; addr->date = 0;
addr->ssu.reset (new SSUExt ()); addr->ssu.reset (new SSUExt ());
@ -727,7 +773,7 @@ namespace data
addr->host = host; addr->host = host;
addr->port = port; addr->port = port;
addr->published = true; addr->published = true;
addr->caps = 0; addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC;
addr->date = 0; addr->date = 0;
addr->ssu.reset (new SSUExt ()); addr->ssu.reset (new SSUExt ());
addr->ssu->mtu = 0; addr->ssu->mtu = 0;
@ -979,7 +1025,8 @@ namespace data
return GetAddress ( return GetAddress (
[key, isV6](std::shared_ptr<const RouterInfo::Address> address)->bool [key, isV6](std::shared_ptr<const RouterInfo::Address> address)->bool
{ {
return address->IsSSU2 () && !memcmp (address->s, key, 32) && address->IsV6 () == isV6; return address->IsSSU2 () && !memcmp (address->s, key, 32) &&
((isV6 && address->IsV6 ()) || (!isV6 && address->IsV4 ()));
}); });
} }
@ -1065,6 +1112,17 @@ namespace data
}); });
} }
bool RouterInfo::IsSSU2Introducer (bool v4) const
{
if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false;
return (bool)GetAddress (
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
{
return (address->IsSSU2 ()) && address->IsIntroducer () &&
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && !address->host.is_unspecified ();
});
}
void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports) void RouterInfo::SetUnreachableAddressesTransportCaps (uint8_t transports)
{ {
for (auto& addr: *m_Addresses) for (auto& addr: *m_Addresses)
@ -1296,7 +1354,7 @@ namespace data
else else
WriteString ("", s); WriteString ("", s);
if (isPublished) if (isPublished && !address.host.is_unspecified ())
{ {
WriteString ("host", properties); WriteString ("host", properties);
properties << '='; properties << '=';
@ -1399,7 +1457,7 @@ namespace data
properties << ';'; properties << ';';
} }
} }
if (isPublished || (address.ssu && !address.IsSSU2 ())) if ((isPublished || (address.ssu && !address.IsSSU2 ())) && address.port)
{ {
WriteString ("port", properties); WriteString ("port", properties);
properties << '='; properties << '=';
@ -1467,5 +1525,40 @@ namespace data
{ {
return std::make_shared<Buffer> (); return std::make_shared<Buffer> ();
} }
bool LocalRouterInfo::AddSSU2Introducer (const Introducer& introducer, bool v4)
{
for (auto& addr : GetAddresses ())
{
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
{
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)
{
for (auto& addr: GetAddresses ())
{
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
{
for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it)
if (h == it->iKey)
{
addr->ssu->introducers.erase (it);
if (addr->ssu->introducers.empty ())
SetReachableTransports (GetReachableTransports () & ~(addr->IsV4 () ? eSSU2V4 : eSSU2V6));
return true;
}
}
}
return false;
}
} }
} }

View File

@ -235,6 +235,7 @@ namespace data
bool IsPeerTesting (bool v4) const; bool IsPeerTesting (bool v4) const;
bool IsSSU2PeerTesting (bool v4) const; bool IsSSU2PeerTesting (bool v4) const;
bool IsIntroducer (bool v4) const; bool IsIntroducer (bool v4) const;
bool IsSSU2Introducer (bool v4) const;
uint8_t GetCaps () const { return m_Caps; }; uint8_t GetCaps () const { return m_Caps; };
void SetCaps (uint8_t caps) { m_Caps = caps; }; void SetCaps (uint8_t caps) { m_Caps = caps; };
@ -274,6 +275,8 @@ namespace data
void SetBufferLen (size_t len) { m_BufferLen = len; }; void SetBufferLen (size_t len) { m_BufferLen = len; };
void RefreshTimestamp (); void RefreshTimestamp ();
const Addresses& GetAddresses () const { return *m_Addresses; }; const Addresses& GetAddresses () const { return *m_Addresses; };
CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; };
void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; };
private: private:
@ -316,6 +319,9 @@ namespace data
std::string GetProperty (const std::string& key) const; std::string GetProperty (const std::string& key) const;
void ClearProperties () override { m_Properties.clear (); }; void ClearProperties () override { m_Properties.clear (); };
bool AddSSU2Introducer (const Introducer& introducer, bool v4);
bool RemoveSSU2Introducer (const IdentHash& h, bool v4);
private: private:
void WriteToStream (std::ostream& s) const; void WriteToStream (std::ostream& s) const;

View File

@ -6,6 +6,7 @@
* See full license text in LICENSE file at top of project tree * See full license text in LICENSE file at top of project tree
*/ */
#include <random>
#include "Log.h" #include "Log.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "Transports.h" #include "Transports.h"
@ -21,7 +22,9 @@ namespace transport
RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"), RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"),
m_SocketV4 (m_ReceiveService.GetService ()), m_SocketV6 (m_ReceiveService.GetService ()), 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_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()),
m_TerminationTimer (GetService ()), m_ResendTimer (GetService ()) m_TerminationTimer (GetService ()), m_ResendTimer (GetService ()),
m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()),
m_IsPublished (true), m_IsSyncClockFromPeers (true), m_IsThroughProxy (false)
{ {
} }
@ -30,6 +33,8 @@ namespace transport
if (!IsRunning ()) if (!IsRunning ())
{ {
StartIOService (); StartIOService ();
i2p::config::GetOption ("ssu2.published", m_IsPublished);
i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers);
bool found = false; bool found = false;
auto& addresses = i2p::context.GetRouterInfo ().GetAddresses (); auto& addresses = i2p::context.GetRouterInfo ().GetAddresses ();
for (const auto& address: addresses) for (const auto& address: addresses)
@ -37,6 +42,25 @@ namespace transport
if (!address) continue; if (!address) continue;
if (address->transportStyle == i2p::data::RouterInfo::eTransportSSU2) 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; auto port = address->port;
if (!port) if (!port)
{ {
@ -44,8 +68,9 @@ namespace transport
if (ssu2Port) port = ssu2Port; if (ssu2Port) port = ssu2Port;
else else
{ {
bool ssu; i2p::config::GetOption("ssu", ssu);
uint16_t p; i2p::config::GetOption ("port", p); uint16_t p; i2p::config::GetOption ("port", p);
if (p) port = p; if (p) port = ssu ? (p + 1) : p;
} }
} }
if (port) if (port)
@ -59,6 +84,7 @@ namespace transport
{ {
Receive (m_SocketV4); Receive (m_SocketV4);
}); });
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
} }
if (address->IsV6 ()) if (address->IsV6 ())
{ {
@ -69,6 +95,7 @@ namespace transport
{ {
Receive (m_SocketV6); Receive (m_SocketV6);
}); });
ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers
} }
} }
else else
@ -76,41 +103,87 @@ namespace transport
} }
} }
if (found) if (found)
{
if (m_IsThroughProxy)
ConnectToProxy ();
m_ReceiveService.Start (); m_ReceiveService.Start ();
}
ScheduleTermination (); ScheduleTermination ();
ScheduleResend (false);
} }
} }
void SSU2Server::Stop () void SSU2Server::Stop ()
{ {
for (auto& it: m_Sessions) if (IsRunning ())
{
m_TerminationTimer.cancel ();
m_ResendTimer.cancel ();
m_IntroducersUpdateTimer.cancel ();
m_IntroducersUpdateTimerV6.cancel ();
}
auto sessions = m_Sessions;
for (auto& it: sessions)
{
it.second->RequestTermination (eSSU2TerminationReasonRouterShutdown);
it.second->Done (); it.second->Done ();
m_Sessions.clear (); }
m_SessionsByRouterHash.clear ();
m_PendingOutgoingSessions.clear ();
if (context.SupportsV4 () || context.SupportsV6 ()) if (context.SupportsV4 () || context.SupportsV6 ())
m_ReceiveService.Stop (); m_ReceiveService.Stop ();
m_SocketV4.close (); m_SocketV4.close ();
m_SocketV6.close (); m_SocketV6.close ();
if (IsRunning ())
m_TerminationTimer.cancel (); if (m_UDPAssociateSocket)
{
m_UDPAssociateSocket->close ();
m_UDPAssociateSocket.reset (nullptr);
}
StopIOService (); StopIOService ();
m_Sessions.clear ();
m_SessionsByRouterHash.clear ();
m_PendingOutgoingSessions.clear ();
m_Relays.clear ();
m_Introducers.clear ();
m_IntroducersV6.clear ();
} }
void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
{ {
if (localAddress.is_unspecified ()) return; if (localAddress.is_unspecified ()) return;
if (localAddress.is_v4 ()) if (localAddress.is_v4 ())
{
m_AddressV4 = localAddress; 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 ()) else if (localAddress.is_v6 ())
{
m_AddressV6 = localAddress; 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 bool SSU2Server::IsSupported (const boost::asio::ip::address& addr) const
{ {
if (m_IsThroughProxy)
return m_SocketV4.is_open ();
if (addr.is_v4 ()) if (addr.is_v4 ())
{ {
if (m_SocketV4.is_open ()) if (m_SocketV4.is_open ())
@ -124,6 +197,14 @@ namespace transport
return false; 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 ();
}
boost::asio::ip::udp::socket& SSU2Server::OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint) 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; boost::asio::ip::udp::socket& socket = localEndpoint.address ().is_v6 () ? m_SocketV6 : m_SocketV4;
@ -148,7 +229,7 @@ namespace transport
void SSU2Server::Receive (boost::asio::ip::udp::socket& socket) void SSU2Server::Receive (boost::asio::ip::udp::socket& socket)
{ {
Packet * packet = m_PacketsPool.AcquireMt (); Packet * packet = m_PacketsPool.AcquireMt ();
socket.async_receive_from (boost::asio::buffer (packet->buf, SSU2_MTU), packet->from, 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))); std::bind (&SSU2Server::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet, std::ref (socket)));
} }
@ -169,7 +250,7 @@ namespace transport
while (moreBytes && packets.size () < 32) while (moreBytes && packets.size () < 32)
{ {
packet = m_PacketsPool.AcquireMt (); packet = m_PacketsPool.AcquireMt ();
packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MTU), packet->from, 0, ec); packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec);
if (!ec) if (!ec)
{ {
i2p::transport::transports.UpdateReceivedBytes (packet->len); i2p::transport::transports.UpdateReceivedBytes (packet->len);
@ -196,6 +277,15 @@ namespace transport
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
LogPrint (eLogError, "SSU2: Receive error: code ", ecode.value(), ": ", ecode.message ()); 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 (); auto ep = socket.local_endpoint ();
socket.close (); socket.close ();
OpenSocket (ep); OpenSocket (ep);
@ -203,23 +293,33 @@ namespace transport
} }
} }
} }
}
void SSU2Server::HandleReceivedPacket (Packet * packet) void SSU2Server::HandleReceivedPacket (Packet * packet)
{ {
if (packet) if (packet)
{ {
if (m_IsThroughProxy)
ProcessNextPacketFromProxy (packet->buf, packet->len);
else
ProcessNextPacket (packet->buf, packet->len, packet->from); ProcessNextPacket (packet->buf, packet->len, packet->from);
m_PacketsPool.ReleaseMt (packet); m_PacketsPool.ReleaseMt (packet);
if (m_LastSession) m_LastSession->FlushData (); if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
m_LastSession->FlushData ();
} }
} }
void SSU2Server::HandleReceivedPackets (std::vector<Packet *> packets) void SSU2Server::HandleReceivedPackets (std::vector<Packet *> packets)
{ {
if (m_IsThroughProxy)
for (auto& packet: packets)
ProcessNextPacketFromProxy (packet->buf, packet->len);
else
for (auto& packet: packets) for (auto& packet: packets)
ProcessNextPacket (packet->buf, packet->len, packet->from); ProcessNextPacket (packet->buf, packet->len, packet->from);
m_PacketsPool.ReleaseMt (packets); m_PacketsPool.ReleaseMt (packets);
if (m_LastSession) m_LastSession->FlushData (); if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
m_LastSession->FlushData ();
} }
void SSU2Server::AddSession (std::shared_ptr<SSU2Session> session) void SSU2Server::AddSession (std::shared_ptr<SSU2Session> session)
@ -239,6 +339,8 @@ namespace transport
auto ident = it->second->GetRemoteIdentity (); auto ident = it->second->GetRemoteIdentity ();
if (ident) if (ident)
m_SessionsByRouterHash.erase (ident->GetIdentHash ()); m_SessionsByRouterHash.erase (ident->GetIdentHash ());
if (m_LastSession == it->second)
m_LastSession = nullptr;
m_Sessions.erase (it); m_Sessions.erase (it);
} }
} }
@ -254,9 +356,9 @@ namespace transport
if (!ret.second) if (!ret.second)
{ {
// session already exists // session already exists
LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " aready exists"); LogPrint (eLogWarning, "SSU2: Session to ", ident->GetIdentHash ().ToBase64 (), " already exists");
// terminate existing // terminate existing
GetService ().post (std::bind (&SSU2Session::Terminate, ret.first->second)); GetService ().post (std::bind (&SSU2Session::RequestTermination, ret.first->second, eSSU2TerminationReasonReplacedByNewSession));
// update session // update session
ret.first->second = session; ret.first->second = session;
} }
@ -363,21 +465,25 @@ namespace transport
{ {
case eSSU2SessionStateEstablished: case eSSU2SessionStateEstablished:
case eSSU2SessionStateSessionConfirmedSent: case eSSU2SessionStateSessionConfirmedSent:
m_LastSession->ProcessData (buf, len); m_LastSession->ProcessData (buf, len, senderEndpoint);
break; break;
case eSSU2SessionStateSessionCreatedSent: case eSSU2SessionStateSessionCreatedSent:
m_LastSession->ProcessSessionConfirmed (buf, len); if (!m_LastSession->ProcessSessionConfirmed (buf, len))
{
m_LastSession->Done ();
m_LastSession = nullptr;
}
break; break;
case eSSU2SessionStateIntroduced: case eSSU2SessionStateIntroduced:
if (m_LastSession->GetRemoteEndpoint ().address ().is_unspecified ()) if (m_LastSession->GetRemoteEndpoint ().address ().is_unspecified ())
m_LastSession->SetRemoteEndpoint (senderEndpoint); m_LastSession->SetRemoteEndpoint (senderEndpoint);
if (m_LastSession->GetRemoteEndpoint () == senderEndpoint) if (m_LastSession->GetRemoteEndpoint ().address () == senderEndpoint.address ()) // port might be different
m_LastSession->ProcessHolePunch (buf, len); m_LastSession->ProcessHolePunch (buf, len);
else else
{ {
LogPrint (eLogWarning, "SSU2: HolePunch endpoint ", senderEndpoint, LogPrint (eLogWarning, "SSU2: HolePunch address ", senderEndpoint.address (),
" doesn't match RelayResponse ", m_LastSession->GetRemoteEndpoint ()); " doesn't match RelayResponse ", m_LastSession->GetRemoteEndpoint ().address ());
m_LastSession->Terminate (); m_LastSession->Done ();
m_LastSession = nullptr; m_LastSession = nullptr;
} }
break; break;
@ -385,6 +491,11 @@ namespace transport
m_LastSession->SetRemoteEndpoint (senderEndpoint); m_LastSession->SetRemoteEndpoint (senderEndpoint);
m_LastSession->ProcessPeerTest (buf, len); m_LastSession->ProcessPeerTest (buf, len);
break; break;
case eSSU2SessionStateClosing:
m_LastSession->ProcessData (buf, len, senderEndpoint); // we might receive termintaion block
if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated)
m_LastSession->RequestTermination (eSSU2TerminationReasonIdleTimeout); // send termination again
break;
case eSSU2SessionStateTerminated: case eSSU2SessionStateTerminated:
m_LastSession = nullptr; m_LastSession = nullptr;
break; break;
@ -417,6 +528,11 @@ namespace transport
void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen, void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen,
const boost::asio::ip::udp::endpoint& to) const boost::asio::ip::udp::endpoint& to)
{ {
if (m_IsThroughProxy)
{
SendThroughProxy (header, headerLen, nullptr, 0, payload, payloadLen, to);
return;
}
std::vector<boost::asio::const_buffer> bufs std::vector<boost::asio::const_buffer> bufs
{ {
boost::asio::buffer (header, headerLen), boost::asio::buffer (header, headerLen),
@ -436,6 +552,11 @@ namespace transport
void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, 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) 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<boost::asio::const_buffer> bufs std::vector<boost::asio::const_buffer> bufs
{ {
boost::asio::buffer (header, headerLen), boost::asio::buffer (header, headerLen),
@ -459,10 +580,23 @@ namespace transport
{ {
if (router && address) if (router && address)
{ {
// check is no peding session // check if no session
auto it = m_SessionsByRouterHash.find (router->GetIdentHash ());
if (it != m_SessionsByRouterHash.end ())
{
// session with router found, trying to send peer test if requested
if (peerTest && it->second->IsEstablished ())
{
auto session = it->second;
GetService ().post ([session]() { session->SendPeerTest (); });
}
return false;
}
// check is no pending session
bool isValidEndpoint = !address->host.is_unspecified () && address->port; bool isValidEndpoint = !address->host.is_unspecified () && address->port;
if (isValidEndpoint) if (isValidEndpoint)
{ {
if (i2p::util::net::IsInReservedRange(address->host)) return false;
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port)); auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port));
if (s) if (s)
{ {
@ -519,18 +653,27 @@ namespace transport
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
std::shared_ptr<i2p::data::RouterInfo> r; std::shared_ptr<i2p::data::RouterInfo> r;
uint32_t relayTag = 0; uint32_t relayTag = 0;
for (auto& it: address->ssu->introducers) if (!address->ssu->introducers.empty ())
{ {
if (it.iTag && ts < it.iExp) std::vector<int> indicies;
for (int i = 0; i < (int)address->ssu->introducers.size (); i++) indicies.push_back(i);
if (indicies.size () > 1)
std::shuffle (indicies.begin(), indicies.end(), std::mt19937(std::random_device()()));
for (auto i: indicies)
{ {
r = i2p::data::netdb.FindRouter (it.iKey); const auto& introducer = address->ssu->introducers[indicies[i]];
if (introducer.iTag && ts < introducer.iExp)
{
r = i2p::data::netdb.FindRouter (introducer.iKey);
if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ())) if (r && r->IsReachableFrom (i2p::context.GetRouterInfo ()))
{ {
relayTag = it.iTag; relayTag = introducer.iTag;
if (relayTag) break; if (relayTag) break;
} }
} }
} }
}
if (r) if (r)
{ {
if (relayTag) if (relayTag)
@ -539,7 +682,8 @@ namespace transport
auto addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); auto addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address ();
if (addr) if (addr)
{ {
bool isValidEndpoint = !addr->host.is_unspecified () && addr->port; bool isValidEndpoint = !addr->host.is_unspecified () && addr->port &&
!i2p::util::net::IsInReservedRange(addr->host);
if (isValidEndpoint) if (isValidEndpoint)
{ {
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port)); auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port));
@ -589,6 +733,7 @@ namespace transport
s->SetOnEstablished ([s]() { s->SendPeerTest (); }); s->SetOnEstablished ([s]() { s->SendPeerTest (); });
return true; return true;
} }
else
CreateSession (router, addr, true); CreateSession (router, addr, true);
return true; return true;
} }
@ -616,22 +761,36 @@ namespace transport
it++; it++;
} }
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();) for (auto it: m_Sessions)
{ {
if (it->second->GetState () == eSSU2SessionStateTerminated || auto state = it.second->GetState ();
it->second->IsTerminationTimeoutExpired (ts)) if (state == eSSU2SessionStateTerminated || state == eSSU2SessionStateClosing)
it.second->Done ();
else if (it.second->IsTerminationTimeoutExpired (ts))
{ {
if (it->second->IsEstablished ()) if (it.second->IsEstablished ())
it->second->TerminateByTimeout (); it.second->RequestTermination (eSSU2TerminationReasonIdleTimeout);
if (it->second == m_LastSession) else
m_LastSession = nullptr; it.second->Done ();
it = m_Sessions.erase (it);
} }
else else
it.second->CleanUp (ts);
}
for (auto it = m_SessionsByRouterHash.begin (); it != m_SessionsByRouterHash.begin ();)
{ {
it->second->CleanUp (ts); if (it->second && it->second->GetState () == eSSU2SessionStateTerminated)
it = m_SessionsByRouterHash.erase (it);
else
it++; it++;
} }
for (auto it = m_Relays.begin (); it != m_Relays.begin ();)
{
if (it->second && it->second->GetState () == eSSU2SessionStateTerminated)
it = m_Relays.erase (it);
else
it++;
} }
for (auto it = m_IncomingTokens.begin (); it != m_IncomingTokens.end (); ) for (auto it = m_IncomingTokens.begin (); it != m_IncomingTokens.end (); )
@ -650,13 +809,16 @@ namespace transport
it++; it++;
} }
m_PacketsPool.CleanUpMt ();
m_SentPacketsPool.CleanUp ();
ScheduleTermination (); ScheduleTermination ();
} }
} }
void SSU2Server::ScheduleResend () void SSU2Server::ScheduleResend (bool more)
{ {
m_ResendTimer.expires_from_now (boost::posix_time::seconds(SSU2_RESEND_INTERVAL)); m_ResendTimer.expires_from_now (boost::posix_time::milliseconds (more ? SSU2_RESEND_CHECK_MORE_TIMEOUT :
(SSU2_RESEND_CHECK_TIMEOUT + rand () % SSU2_RESEND_CHECK_TIMEOUT_VARIANCE)));
m_ResendTimer.async_wait (std::bind (&SSU2Server::HandleResendTimer, m_ResendTimer.async_wait (std::bind (&SSU2Server::HandleResendTimer,
this, std::placeholders::_1)); this, std::placeholders::_1));
} }
@ -665,12 +827,16 @@ namespace transport
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
auto ts = i2p::util::GetSecondsSinceEpoch (); size_t resentPacketsNum = 0;
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
for (auto it: m_Sessions) for (auto it: m_Sessions)
it.second->Resend (ts); {
resentPacketsNum += it.second->Resend (ts);
if (resentPacketsNum > SSU2_MAX_RESEND_PACKETS) break;
}
for (auto it: m_PendingOutgoingSessions) for (auto it: m_PendingOutgoingSessions)
it.second->Resend (ts); it.second->Resend (ts);
ScheduleResend (); ScheduleResend (resentPacketsNum > SSU2_MAX_RESEND_PACKETS);
} }
} }
@ -711,5 +877,489 @@ namespace transport
m_IncomingTokens.emplace (ep, ret); m_IncomingTokens.emplace (ep, ret);
return ret; return ret;
} }
std::list<std::shared_ptr<SSU2Session> > SSU2Server::FindIntroducers (int maxNumIntroducers,
bool v4, const std::set<i2p::data::IdentHash>& excluded) const
{
std::list<std::shared_ptr<SSU2Session> > ret;
for (const auto& s : m_Sessions)
{
if (s.second->IsEstablished () && (s.second->GetRelayTag () && s.second->IsOutgoing ()) &&
!excluded.count (s.second->GetRemoteIdentity ()->GetIdentHash ()) &&
((v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V4)) ||
(!v4 && (s.second->GetRemoteTransports () & i2p::data::RouterInfo::eSSU2V6))))
ret.push_back (s.second);
}
if ((int)ret.size () > maxNumIntroducers)
{
// shink ret randomly
int sz = ret.size () - maxNumIntroducers;
for (int i = 0; i < sz; i++)
{
auto ind = rand () % ret.size ();
auto it = ret.begin ();
std::advance (it, ind);
ret.erase (it);
}
}
return ret;
}
void SSU2Server::UpdateIntroducers (bool v4)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
std::list<i2p::data::IdentHash> newList;
auto& introducers = v4 ? m_Introducers : m_IntroducersV6;
std::set<i2p::data::IdentHash> excluded;
for (const auto& it : introducers)
{
std::shared_ptr<SSU2Session> session;
auto it1 = m_SessionsByRouterHash.find (it);
if (it1 != m_SessionsByRouterHash.end ())
{
session = it1->second;
excluded.insert (it);
}
if (session && session->IsEstablished ())
{
if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION)
session->SendKeepAlive ();
if (ts < session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION)
newList.push_back (it);
else
session = nullptr;
}
if (!session)
i2p::context.RemoveSSU2Introducer (it, v4);
}
if (newList.size () < SSU2_MAX_NUM_INTRODUCERS)
{
auto sessions = FindIntroducers (SSU2_MAX_NUM_INTRODUCERS - newList.size (), v4, excluded);
if (sessions.empty () && !introducers.empty ())
{
// bump creation time for previous introducers if no new sessions found
LogPrint (eLogDebug, "SSU2: No new introducers found. Trying to reuse existing");
for (auto& it : introducers)
{
auto it1 = m_SessionsByRouterHash.find (it);
if (it1 != m_SessionsByRouterHash.end ())
{
auto session = it1->second;
if (session->IsEstablished ())
{
session->SetCreationTime (session->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_DURATION);
if (std::find (newList.begin (), newList.end (), it) == newList.end ())
{
newList.push_back (it);
sessions.push_back (session);
}
}
}
}
}
for (const auto& it : sessions)
{
i2p::data::RouterInfo::Introducer introducer;
introducer.iTag = it->GetRelayTag ();
introducer.iKey = it->GetRemoteIdentity ()->GetIdentHash ();
introducer.iExp = it->GetCreationTime () + SSU2_TO_INTRODUCER_SESSION_EXPIRATION;
excluded.insert (it->GetRemoteIdentity ()->GetIdentHash ());
if (i2p::context.AddSSU2Introducer (introducer, v4))
{
LogPrint (eLogDebug, "SSU2: Introducer added ", it->GetRelayTag (), " at ",
i2p::data::GetIdentHashAbbreviation (it->GetRemoteIdentity ()->GetIdentHash ()));
newList.push_back (it->GetRemoteIdentity ()->GetIdentHash ());
if (newList.size () >= SSU2_MAX_NUM_INTRODUCERS) break;
}
}
}
introducers = newList;
if (introducers.size () < SSU2_MAX_NUM_INTRODUCERS)
{
for (auto i = introducers.size (); i < SSU2_MAX_NUM_INTRODUCERS; i++)
{
auto introducer = i2p::data::netdb.GetRandomSSU2Introducer (v4, excluded);
if (introducer)
{
auto address = v4 ? introducer->GetSSU2V4Address () : introducer->GetSSU2V6Address ();
if (address)
{
CreateSession (introducer, address);
excluded.insert (introducer->GetIdentHash ());
}
}
else
{
LogPrint (eLogDebug, "SSU2: Can't find more introducers");
break;
}
}
}
}
void SSU2Server::ScheduleIntroducersUpdateTimer ()
{
if (m_IsPublished)
{
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, true));
}
}
void SSU2Server::RescheduleIntroducersUpdateTimer ()
{
if (m_IsPublished)
{
m_IntroducersUpdateTimer.cancel ();
i2p::context.ClearSSU2Introducers (true);
m_Introducers.clear ();
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2));
m_IntroducersUpdateTimer.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, true));
}
}
void SSU2Server::ScheduleIntroducersUpdateTimerV6 ()
{
if (m_IsPublished)
{
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false));
}
}
void SSU2Server::RescheduleIntroducersUpdateTimerV6 ()
{
if (m_IsPublished)
{
m_IntroducersUpdateTimerV6.cancel ();
i2p::context.ClearSSU2Introducers (false);
m_IntroducersV6.clear ();
m_IntroducersUpdateTimerV6.expires_from_now (boost::posix_time::seconds(SSU2_KEEP_ALIVE_INTERVAL/2));
m_IntroducersUpdateTimerV6.async_wait (std::bind (&SSU2Server::HandleIntroducersUpdateTimer,
this, std::placeholders::_1, false));
}
}
void SSU2Server::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode, bool v4)
{
if (ecode != boost::asio::error::operation_aborted)
{
// timeout expired
if (v4)
{
if (i2p::context.GetStatus () == eRouterStatusTesting)
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimer ();
return;
}
if (i2p::context.GetStatus () != eRouterStatusFirewalled)
{
// we don't need introducers
i2p::context.ClearSSU2Introducers (true);
m_Introducers.clear ();
return;
}
// we are firewalled
auto addr = i2p::context.GetRouterInfo ().GetSSU2V4Address ();
if (addr && addr->ssu && addr->ssu->introducers.empty ())
i2p::context.SetUnreachableSSU2 (true, false); // v4
UpdateIntroducers (true);
ScheduleIntroducersUpdateTimer ();
}
else
{
if (i2p::context.GetStatusV6 () == eRouterStatusTesting)
{
// we still don't know if we need introducers
ScheduleIntroducersUpdateTimerV6 ();
return;
}
if (i2p::context.GetStatusV6 () != eRouterStatusFirewalled)
{
// we don't need introducers
i2p::context.ClearSSU2Introducers (false);
m_IntroducersV6.clear ();
return;
}
// we are firewalled
auto addr = i2p::context.GetRouterInfo ().GetSSU2V6Address ();
if (addr && addr->ssu && addr->ssu->introducers.empty ())
i2p::context.SetUnreachableSSU2 (false, true); // v6
UpdateIntroducers (false);
ScheduleIntroducersUpdateTimerV6 ();
}
}
}
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<boost::asio::const_buffer> 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::address::from_string (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;
}
} }
} }

View File

@ -17,15 +17,24 @@ namespace i2p
{ {
namespace transport namespace transport
{ {
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds
const int SSU2_RESEND_CHECK_TIMEOUT = 400; // in milliseconds
const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 100; // in milliseconds
const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 10; // in milliseconds
const size_t SSU2_MAX_RESEND_PACKETS = 128; // packets to resend at the time
const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
const size_t SSU2_MAX_NUM_INTRODUCERS = 3;
const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
const int SSU2_TO_INTRODUCER_SESSION_EXPIRATION = 4800; // 80 minutes
const int SSU2_KEEP_ALIVE_INTERVAL = 30; // in seconds
const int SSU2_PROXY_CONNECT_RETRY_TIMEOUT = 30; // in seconds
class SSU2Server: private i2p::util::RunnableServiceWithWork class SSU2Server: private i2p::util::RunnableServiceWithWork
{ {
struct Packet struct Packet
{ {
uint8_t buf[SSU2_MTU]; uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len; size_t len;
boost::asio::ip::udp::endpoint from; boost::asio::ip::udp::endpoint from;
}; };
@ -49,7 +58,11 @@ namespace transport
void Stop (); void Stop ();
boost::asio::io_service& GetService () { return GetIOService (); }; boost::asio::io_service& GetService () { return GetIOService (); };
void SetLocalAddress (const boost::asio::ip::address& localAddress); 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; bool IsSupported (const boost::asio::ip::address& addr) const;
uint16_t GetPort (bool v4) const;
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
void AddSession (std::shared_ptr<SSU2Session> session); void AddSession (std::shared_ptr<SSU2Session> session);
void RemoveSession (uint64_t connID); void RemoveSession (uint64_t connID);
@ -79,6 +92,10 @@ namespace transport
uint64_t GetIncomingToken (const boost::asio::ip::udp::endpoint& ep); uint64_t GetIncomingToken (const boost::asio::ip::udp::endpoint& ep);
std::pair<uint64_t, uint32_t> NewIncomingToken (const boost::asio::ip::udp::endpoint& ep); std::pair<uint64_t, uint32_t> NewIncomingToken (const boost::asio::ip::udp::endpoint& ep);
void RescheduleIntroducersUpdateTimer ();
void RescheduleIntroducersUpdateTimerV6 ();
i2p::util::MemoryPool<SSU2SentPacket>& GetSentPacketsPool () { return m_SentPacketsPool; };
private: private:
@ -93,10 +110,27 @@ namespace transport
void ScheduleTermination (); void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode); void HandleTerminationTimer (const boost::system::error_code& ecode);
void ScheduleResend (); void ScheduleResend (bool more);
void HandleResendTimer (const boost::system::error_code& ecode); void HandleResendTimer (const boost::system::error_code& ecode);
void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session); void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session);
std::list<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
bool v4, const std::set<i2p::data::IdentHash>& excluded) const;
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: private:
@ -104,13 +138,26 @@ namespace transport
boost::asio::ip::udp::socket m_SocketV4, m_SocketV6; boost::asio::ip::udp::socket m_SocketV4, m_SocketV6;
boost::asio::ip::address m_AddressV4, m_AddressV6; boost::asio::ip::address m_AddressV4, m_AddressV6;
std::unordered_map<uint64_t, std::shared_ptr<SSU2Session> > m_Sessions; std::unordered_map<uint64_t, std::shared_ptr<SSU2Session> > m_Sessions;
std::map<i2p::data::IdentHash, std::shared_ptr<SSU2Session> > m_SessionsByRouterHash; std::unordered_map<i2p::data::IdentHash, std::shared_ptr<SSU2Session> > m_SessionsByRouterHash;
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSU2Session> > m_PendingOutgoingSessions; std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSU2Session> > m_PendingOutgoingSessions;
std::map<boost::asio::ip::udp::endpoint, std::pair<uint64_t, uint32_t> > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds) std::map<boost::asio::ip::udp::endpoint, std::pair<uint64_t, uint32_t> > m_IncomingTokens, m_OutgoingTokens; // remote endpoint -> (token, expires in seconds)
std::map<uint32_t, std::shared_ptr<SSU2Session> > m_Relays; // we are introducer, relay tag -> session std::map<uint32_t, std::shared_ptr<SSU2Session> > m_Relays; // we are introducer, relay tag -> session
std::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to
i2p::util::MemoryPoolMt<Packet> m_PacketsPool; i2p::util::MemoryPoolMt<Packet> m_PacketsPool;
boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer; i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool;
boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer,
m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6;
std::shared_ptr<SSU2Session> m_LastSession; std::shared_ptr<SSU2Session> m_LastSession;
bool m_IsPublished; // if we maintain introducers
bool m_IsSyncClockFromPeers;
// proxy
bool m_IsThroughProxy;
uint8_t m_UDPRequestHeader[SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE];
std::unique_ptr<boost::asio::ip::tcp::endpoint> m_ProxyEndpoint;
std::unique_ptr<boost::asio::ip::tcp::socket> m_UDPAssociateSocket;
std::unique_ptr<boost::asio::ip::udp::endpoint> m_ProxyRelayEndpoint;
std::unique_ptr<boost::asio::deadline_timer> m_ProxyConnectRetryTimer;
public: public:

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "Crypto.h" #include "Crypto.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "RouterContext.h"
#include "TransportSession.h" #include "TransportSession.h"
namespace i2p namespace i2p
@ -25,19 +26,30 @@ namespace transport
{ {
const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds
const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
const int SSU2_CLOCK_SKEW = 60; // in seconds
const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust
const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds const int SSU2_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_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52*60; // for next token block, in seconds
const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds
const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds
const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds
const size_t SSU2_MTU = 1440; // TODO: should be 1456 for ipv4 const size_t SSU2_MAX_PACKET_SIZE = 1500;
const size_t SSU2_MAX_PAYLOAD_SIZE = SSU2_MTU - 32; const size_t SSU2_MIN_PACKET_SIZE = 1280;
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1; // in seconds const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in millseconds
const int SSU2_RESEND_INTERVAL = 3; // in seconds const int SSU2_RESEND_INTERVAL = 300; // in milliseconds
const int SSU2_MAX_NUM_RESENDS = 5; const int SSU2_MAX_NUM_RESENDS = 5;
const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
const size_t SSU2_MAX_WINDOW_SIZE = 128; // in packets const size_t SSU2_MIN_WINDOW_SIZE = 16; // in packets
const size_t SSU2_MAX_WINDOW_SIZE = 256; // in packets
const size_t SSU2_MIN_RTO = 100; // in milliseconds
const size_t SSU2_MAX_RTO = 2500; // in milliseconds
const float SSU2_kAPPA = 1.8;
const size_t SSU2_MAX_OUTGOING_QUEUE_SIZE = 500; // in messages
const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send
const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64;
// flags
const uint8_t SSU2_FLAG_IMMEDIATE_ACK_REQUESTED = 0x01;
enum SSU2MessageType enum SSU2MessageType
{ {
@ -82,14 +94,18 @@ namespace transport
eSSU2SessionStateUnknown, eSSU2SessionStateUnknown,
eSSU2SessionStateTokenReceived, eSSU2SessionStateTokenReceived,
eSSU2SessionStateSessionRequestSent, eSSU2SessionStateSessionRequestSent,
eSSU2SessionStateSessionRequestReceived,
eSSU2SessionStateSessionCreatedSent, eSSU2SessionStateSessionCreatedSent,
eSSU2SessionStateSessionCreatedReceived,
eSSU2SessionStateSessionConfirmedSent, eSSU2SessionStateSessionConfirmedSent,
eSSU2SessionStateEstablished, eSSU2SessionStateEstablished,
eSSU2SessionStateClosing,
eSSU2SessionStateTerminated, eSSU2SessionStateTerminated,
eSSU2SessionStateFailed, eSSU2SessionStateFailed,
eSSU2SessionStateIntroduced, eSSU2SessionStateIntroduced,
eSSU2SessionStatePeerTest, eSSU2SessionStatePeerTest,
eSSU2SessionStatePeerTestReceived // 5 before 4 eSSU2SessionStatePeerTestReceived, // 5 before 4
eSSU2SessionStateTokenRequestReceived
}; };
enum SSU2PeerTestCode enum SSU2PeerTestCode
@ -118,11 +134,38 @@ namespace transport
eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70 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 SSU2IncompleteMessage
{ {
struct Fragment struct Fragment
{ {
uint8_t buf[SSU2_MTU]; uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len; size_t len;
bool isLast; bool isLast;
}; };
@ -131,6 +174,16 @@ namespace transport
int nextFragmentNum; int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds uint32_t lastFragmentInsertTime; // in seconds
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments; std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments;
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
};
struct SSU2SentPacket
{
uint8_t payload[SSU2_MAX_PACKET_SIZE];
size_t payloadSize = 0;
uint64_t sendTime; // in milliseconds
int numResends = 0;
}; };
// RouterInfo flags // RouterInfo flags
@ -153,18 +206,14 @@ namespace transport
} h; } h;
}; };
struct SentPacket struct HandshakePacket
{
uint8_t payload[SSU2_MAX_PAYLOAD_SIZE];
size_t payloadSize = 0;
uint32_t nextResendTime; // in seconds
int numResends = 0;
};
struct HandshakePacket: public SentPacket
{ {
Header header; Header header;
uint8_t headerX[48]; // part1 for SessionConfirmed 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<void ()> OnEstablished; typedef std::function<void ()> OnEstablished;
@ -186,15 +235,15 @@ namespace transport
bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag); bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag);
void WaitForIntroduction (); void WaitForIntroduction ();
void SendPeerTest (); // Alice, Data message void SendPeerTest (); // Alice, Data message
void Terminate (); void SendKeepAlive ();
void TerminateByTimeout (); void RequestTermination (SSU2TerminationReason reason);
void CleanUp (uint64_t ts); void CleanUp (uint64_t ts);
void FlushData (); void FlushData ();
void Done () override; void Done () override;
void SendLocalRouterInfo (bool update) override; void SendLocalRouterInfo (bool update) override;
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override; void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
uint32_t GetRelayTag () const override { return m_RelayTag; }; uint32_t GetRelayTag () const override { return m_RelayTag; };
void Resend (uint64_t ts); size_t Resend (uint64_t ts); // return number or resent packets
bool IsEstablished () const { return m_State == eSSU2SessionStateEstablished; }; bool IsEstablished () const { return m_State == eSSU2SessionStateEstablished; };
uint64_t GetConnID () const { return m_SourceConnID; }; uint64_t GetConnID () const { return m_SourceConnID; };
SSU2SessionState GetState () const { return m_State; }; SSU2SessionState GetState () const { return m_State; };
@ -206,16 +255,19 @@ namespace transport
bool ProcessRetry (uint8_t * buf, size_t len); bool ProcessRetry (uint8_t * buf, size_t len);
bool ProcessHolePunch (uint8_t * buf, size_t len); bool ProcessHolePunch (uint8_t * buf, size_t len);
bool ProcessPeerTest (uint8_t * buf, size_t len); bool ProcessPeerTest (uint8_t * buf, size_t len);
void ProcessData (uint8_t * buf, size_t len); void ProcessData (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
private: private:
void Terminate ();
void Established (); void Established ();
void ScheduleConnectTimer (); void ScheduleConnectTimer ();
void HandleConnectTimer (const boost::system::error_code& ecode); void HandleConnectTimer (const boost::system::error_code& ecode);
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs); void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
bool SendQueue (); bool SendQueue (); // returns true if ack block was sent
void SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg); bool SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
void ResendHandshakePacket ();
void ConnectAfterIntroduction ();
void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len); void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len);
void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len); void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len);
@ -226,18 +278,25 @@ namespace transport
void KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba); void KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba);
void SendTokenRequest (); void SendTokenRequest ();
void SendRetry (); void SendRetry ();
uint32_t SendData (const uint8_t * buf, size_t len); // returns packet num uint32_t SendData (const uint8_t * buf, size_t len, uint8_t flags = 0); // returns packet num
void SendQuickAck (); void SendQuickAck ();
void SendTermination (); void SendTermination ();
void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey); void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token);
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message
void SendPathResponse (const uint8_t * data, size_t len);
void SendPathChallenge ();
void HandlePayload (const uint8_t * buf, size_t len); void HandlePayload (const uint8_t * buf, size_t len);
void HandleDateTime (const uint8_t * buf, size_t len);
void HandleAck (const uint8_t * buf, size_t len); void HandleAck (const uint8_t * buf, size_t len);
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum); void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts);
void HandleAddress (const uint8_t * buf, size_t len);
bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep); bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep);
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const; std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const;
void AdjustMaxPayloadSize ();
RouterStatus GetRouterStatus () const;
void SetRouterStatus (RouterStatus status) const;
std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size); std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size);
void CreateNonce (uint64_t seqn, uint8_t * nonce); void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate
@ -257,17 +316,18 @@ namespace transport
size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg); size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg);
size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID); size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> 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 CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen);
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, bool endpoint); // add endpoint for Chralie and no endpoint for Bob size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen); size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen);
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice
size_t CreateTerminationBlock (uint8_t * buf, size_t len);
private: private:
SSU2Server& m_Server; SSU2Server& m_Server;
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys; std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState; std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
std::unique_ptr<HandshakePacket> m_SessionConfirmedFragment1; // for Bob if applicable std::unique_ptr<HandshakePacket> m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice
std::unique_ptr<HandshakePacket> m_SentHandshakePacket; // SessionRequest or SessionCreated std::unique_ptr<HandshakePacket> m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address; std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
boost::asio::ip::udp::endpoint m_RemoteEndpoint; boost::asio::ip::udp::endpoint m_RemoteEndpoint;
i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests
@ -276,17 +336,20 @@ namespace transport
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64]; uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
uint32_t m_SendPacketNum, m_ReceivePacketNum; uint32_t m_SendPacketNum, m_ReceivePacketNum;
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
std::map<uint32_t, std::shared_ptr<SentPacket> > m_SentPackets; // packetNum -> packet std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet
std::map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // I2NP std::map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // I2NP
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue; std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
i2p::I2NPMessagesHandler m_Handler; i2p::I2NPMessagesHandler m_Handler;
bool m_IsDataReceived; bool m_IsDataReceived;
size_t m_WindowSize; size_t m_WindowSize, m_RTT, m_RTO;
uint32_t m_RelayTag; // between Bob and Charlie uint32_t m_RelayTag; // between Bob and Charlie
OnEstablished m_OnEstablished; // callback from Established OnEstablished m_OnEstablished; // callback from Established
boost::asio::deadline_timer m_ConnectTimer; boost::asio::deadline_timer m_ConnectTimer;
SSU2TerminationReason m_TerminationReason;
size_t m_MaxPayloadSize;
std::unique_ptr<i2p::data::IdentHash> m_PathChallenge;
}; };
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce) inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)

View File

@ -19,17 +19,14 @@
#include "I2NPProtocol.h" #include "I2NPProtocol.h"
#include "Identity.h" #include "Identity.h"
#include "RouterInfo.h" #include "RouterInfo.h"
#include "TransportSession.h"
namespace i2p namespace i2p
{ {
namespace transport namespace transport
{ {
const size_t SSU_MTU_V4 = 1484; const size_t SSU_MTU_V4 = 1484;
const size_t SSU_MTU_V6 = 1488; const size_t SSU_MTU_V6 = 1488;
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_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440 const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440
const int RESEND_INTERVAL = 3; // in seconds const int RESEND_INTERVAL = 3; // in seconds

View File

@ -41,7 +41,6 @@ namespace transport
i2p::context.GetRouterInfo ().GetSSUAddress (true); i2p::context.GetRouterInfo ().GetSSUAddress (true);
if (address) m_IntroKey = address->i; if (address) m_IntroKey = address->i;
} }
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
} }
SSUSession::~SSUSession () SSUSession::~SSUSession ()
@ -720,8 +719,8 @@ namespace transport
if (i2p::context.GetStatus () == eRouterStatusTesting) if (i2p::context.GetStatus () == eRouterStatusTesting)
i2p::context.SetError (eRouterErrorSymmetricNAT); i2p::context.SetError (eRouterErrorSymmetricNAT);
} }
else if (i2p::context.GetStatus () == eRouterStatusError && i2p::context.GetError () == eRouterErrorSymmetricNAT) else if (i2p::context.GetError () == eRouterErrorSymmetricNAT)
i2p::context.SetStatus (eRouterStatusTesting); i2p::context.SetError (eRouterErrorNone);
} }
uint32_t nonce = bufbe32toh (buf); uint32_t nonce = bufbe32toh (buf);
buf += 4; // nonce buf += 4; // nonce

View File

@ -103,8 +103,6 @@ namespace transport
void SendKeepAlive (); void SendKeepAlive ();
uint32_t GetRelayTag () const { return m_RelayTag; }; uint32_t GetRelayTag () const { return m_RelayTag; };
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; }; const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
void FlushData (); void FlushData ();
void CleanUp (uint64_t ts); void CleanUp (uint64_t ts);
@ -167,7 +165,6 @@ namespace transport
i2p::crypto::AESKey m_SessionKey; i2p::crypto::AESKey m_SessionKey;
i2p::crypto::MACKey m_MacKey; i2p::crypto::MACKey m_MacKey;
i2p::data::RouterInfo::IntroKey m_IntroKey; i2p::data::RouterInfo::IntroKey m_IntroKey;
uint32_t m_CreationTime; // seconds since epoch
SSUData m_Data; SSUData m_Data;
bool m_IsDataReceived; bool m_IsDataReceived;
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only

View File

@ -474,6 +474,29 @@ namespace stream
Close (); // check is all outgoing messages have been sent and we can send close 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;
std::condition_variable newDataReceived;
std::mutex newDataReceivedMutex;
std::unique_lock<std::mutex> l(newDataReceivedMutex);
AsyncReceive (boost::asio::buffer (buf, len),
[&ret, &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<std::mutex> l(newDataReceivedMutex);
newDataReceived.notify_all ();
},
timeout);
if (newDataReceived.wait_for (l, std::chrono::seconds (timeout)) == std::cv_status::timeout)
ret = 0;
return ret;
}
size_t Stream::Send (const uint8_t * buf, size_t len) size_t Stream::Send (const uint8_t * buf, size_t len)
{ {
AsyncSend (buf, len, nullptr); AsyncSend (buf, len, nullptr);
@ -729,7 +752,7 @@ namespace stream
Terminate (); Terminate ();
break; break;
default: default:
LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status, "sSID=", m_SendStreamID); LogPrint (eLogWarning, "Streaming: Unexpected stream status=", (int)m_Status, " for sSID=", m_SendStreamID);
}; };
} }
@ -855,7 +878,7 @@ namespace stream
for (const auto& it: packets) for (const auto& it: packets)
{ {
auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage ( auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage (
it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets ())); it->GetBuffer (), it->GetLength (), m_Port, !m_RoutingSession->IsRatchets (), it->IsSYN ()));
msgs.push_back (i2p::tunnel::TunnelMessageBlock msgs.push_back (i2p::tunnel::TunnelMessageBlock
{ {
i2p::tunnel::eDeliveryTypeTunnel, i2p::tunnel::eDeliveryTypeTunnel,
@ -1085,8 +1108,6 @@ namespace stream
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
m_PendingIncomingTimer (m_Owner->GetService ()) m_PendingIncomingTimer (m_Owner->GetService ())
{ {
if (m_Gzip)
m_Deflator.reset (new i2p::data::GzipDeflator);
} }
StreamingDestination::~StreamingDestination () StreamingDestination::~StreamingDestination ()
@ -1341,6 +1362,26 @@ namespace stream
acceptor (stream); acceptor (stream);
} }
std::shared_ptr<Stream> StreamingDestination::AcceptStream (int timeout)
{
std::shared_ptr<i2p::stream::Stream> stream;
std::condition_variable streamAccept;
std::mutex streamAcceptMutex;
std::unique_lock<std::mutex> l(streamAcceptMutex);
AcceptOnce (
[&streamAccept, &streamAcceptMutex, &stream](std::shared_ptr<i2p::stream::Stream> s)
{
stream = s;
std::unique_lock<std::mutex> 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) void StreamingDestination::HandlePendingIncomingTimer (const boost::system::error_code& ecode)
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
@ -1365,7 +1406,7 @@ namespace stream
} }
std::shared_ptr<I2NPMessage> StreamingDestination::CreateDataMessage ( std::shared_ptr<I2NPMessage> StreamingDestination::CreateDataMessage (
const uint8_t * payload, size_t len, uint16_t toPort, bool checksum) const uint8_t * payload, size_t len, uint16_t toPort, bool checksum, bool gzip)
{ {
size_t size; size_t size;
auto msg = m_I2NPMsgsPool.AcquireShared (); auto msg = m_I2NPMsgsPool.AcquireShared ();
@ -1373,8 +1414,8 @@ namespace stream
buf += 4; // reserve for lengthlength buf += 4; // reserve for lengthlength
msg->len += 4; msg->len += 4;
if (m_Gzip && m_Deflator) if (m_Gzip || gzip)
size = m_Deflator->Deflate (payload, len, buf, msg->maxLen - msg->len); size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
else else
size = i2p::data::GzipNoCompression (payload, len, buf, msg->maxLen - msg->len); size = i2p::data::GzipNoCompression (payload, len, buf, msg->maxLen - msg->len);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -185,6 +185,7 @@ namespace stream
template<typename Buffer, typename ReceiveHandler> template<typename Buffer, typename ReceiveHandler>
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); 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 ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); };
size_t Receive (uint8_t * buf, size_t len, int timeout);
void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); }; void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); };
@ -278,13 +279,14 @@ namespace stream
bool IsAcceptorSet () const { return m_Acceptor != nullptr; }; bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
void AcceptOnce (const Acceptor& acceptor); void AcceptOnce (const Acceptor& acceptor);
void AcceptOnceAcceptor (std::shared_ptr<Stream> stream, Acceptor acceptor, Acceptor prev); void AcceptOnceAcceptor (std::shared_ptr<Stream> stream, Acceptor acceptor, Acceptor prev);
std::shared_ptr<Stream> AcceptStream (int timeout = 0); // sync
std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_Owner; }; std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_Owner; };
void SetOwner (std::shared_ptr<i2p::client::ClientDestination> owner) { m_Owner = owner; }; void SetOwner (std::shared_ptr<i2p::client::ClientDestination> owner) { m_Owner = owner; };
uint16_t GetLocalPort () const { return m_LocalPort; }; uint16_t GetLocalPort () const { return m_LocalPort; };
void HandleDataMessagePayload (const uint8_t * buf, size_t len); void HandleDataMessagePayload (const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort, bool checksum = true); std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort, bool checksum = true, bool gzip = false);
Packet * NewPacket () { return m_PacketsPool.Acquire(); } Packet * NewPacket () { return m_PacketsPool.Acquire(); }
void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); } void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); }
@ -315,7 +317,7 @@ namespace stream
public: public:
i2p::data::GzipInflator m_Inflator; i2p::data::GzipInflator m_Inflator;
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator; i2p::data::GzipDeflator m_Deflator;
// for HTTP only // for HTTP only
const decltype(m_Streams)& GetStreams () const { return m_Streams; }; const decltype(m_Streams)& GetStreams () const { return m_Streams; };
@ -336,11 +338,10 @@ namespace stream
int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout; int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout;
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t)); s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t));
int left = timeout - t; int left = timeout - t;
auto self = s->shared_from_this(); s->m_ReceiveTimer.async_wait (
self->m_ReceiveTimer.async_wait ( [s, buffer, handler, left](const boost::system::error_code & ec)
[self, buffer, handler, left](const boost::system::error_code & ec)
{ {
self->HandleReceiveTimer(ec, buffer, handler, left); s->HandleReceiveTimer(ec, buffer, handler, left);
}); });
} }
}); });

View File

@ -24,6 +24,10 @@ namespace i2p
{ {
namespace transport namespace transport
{ {
const size_t IPV4_HEADER_SIZE = 20;
const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8;
class SignedData class SignedData
{ {
public: public:
@ -75,6 +79,7 @@ namespace transport
{ {
if (router) if (router)
m_RemoteIdentity = router->GetRouterIdentity (); m_RemoteIdentity = router->GetRouterIdentity ();
m_CreationTime = m_LastActivityTimestamp;
} }
virtual ~TransportSession () {}; virtual ~TransportSession () {};
@ -102,6 +107,9 @@ namespace transport
bool IsTerminationTimeoutExpired (uint64_t ts) const bool IsTerminationTimeoutExpired (uint64_t ts) const
{ return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); }; { return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); };
uint32_t GetCreationTime () const { return m_CreationTime; };
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
virtual uint32_t GetRelayTag () const { return 0; }; virtual uint32_t GetRelayTag () const { return 0; };
virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); }; virtual void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); };
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0; virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
@ -114,7 +122,17 @@ namespace transport
bool m_IsOutgoing; bool m_IsOutgoing;
int m_TerminationTimeout; int m_TerminationTimeout;
uint64_t m_LastActivityTimestamp; uint64_t m_LastActivityTimestamp;
uint32_t m_CreationTime; // seconds since epoch
}; };
// SOCKS5 proxy
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 size_t SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE = 10;
const size_t SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE = 22;
} }
} }

View File

@ -167,6 +167,8 @@ namespace transport
m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service); m_PeerTestTimer = 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); i2p::config::GetOption("nat", m_IsNAT);
m_X25519KeysPairSupplier.Start (); m_X25519KeysPairSupplier.Start ();
m_IsRunning = true; m_IsRunning = true;
@ -190,6 +192,8 @@ namespace transport
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port, proxyurl.user, proxyurl.pass); m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port, proxyurl.user, proxyurl.pass);
i2p::context.SetStatus (eRouterStatusProxy); i2p::context.SetStatus (eRouterStatusProxy);
if (ipv6)
i2p::context.SetStatusV6 (eRouterStatusProxy);
} }
else else
LogPrint(eLogError, "Transports: Unsupported NTCP2 proxy URL ", ntcp2proxy); LogPrint(eLogError, "Transports: Unsupported NTCP2 proxy URL ", ntcp2proxy);
@ -218,10 +222,29 @@ namespace transport
} }
} }
// create SSU2 server // create SSU2 server
if (enableSSU2) m_SSU2Server = new SSU2Server (); 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(eLogError, "Transports: Can't set SSU2 proxy ", ssu2proxy);
}
else
LogPrint(eLogError, "Transports: Invalid SSU2 proxy URL ", ssu2proxy);
}
}
// bind to interfaces // bind to interfaces
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
if (ipv4) if (ipv4)
{ {
std::string address; i2p::config::GetOption("address4", address); std::string address; i2p::config::GetOption("address4", address);
@ -236,9 +259,19 @@ namespace transport
if (m_SSU2Server) m_SSU2Server->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);
}
}
} }
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
if (ipv6) if (ipv6)
{ {
std::string address; i2p::config::GetOption("address6", address); std::string address; i2p::config::GetOption("address6", address);
@ -253,6 +286,17 @@ namespace transport
if (m_SSU2Server) m_SSU2Server->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); bool ygg; i2p::config::GetOption("meshnets.yggdrasil", ygg);
@ -285,8 +329,8 @@ namespace transport
delete m_SSUServer; delete m_SSUServer;
m_SSUServer = nullptr; m_SSUServer = nullptr;
} }
if (m_SSUServer) DetectExternalIP ();
} }
if (m_SSUServer || m_SSU2Server) DetectExternalIP ();
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT)); m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1)); m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
@ -417,8 +461,7 @@ namespace transport
{ {
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {}, it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, {r, ts})).first;
ts, ts + PEER_ROUTER_INFO_UPDATE_INTERVAL, {} })).first;
} }
connected = ConnectToPeer (ident, it->second); connected = ConnectToPeer (ident, it->second);
} }
@ -453,31 +496,22 @@ namespace transport
peer.router = netdb.FindRouter (ident); // try to get new one from netdb peer.router = netdb.FindRouter (ident); // try to get new one from netdb
if (peer.router) // we have RI already if (peer.router) // we have RI already
{ {
if (peer.numAttempts < 2) // NTCP2, 0 - ipv6, 1- ipv4 if (peer.priority.empty ())
SetPriority (peer);
while (peer.numAttempts < (int)peer.priority.size ())
{ {
if (m_NTCP2Server) // we support NTCP2 auto tr = peer.priority[peer.numAttempts];
peer.numAttempts++;
switch (tr)
{ {
std::shared_ptr<const RouterInfo::Address> address; case i2p::data::RouterInfo::eNTCP2V4:
if (!peer.numAttempts) // NTCP2 ipv6 case i2p::data::RouterInfo::eNTCP2V6:
{ {
if (context.GetRouterInfo ().IsNTCP2V6 () && peer.router->IsReachableBy (RouterInfo::eNTCP2V6)) if (!m_NTCP2Server) continue;
{ std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eNTCP2V6) ?
address = peer.router->GetPublishedNTCP2V6Address (); peer.router->GetPublishedNTCP2V6Address () : peer.router->GetPublishedNTCP2V4Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr; address = nullptr;
}
peer.numAttempts++;
}
if (!address && peer.numAttempts == 1) // NTCP2 ipv4
{
if (context.GetRouterInfo ().IsNTCP2 (true) && peer.router->IsReachableBy (RouterInfo::eNTCP2V4))
{
address = peer.router->GetPublishedNTCP2V4Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
}
peer.numAttempts++;
}
if (address) if (address)
{ {
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address); auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
@ -487,49 +521,41 @@ namespace transport
m_NTCP2Server->Connect (s); m_NTCP2Server->Connect (s);
return true; return true;
} }
break;
} }
else case i2p::data::RouterInfo::eSSU2V4:
peer.numAttempts = 2; // switch to SSU case i2p::data::RouterInfo::eSSU2V6:
}
if (peer.numAttempts == 2 || peer.numAttempts == 3) // SSU
{ {
if (m_SSUServer) if (!m_SSU2Server) continue;
{ std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eSSU2V6) ?
std::shared_ptr<const RouterInfo::Address> address; peer.router->GetSSU2V6Address () : peer.router->GetSSU2V4Address ();
if (peer.numAttempts == 2) // SSU ipv6
{
if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsReachableBy (RouterInfo::eSSUV6))
{
address = peer.router->GetSSUV6Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr; address = nullptr;
} if (address && address->IsReachableSSU ())
peer.numAttempts++;
}
if (!address && peer.numAttempts == 3) // SSU ipv4
{ {
if (context.GetRouterInfo ().IsSSU (true) && peer.router->IsReachableBy (RouterInfo::eSSUV4)) if (m_SSU2Server->CreateSession (peer.router, address))
return true;
}
break;
}
case i2p::data::RouterInfo::eSSUV4:
case i2p::data::RouterInfo::eSSUV6:
{ {
address = peer.router->GetSSUAddress (true); if (!m_SSUServer) continue;
std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eSSUV6) ?
peer.router->GetSSUV6Address () : peer.router->GetSSUAddress (true);
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host)) if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr; address = nullptr;
}
peer.numAttempts++;
}
if (address && address->IsReachableSSU ()) if (address && address->IsReachableSSU ())
{ {
if (m_SSUServer->CreateSession (peer.router, address)) if (m_SSUServer->CreateSession (peer.router, address))
return true; return true;
} }
break;
} }
else case i2p::data::RouterInfo::eNTCP2V6Mesh:
peer.numAttempts += 2; // switch to Mesh
}
if (peer.numAttempts == 4) // Mesh
{
peer.numAttempts++;
if (m_NTCP2Server && context.GetRouterInfo ().IsMesh () && peer.router->IsMesh ())
{ {
if (!m_NTCP2Server) continue;
auto address = peer.router->GetYggdrasilAddress (); auto address = peer.router->GetYggdrasilAddress ();
if (address) if (address)
{ {
@ -537,43 +563,14 @@ namespace transport
m_NTCP2Server->Connect (s); m_NTCP2Server->Connect (s);
return true; return true;
} }
break;
}
default:
LogPrint (eLogError, "Transports: Unknown transport ", (int)tr);
} }
} }
if (peer.numAttempts == 5 || peer.numAttempts == 6) // SSU2
{ LogPrint (eLogInfo, "Transports: No compatible addresses available");
if (m_SSU2Server)
{
std::shared_ptr<const RouterInfo::Address> address;
if (peer.numAttempts == 5) // SSU2 ipv6
{
if (context.GetRouterInfo ().IsSSU2V6 () && peer.router->IsReachableBy (RouterInfo::eSSU2V6))
{
address = peer.router->GetSSU2V6Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
}
peer.numAttempts++;
}
if (!address && peer.numAttempts == 6) // SSU2 ipv4
{
if (context.GetRouterInfo ().IsSSU2V4 () && peer.router->IsReachableBy (RouterInfo::eSSU2V4))
{
address = peer.router->GetSSU2V4Address ();
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
address = nullptr;
}
peer.numAttempts++;
}
if (address && address->IsReachableSSU ())
{
if (m_SSU2Server->CreateSession (peer.router, address))
return true;
}
}
else
peer.numAttempts += 2;
}
LogPrint (eLogInfo, "Transports: No compatble NTCP2 or SSU addresses available");
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed
peer.Done (); peer.Done ();
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
@ -589,6 +586,41 @@ namespace transport
return true; return true;
} }
void Transports::SetPriority (Peer& peer) const
{
static const std::vector<i2p::data::RouterInfo::SupportedTransports>
ntcp2Priority =
{
i2p::data::RouterInfo::eNTCP2V6,
i2p::data::RouterInfo::eNTCP2V4,
i2p::data::RouterInfo::eSSU2V6,
i2p::data::RouterInfo::eSSU2V4,
i2p::data::RouterInfo::eNTCP2V6Mesh,
i2p::data::RouterInfo::eSSUV6,
i2p::data::RouterInfo::eSSUV4
},
ssu2Priority =
{
i2p::data::RouterInfo::eSSU2V6,
i2p::data::RouterInfo::eSSU2V4,
i2p::data::RouterInfo::eNTCP2V6,
i2p::data::RouterInfo::eNTCP2V4,
i2p::data::RouterInfo::eNTCP2V6Mesh,
i2p::data::RouterInfo::eSSUV6,
i2p::data::RouterInfo::eSSUV4
};
if (!peer.router) return;
auto compatibleTransports = context.GetRouterInfo ().GetCompatibleTransports (false) &
peer.router->GetCompatibleTransports (true);
peer.numAttempts = 0;
peer.priority.clear ();
bool ssu2 = rand () & 1;
const auto& priority = ssu2 ? ssu2Priority : ntcp2Priority;
for (auto transport: priority)
if (transport & compatibleTransports)
peer.priority.push_back (transport);
}
void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident) void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
{ {
m_Service->post (std::bind (&Transports::HandleRequestComplete, this, r, ident)); m_Service->post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
@ -622,20 +654,22 @@ namespace transport
i2p::context.SetStatus (eRouterStatusOK); i2p::context.SetStatus (eRouterStatusOK);
return; return;
} }
if (m_SSUServer) if (m_SSUServer || m_SSU2Server)
PeerTest (); PeerTest ();
else else
LogPrint (eLogError, "Transports: Can't detect external IP. SSU is not available"); LogPrint (eLogWarning, "Transports: Can't detect external IP. SSU or SSU2 is not available");
} }
void Transports::PeerTest (bool ipv4, bool ipv6) void Transports::PeerTest (bool ipv4, bool ipv6)
{ {
if (RoutesRestricted() || !m_SSUServer) return; if (RoutesRestricted() || (!m_SSUServer && !m_SSU2Server)) return;
if (ipv4 && i2p::context.SupportsV4 ()) if (ipv4 && i2p::context.SupportsV4 ())
{ {
LogPrint (eLogInfo, "Transports: Started peer test IPv4"); LogPrint (eLogInfo, "Transports: Started peer test IPv4");
std::set<i2p::data::IdentHash> excluded; std::set<i2p::data::IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
if (m_SSUServer)
{
bool statusChanged = false; bool statusChanged = false;
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
@ -657,15 +691,24 @@ namespace transport
} }
if (!statusChanged) if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4"); LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
}
// SSU2 // SSU2
if (m_SSU2Server) if (m_SSU2Server && !m_SSU2Server->UsesProxy ())
{ {
excluded.clear (); excluded.clear ();
excluded.insert (i2p::context.GetIdentHash ()); excluded.insert (i2p::context.GetIdentHash ());
int numTests = m_SSUServer ? 3 : 5;
for (int i = 0; i < numTests; i++)
{
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4 auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4
if (router) if (router)
{
if (i2p::context.GetStatus () != eRouterStatusTesting)
i2p::context.SetStatusSSU2 (eRouterStatusTesting);
m_SSU2Server->StartPeerTest (router, true); m_SSU2Server->StartPeerTest (router, true);
excluded.insert (router->GetIdentHash ());
}
}
} }
} }
if (ipv6 && i2p::context.SupportsV6 ()) if (ipv6 && i2p::context.SupportsV6 ())
@ -673,6 +716,8 @@ namespace transport
LogPrint (eLogInfo, "Transports: Started peer test IPv6"); LogPrint (eLogInfo, "Transports: Started peer test IPv6");
std::set<i2p::data::IdentHash> excluded; std::set<i2p::data::IdentHash> excluded;
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
if (m_SSUServer)
{
bool statusChanged = false; bool statusChanged = false;
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
@ -694,15 +739,25 @@ namespace transport
} }
if (!statusChanged) if (!statusChanged)
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6"); LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
}
// SSU2 // SSU2
if (m_SSU2Server) if (m_SSU2Server && !m_SSU2Server->UsesProxy ())
{ {
excluded.clear (); excluded.clear ();
excluded.insert (i2p::context.GetIdentHash ()); excluded.insert (i2p::context.GetIdentHash ());
int numTests = m_SSUServer ? 3 : 5;
for (int i = 0; i < numTests; i++)
{
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6 auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6
if (router) if (router)
{
if (i2p::context.GetStatusV6 () != eRouterStatusTesting)
i2p::context.SetStatusV6SSU2 (eRouterStatusTesting);
m_SSU2Server->StartPeerTest (router, false); m_SSU2Server->StartPeerTest (router, false);
excluded.insert (router->GetIdentHash ());
}
}
} }
} }
} }
@ -756,8 +811,7 @@ namespace transport
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
auto ts = i2p::util::GetSecondsSinceEpoch (); auto ts = i2p::util::GetSecondsSinceEpoch ();
std::unique_lock<std::mutex> l(m_PeersMutex); std::unique_lock<std::mutex> l(m_PeersMutex);
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, m_Peers.insert (std::make_pair (ident, Peer{ nullptr, ts }));
ts, ts + PEER_ROUTER_INFO_UPDATE_INTERVAL, {} }));
} }
}); });
} }
@ -954,5 +1008,122 @@ namespace transport
i2p::context.SetError (eRouterErrorOffline); i2p::context.SetError (eRouterErrorOffline);
} }
} }
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::address_v6::from_string (yggaddress);
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);
i2p::context.RemoveNTCPAddress (!ipv6); // TODO: remove later
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::address_v6::from_string (ipv6Addr);
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 ssu; i2p::config::GetOption("ssu", ssu);
if (!ssu) i2p::context.RemoveSSUAddress (); // TODO: remove later
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 = ssu ? (port + 1) : port;
bool published; i2p::config::GetOption("ssu2.published", published);
if (published)
i2p::context.PublishSSU2Address (ssu2port, true, ipv4, ipv6); // publish
else
i2p::context.PublishSSU2Address (ssu2port, false, ipv4, ipv6); // unpublish
}
}
} }
} }

View File

@ -71,6 +71,13 @@ namespace transport
std::list<std::shared_ptr<TransportSession> > sessions; std::list<std::shared_ptr<TransportSession> > sessions;
uint64_t creationTime, nextRouterInfoUpdateTime; uint64_t creationTime, nextRouterInfoUpdateTime;
std::vector<std::shared_ptr<i2p::I2NPMessage> > delayedMessages; std::vector<std::shared_ptr<i2p::I2NPMessage> > delayedMessages;
std::vector<i2p::data::RouterInfo::SupportedTransports> priority;
Peer (std::shared_ptr<const i2p::data::RouterInfo> r, uint64_t ts):
numAttempts (0), router (r), creationTime (ts),
nextRouterInfoUpdateTime (ts + PEER_ROUTER_INFO_UPDATE_INTERVAL)
{
}
void Done () void Done ()
{ {
@ -93,6 +100,7 @@ namespace transport
void Stop (); void Stop ();
bool IsBoundSSU() const { return m_SSUServer != nullptr; } bool IsBoundSSU() const { return m_SSUServer != nullptr; }
bool IsBoundSSU2() const { return m_SSU2Server != nullptr; }
bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; } bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; }
bool IsOnline() const { return m_IsOnline; }; bool IsOnline() const { return m_IsOnline; };
@ -146,6 +154,7 @@ namespace transport
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident); void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident);
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs); void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer); bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
void SetPriority (Peer& peer) const;
void HandlePeerCleanupTimer (const boost::system::error_code& ecode); void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
void HandlePeerTestTimer (const boost::system::error_code& ecode); void HandlePeerTestTimer (const boost::system::error_code& ecode);
@ -194,6 +203,9 @@ namespace transport
}; };
extern Transports transports; extern Transports transports;
void InitAddressFromIface ();
void InitTransports ();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -60,6 +60,7 @@ namespace api
else else
i2p::log::Logger().SendTo (i2p::fs::DataDirPath (i2p::fs::GetAppName () + ".log")); i2p::log::Logger().SendTo (i2p::fs::DataDirPath (i2p::fs::GetAppName () + ".log"));
i2p::log::Logger().Start (); i2p::log::Logger().Start ();
i2p::transport::InitTransports ();
LogPrint(eLogInfo, "API: Starting NetDB"); LogPrint(eLogInfo, "API: Starting NetDB");
i2p::data::netdb.Start(); i2p::data::netdb.Start();
LogPrint(eLogInfo, "API: Starting Transports"); LogPrint(eLogInfo, "API: Starting Transports");

View File

@ -12,6 +12,7 @@
#include "util.h" #include "util.h"
#include "Log.h" #include "Log.h"
#include "I2PEndian.h"
#if not defined (__FreeBSD__) #if not defined (__FreeBSD__)
#include <pthread.h> #include <pthread.h>
@ -21,6 +22,9 @@
#include <pthread_np.h> #include <pthread_np.h>
#endif #endif
#if defined(__APPLE__)
# include <AvailabilityMacros.h>
#endif
#ifdef _WIN32 #ifdef _WIN32
#include <stdlib.h> #include <stdlib.h>
@ -35,7 +39,7 @@
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) #define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
// inet_pton exists Windows since Vista, but XP doesn't have that function! // inet_pton and inet_ntop have been in Windows since Vista, but XP doesn't have these functions!
// This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found // 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) int inet_pton_xp (int af, const char *src, void *dst)
{ {
@ -61,6 +65,29 @@ int inet_pton_xp (int af, const char *src, void *dst)
} }
return 0; return 0;
} }
const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size)
{
struct sockaddr_storage ss;
unsigned long s = size;
ZeroMemory(&ss, sizeof(ss));
ss.ss_family = af;
switch(af) {
case AF_INET:
((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
break;
case AF_INET6:
((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
break;
default:
return NULL;
}
/* cannot direclty use &size because of strict aliasing rules */
return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? dst : NULL;
}
#else /* !_WIN32 => UNIX */ #else /* !_WIN32 => UNIX */
#include <sys/types.h> #include <sys/types.h>
#ifdef ANDROID #ifdef ANDROID
@ -119,8 +146,15 @@ namespace util
} }
void SetThreadName (const char *name) { void SetThreadName (const char *name) {
#if defined(__APPLE__) && !defined(__powerpc__) #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); pthread_setname_np((char*)name);
# endif
#elif defined(__FreeBSD__) || defined(__OpenBSD__) #elif defined(__FreeBSD__) || defined(__OpenBSD__)
pthread_set_name_np(pthread_self(), name); pthread_set_name_np(pthread_self(), name);
#elif defined(__NetBSD__) #elif defined(__NetBSD__)
@ -133,27 +167,12 @@ namespace util
namespace net namespace net
{ {
#ifdef _WIN32 #ifdef _WIN32
bool IsWindowsXPorLater ()
{
static bool isRequested = false;
static bool isXP = false;
if (!isRequested)
{
// request
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
isXP = osvi.dwMajorVersion <= 5;
isRequested = true;
}
return isXP;
}
int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback) int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback)
{ {
typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size);
IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop");
if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found
ULONG outBufLen = 0; ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr; PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
@ -172,7 +191,7 @@ namespace net
if(dwRetVal != NO_ERROR) if(dwRetVal != NO_ERROR)
{ {
LogPrint(eLogError, "NetIface: GetMTU(): Enclosed GetAdaptersAddresses() call has failed"); LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed");
FREE(pAddresses); FREE(pAddresses);
return fallback; return fallback;
} }
@ -184,7 +203,7 @@ namespace net
pUnicast = pCurrAddresses->FirstUnicastAddress; pUnicast = pCurrAddresses->FirstUnicastAddress;
if(pUnicast == nullptr) if(pUnicast == nullptr)
LogPrint(eLogError, "NetIface: GetMTU(): Not a unicast IPv4 address, this is not supported"); LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv4 address, this is not supported");
for(int i = 0; pUnicast != nullptr; ++i) for(int i = 0; pUnicast != nullptr; ++i)
{ {
@ -192,8 +211,13 @@ namespace net
sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr; sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr;
if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr) if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr)
{ {
auto result = pAddresses->Mtu; char addr[INET_ADDRSTRLEN];
inetntop(AF_INET, &(((struct sockaddr_in *)localInterfaceAddress)->sin_addr), addr, INET_ADDRSTRLEN);
auto result = pCurrAddresses->Mtu;
FREE(pAddresses); FREE(pAddresses);
pAddresses = nullptr;
LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv4 address ", addr);
return result; return result;
} }
pUnicast = pUnicast->Next; pUnicast = pUnicast->Next;
@ -201,13 +225,17 @@ namespace net
pCurrAddresses = pCurrAddresses->Next; pCurrAddresses = pCurrAddresses->Next;
} }
LogPrint(eLogError, "NetIface: GetMTU(): No usable unicast IPv4 addresses found"); LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv4 addresses found");
FREE(pAddresses); FREE(pAddresses);
return fallback; return fallback;
} }
int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback) int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback)
{ {
typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size);
IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop");
if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found
ULONG outBufLen = 0; ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr; PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr; PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
@ -226,7 +254,7 @@ namespace net
if (dwRetVal != NO_ERROR) if (dwRetVal != NO_ERROR)
{ {
LogPrint(eLogError, "NetIface: GetMTU(): Enclosed GetAdaptersAddresses() call has failed"); LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed");
FREE(pAddresses); FREE(pAddresses);
return fallback; return fallback;
} }
@ -238,7 +266,7 @@ namespace net
PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress; PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress;
pUnicast = pCurrAddresses->FirstUnicastAddress; pUnicast = pCurrAddresses->FirstUnicastAddress;
if (pUnicast == nullptr) if (pUnicast == nullptr)
LogPrint(eLogError, "NetIface: GetMTU(): Not a unicast IPv6 address, this is not supported"); LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv6 address, this is not supported");
for (int i = 0; pUnicast != nullptr; ++i) for (int i = 0; pUnicast != nullptr; ++i)
{ {
@ -255,9 +283,13 @@ namespace net
if (found_address) if (found_address)
{ {
auto result = pAddresses->Mtu; char addr[INET6_ADDRSTRLEN];
inetntop(AF_INET6, &(((struct sockaddr_in6 *)localInterfaceAddress)->sin6_addr), addr, INET6_ADDRSTRLEN);
auto result = pCurrAddresses->Mtu;
FREE(pAddresses); FREE(pAddresses);
pAddresses = nullptr; pAddresses = nullptr;
LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv6 address ", addr);
return result; return result;
} }
pUnicast = pUnicast->Next; pUnicast = pUnicast->Next;
@ -266,7 +298,7 @@ namespace net
pCurrAddresses = pCurrAddresses->Next; pCurrAddresses = pCurrAddresses->Next;
} }
LogPrint(eLogError, "NetIface: GetMTU(): No usable unicast IPv6 addresses found"); LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv6 addresses found");
FREE(pAddresses); FREE(pAddresses);
return fallback; return fallback;
} }
@ -298,7 +330,7 @@ namespace net
} }
else else
{ {
LogPrint(eLogError, "NetIface: GetMTU(): Address family is not supported"); LogPrint(eLogError, "NetIface: GetMTU: Address family is not supported");
return fallback; return fallback;
} }
} }
@ -423,6 +455,27 @@ namespace net
#endif #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]) static bool IsYggdrasilAddress (const uint8_t addr[16])
{ {
return addr[0] == 0x02 || addr[0] == 0x03; return addr[0] == 0x02 || addr[0] == 0x03;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -218,6 +218,7 @@ namespace util
namespace net namespace net
{ {
int GetMTU (const boost::asio::ip::address& localAddress); 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); const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6=false);
boost::asio::ip::address_v6 GetYggdrasilAddress (); boost::asio::ip::address_v6 GetYggdrasilAddress ();
bool IsLocalAddress (const boost::asio::ip::address& addr); bool IsLocalAddress (const boost::asio::ip::address& addr);

View File

@ -16,8 +16,8 @@
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c) #define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
#define I2PD_VERSION_MAJOR 2 #define I2PD_VERSION_MAJOR 2
#define I2PD_VERSION_MINOR 42 #define I2PD_VERSION_MINOR 44
#define I2PD_VERSION_MICRO 1 #define I2PD_VERSION_MICRO 0
#define I2PD_VERSION_PATCH 0 #define I2PD_VERSION_PATCH 0
#ifdef GITVER #ifdef GITVER
#define I2PD_VERSION GITVER #define I2PD_VERSION GITVER
@ -31,7 +31,7 @@
#define I2P_VERSION_MAJOR 0 #define I2P_VERSION_MAJOR 0
#define I2P_VERSION_MINOR 9 #define I2P_VERSION_MINOR 9
#define I2P_VERSION_MICRO 54 #define I2P_VERSION_MICRO 56
#define I2P_VERSION_PATCH 0 #define I2P_VERSION_PATCH 0
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO) #define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)

View File

@ -397,6 +397,19 @@ namespace client
return nullptr; return nullptr;
} }
bool AddressBook::RecordExists (const std::string& address, const std::string& jump)
{
auto addr = FindAddress(address);
if (!addr)
return false;
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) void AddressBook::InsertAddress (const std::string& address, const std::string& jump)
{ {
auto pos = jump.find(".b32.i2p"); auto pos = jump.find(".b32.i2p");
@ -567,6 +580,7 @@ namespace client
void AddressBook::LoadLocal () void AddressBook::LoadLocal ()
{ {
if (!m_Storage) return;
std::map<std::string, std::shared_ptr<Address>> localAddresses; std::map<std::string, std::shared_ptr<Address>> localAddresses;
m_Storage->LoadLocal (localAddresses); m_Storage->LoadLocal (localAddresses);
for (const auto& it: localAddresses) for (const auto& it: localAddresses)
@ -819,40 +833,22 @@ namespace client
} }
else else
m_Ident = addr->identHash; m_Ident = addr->identHash;
/* this code block still needs some love */ // save url parts for later use
std::condition_variable newDataReceived; std::string dest_host = url.host;
std::mutex newDataReceivedMutex; int dest_port = url.port ? url.port : 80;
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (m_Ident); // try to create stream to addressbook site
if (!leaseSet) auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (m_Ident, dest_port);
if (!stream)
{ {
std::unique_lock<std::mutex> l(newDataReceivedMutex);
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (m_Ident,
[&newDataReceived, &leaseSet, &newDataReceivedMutex](std::shared_ptr<i2p::data::LeaseSet> ls)
{
leaseSet = ls;
std::unique_lock<std::mutex> l1(newDataReceivedMutex);
newDataReceived.notify_all ();
});
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
{
LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired");
i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (m_Ident, false); // don't notify, because we know it already
return false;
}
}
if (!leaseSet) {
/* still no leaseset found */
LogPrint (eLogError, "Addressbook: LeaseSet for address ", url.host, " not found"); LogPrint (eLogError, "Addressbook: LeaseSet for address ", url.host, " not found");
return false; return false;
} }
if (m_Etag.empty() && m_LastModified.empty()) { if (m_Etag.empty() && m_LastModified.empty())
{
m_Book.GetEtag (m_Ident, m_Etag, m_LastModified); m_Book.GetEtag (m_Ident, m_Etag, m_LastModified);
LogPrint (eLogDebug, "Addressbook: Loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified); LogPrint (eLogDebug, "Addressbook: Loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified);
} }
/* save url parts for later use */ // create http request & send it
std::string dest_host = url.host;
int dest_port = url.port ? url.port : 80;
/* create http request & send it */
i2p::http::HTTPReq req; i2p::http::HTTPReq req;
req.AddHeader("Host", dest_host); req.AddHeader("Host", dest_host);
req.AddHeader("User-Agent", "Wget/1.11.4"); req.AddHeader("User-Agent", "Wget/1.11.4");
@ -863,34 +859,29 @@ namespace client
req.AddHeader("If-None-Match", m_Etag); req.AddHeader("If-None-Match", m_Etag);
if (!m_LastModified.empty()) if (!m_LastModified.empty())
req.AddHeader("If-Modified-Since", m_LastModified); req.AddHeader("If-Modified-Since", m_LastModified);
/* convert url to relative */ // convert url to relative
url.schema = ""; url.schema = "";
url.host = ""; url.host = "";
req.uri = url.to_string(); req.uri = url.to_string();
req.version = "HTTP/1.1"; req.version = "HTTP/1.1";
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port);
std::string request = req.to_string(); std::string request = req.to_string();
stream->Send ((const uint8_t *) request.data(), request.length()); stream->Send ((const uint8_t *) request.data(), request.length());
/* read response */ // read response
std::string response; std::string response;
uint8_t recv_buf[4096]; uint8_t recv_buf[4096];
bool end = false; bool end = false;
int numAttempts = 0; int numAttempts = 0;
while (!end) while (!end)
{ {
stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096), size_t received = stream->Receive (recv_buf, 4096, SUBSCRIPTION_REQUEST_TIMEOUT);
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred) if (received)
{ {
if (bytes_transferred) response.append ((char *)recv_buf, received);
response.append ((char *)recv_buf, bytes_transferred); if (!stream->IsOpen ()) end = true;
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ()) }
else if (!stream->IsOpen ())
end = true; end = true;
newDataReceived.notify_all (); else
},
SUBSCRIPTION_REQUEST_TIMEOUT);
std::unique_lock<std::mutex> l(newDataReceivedMutex);
// wait 1 more second
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT + 1)) == std::cv_status::timeout)
{ {
LogPrint (eLogError, "Addressbook: Subscriptions request timeout expired"); LogPrint (eLogError, "Addressbook: Subscriptions request timeout expired");
numAttempts++; numAttempts++;
@ -900,7 +891,7 @@ namespace client
// process remaining buffer // process remaining buffer
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf)))
response.append ((char *)recv_buf, len); response.append ((char *)recv_buf, len);
/* parse response */ // parse response
i2p::http::HTTPRes res; i2p::http::HTTPRes res;
int res_head_len = res.parse(response); int res_head_len = res.parse(response);
if (res_head_len < 0) if (res_head_len < 0)
@ -913,7 +904,7 @@ namespace client
LogPrint(eLogError, "Addressbook: Incomplete http response from ", dest_host, ", interrupted by timeout"); LogPrint(eLogError, "Addressbook: Incomplete http response from ", dest_host, ", interrupted by timeout");
return false; return false;
} }
/* assert: res_head_len > 0 */ // assert: res_head_len > 0
response.erase(0, res_head_len); response.erase(0, res_head_len);
if (res.code == 304) if (res.code == 304)
{ {
@ -936,7 +927,7 @@ namespace client
LogPrint(eLogError, "Addressbook: Response size mismatch, expected: ", len, ", got: ", response.length(), "bytes"); LogPrint(eLogError, "Addressbook: Response size mismatch, expected: ", len, ", got: ", response.length(), "bytes");
return false; return false;
} }
/* assert: res.code == 200 */ // assert: res.code == 200
auto it = res.headers.find("ETag"); auto it = res.headers.find("ETag");
if (it != res.headers.end()) m_Etag = it->second; if (it != res.headers.end()) m_Etag = it->second;
it = res.headers.find("Last-Modified"); it = res.headers.find("Last-Modified");

View File

@ -90,6 +90,8 @@ namespace client
void InsertAddress (const std::string& address, const std::string& jump); // for jump links void InsertAddress (const std::string& address, const std::string& jump); // for jump links
void InsertFullAddress (std::shared_ptr<const i2p::data::IdentityEx> address); void InsertFullAddress (std::shared_ptr<const i2p::data::IdentityEx> address);
bool RecordExists (const std::string& address, const std::string& jump);
bool LoadHostsFromStream (std::istream& f, bool is_update); 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); void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified);
//This method returns the ".b32.i2p" address //This method returns the ".b32.i2p" address

View File

@ -156,7 +156,7 @@ namespace client
{ {
if (stream) if (stream)
{ {
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), m_Endpoint, m_IsQuiet); auto conn = std::make_shared<I2PTunnelConnection> (this, stream, m_Endpoint, m_IsQuiet);
AddHandler (conn); AddHandler (conn);
conn->Connect (); conn->Connect ();
} }
@ -547,7 +547,7 @@ namespace client
} }
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType); m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType, true);
SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ()); SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
} }

View File

@ -261,7 +261,7 @@ namespace client
static const std::string transient("transient"); static const std::string transient("transient");
if (!filename.compare (0, transient.length (), transient)) // starts with transient if (!filename.compare (0, transient.length (), transient)) // starts with transient
{ {
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created"); LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
return true; return true;
} }
@ -288,7 +288,7 @@ namespace client
else else
{ {
LogPrint (eLogError, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType); LogPrint (eLogError, "Clients: Can't open file ", fullPath, " Creating new one with signature type ", sigType, " crypto type ", cryptoType);
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out); std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
size_t len = keys.GetFullLen (); size_t len = keys.GetFullLen ();
uint8_t * buf = new uint8_t[len]; uint8_t * buf = new uint8_t[len];
@ -328,7 +328,7 @@ namespace client
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType, i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType,
const std::map<std::string, std::string> * params) const std::map<std::string, std::string> * params)
{ {
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
auto localDestination = std::make_shared<RunnableClientDestination> (keys, isPublic, params); auto localDestination = std::make_shared<RunnableClientDestination> (keys, isPublic, params);
AddLocalDestination (localDestination); AddLocalDestination (localDestination);
return localDestination; return localDestination;
@ -339,7 +339,7 @@ namespace client
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType, i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType,
const std::map<std::string, std::string> * params) const std::map<std::string, std::string> * params)
{ {
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType); i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
auto localDestination = std::make_shared<ClientDestination> (service, keys, isPublic, params); auto localDestination = std::make_shared<ClientDestination> (service, keys, isPublic, params);
AddLocalDestination (localDestination); AddLocalDestination (localDestination);
return localDestination; return localDestination;
@ -726,6 +726,7 @@ namespace client
std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, ""); std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, "");
bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true);
bool ssl = section.second.get(I2P_SERVER_TUNNEL_SSL, false);
// I2CP // I2CP
std::map<std::string, std::string> options; std::map<std::string, std::string> options;
@ -804,6 +805,8 @@ namespace client
LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); LogPrint(eLogInfo, "Clients: Disabling loopback address mapping");
serverTunnel->SetUniqueLocal(isUniqueLocal); serverTunnel->SetUniqueLocal(isUniqueLocal);
} }
if (ssl)
serverTunnel->SetSSL (true);
if (accessList.length () > 0) if (accessList.length () > 0)
{ {
std::set<i2p::data::IdentHash> idents; std::set<i2p::data::IdentHash> idents;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -18,6 +18,7 @@
#include "HTTPProxy.h" #include "HTTPProxy.h"
#include "SOCKS.h" #include "SOCKS.h"
#include "I2PTunnel.h" #include "I2PTunnel.h"
#include "UDPTunnel.h"
#include "SAM.h" #include "SAM.h"
#include "BOB.h" #include "BOB.h"
#include "I2CP.h" #include "I2CP.h"
@ -61,7 +62,7 @@ namespace client
const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword"; const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword";
const char I2P_SERVER_TUNNEL_ADDRESS[] = "address"; const char I2P_SERVER_TUNNEL_ADDRESS[] = "address";
const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal"; const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal";
const char I2P_SERVER_TUNNEL_SSL[] = "ssl";
class ClientContext class ClientContext
{ {

View File

@ -36,12 +36,14 @@ namespace proxy {
"reg.i2p", "reg.i2p",
"stats.i2p", "stats.i2p",
"identiguy.i2p", "identiguy.i2p",
"notbob.i2p"
}; };
static const std::map<std::string, std::string> jumpservices = { static const std::map<std::string, std::string> jumpservices = {
{ "reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/" }, { "reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/" },
{ "identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname=" }, { "identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname=" },
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" }, { "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 = static const char *pageHead =
@ -82,6 +84,7 @@ namespace proxy {
void GenericProxyInfo(const std::string& title, const std::string& description); void GenericProxyInfo(const std::string& title, const std::string& description);
void HostNotFound(std::string& host); void HostNotFound(std::string& host);
void SendProxyError(std::string& content); void SendProxyError(std::string& content);
void SendRedirect(std::string& address);
void ForwardToUpstreamProxy(); void ForwardToUpstreamProxy();
void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec); void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec);
@ -207,6 +210,17 @@ namespace proxy {
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1)); std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
} }
void HTTPReqHandler::SendRedirect(std::string& address)
{
i2p::http::HTTPRes res;
res.code = 302;
res.add_header("Location", address);
res.add_header("Connection", "close");
std::string response = res.to_string();
boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(),
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
}
bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64, bool & confirm) bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64, bool & confirm)
{ {
confirm = false; confirm = false;
@ -237,6 +251,7 @@ namespace proxy {
req.RemoveHeader("Via"); req.RemoveHeader("Via");
req.RemoveHeader("From"); req.RemoveHeader("From");
req.RemoveHeader("Forwarded"); req.RemoveHeader("Forwarded");
req.RemoveHeader("DNT"); // Useless DoNotTrack flag
req.RemoveHeader("Accept", "Accept-Encoding"); // Accept*, but Accept-Encoding req.RemoveHeader("Accept", "Accept-Encoding"); // Accept*, but Accept-Encoding
/* drop proxy-disclosing headers */ /* drop proxy-disclosing headers */
req.RemoveHeader("X-Forwarded"); req.RemoveHeader("X-Forwarded");
@ -297,7 +312,14 @@ namespace proxy {
GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported")); GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported"));
return true; return true;
} }
if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm)
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)
{ {
i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump); i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump);
LogPrint (eLogInfo, "HTTPProxy: Added address from addresshelper for ", m_RequestURL.host); LogPrint (eLogInfo, "HTTPProxy: Added address from addresshelper for ", m_RequestURL.host);
@ -313,7 +335,8 @@ namespace proxy {
std::string full_url = m_RequestURL.to_string(); std::string full_url = m_RequestURL.to_string();
std::stringstream ss; std::stringstream ss;
ss << tr("Host") << " " << m_RequestURL.host << " <font color=red>" << tr("already in router's addressbook") << "</font>. "; ss << tr("Host") << " " << m_RequestURL.host << " <font color=red>" << tr("already in router's addressbook") << "</font>. ";
ss << tr("Click here to update record:") << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="); ss << tr(/* tr: The "record" means addressbook's record. That message appears when domain was already added to addressbook, but helper link is opened for it. */ "Click here to update record:" );
ss << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper=");
ss << jump << "&update=true\">" << tr("Continue") << "</a>."; ss << jump << "&update=true\">" << tr("Continue") << "</a>.";
GenericProxyInfo(tr("Addresshelper found"), ss.str()); GenericProxyInfo(tr("Addresshelper found"), ss.str());
return true; /* request processed */ return true; /* request processed */
@ -422,8 +445,8 @@ namespace proxy {
void HTTPReqHandler::ForwardToUpstreamProxy() void HTTPReqHandler::ForwardToUpstreamProxy()
{ {
LogPrint(eLogDebug, "HTTPProxy: Forwarded to upstream"); LogPrint(eLogDebug, "HTTPProxy: Forwarded to upstream");
// build http request
/* build http request */
m_ClientRequestURL = m_RequestURL; m_ClientRequestURL = m_RequestURL;
LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host); LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host);
m_ClientRequestURL.schema = ""; m_ClientRequestURL.schema = "";
@ -431,17 +454,17 @@ namespace proxy {
std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for? std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for?
m_ClientRequest.uri = m_ClientRequestURL.to_string(); m_ClientRequest.uri = m_ClientRequestURL.to_string();
// update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections /* update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections */
if(m_ClientRequest.method != "CONNECT") if(m_ClientRequest.method != "CONNECT")
m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0"); m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0");
m_ClientRequest.write(m_ClientRequestBuffer); m_ClientRequest.write(m_ClientRequestBuffer);
m_ClientRequestBuffer << m_recv_buf.substr(m_req_len); m_ClientRequestBuffer << m_recv_buf.substr(m_req_len);
// assume http if empty schema /* assume http if empty schema */
if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http")
{ {
// handle upstream http proxy /* handle upstream http proxy */
if (!m_ProxyURL.port) m_ProxyURL.port = 80; if (!m_ProxyURL.port) m_ProxyURL.port = 80;
if (m_ProxyURL.is_i2p()) if (m_ProxyURL.is_i2p())
{ {
@ -449,9 +472,9 @@ namespace proxy {
auto auth = i2p::http::CreateBasicAuthorizationString (m_ProxyURL.user, m_ProxyURL.pass); auto auth = i2p::http::CreateBasicAuthorizationString (m_ProxyURL.user, m_ProxyURL.pass);
if (!auth.empty ()) if (!auth.empty ())
{ {
// remove existing authorization if any /* remove existing authorization if any */
m_ClientRequest.RemoveHeader("Proxy-"); m_ClientRequest.RemoveHeader("Proxy-");
// add own http proxy authorization /* add own http proxy authorization */
m_ClientRequest.AddHeader("Proxy-Authorization", auth); m_ClientRequest.AddHeader("Proxy-Authorization", auth);
} }
m_send_buf = m_ClientRequest.to_string(); m_send_buf = m_ClientRequest.to_string();
@ -470,7 +493,7 @@ namespace proxy {
} }
else if (m_ProxyURL.schema == "socks") else if (m_ProxyURL.schema == "socks")
{ {
// handle upstream socks proxy /* handle upstream socks proxy */
if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port)); boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) { m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
@ -479,7 +502,7 @@ namespace proxy {
} }
else else
{ {
// unknown type, complain /* unknown type, complain */
GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string()); GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string());
} }
} }

View File

@ -46,10 +46,13 @@ namespace client
} }
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::asio::ip::tcp::endpoint& target, bool quiet): const boost::asio::ip::tcp::endpoint& target, bool quiet,
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), std::shared_ptr<boost::asio::ssl::context> sslCtx):
m_RemoteEndpoint (target), m_IsQuiet (quiet) I2PServiceHandler(owner), m_Stream (stream), m_RemoteEndpoint (target), m_IsQuiet (quiet)
{ {
m_Socket = std::make_shared<boost::asio::ip::tcp::socket> (owner->GetService ());
if (sslCtx)
m_SSL = std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > (*m_Socket, *sslCtx);
} }
I2PTunnelConnection::~I2PTunnelConnection () I2PTunnelConnection::~I2PTunnelConnection ()
@ -69,7 +72,7 @@ namespace client
Receive (); Receive ();
} }
static boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr) boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr)
{ {
boost::asio::ip::address_v4::bytes_type bytes; boost::asio::ip::address_v4::bytes_type bytes;
const uint8_t * ident = addr; const uint8_t * ident = addr;
@ -80,7 +83,9 @@ namespace client
} }
#ifdef __linux__ #ifdef __linux__
static void MapToLoopback(const std::shared_ptr<boost::asio::ip::tcp::socket> & sock, const i2p::data::IdentHash & addr) static void MapToLoopback(std::shared_ptr<boost::asio::ip::tcp::socket> sock, const i2p::data::IdentHash & addr)
{
if (sock)
{ {
// bind to 127.x.x.x address // bind to 127.x.x.x address
// where x.x.x are first three bytes from ident // where x.x.x are first three bytes from ident
@ -89,15 +94,15 @@ namespace client
sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec); sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec);
if (ec) if (ec)
LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ()); LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ());
}
} }
#endif #endif
void I2PTunnelConnection::Connect (bool isUniqueLocal) void I2PTunnelConnection::Connect (bool isUniqueLocal)
{ {
I2PTunnelSetSocketOptions(m_Socket);
if (m_Socket) if (m_Socket)
{ {
I2PTunnelSetSocketOptions (m_Socket);
#ifdef __linux__ #ifdef __linux__
if (isUniqueLocal && m_RemoteEndpoint.address ().is_v4 () && if (isUniqueLocal && m_RemoteEndpoint.address ().is_v4 () &&
m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127) m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127)
@ -131,6 +136,7 @@ namespace client
void I2PTunnelConnection::Terminate () void I2PTunnelConnection::Terminate ()
{ {
if (Kill()) return; if (Kill()) return;
if (m_SSL) m_SSL = nullptr;
if (m_Stream) if (m_Stream)
{ {
m_Stream->Close (); m_Stream->Close ();
@ -145,12 +151,17 @@ namespace client
void I2PTunnelConnection::Receive () 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), m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (), std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (),
std::placeholders::_1, std::placeholders::_2)); std::placeholders::_1, std::placeholders::_2));
} }
void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) void I2PTunnelConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{ {
if (ecode) if (ecode)
{ {
@ -239,6 +250,10 @@ namespace client
void I2PTunnelConnection::Write (const uint8_t * buf, size_t len) 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 (), 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)); std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
} }
@ -253,6 +268,30 @@ namespace client
else else
{ {
LogPrint (eLogDebug, "I2PTunnel: Connected"); 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 ()
{
if (m_IsQuiet) if (m_IsQuiet)
StreamReceive (); StreamReceive ();
else else
@ -267,7 +306,6 @@ namespace client
} }
Receive (); Receive ();
} }
}
void I2PClientTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) void I2PClientTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
{ {
@ -321,15 +359,24 @@ namespace client
m_HeaderSent = true; m_HeaderSent = true;
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
} }
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<i2p::stream::Stream> stream, I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::asio::ip::tcp::endpoint& target, const std::string& host,
const boost::asio::ip::tcp::endpoint& target, const std::string& host): std::shared_ptr<boost::asio::ssl::context> sslCtx):
I2PTunnelConnection (owner, stream, socket, target), m_Host (host), I2PTunnelConnection (owner, stream, target, true, sslCtx), m_Host (host),
m_HeaderSent (false), m_ResponseHeaderSent (false), m_From (stream->GetRemoteIdentity ()) m_HeaderSent (false), m_ResponseHeaderSent (false), m_From (stream->GetRemoteIdentity ())
{ {
if (sslCtx)
SSL_set_tlsext_host_name(GetSSL ()->native_handle(), host.c_str ());
} }
void I2PServerTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) void I2PServerTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
@ -404,6 +451,13 @@ namespace client
m_HeaderSent = true; m_HeaderSent = true;
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ()); I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
} }
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 ();
}
} }
} }
@ -460,9 +514,9 @@ namespace client
} }
I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass,
const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass): std::shared_ptr<boost::asio::ssl::context> sslCtx):
I2PTunnelConnection (owner, stream, socket, target), m_From (stream->GetRemoteIdentity ()), I2PTunnelConnection (owner, stream, target, true, sslCtx), m_From (stream->GetRemoteIdentity ()),
m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass) m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass)
{ {
} }
@ -473,7 +527,8 @@ namespace client
if (m_NeedsWebIrc) if (m_NeedsWebIrc)
{ {
m_NeedsWebIrc = false; m_NeedsWebIrc = false;
m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " " << GetSocket ()->local_endpoint ().address () << std::endl; m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ())
<< " " << GetSocket ()->local_endpoint ().address () << std::endl;
} }
m_InPacket.clear (); m_InPacket.clear ();
@ -660,6 +715,12 @@ namespace client
void I2PServerTunnel::Stop () void I2PServerTunnel::Stop ()
{ {
if (m_PortDestination)
m_PortDestination->ResetAcceptor ();
auto localDestination = GetLocalDestination ();
if (localDestination)
localDestination->StopAcceptingStreams ();
ClearHandlers (); ClearHandlers ();
} }
@ -733,6 +794,17 @@ namespace client
LogPrint (eLogError, "I2PTunnel: Can't set local address ", localAddress); 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> (boost::asio::ssl::context::sslv23);
m_SSLCtx->set_verify_mode(boost::asio::ssl::context::verify_none);
}
else
m_SSLCtx = nullptr;
}
void I2PServerTunnel::Accept () void I2PServerTunnel::Accept ()
{ {
if (m_PortDestination) if (m_PortDestination)
@ -773,7 +845,7 @@ namespace client
std::shared_ptr<I2PTunnelConnection> I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream) std::shared_ptr<I2PTunnelConnection> I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{ {
return std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint ()); return std::make_shared<I2PTunnelConnection> (this, stream, GetEndpoint (), true, m_SSLCtx);
} }
@ -787,8 +859,7 @@ namespace client
std::shared_ptr<I2PTunnelConnection> I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream) std::shared_ptr<I2PTunnelConnection> I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{ {
return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream, return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream, GetEndpoint (), m_Host, GetSSLCtx ());
std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), m_Host);
} }
I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address, I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address,
@ -801,371 +872,7 @@ namespace client
std::shared_ptr<I2PTunnelConnection> I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream) std::shared_ptr<I2PTunnelConnection> I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{ {
return std::make_shared<I2PTunnelConnectionIRC> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), this->m_WebircPass); return std::make_shared<I2PTunnelConnectionIRC> (this, stream, GetEndpoint (), m_WebircPass, GetSSLCtx ());
}
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)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
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, uint16_t, const uint8_t * buf, size_t len)
{
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<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
auto itr = m_Sessions.begin();
while(itr != m_Sessions.end()) {
if(now - (*itr)->LastActivity >= delta )
itr = m_Sessions.erase(itr);
else
++itr;
}
}
void I2PUDPClientTunnel::ExpireStale(const uint64_t delta)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
std::vector<uint16_t> 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();
for (auto & s : m_Sessions )
{
if (s->Identity.GetLL()[0] == ih.GetLL()[0] && remotePort == s->RemotePort)
{
/** found existing session */
LogPrint(eLogDebug, "UDPServer: Found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32());
return s;
}
}
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;
boost::asio::ip::udp::endpoint ep(addr, 0);
m_Sessions.push_back(std::make_shared<UDPSession>(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort));
auto & back = m_Sessions.back();
return back;
}
UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
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),
SendEndpoint(endpoint),
LastActivity(i2p::util::GetMillisecondsSinceEpoch()),
LocalPort(ourPort),
RemotePort(theirPort)
{
IPSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU ));
memcpy(Identity, to->data(), 32);
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<i2p::client::ClientDestination> localDestination,
boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip) :
m_IsUniqueLocal (true), m_Name (name), m_LocalAddress (localAddress),
m_RemoteEndpoint (forwardTo), m_LocalDest (localDestination), 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));
dgram->SetRawReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
void I2PUDPServerTunnel::Stop ()
{
auto dgram = m_LocalDest->GetDatagramDestination ();
if (dgram) dgram->ResetReceiver ();
}
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPServerTunnel::GetSessions ()
{
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
std::lock_guard<std::mutex> lock (m_SessionsMutex);
for (UDPSessionPtr s: m_Sessions)
{
if (!s->m_Destination) continue;
auto info = s->m_Destination->GetInfoForRemote (s->Identity);
if (!info) continue;
auto sinfo = std::make_shared<DatagramSessionInfo> ();
sinfo->Name = m_Name;
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash> (m_LocalDest->GetIdentHash ().data ());
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash> (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,
boost::asio::ip::udp::endpoint localEndpoint,
std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort, bool gzip) :
m_Name (name), m_RemoteDest (remoteDest), m_LocalDest (localDestination), m_LocalEndpoint (localEndpoint),
m_RemoteIdent (nullptr), m_ResolveThread (nullptr), m_LocalSocket (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));
dgram->SetRawReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
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 ();
m_cancel_resolve = true;
m_Sessions.clear();
if(m_LocalSocket && m_LocalSocket->is_open ())
m_LocalSocket->close ();
if(m_ResolveThread)
{
m_ResolveThread->join ();
delete m_ResolveThread;
m_ResolveThread = nullptr;
}
if (m_RemoteIdent)
{
delete m_RemoteIdent;
m_RemoteIdent = nullptr;
}
}
void I2PUDPClientTunnel::RecvFromLocal ()
{
m_LocalSocket->async_receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU),
m_RecvEndpoint, std::bind (&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2));
}
void I2PUDPClientTunnel::HandleRecvFromLocal (const boost::system::error_code & ec, std::size_t transferred)
{
if (m_cancel_resolve) {
LogPrint (eLogDebug, "UDP Client: Ignoring incomming data: stopping");
return;
}
if (ec) {
LogPrint (eLogError, "UDP Client: Reading from socket error: ", ec.message (), ". Restarting listener...");
RecvFromLocal (); // Restart listener and continue work
return;
}
if (!m_RemoteIdent) {
LogPrint (eLogWarning, "UDP Client: Remote endpoint not resolved yet");
RecvFromLocal ();
return; // drop, remote not resolved
}
auto remotePort = m_RecvEndpoint.port ();
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<UDPConvo> (boost::asio::ip::udp::endpoint (m_RecvEndpoint), 0);
m_Sessions.emplace (remotePort, m_LastSession);
}
m_LastPort = remotePort;
}
// send off to remote i2p destination
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
LogPrint (eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteIdent->ToBase32 (), ":", RemotePort);
auto session = m_LocalDest->GetDatagramDestination ()->GetSession (*m_RemoteIdent);
if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
m_LocalDest->GetDatagramDestination ()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
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_RemoteIdent->ToBase32 ());
m_LocalDest->GetDatagramDestination ()->FlushSendQueue (session);
// mark convo as active
if (m_LastSession)
m_LastSession->second = ts;
RecvFromLocal ();
}
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions ()
{
// TODO: implement
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
return infos;
}
void I2PUDPClientTunnel::TryResolving ()
{
i2p::util::SetThreadName ("UDP Resolver");
LogPrint (eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
std::shared_ptr<const Address> addr;
while (!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve)
{
LogPrint (eLogWarning, "UDP Tunnel: Failed to lookup ", m_RemoteDest);
std::this_thread::sleep_for (std::chrono::seconds (1));
}
if (m_cancel_resolve)
{
LogPrint(eLogError, "UDP Tunnel: Lookup of ", m_RemoteDest, " was cancelled");
return;
}
if (!addr || !addr->IsIdentHash ())
{
LogPrint (eLogError, "UDP Tunnel: ", m_RemoteDest, " not found");
return;
}
m_RemoteIdent = new i2p::data::IdentHash;
*m_RemoteIdent = addr->identHash;
LogPrint(eLogInfo, "UDP Tunnel: Resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32 ());
}
void I2PUDPClientTunnel::HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
{
if (m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
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_RemoteIdent ? m_RemoteIdent->ToBase32 () : "");
m_LocalSocket->send_to (boost::asio::buffer (buf, len), itr->second->first);
// mark convo as active
itr->second->second = i2p::util::GetMillisecondsSinceEpoch ();
}
}
else
LogPrint (eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort);
} }
} }
} }

View File

@ -16,9 +16,9 @@
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include "Identity.h" #include "Identity.h"
#include "Destination.h" #include "Destination.h"
#include "Datagram.h"
#include "Streaming.h" #include "Streaming.h"
#include "I2PService.h" #include "I2PService.h"
#include "AddressBook.h" #include "AddressBook.h"
@ -34,6 +34,7 @@ namespace client
const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64 const char X_I2P_DEST_HASH[] = "X-I2P-DestHash"; // hash in base64
const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64 const char X_I2P_DEST_B64[] = "X-I2P-DestB64"; // full address in base64
const char X_I2P_DEST_B32[] = "X-I2P-DestB32"; // .b32.i2p address const 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<I2PTunnelConnection> class I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this<I2PTunnelConnection>
{ {
@ -43,8 +44,9 @@ namespace client
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port = 0); // to I2P std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port = 0); // to I2P
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket, I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, std::shared_ptr<boost::asio::ip::tcp::socket> socket, I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P const boost::asio::ip::tcp::endpoint& target, bool quiet = true,
std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr); // from I2P
~I2PTunnelConnection (); ~I2PTunnelConnection ();
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
void Connect (bool isUniqueLocal = true); void Connect (bool isUniqueLocal = true);
@ -55,21 +57,27 @@ namespace client
void Terminate (); void Terminate ();
void Receive (); void Receive ();
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void StreamReceive ();
virtual void Write (const uint8_t * buf, size_t len); // can be overloaded virtual void Write (const uint8_t * buf, size_t len); // can be overloaded
void HandleWrite (const boost::system::error_code& ecode);
virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded
void StreamReceive (); std::shared_ptr<boost::asio::ip::tcp::socket> GetSocket () const { return m_Socket; };
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > GetSSL () const { return m_SSL; };
void HandleConnect (const boost::system::error_code& ecode);
std::shared_ptr<const boost::asio::ip::tcp::socket> GetSocket () const { return m_Socket; }; private:
void HandleConnect (const boost::system::error_code& ecode);
void HandleHandshake (const boost::system::error_code& ecode);
void Established ();
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleWrite (const boost::system::error_code& ecode);
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
private: private:
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE]; uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket; std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > m_SSL;
std::shared_ptr<i2p::stream::Stream> m_Stream; std::shared_ptr<i2p::stream::Stream> m_Stream;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint; boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsQuiet; // don't send destination bool m_IsQuiet; // don't send destination
@ -99,8 +107,8 @@ namespace client
public: public:
I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::asio::ip::tcp::endpoint& target, const std::string& host,
const boost::asio::ip::tcp::endpoint& target, const std::string& host); std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr);
protected: protected:
@ -120,8 +128,8 @@ namespace client
public: public:
I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass,
const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass); std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr);
protected: protected:
@ -171,162 +179,6 @@ namespace client
std::unique_ptr<boost::asio::deadline_timer> m_KeepAliveTimer; std::unique_ptr<boost::asio::deadline_timer> m_KeepAliveTimer;
}; };
/** 2 minute timeout for udp sessions */
const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2;
const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds
/** 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<i2p::client::ClientDestination> & localDestination,
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<const i2p::data::IdentHash> LocalIdent;
/** ident hash of remote destination */
std::shared_ptr<const i2p::data::IdentHash> RemoteIdent;
/** ident hash of IBGW in use currently in this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> CurrentIBGW;
/** ident hash of OBEP in use for this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> 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 converstation been idle in ms */
uint64_t idle;
};
typedef std::shared_ptr<UDPSession> UDPSessionPtr;
/** server side udp tunnel, many i2p inbound to 1 ip outbound */
class I2PUDPServerTunnel
{
public:
I2PUDPServerTunnel (const std::string & name,
std::shared_ptr<i2p::client::ClientDestination> localDestination,
boost::asio::ip::address localAddress,
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<std::shared_ptr<DatagramSessionInfo> > GetSessions ();
std::shared_ptr<ClientDestination> 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);
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::vector<UDPSessionPtr> m_Sessions;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
UDPSessionPtr m_LastSession;
bool m_Gzip;
public:
bool isUpdated; // transient, used during reload only
};
class I2PUDPClientTunnel
{
public:
I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest,
boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort, bool gzip);
~I2PUDPClientTunnel ();
void Start ();
void Stop ();
const char * GetName () const { return m_Name.c_str(); }
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions ();
bool IsLocalDestination (const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
inline void SetLocalDestination (std::shared_ptr<ClientDestination> 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<boost::asio::ip::udp::endpoint, uint64_t> 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<uint16_t, std::shared_ptr<UDPConvo> > m_Sessions; // maps i2p port -> local udp convo
const std::string m_RemoteDest;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
i2p::data::IdentHash * m_RemoteIdent;
std::thread * m_ResolveThread;
std::unique_ptr<boost::asio::ip::udp::socket> 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<UDPConvo> m_LastSession;
public:
bool isUpdated; // transient, used during reload only
};
class I2PServerTunnel: public I2PService class I2PServerTunnel: public I2PService
{ {
public: public:
@ -342,6 +194,9 @@ namespace client
void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; } void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; }
bool IsUniqueLocal () const { return m_IsUniqueLocal; } bool IsUniqueLocal () const { return m_IsUniqueLocal; }
void SetSSL (bool ssl);
std::shared_ptr<boost::asio::ssl::context> GetSSLCtx () const { return m_SSLCtx; };
void SetLocalAddress (const std::string& localAddress); void SetLocalAddress (const std::string& localAddress);
const std::string& GetAddress() const { return m_Address; } const std::string& GetAddress() const { return m_Address; }
@ -370,6 +225,7 @@ namespace client
std::set<i2p::data::IdentHash> m_AccessList; std::set<i2p::data::IdentHash> m_AccessList;
bool m_IsAccessList; bool m_IsAccessList;
std::unique_ptr<boost::asio::ip::address> m_LocalAddress; std::unique_ptr<boost::asio::ip::address> m_LocalAddress;
std::shared_ptr<boost::asio::ssl::context> m_SSLCtx;
}; };
class I2PServerTunnelHTTP: public I2PServerTunnel class I2PServerTunnelHTTP: public I2PServerTunnel
@ -405,6 +261,8 @@ namespace client
std::string m_WebircPass; std::string m_WebircPass;
}; };
boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2022, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@ -709,7 +709,7 @@ namespace client
LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ()); LogPrint (eLogWarning, "SAM: ", SAM_PARAM_CRYPTO_TYPE, "error: ", ex.what ());
} }
} }
auto keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType); auto keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType, true);
#ifdef _MSC_VER #ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
@ -1477,15 +1477,22 @@ namespace client
*destination = 0; destination++; *destination = 0; destination++;
auto session = FindSession (sessionID); auto session = FindSession (sessionID);
if (session) if (session)
{
auto localDest = session->GetLocalDestination ();
auto datagramDest = localDest ? localDest->GetDatagramDestination () : nullptr;
if (datagramDest)
{ {
i2p::data::IdentityEx dest; i2p::data::IdentityEx dest;
dest.FromBase64 (destination); dest.FromBase64 (destination);
if (session->Type == eSAMSessionTypeDatagram) if (session->Type == eSAMSessionTypeDatagram)
session->GetLocalDestination ()->GetDatagramDestination ()-> datagramDest->SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); else if (session->Type == eSAMSessionTypeRaw)
else // raw datagramDest->SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
session->GetLocalDestination ()->GetDatagramDestination ()-> else
SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); 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 else
LogPrint (eLogError, "SAM: Session ", sessionID, " not found"); LogPrint (eLogError, "SAM: Session ", sessionID, " not found");

View File

@ -0,0 +1,377 @@
/*
* 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 "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)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
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, uint16_t, const uint8_t * buf, size_t len)
{
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<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
auto itr = m_Sessions.begin();
while(itr != m_Sessions.end()) {
if(now - (*itr)->LastActivity >= delta )
itr = m_Sessions.erase(itr);
else
++itr;
}
}
void I2PUDPClientTunnel::ExpireStale(const uint64_t delta)
{
std::lock_guard<std::mutex> lock(m_SessionsMutex);
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
std::vector<uint16_t> 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();
for (auto & s : m_Sessions )
{
if (s->Identity.GetLL()[0] == ih.GetLL()[0] && remotePort == s->RemotePort)
{
/** found existing session */
LogPrint(eLogDebug, "UDPServer: Found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32());
return s;
}
}
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;
boost::asio::ip::udp::endpoint ep(addr, 0);
m_Sessions.push_back(std::make_shared<UDPSession>(ep, m_LocalDest, m_RemoteEndpoint, ih, localPort, remotePort));
auto & back = m_Sessions.back();
return back;
}
UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
const std::shared_ptr<i2p::client::ClientDestination> & 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<i2p::client::ClientDestination> localDestination,
const boost::asio::ip::address& localAddress, const boost::asio::ip::udp::endpoint& forwardTo, uint16_t port, bool gzip) :
m_IsUniqueLocal (true), m_Name (name), m_LocalAddress (localAddress),
m_RemoteEndpoint (forwardTo), m_LocalDest (localDestination), 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));
dgram->SetRawReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
void I2PUDPServerTunnel::Stop ()
{
auto dgram = m_LocalDest->GetDatagramDestination ();
if (dgram) dgram->ResetReceiver ();
}
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPServerTunnel::GetSessions ()
{
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
std::lock_guard<std::mutex> lock (m_SessionsMutex);
for (UDPSessionPtr s: m_Sessions)
{
if (!s->m_Destination) continue;
auto info = s->m_Destination->GetInfoForRemote (s->Identity);
if (!info) continue;
auto sinfo = std::make_shared<DatagramSessionInfo> ();
sinfo->Name = m_Name;
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash> (m_LocalDest->GetIdentHash ().data ());
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash> (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<i2p::client::ClientDestination> 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));
dgram->SetRawReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
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 ();
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 incomming 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<UDPConvo> (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<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions ()
{
// TODO: implement
std::vector<std::shared_ptr<DatagramSessionInfo> > 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);
}
}
}

187
libi2pd_client/UDPTunnel.h Normal file
View File

@ -0,0 +1,187 @@
/*
* 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 UDPTUNNEL_H__
#define UDPTUNNEL_H__
#include <inttypes.h>
#include <string>
#include <memory>
#include <thread>
#include <vector>
#include <unordered_map>
#include <boost/asio.hpp>
#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<i2p::client::ClientDestination> & 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<const i2p::data::IdentHash> LocalIdent;
/** ident hash of remote destination */
std::shared_ptr<const i2p::data::IdentHash> RemoteIdent;
/** ident hash of IBGW in use currently in this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> CurrentIBGW;
/** ident hash of OBEP in use for this session or nullptr if none is set */
std::shared_ptr<const i2p::data::IdentHash> 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 converstation been idle in ms */
uint64_t idle;
};
typedef std::shared_ptr<UDPSession> UDPSessionPtr;
/** server side udp tunnel, many i2p inbound to 1 ip outbound */
class I2PUDPServerTunnel
{
public:
I2PUDPServerTunnel (const std::string & name,
std::shared_ptr<i2p::client::ClientDestination> 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<std::shared_ptr<DatagramSessionInfo> > GetSessions ();
std::shared_ptr<ClientDestination> 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);
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::vector<UDPSessionPtr> m_Sessions;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
UDPSessionPtr m_LastSession;
bool m_Gzip;
public:
bool isUpdated; // transient, used during reload only
};
class I2PUDPClientTunnel
{
public:
I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest,
const boost::asio::ip::udp::endpoint& localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
uint16_t remotePort, bool gzip);
~I2PUDPClientTunnel ();
void Start ();
void Stop ();
const char * GetName () const { return m_Name.c_str(); }
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions ();
bool IsLocalDestination (const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
inline void SetLocalDestination (std::shared_ptr<ClientDestination> 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<boost::asio::ip::udp::endpoint, uint64_t> 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<uint16_t, std::shared_ptr<UDPConvo> > m_Sessions; // maps i2p port -> local udp convo
const std::string m_RemoteDest;
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
std::shared_ptr<const Address> m_RemoteAddr;
std::thread * m_ResolveThread;
std::unique_ptr<boost::asio::ip::udp::socket> 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<UDPConvo> m_LastSession;
public:
bool isUpdated; // transient, used during reload only
};
}
}
#endif