mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-01-24 06:17:16 +01:00
commit
74b2ba7ae2
32
.github/workflows/build-deb.yml
vendored
Normal file
32
.github/workflows/build-deb.yml
vendored
Normal 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
|
11
.github/workflows/build-freebsd.yml
vendored
11
.github/workflows/build-freebsd.yml
vendored
|
@ -4,18 +4,25 @@ on: [push, pull_request]
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-10.15
|
||||
runs-on: macos-12
|
||||
name: with UPnP
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Test in FreeBSD
|
||||
id: test
|
||||
uses: vmactions/freebsd-vm@v0.1.5
|
||||
uses: vmactions/freebsd-vm@v0.3.0
|
||||
with:
|
||||
usesh: true
|
||||
mem: 2048
|
||||
sync: rsync
|
||||
copyback: true
|
||||
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
|
||||
run: |
|
||||
cd build
|
||||
cmake -DWITH_UPNP=ON -DCMAKE_BUILD_TYPE=Release .
|
||||
gmake -j2
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: i2pd-freebsd
|
||||
path: build/i2pd
|
48
.github/workflows/build.yml
vendored
48
.github/workflows/build.yml
vendored
|
@ -38,51 +38,3 @@ jobs:
|
|||
cd build
|
||||
cmake -DWITH_UPNP=${{ matrix.with_upnp }} .
|
||||
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 }}
|
||||
|
|
118
.github/workflows/docker.yml
vendored
118
.github/workflows/docker.yml
vendored
|
@ -4,67 +4,137 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- openssl
|
||||
- docker
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include: [
|
||||
{ platform: 'linux/amd64', archname: 'amd64' },
|
||||
{ platform: 'linux/386', archname: 'i386' },
|
||||
{ platform: 'linux/arm64', archname: 'arm64' },
|
||||
{ platform: 'linux/arm/v7', archname: 'armv7' },
|
||||
]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container registry
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push trunk container
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: docker/build-push-action@v2
|
||||
- name: Build container for ${{ matrix.archname }}
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./contrib/docker
|
||||
file: ./contrib/docker/Dockerfile
|
||||
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
|
||||
platforms: ${{ matrix.platform }}
|
||||
push: true
|
||||
tags: |
|
||||
purplei2p/i2pd:latest
|
||||
ghcr.io/purplei2p/i2pd:latest
|
||||
purplei2p/i2pd:latest-${{ matrix.archname }}
|
||||
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/') }}
|
||||
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/') }}
|
||||
uses: docker/build-push-action@v2
|
||||
uses: Noelware/docker-manifest-action@master
|
||||
with:
|
||||
context: ./contrib/docker
|
||||
file: ./contrib/docker/Dockerfile
|
||||
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7
|
||||
base-image: purplei2p/i2pd:latest-release
|
||||
extra-images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||
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
|
||||
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 }}
|
||||
|
|
60
ChangeLog
60
ChangeLog
|
@ -1,6 +1,66 @@
|
|||
# for this file format description,
|
||||
# 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
|
||||
### Fixed
|
||||
- Incorrect jump link in HTTP Proxy
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -154,9 +154,10 @@ namespace win32
|
|||
case eRouterStatusUnknown: s << "Unk"; break;
|
||||
case eRouterStatusProxy: s << "Proxy"; break;
|
||||
case eRouterStatusMesh: s << "Mesh"; break;
|
||||
case eRouterStatusError:
|
||||
default: s << "Unk";
|
||||
};
|
||||
if (i2p::context.GetError () != eRouterErrorNone)
|
||||
{
|
||||
s << "Err";
|
||||
switch (i2p::context.GetError ())
|
||||
{
|
||||
case eRouterErrorClockSkew:
|
||||
|
@ -170,9 +171,6 @@ namespace win32
|
|||
break;
|
||||
default: ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: s << "Unk";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ set(DAEMON_SRC
|
|||
"${DAEMON_SRC_DIR}/Daemon.cpp"
|
||||
"${DAEMON_SRC_DIR}/HTTPServer.cpp"
|
||||
"${DAEMON_SRC_DIR}/I2PControl.cpp"
|
||||
"${DAEMON_SRC_DIR}/I2PControlHandlers.cpp"
|
||||
"${DAEMON_SRC_DIR}/i2pd.cpp"
|
||||
"${DAEMON_SRC_DIR}/UPnP.cpp"
|
||||
)
|
||||
|
@ -171,6 +172,13 @@ if(WITH_THREADSANITIZER)
|
|||
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
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
|
|
@ -61,7 +61,7 @@ set(archdetect_c_code "
|
|||
#else
|
||||
#error cmake_ARCH mips
|
||||
#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(_M_MPPC) || defined(_M_PPC)
|
||||
#if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)
|
||||
|
|
|
@ -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
|
||||
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
|
||||
ARG GIT_BRANCH="openssl"
|
||||
|
@ -11,27 +24,28 @@ ENV REPO_URL=${REPO_URL}
|
|||
|
||||
ENV I2PD_HOME="/home/i2pd"
|
||||
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" \
|
||||
&& adduser -S -h "$I2PD_HOME" i2pd \
|
||||
&& chown -R i2pd:nobody "$I2PD_HOME"
|
||||
|
||||
#
|
||||
|
||||
# 1. Building binary
|
||||
# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the
|
||||
# image under 20mb we need to remove all the build dependencies in the same "RUN" / layer.
|
||||
#
|
||||
|
||||
# 1. install deps, clone and build.
|
||||
# 2. strip binaries.
|
||||
# 3. Purge all dependencies and other unrelated packages, including build directory.
|
||||
|
||||
RUN apk update \
|
||||
&& apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl miniupnpc-dev git \
|
||||
&& mkdir -p /tmp/build \
|
||||
&& cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \
|
||||
&& cd i2pd \
|
||||
&& if [ -n "${GIT_TAG}" ]; then git checkout tags/${GIT_TAG}; fi \
|
||||
&& make USE_UPNP=yes \
|
||||
&& make -j$(nproc) USE_UPNP=yes \
|
||||
&& cp -R contrib/certificates /i2pd_certificates \
|
||||
&& mkdir -p /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.
|
||||
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
|
||||
RUN chmod a+x /entrypoint.sh
|
||||
|
||||
|
|
52
contrib/docker/i2pd-docker.conf
Normal file
52
contrib/docker/i2pd-docker.conf
Normal 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
|
|
@ -1,13 +1,13 @@
|
|||
# i2pd
|
||||
# Copyright (C) 2021 PurpleI2P team
|
||||
# Copyright (C) 2021-2022 PurpleI2P team
|
||||
# This file is distributed under the same license as the i2pd package.
|
||||
# R4SAS <r4sas@i2pmail.org>, 2021.
|
||||
# R4SAS <r4sas@i2pmail.org>, 2021-2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: i2pd\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n"
|
||||
"POT-Creation-Date: 2021-08-06 17:12\n"
|
||||
"POT-Creation-Date: 2022-07-26 21:22\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
@ -18,706 +18,712 @@ msgstr ""
|
|||
"X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n"
|
||||
"X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n"
|
||||
|
||||
#: daemon/HTTPServer.cpp:177
|
||||
#: daemon/HTTPServer.cpp:108
|
||||
msgid "day"
|
||||
msgid_plural "days"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:181
|
||||
#: daemon/HTTPServer.cpp:112
|
||||
msgid "hour"
|
||||
msgid_plural "hours"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:185
|
||||
#: daemon/HTTPServer.cpp:116
|
||||
msgid "minute"
|
||||
msgid_plural "minutes"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:188
|
||||
#: daemon/HTTPServer.cpp:119
|
||||
msgid "second"
|
||||
msgid_plural "seconds"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#. tr: Kibibit
|
||||
#: daemon/HTTPServer.cpp:196 daemon/HTTPServer.cpp:224
|
||||
#: daemon/HTTPServer.cpp:127 daemon/HTTPServer.cpp:155
|
||||
msgid "KiB"
|
||||
msgstr ""
|
||||
|
||||
#. tr: Mebibit
|
||||
#: daemon/HTTPServer.cpp:198
|
||||
#: daemon/HTTPServer.cpp:129
|
||||
msgid "MiB"
|
||||
msgstr ""
|
||||
|
||||
#. tr: Gibibit
|
||||
#: daemon/HTTPServer.cpp:200
|
||||
#: daemon/HTTPServer.cpp:131
|
||||
msgid "GiB"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:217
|
||||
#: daemon/HTTPServer.cpp:148
|
||||
msgid "building"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:218
|
||||
#: daemon/HTTPServer.cpp:149
|
||||
msgid "failed"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:219
|
||||
#: daemon/HTTPServer.cpp:150
|
||||
msgid "expiring"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:220
|
||||
#: daemon/HTTPServer.cpp:151
|
||||
msgid "established"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:221
|
||||
#: daemon/HTTPServer.cpp:152
|
||||
msgid "unknown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:223
|
||||
#: daemon/HTTPServer.cpp:154
|
||||
msgid "exploratory"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:259
|
||||
#. tr: Webconsole page title
|
||||
#: daemon/HTTPServer.cpp:185
|
||||
msgid "Purple I2P Webconsole"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:190
|
||||
msgid "<b>i2pd</b> webconsole"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:262
|
||||
#: daemon/HTTPServer.cpp:193
|
||||
msgid "Main page"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:725
|
||||
#: daemon/HTTPServer.cpp:194 daemon/HTTPServer.cpp:700
|
||||
msgid "Router commands"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:448
|
||||
#: daemon/HTTPServer.cpp:460
|
||||
#: daemon/HTTPServer.cpp:195 daemon/HTTPServer.cpp:382
|
||||
#: daemon/HTTPServer.cpp:394
|
||||
msgid "Local Destinations"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:266 daemon/HTTPServer.cpp:418
|
||||
#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:510
|
||||
#: daemon/HTTPServer.cpp:641 daemon/HTTPServer.cpp:684
|
||||
#: daemon/HTTPServer.cpp:688
|
||||
#: daemon/HTTPServer.cpp:197 daemon/HTTPServer.cpp:352
|
||||
#: daemon/HTTPServer.cpp:438 daemon/HTTPServer.cpp:444
|
||||
#: daemon/HTTPServer.cpp:597 daemon/HTTPServer.cpp:640
|
||||
#: daemon/HTTPServer.cpp:644
|
||||
msgid "LeaseSets"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:268 daemon/HTTPServer.cpp:694
|
||||
#: daemon/HTTPServer.cpp:199 daemon/HTTPServer.cpp:650
|
||||
msgid "Tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:269 daemon/HTTPServer.cpp:425
|
||||
#: daemon/HTTPServer.cpp:787 daemon/HTTPServer.cpp:803
|
||||
#: daemon/HTTPServer.cpp:201 daemon/HTTPServer.cpp:359
|
||||
#: daemon/HTTPServer.cpp:770 daemon/HTTPServer.cpp:786
|
||||
msgid "Transit Tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:270 daemon/HTTPServer.cpp:852
|
||||
#: daemon/HTTPServer.cpp:203 daemon/HTTPServer.cpp:839
|
||||
msgid "Transports"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:271
|
||||
#: daemon/HTTPServer.cpp:204
|
||||
msgid "I2P tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:914
|
||||
#: daemon/HTTPServer.cpp:924
|
||||
#: daemon/HTTPServer.cpp:206 daemon/HTTPServer.cpp:908
|
||||
#: daemon/HTTPServer.cpp:918
|
||||
msgid "SAM sessions"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:289 daemon/HTTPServer.cpp:1306
|
||||
#: daemon/HTTPServer.cpp:1309 daemon/HTTPServer.cpp:1312
|
||||
#: daemon/HTTPServer.cpp:1326 daemon/HTTPServer.cpp:1371
|
||||
#: daemon/HTTPServer.cpp:1374 daemon/HTTPServer.cpp:1377
|
||||
#: daemon/HTTPServer.cpp:222 daemon/HTTPServer.cpp:1302
|
||||
#: daemon/HTTPServer.cpp:1305 daemon/HTTPServer.cpp:1308
|
||||
#: daemon/HTTPServer.cpp:1322 daemon/HTTPServer.cpp:1367
|
||||
#: daemon/HTTPServer.cpp:1370 daemon/HTTPServer.cpp:1373
|
||||
msgid "ERROR"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:296
|
||||
#: daemon/HTTPServer.cpp:229
|
||||
msgid "OK"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:297
|
||||
#: daemon/HTTPServer.cpp:230
|
||||
msgid "Testing"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:298
|
||||
#: daemon/HTTPServer.cpp:231
|
||||
msgid "Firewalled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:299 daemon/HTTPServer.cpp:320
|
||||
#: daemon/HTTPServer.cpp:406
|
||||
#: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:253
|
||||
#: daemon/HTTPServer.cpp:325
|
||||
msgid "Unknown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:300 daemon/HTTPServer.cpp:435
|
||||
#: daemon/HTTPServer.cpp:436 daemon/HTTPServer.cpp:982
|
||||
#: daemon/HTTPServer.cpp:991
|
||||
#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:369
|
||||
#: daemon/HTTPServer.cpp:370 daemon/HTTPServer.cpp:976
|
||||
#: daemon/HTTPServer.cpp:985
|
||||
msgid "Proxy"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:301
|
||||
#: daemon/HTTPServer.cpp:234
|
||||
msgid "Mesh"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:304
|
||||
#: daemon/HTTPServer.cpp:237
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:308
|
||||
#: daemon/HTTPServer.cpp:241
|
||||
msgid "Clock skew"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:311
|
||||
#: daemon/HTTPServer.cpp:244
|
||||
msgid "Offline"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:314
|
||||
#: daemon/HTTPServer.cpp:247
|
||||
msgid "Symmetric NAT"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:326
|
||||
#: daemon/HTTPServer.cpp:259
|
||||
msgid "Uptime"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:329
|
||||
#: daemon/HTTPServer.cpp:262
|
||||
msgid "Network status"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:334
|
||||
#: daemon/HTTPServer.cpp:267
|
||||
msgid "Network status v6"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:340 daemon/HTTPServer.cpp:347
|
||||
#: daemon/HTTPServer.cpp:273 daemon/HTTPServer.cpp:280
|
||||
msgid "Stopping in"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:354
|
||||
#: daemon/HTTPServer.cpp:287
|
||||
msgid "Family"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:355
|
||||
#: daemon/HTTPServer.cpp:288
|
||||
msgid "Tunnel creation success rate"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:356
|
||||
#: daemon/HTTPServer.cpp:289
|
||||
msgid "Received"
|
||||
msgstr ""
|
||||
|
||||
#. tr: Kibibit/s
|
||||
#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:361
|
||||
#: daemon/HTTPServer.cpp:364
|
||||
#: daemon/HTTPServer.cpp:291 daemon/HTTPServer.cpp:294
|
||||
#: daemon/HTTPServer.cpp:297
|
||||
msgid "KiB/s"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:359
|
||||
#: daemon/HTTPServer.cpp:292
|
||||
msgid "Sent"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:362
|
||||
#: daemon/HTTPServer.cpp:295
|
||||
msgid "Transit"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:365
|
||||
#: daemon/HTTPServer.cpp:298
|
||||
msgid "Data path"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:368
|
||||
#: daemon/HTTPServer.cpp:301
|
||||
msgid "Hidden content. Press on text to see."
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:371
|
||||
#: daemon/HTTPServer.cpp:304
|
||||
msgid "Router Ident"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:373
|
||||
#: daemon/HTTPServer.cpp:306
|
||||
msgid "Router Family"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:374
|
||||
#: daemon/HTTPServer.cpp:307
|
||||
msgid "Router Caps"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:375
|
||||
#: daemon/HTTPServer.cpp:308
|
||||
msgid "Version"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:376
|
||||
#: daemon/HTTPServer.cpp:309
|
||||
msgid "Our external address"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:384
|
||||
#: daemon/HTTPServer.cpp:337
|
||||
msgid "supported"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:416
|
||||
#: daemon/HTTPServer.cpp:350
|
||||
msgid "Routers"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:417
|
||||
#: daemon/HTTPServer.cpp:351
|
||||
msgid "Floodfills"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:424 daemon/HTTPServer.cpp:968
|
||||
#: daemon/HTTPServer.cpp:358 daemon/HTTPServer.cpp:962
|
||||
msgid "Client Tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:434
|
||||
#: daemon/HTTPServer.cpp:368
|
||||
msgid "Services"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
|
||||
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
|
||||
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
|
||||
#: daemon/HTTPServer.cpp:369 daemon/HTTPServer.cpp:370
|
||||
#: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:372
|
||||
#: daemon/HTTPServer.cpp:373 daemon/HTTPServer.cpp:374
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:435 daemon/HTTPServer.cpp:436
|
||||
#: daemon/HTTPServer.cpp:437 daemon/HTTPServer.cpp:438
|
||||
#: daemon/HTTPServer.cpp:439 daemon/HTTPServer.cpp:440
|
||||
#: daemon/HTTPServer.cpp:369 daemon/HTTPServer.cpp:370
|
||||
#: daemon/HTTPServer.cpp:371 daemon/HTTPServer.cpp:372
|
||||
#: daemon/HTTPServer.cpp:373 daemon/HTTPServer.cpp:374
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:483
|
||||
#: daemon/HTTPServer.cpp:417
|
||||
msgid "Encrypted B33 address"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:492
|
||||
#: daemon/HTTPServer.cpp:426
|
||||
msgid "Address registration line"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:497
|
||||
#: daemon/HTTPServer.cpp:431
|
||||
msgid "Domain"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:498
|
||||
#: daemon/HTTPServer.cpp:432
|
||||
msgid "Generate"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:499
|
||||
#: daemon/HTTPServer.cpp:433
|
||||
msgid ""
|
||||
"<b>Note:</b> result string can be used only for registering 2LD domains "
|
||||
"(example.i2p). For registering subdomains please use i2pd-tools."
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:505
|
||||
#: daemon/HTTPServer.cpp:439
|
||||
msgid "Address"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:505
|
||||
#: daemon/HTTPServer.cpp:439
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:505
|
||||
#: daemon/HTTPServer.cpp:439
|
||||
msgid "EncType"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:515 daemon/HTTPServer.cpp:699
|
||||
#: daemon/HTTPServer.cpp:449 daemon/HTTPServer.cpp:655
|
||||
msgid "Inbound tunnels"
|
||||
msgstr ""
|
||||
|
||||
#. tr: Milliseconds
|
||||
#: daemon/HTTPServer.cpp:520 daemon/HTTPServer.cpp:530
|
||||
#: daemon/HTTPServer.cpp:704 daemon/HTTPServer.cpp:714
|
||||
#: daemon/HTTPServer.cpp:464 daemon/HTTPServer.cpp:484
|
||||
#: daemon/HTTPServer.cpp:669 daemon/HTTPServer.cpp:689
|
||||
msgid "ms"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:525 daemon/HTTPServer.cpp:709
|
||||
#: daemon/HTTPServer.cpp:469 daemon/HTTPServer.cpp:674
|
||||
msgid "Outbound tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:537
|
||||
#: daemon/HTTPServer.cpp:491
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:537
|
||||
#: daemon/HTTPServer.cpp:491
|
||||
msgid "Incoming"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:544 daemon/HTTPServer.cpp:547
|
||||
#: daemon/HTTPServer.cpp:498 daemon/HTTPServer.cpp:501
|
||||
msgid "Outgoing"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:545 daemon/HTTPServer.cpp:561
|
||||
#: daemon/HTTPServer.cpp:499 daemon/HTTPServer.cpp:515
|
||||
msgid "Destination"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:545
|
||||
#: daemon/HTTPServer.cpp:499
|
||||
msgid "Amount"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:552
|
||||
#: daemon/HTTPServer.cpp:506
|
||||
msgid "Incoming Tags"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:560 daemon/HTTPServer.cpp:563
|
||||
#: daemon/HTTPServer.cpp:514 daemon/HTTPServer.cpp:517
|
||||
msgid "Tags sessions"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:561
|
||||
#: daemon/HTTPServer.cpp:515
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:570 daemon/HTTPServer.cpp:626
|
||||
#: daemon/HTTPServer.cpp:524 daemon/HTTPServer.cpp:582
|
||||
msgid "Local Destination"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:580 daemon/HTTPServer.cpp:947
|
||||
#: daemon/HTTPServer.cpp:535 daemon/HTTPServer.cpp:941
|
||||
msgid "Streams"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:602
|
||||
#: daemon/HTTPServer.cpp:558
|
||||
msgid "Close stream"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:631
|
||||
#: daemon/HTTPServer.cpp:587
|
||||
msgid "I2CP session not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:634
|
||||
#: daemon/HTTPServer.cpp:590
|
||||
msgid "I2CP is not enabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:660
|
||||
#: daemon/HTTPServer.cpp:616
|
||||
msgid "Invalid"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:663
|
||||
#: daemon/HTTPServer.cpp:619
|
||||
msgid "Store type"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:664
|
||||
#: daemon/HTTPServer.cpp:620
|
||||
msgid "Expires"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:669
|
||||
#: daemon/HTTPServer.cpp:625
|
||||
msgid "Non Expired Leases"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:672
|
||||
#: daemon/HTTPServer.cpp:628
|
||||
msgid "Gateway"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:673
|
||||
#: daemon/HTTPServer.cpp:629
|
||||
msgid "TunnelID"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:674
|
||||
#: daemon/HTTPServer.cpp:630
|
||||
msgid "EndDate"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:684
|
||||
#: daemon/HTTPServer.cpp:640
|
||||
msgid "not floodfill"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:695
|
||||
#: daemon/HTTPServer.cpp:651
|
||||
msgid "Queue size"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:726
|
||||
#: daemon/HTTPServer.cpp:701
|
||||
msgid "Run peer test"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:731
|
||||
#: daemon/HTTPServer.cpp:706
|
||||
msgid "Decline transit tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:733
|
||||
#: daemon/HTTPServer.cpp:708
|
||||
msgid "Accept transit tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:737 daemon/HTTPServer.cpp:742
|
||||
#: daemon/HTTPServer.cpp:712 daemon/HTTPServer.cpp:717
|
||||
msgid "Cancel graceful shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:739 daemon/HTTPServer.cpp:744
|
||||
#: daemon/HTTPServer.cpp:714 daemon/HTTPServer.cpp:719
|
||||
msgid "Start graceful shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:747
|
||||
#: daemon/HTTPServer.cpp:722
|
||||
msgid "Force shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:748
|
||||
#: daemon/HTTPServer.cpp:723
|
||||
msgid "Reload external CSS styles"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:751
|
||||
#: daemon/HTTPServer.cpp:726
|
||||
msgid ""
|
||||
"<b>Note:</b> any action done here are not persistent and not changes your "
|
||||
"config files."
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:753
|
||||
#: daemon/HTTPServer.cpp:728
|
||||
msgid "Logging level"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:761
|
||||
#: daemon/HTTPServer.cpp:736
|
||||
msgid "Transit tunnels limit"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:766 daemon/HTTPServer.cpp:778
|
||||
#: daemon/HTTPServer.cpp:741 daemon/HTTPServer.cpp:760
|
||||
msgid "Change"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:770
|
||||
#: daemon/HTTPServer.cpp:748
|
||||
msgid "Change language"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:803
|
||||
#: daemon/HTTPServer.cpp:786
|
||||
msgid "no transit tunnels currently built"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:908 daemon/HTTPServer.cpp:931
|
||||
#: daemon/HTTPServer.cpp:902 daemon/HTTPServer.cpp:925
|
||||
msgid "SAM disabled"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:924
|
||||
#: daemon/HTTPServer.cpp:918
|
||||
msgid "no sessions currently running"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:937
|
||||
#: daemon/HTTPServer.cpp:931
|
||||
msgid "SAM session not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:942
|
||||
#: daemon/HTTPServer.cpp:936
|
||||
msgid "SAM Session"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:999
|
||||
#: daemon/HTTPServer.cpp:993
|
||||
msgid "Server Tunnels"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1015
|
||||
#: daemon/HTTPServer.cpp:1009
|
||||
msgid "Client Forwards"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1029
|
||||
#: daemon/HTTPServer.cpp:1023
|
||||
msgid "Server Forwards"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1227
|
||||
#: daemon/HTTPServer.cpp:1223
|
||||
msgid "Unknown page"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1246
|
||||
#: daemon/HTTPServer.cpp:1242
|
||||
msgid "Invalid token"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1304 daemon/HTTPServer.cpp:1361
|
||||
#: daemon/HTTPServer.cpp:1401
|
||||
#: daemon/HTTPServer.cpp:1300 daemon/HTTPServer.cpp:1357
|
||||
#: daemon/HTTPServer.cpp:1397
|
||||
msgid "SUCCESS"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1304
|
||||
#: daemon/HTTPServer.cpp:1300
|
||||
msgid "Stream closed"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1306
|
||||
#: daemon/HTTPServer.cpp:1302
|
||||
msgid "Stream not found or already was closed"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1309
|
||||
#: daemon/HTTPServer.cpp:1305
|
||||
msgid "Destination not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1312
|
||||
#: daemon/HTTPServer.cpp:1308
|
||||
msgid "StreamID can't be null"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1314 daemon/HTTPServer.cpp:1379
|
||||
#: daemon/HTTPServer.cpp:1310 daemon/HTTPServer.cpp:1375
|
||||
msgid "Return to destination page"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1315 daemon/HTTPServer.cpp:1328
|
||||
#: daemon/HTTPServer.cpp:1403
|
||||
#: daemon/HTTPServer.cpp:1311 daemon/HTTPServer.cpp:1324
|
||||
#: daemon/HTTPServer.cpp:1399
|
||||
msgid "You will be redirected in 5 seconds"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1326
|
||||
#: daemon/HTTPServer.cpp:1322
|
||||
msgid "Transit tunnels count must not exceed 65535"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1327 daemon/HTTPServer.cpp:1402
|
||||
#: daemon/HTTPServer.cpp:1323 daemon/HTTPServer.cpp:1398
|
||||
msgid "Back to commands list"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1363
|
||||
#: daemon/HTTPServer.cpp:1359
|
||||
msgid "Register at reg.i2p"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1364
|
||||
#: daemon/HTTPServer.cpp:1360
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1364
|
||||
#: daemon/HTTPServer.cpp:1360
|
||||
msgid "A bit information about service on domain"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1365
|
||||
#: daemon/HTTPServer.cpp:1361
|
||||
msgid "Submit"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1371
|
||||
#: daemon/HTTPServer.cpp:1367
|
||||
msgid "Domain can't end with .b32.i2p"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1374
|
||||
#: daemon/HTTPServer.cpp:1370
|
||||
msgid "Domain must end with .i2p"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1377
|
||||
#: daemon/HTTPServer.cpp:1373
|
||||
msgid "Such destination is not found"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1397
|
||||
#: daemon/HTTPServer.cpp:1393
|
||||
msgid "Unknown command"
|
||||
msgstr ""
|
||||
|
||||
#: daemon/HTTPServer.cpp:1401
|
||||
#: daemon/HTTPServer.cpp:1397
|
||||
msgid "Command accepted"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:157
|
||||
#: libi2pd_client/HTTPProxy.cpp:163
|
||||
msgid "Proxy error"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:165
|
||||
#: libi2pd_client/HTTPProxy.cpp:171
|
||||
msgid "Proxy info"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:173
|
||||
#: libi2pd_client/HTTPProxy.cpp:179
|
||||
msgid "Proxy error: Host not found"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:174
|
||||
#: libi2pd_client/HTTPProxy.cpp:180
|
||||
msgid "Remote host not found in router's addressbook"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:175
|
||||
#: libi2pd_client/HTTPProxy.cpp:181
|
||||
msgid "You may try to find this host on jump services below"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:273 libi2pd_client/HTTPProxy.cpp:288
|
||||
#: libi2pd_client/HTTPProxy.cpp:322 libi2pd_client/HTTPProxy.cpp:365
|
||||
#: libi2pd_client/HTTPProxy.cpp:282 libi2pd_client/HTTPProxy.cpp:297
|
||||
#: libi2pd_client/HTTPProxy.cpp:331 libi2pd_client/HTTPProxy.cpp:372
|
||||
msgid "Invalid request"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:273
|
||||
#: libi2pd_client/HTTPProxy.cpp:282
|
||||
msgid "Proxy unable to parse your request"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:288
|
||||
#: libi2pd_client/HTTPProxy.cpp:297
|
||||
msgid "addresshelper is not supported"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:297 libi2pd_client/HTTPProxy.cpp:306
|
||||
#: libi2pd_client/HTTPProxy.cpp:385
|
||||
#: libi2pd_client/HTTPProxy.cpp:306 libi2pd_client/HTTPProxy.cpp:315
|
||||
#: libi2pd_client/HTTPProxy.cpp:392
|
||||
msgid "Host"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:297
|
||||
#: libi2pd_client/HTTPProxy.cpp:306
|
||||
msgid "added to router's addressbook from helper"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:298
|
||||
#: libi2pd_client/HTTPProxy.cpp:307
|
||||
msgid "Click here to proceed:"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308
|
||||
#: libi2pd_client/HTTPProxy.cpp:307 libi2pd_client/HTTPProxy.cpp:317
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309
|
||||
#: libi2pd_client/HTTPProxy.cpp:308 libi2pd_client/HTTPProxy.cpp:318
|
||||
msgid "Addresshelper found"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:306
|
||||
#: libi2pd_client/HTTPProxy.cpp:315
|
||||
msgid "already in router's addressbook"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:307
|
||||
#. tr: The "record" means addressbook's record. That message appears when domain was already added to addressbook, but helper link is opened for it.
|
||||
#: libi2pd_client/HTTPProxy.cpp:316
|
||||
msgid "Click here to update record:"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:322
|
||||
#: libi2pd_client/HTTPProxy.cpp:331
|
||||
msgid "invalid request uri"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:365
|
||||
#: libi2pd_client/HTTPProxy.cpp:372
|
||||
msgid "Can't detect destination host from request"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:382 libi2pd_client/HTTPProxy.cpp:386
|
||||
#: libi2pd_client/HTTPProxy.cpp:389 libi2pd_client/HTTPProxy.cpp:393
|
||||
msgid "Outproxy failure"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:382
|
||||
#: libi2pd_client/HTTPProxy.cpp:389
|
||||
msgid "bad outproxy settings"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:385
|
||||
#: libi2pd_client/HTTPProxy.cpp:392
|
||||
msgid "not inside I2P network, but outproxy is not enabled"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:474
|
||||
#: libi2pd_client/HTTPProxy.cpp:482
|
||||
msgid "unknown outproxy url"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:480
|
||||
#: libi2pd_client/HTTPProxy.cpp:490
|
||||
msgid "cannot resolve upstream proxy"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:488
|
||||
#: libi2pd_client/HTTPProxy.cpp:498
|
||||
msgid "hostname too long"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:515
|
||||
#: libi2pd_client/HTTPProxy.cpp:525
|
||||
msgid "cannot connect to upstream socks proxy"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:521
|
||||
#: libi2pd_client/HTTPProxy.cpp:531
|
||||
msgid "Cannot negotiate with socks proxy"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:563
|
||||
#: libi2pd_client/HTTPProxy.cpp:573
|
||||
msgid "CONNECT error"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:563
|
||||
#: libi2pd_client/HTTPProxy.cpp:573
|
||||
msgid "Failed to Connect"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:574 libi2pd_client/HTTPProxy.cpp:600
|
||||
#: libi2pd_client/HTTPProxy.cpp:584 libi2pd_client/HTTPProxy.cpp:610
|
||||
msgid "socks proxy error"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:582
|
||||
#: libi2pd_client/HTTPProxy.cpp:592
|
||||
msgid "failed to send request to upstream"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:603
|
||||
#: libi2pd_client/HTTPProxy.cpp:613
|
||||
msgid "No Reply From socks proxy"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:610
|
||||
#: libi2pd_client/HTTPProxy.cpp:620
|
||||
msgid "cannot connect"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:610
|
||||
#: libi2pd_client/HTTPProxy.cpp:620
|
||||
msgid "http out proxy not implemented"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:611
|
||||
#: libi2pd_client/HTTPProxy.cpp:621
|
||||
msgid "cannot connect to upstream http proxy"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:644
|
||||
#: libi2pd_client/HTTPProxy.cpp:654
|
||||
msgid "Host is down"
|
||||
msgstr ""
|
||||
|
||||
#: libi2pd_client/HTTPProxy.cpp:644
|
||||
#: libi2pd_client/HTTPProxy.cpp:654
|
||||
msgid ""
|
||||
"Can't create connection to requested host, it may be down. Please try again "
|
||||
"later."
|
||||
|
|
|
@ -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)?
|
||||
out: #{"$2", {"$3", "$4", "$6", "$8", "$10"}},\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", "$5", "$7", "$9", "$11"}},\n
|
||||
```
|
||||
|
||||
```
|
||||
|
|
|
@ -75,8 +75,8 @@ ipv4 = true
|
|||
## Enable communication through ipv6
|
||||
ipv6 = false
|
||||
|
||||
## Enable SSU transport (default = true)
|
||||
# ssu = true
|
||||
## Enable SSU transport
|
||||
ssu = false
|
||||
|
||||
## Bandwidth configuration
|
||||
## 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!
|
||||
# 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]
|
||||
## Web Console settings
|
||||
## Uncomment and set to 'false' to disable Web Console
|
||||
|
@ -110,8 +126,8 @@ port = 7070
|
|||
# user = i2pd
|
||||
# pass = changeme
|
||||
## Select webconsole language
|
||||
## Currently supported english (default), afrikaans, armenian, french, german,
|
||||
## russian, turkmen, ukrainian and uzbek languages
|
||||
## Currently supported english (default), afrikaans, armenian, chinese, french,
|
||||
## german, italian, russian, spanish, turkmen, ukrainian and uzbek languages
|
||||
# lang = english
|
||||
|
||||
[httpproxy]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
||||
|
||||
Name: i2pd-git
|
||||
Version: 2.42.1
|
||||
Version: 2.44.0
|
||||
Release: git%{git_hash}%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd
|
||||
|
@ -62,9 +62,7 @@ pushd redhat-linux-build
|
|||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
%if 0%{?fedora} < 37
|
||||
pushd redhat-linux-build
|
||||
%endif
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
|
@ -82,10 +80,8 @@ popd
|
|||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 33
|
||||
%if 0%{?fedora} < 37
|
||||
popd
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?mageia} > 7
|
||||
popd
|
||||
|
@ -99,9 +95,7 @@ pushd redhat-linux-build
|
|||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
%if 0%{?fedora} < 37
|
||||
pushd redhat-linux-build
|
||||
%endif
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
|
@ -164,6 +158,12 @@ getent passwd i2pd >/dev/null || \
|
|||
|
||||
|
||||
%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
|
||||
- update to 2.42.1
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Name: i2pd
|
||||
Version: 2.42.1
|
||||
Version: 2.44.0
|
||||
Release: 1%{?dist}
|
||||
Summary: I2P router written in C++
|
||||
Conflicts: i2pd-git
|
||||
|
@ -59,9 +59,7 @@ pushd redhat-linux-build
|
|||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
%if 0%{?fedora} < 37
|
||||
pushd redhat-linux-build
|
||||
%endif
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
|
@ -79,10 +77,8 @@ popd
|
|||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 33
|
||||
%if 0%{?fedora} < 37
|
||||
popd
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if 0%{?mageia} > 7
|
||||
popd
|
||||
|
@ -96,9 +92,7 @@ pushd redhat-linux-build
|
|||
%endif
|
||||
|
||||
%if 0%{?fedora} >= 35
|
||||
%if 0%{?fedora} < 37
|
||||
pushd redhat-linux-build
|
||||
%endif
|
||||
%else
|
||||
%if 0%{?fedora} >= 33
|
||||
pushd %{_target_platform}
|
||||
|
@ -161,6 +155,12 @@ getent passwd i2pd >/dev/null || \
|
|||
|
||||
|
||||
%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
|
||||
- update to 2.42.1
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include "Crypto.h"
|
||||
#include "UPnP.h"
|
||||
#include "Timestamp.h"
|
||||
#include "util.h"
|
||||
#include "I18N.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -153,115 +152,18 @@ namespace util
|
|||
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);
|
||||
bool avx; i2p::config::GetOption("cpuext.avx", avx);
|
||||
bool forceCpuExt; i2p::config::GetOption("cpuext.force", forceCpuExt);
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
if (!ssu && i2p::config::IsDefault ("precomputation.elgamal"))
|
||||
precomputation = false; // we don't elgamal table if no ssu, unless it's specified explicitly
|
||||
i2p::crypto::InitCrypto (precomputation, aesni, avx, forceCpuExt);
|
||||
|
||||
i2p::transport::InitAddressFromIface (); // get address4/6 from interfaces
|
||||
|
||||
int netID; i2p::config::GetOption("netid", netID);
|
||||
i2p::context.SetNetID (netID);
|
||||
i2p::context.Init ();
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
i2p::transport::InitTransports ();
|
||||
|
||||
bool transit; i2p::config::GetOption("notransit", transit);
|
||||
i2p::context.SetAcceptsTunnels (!transit);
|
||||
|
@ -404,7 +306,7 @@ namespace util
|
|||
|
||||
i2p::transport::transports.SetCheckReserved(checkInReserved);
|
||||
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");
|
||||
else
|
||||
{
|
||||
|
|
|
@ -80,7 +80,7 @@ namespace http {
|
|||
const char HTTP_COMMAND_SHUTDOWN_CANCEL[] = "shutdown_cancel";
|
||||
const char HTTP_COMMAND_SHUTDOWN_NOW[] = "terminate";
|
||||
const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test";
|
||||
const char HTTP_COMMAND_RELOAD_CONFIG[] = "reload_config";
|
||||
const char HTTP_COMMAND_RELOAD_TUNNELS_CONFIG[] = "reload_tunnels_config";
|
||||
const char HTTP_COMMAND_LOGLEVEL[] = "set_loglevel";
|
||||
const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
|
||||
const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit";
|
||||
|
@ -182,7 +182,7 @@ namespace http {
|
|||
" <meta charset=\"UTF-8\">\r\n"
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\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);
|
||||
s <<
|
||||
"</head>\r\n"
|
||||
|
@ -222,7 +222,7 @@ namespace http {
|
|||
s << "<b>" << tr("ERROR") << ":</b> " << string << "<br>\r\n";
|
||||
}
|
||||
|
||||
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status)
|
||||
static void ShowNetworkStatus (std::stringstream& s, RouterStatus status, RouterError error)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
|
@ -232,10 +232,12 @@ namespace http {
|
|||
case eRouterStatusUnknown: s << tr("Unknown"); break;
|
||||
case eRouterStatusProxy: s << tr("Proxy"); break;
|
||||
case eRouterStatusMesh: s << tr("Mesh"); break;
|
||||
case eRouterStatusError:
|
||||
default: s << tr("Unknown");
|
||||
}
|
||||
if (error != eRouterErrorNone)
|
||||
{
|
||||
s << tr("Error");
|
||||
switch (i2p::context.GetError ())
|
||||
s << "<br>";
|
||||
switch (error)
|
||||
{
|
||||
case eRouterErrorClockSkew:
|
||||
s << " - " << tr("Clock skew");
|
||||
|
@ -248,9 +250,6 @@ namespace http {
|
|||
break;
|
||||
default: ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: s << tr("Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,12 +259,12 @@ namespace http {
|
|||
ShowUptime(s, i2p::context.GetUptime ());
|
||||
s << "<br>\r\n";
|
||||
s << "<b>" << tr("Network status") << ":</b> ";
|
||||
ShowNetworkStatus (s, i2p::context.GetStatus ());
|
||||
ShowNetworkStatus (s, i2p::context.GetStatus (), i2p::context.GetError ());
|
||||
s << "<br>\r\n";
|
||||
if (i2p::context.SupportsV6 ())
|
||||
{
|
||||
s << "<b>" << tr("Network status v6") << ":</b> ";
|
||||
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
|
||||
ShowNetworkStatus (s, i2p::context.GetStatusV6 (), i2p::context.GetErrorV6 ());
|
||||
s << "<br>\r\n";
|
||||
}
|
||||
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
||||
|
@ -531,19 +530,21 @@ namespace http {
|
|||
ShowLeaseSetDestination (s, dest, token);
|
||||
|
||||
// Print table with streams information
|
||||
s << "<table>\r\n<caption>" << tr("Streams") << "</caption>\r\n<thead>\r\n<tr>";
|
||||
s << "<th style=\"width:25px;\">StreamID</th>";
|
||||
s << "<th style=\"width:5px;\" \\>"; // Stream closing button column
|
||||
s << "<th class=\"streamdest\">Destination</th>";
|
||||
s << "<th>Sent</th>";
|
||||
s << "<th>Received</th>";
|
||||
s << "<th>Out</th>";
|
||||
s << "<th>In</th>";
|
||||
s << "<th>Buf</th>";
|
||||
s << "<th>RTT</th>";
|
||||
s << "<th>Window</th>";
|
||||
s << "<th>Status</th>";
|
||||
s << "</tr>\r\n</thead>\r\n<tbody class=\"tableitem\">\r\n";
|
||||
s << "<table>\r\n<caption>"
|
||||
<< tr("Streams")
|
||||
<< "</caption>\r\n<thead>\r\n<tr>"
|
||||
<< "<th style=\"width:25px;\">StreamID</th>"
|
||||
<< "<th style=\"width:5px;\" \\>" // Stream closing button column
|
||||
<< "<th class=\"streamdest\">Destination</th>"
|
||||
<< "<th>Sent</th>"
|
||||
<< "<th>Received</th>"
|
||||
<< "<th>Out</th>"
|
||||
<< "<th>In</th>"
|
||||
<< "<th>Buf</th>"
|
||||
<< "<th>RTT</th>"
|
||||
<< "<th>Window</th>"
|
||||
<< "<th>Status</th>"
|
||||
<< "</tr>\r\n</thead>\r\n<tbody class=\"tableitem\">\r\n";
|
||||
|
||||
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 << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a><br>\r\n";
|
||||
|
||||
// s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
|
||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RELOAD_TUNNELS_CONFIG << "&token=" << token << "\">" << tr("Reload tunnels configuration") << "</a><br>\r\n";
|
||||
|
||||
if (i2p::context.AcceptsTunnels ())
|
||||
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 << "</form>\r\n<br>\r\n";
|
||||
|
||||
std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language
|
||||
s << "<b>" << tr("Change language") << "</b><br>\r\n";
|
||||
s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
|
||||
s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_SETLANGUAGE << "\">\r\n";
|
||||
s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n";
|
||||
s << " <select name=\"lang\" id=\"lang\">\r\n";
|
||||
// get current used language
|
||||
std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage();
|
||||
|
||||
s << "<b>"
|
||||
<< tr("Change language")
|
||||
<< "</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)
|
||||
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 << "</form>\r\n<br>\r\n";
|
||||
|
||||
s << " </select>\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;
|
||||
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";
|
||||
if (it.second->IsOutgoing ()) tmp_s << " ⇒ ";
|
||||
tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
|
||||
<< it.second->GetRemoteEndpoint ().address ().to_string ();
|
||||
<< endpoint.address ().to_string () << ":" << endpoint.port ();
|
||||
if (!it.second->IsOutgoing ()) tmp_s << " ⇒ ";
|
||||
tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
if (it.second->GetRelayTag ())
|
||||
|
@ -796,12 +805,12 @@ namespace http {
|
|||
tmp_s << "</div>\r\n" << std::endl;
|
||||
cnt++;
|
||||
}
|
||||
if (it.second && it.second->IsEstablished () && it.second->GetRemoteEndpoint ().address ().is_v6 ())
|
||||
if (it.second && it.second->IsEstablished () && endpoint.address ().is_v6 ())
|
||||
{
|
||||
tmp_s6 << "<div class=\"listitem\">\r\n";
|
||||
if (it.second->IsOutgoing ()) tmp_s6 << " ⇒ ";
|
||||
tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": "
|
||||
<< "[" << it.second->GetRemoteEndpoint ().address ().to_string () << "]";
|
||||
<< "[" << endpoint.address ().to_string () << "]:" << endpoint.port ();
|
||||
if (!it.second->IsOutgoing ()) tmp_s6 << " ⇒ ";
|
||||
tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
if (it.second->GetRelayTag ())
|
||||
|
@ -1236,7 +1245,7 @@ namespace http {
|
|||
std::string cmd = params["cmd"];
|
||||
if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
|
||||
i2p::transport::transports.PeerTest ();
|
||||
else if (cmd == HTTP_COMMAND_RELOAD_CONFIG)
|
||||
else if (cmd == HTTP_COMMAND_RELOAD_TUNNELS_CONFIG)
|
||||
i2p::client::context.ReloadConfig ();
|
||||
else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT)
|
||||
i2p::context.SetAcceptsTunnels (true);
|
||||
|
|
|
@ -14,25 +14,17 @@
|
|||
// Use global placeholders from boost introduced when local_time.hpp is loaded
|
||||
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/date_time/local_time/local_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/lexical_cast.hpp>
|
||||
|
||||
#include "Crypto.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "Config.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "RouterContext.h"
|
||||
#include "Daemon.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Transports.h"
|
||||
#include "version.h"
|
||||
#include "util.h"
|
||||
#include "ClientContext.h"
|
||||
#include "Daemon.h"
|
||||
#include "I2PControl.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -69,44 +61,18 @@ namespace client
|
|||
m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler;
|
||||
m_MethodHandlers["Echo"] = &I2PControlService::EchoHandler;
|
||||
m_MethodHandlers["I2PControl"] = &I2PControlService::I2PControlHandler;
|
||||
m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler;
|
||||
m_MethodHandlers["RouterInfo"] = &I2PControlHandlers::RouterInfoHandler;
|
||||
m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler;
|
||||
m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler;
|
||||
m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler;
|
||||
m_MethodHandlers["NetworkSetting"] = &I2PControlHandlers::NetworkSettingHandler;
|
||||
m_MethodHandlers["ClientServicesInfo"] = &I2PControlHandlers::ClientServicesInfoHandler;
|
||||
|
||||
// I2PControl
|
||||
m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler;
|
||||
|
||||
// RouterInfo
|
||||
m_RouterInfoHandlers["i2p.router.uptime"] = &I2PControlService::UptimeHandler;
|
||||
m_RouterInfoHandlers["i2p.router.version"] = &I2PControlService::VersionHandler;
|
||||
m_RouterInfoHandlers["i2p.router.status"] = &I2PControlService::StatusHandler;
|
||||
m_RouterInfoHandlers["i2p.router.netdb.knownpeers"] = &I2PControlService::NetDbKnownPeersHandler;
|
||||
m_RouterInfoHandlers["i2p.router.netdb.activepeers"] = &I2PControlService::NetDbActivePeersHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.bw.inbound.1s"] = &I2PControlService::InboundBandwidth1S;
|
||||
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
|
||||
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
|
||||
m_RouterInfoHandlers["i2p.router.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
|
||||
m_RouterManagerHandlers["Reseed"] = &I2PControlService::ReseedHandler;
|
||||
m_RouterManagerHandlers["Shutdown"] = &I2PControlService::ShutdownHandler;
|
||||
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 ()
|
||||
|
@ -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,
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
|
||||
{
|
||||
|
@ -396,91 +331,6 @@ namespace client
|
|||
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
|
||||
|
||||
|
@ -532,37 +382,6 @@ namespace client
|
|||
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
|
||||
void I2PControlService::CreateCertificate (const char *crt_path, const char *key_path)
|
||||
{
|
||||
|
@ -611,178 +430,5 @@ namespace client
|
|||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include "I2PControlHandlers.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
@ -32,7 +33,7 @@ namespace client
|
|||
const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol";
|
||||
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;
|
||||
|
||||
|
@ -63,61 +64,24 @@ namespace client
|
|||
|
||||
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
|
||||
typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
|
||||
void AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
||||
void RouterInfoHandler (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
|
||||
typedef void (I2PControlService::*I2PControlRequestHandler)(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
|
||||
typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results);
|
||||
void ShutdownHandler (std::ostringstream& results);
|
||||
void ShutdownGracefulHandler (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:
|
||||
|
||||
std::string m_Password;
|
||||
|
@ -132,10 +96,7 @@ namespace client
|
|||
|
||||
std::map<std::string, MethodHandler> m_MethodHandlers;
|
||||
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, NetworkSettingRequestHandler> m_NetworkSettingHandlers;
|
||||
std::map<std::string, ClientServicesInfoRequestHandler> m_ClientServicesInfoHandlers;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
376
daemon/I2PControlHandlers.cpp
Normal file
376
daemon/I2PControlHandlers.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
80
daemon/I2PControlHandlers.h
Normal file
80
daemon/I2PControlHandlers.h
Normal 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
12
debian/changelog
vendored
|
@ -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
|
||||
|
||||
* updated to version 2.42.1/0.9.54
|
||||
|
|
217
i18n/Chinese.cpp
Normal file
217
i18n/Chinese.cpp
Normal 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
|
115
i18n/French.cpp
115
i18n/French.cpp
|
@ -35,24 +35,33 @@ namespace french // language namespace
|
|||
{"MiB", "Mio"},
|
||||
{"GiB", "Gio"},
|
||||
{"building", "En construction"},
|
||||
{"failed", "echoué"},
|
||||
{"failed", "échoué"},
|
||||
{"expiring", "expiré"},
|
||||
{"established", "établi"},
|
||||
{"unknown", "inconnu"},
|
||||
{"exploratory", "exploratoire"},
|
||||
{"Purple I2P Webconsole", "Console web Purple I2P"},
|
||||
{"<b>i2pd</b> webconsole", "Console web <b>i2pd</b>"},
|
||||
{"Main page", "Page principale"},
|
||||
{"Router commands", "Commandes du routeur"},
|
||||
{"Local Destinations", "Destinations locales"},
|
||||
{"LeaseSets", "Jeu de baux"},
|
||||
{"Tunnels", "Tunnels"},
|
||||
{"Transit Tunnels", "Tunnels transitoires"},
|
||||
{"Transports", "Transports"},
|
||||
{"I2P tunnels", "Tunnels I2P"},
|
||||
{"SAM sessions", "Sessions SAM"},
|
||||
{"ERROR", "ERREUR"},
|
||||
{"OK", "OK"},
|
||||
{"Testing", "Test en cours"},
|
||||
{"Firewalled", "Derrière un pare-feu"},
|
||||
{"Unknown", "Inconnu"},
|
||||
{"Proxy", "Proxy"},
|
||||
{"Mesh", "Maillé"},
|
||||
{"Error", "Erreur"},
|
||||
{"Clock skew", "Horloge décalée"},
|
||||
{"Offline", "Hors ligne"},
|
||||
{"Symmetric NAT", "NAT symétrique"},
|
||||
{"Uptime", "Temps de fonctionnement"},
|
||||
{"Network status", "État du réseau"},
|
||||
{"Network status v6", "État du réseau v6"},
|
||||
|
@ -62,24 +71,124 @@ namespace french // language namespace
|
|||
{"Received", "Reçu"},
|
||||
{"KiB/s", "kio/s"},
|
||||
{"Sent", "Envoyé"},
|
||||
{"Transit", "Transit"},
|
||||
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour regarder."},
|
||||
{"Transit", "Transité"},
|
||||
{"Data path", "Emplacement des données"},
|
||||
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour afficher."},
|
||||
{"Router Ident", "Identifiant du routeur"},
|
||||
{"Router Family", "Famille du routeur"},
|
||||
{"Router Caps", "Limiteurs du routeur"},
|
||||
{"Version", "Version"},
|
||||
{"Our external address", "Notre adresse externe"},
|
||||
{"supported", "supporté"},
|
||||
{"Routers", "Routeurs"},
|
||||
{"Client Tunnels", "Tunnels clients"},
|
||||
{"Services", "Services"},
|
||||
{"Enabled", "Activé"},
|
||||
{"Disabled", "Désactivé"},
|
||||
{"Encrypted B33 address", "Adresse B33 chiffrée"},
|
||||
{"Address registration line", "Ligne d'inscription de l'adresse"},
|
||||
{"Domain", "Domaine"},
|
||||
{"Generate", "Générer"},
|
||||
{"<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"},
|
||||
{"Type", "Type"},
|
||||
{"Inbound tunnels", "Tunnels entrants"},
|
||||
{"ms", "ms"},
|
||||
{"Outbound tunnels", "Tunnels sortants"},
|
||||
{"Tags", "Balises"},
|
||||
{"Incoming", "Entrant"},
|
||||
{"Outgoing", "Sortant"},
|
||||
{"Destination", "Destination"},
|
||||
{"Amount", "Quantité"},
|
||||
{"Incoming Tags", "Balises entrantes"},
|
||||
{"Tags sessions", "Sessions des balises"},
|
||||
{"Status", "Statut"},
|
||||
{"Local Destination", "Destination locale"},
|
||||
{"Streams", "Flux"},
|
||||
{"Close stream", "Fermer le flux"},
|
||||
{"I2CP session not found", "Session I2CP introuvable"},
|
||||
{"I2CP is not enabled", "I2CP est désactivé"},
|
||||
{"Invalid", "Invalide"},
|
||||
{"Store type", "Type de stockage"},
|
||||
{"Expires", "Expire"},
|
||||
{"Non Expired Leases", "Baux non expirés"},
|
||||
{"Gateway", "Passerelle"},
|
||||
{"TunnelID", "ID du tunnel"},
|
||||
{"EndDate", "Date de fin"},
|
||||
{"Queue size", "Longueur de la file"},
|
||||
{"Run peer test", "Lancer test des pairs"},
|
||||
{"Decline transit tunnels", "Refuser les tunnels transitoires"},
|
||||
{"Accept transit tunnels", "Accepter les tunnels transitoires"},
|
||||
{"Cancel graceful shutdown", "Annuler l'arrêt gracieux"},
|
||||
{"Start graceful shutdown", "Démarrer l'arrêt gracieux"},
|
||||
{"Force shutdown", "Forcer l'arrêt"},
|
||||
{"Reload external CSS styles", "Rafraîchir les styles CSS externes"},
|
||||
{"<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."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
|
|
|
@ -36,14 +36,15 @@ namespace german // language namespace
|
|||
{"GiB", "GiB"},
|
||||
{"building", "In Bau"},
|
||||
{"failed", "fehlgeschlagen"},
|
||||
{"expiring", "läuft ab in"},
|
||||
{"expiring", "läuft ab"},
|
||||
{"established", "hergestellt"},
|
||||
{"unknown", "Unbekannt"},
|
||||
{"exploratory", "erforschende"},
|
||||
{"<b>i2pd</b> webconsole", "<b>i2pd</b> Webkonsole"},
|
||||
{"exploratory", "erforschend"},
|
||||
{"Purple I2P Webconsole", "Purple I2P-Webkonsole"},
|
||||
{"<b>i2pd</b> webconsole", "<b>i2pd</b>-Webkonsole"},
|
||||
{"Main page", "Startseite"},
|
||||
{"Router commands", "Router Befehle"},
|
||||
{"Local Destinations", "Lokale Destination"},
|
||||
{"Router commands", "Routerbefehle"},
|
||||
{"Local Destinations", "Lokale Ziele"},
|
||||
{"LeaseSets", "LeaseSets"},
|
||||
{"Tunnels", "Tunnel"},
|
||||
{"Transit Tunnels", "Transittunnel"},
|
||||
|
@ -53,7 +54,7 @@ namespace german // language namespace
|
|||
{"ERROR", "FEHLER"},
|
||||
{"OK", "OK"},
|
||||
{"Testing", "Testen"},
|
||||
{"Firewalled", "Hinter eine Firewall"},
|
||||
{"Firewalled", "Hinter einer Firewall"},
|
||||
{"Unknown", "Unbekannt"},
|
||||
{"Proxy", "Proxy"},
|
||||
{"Mesh", "Mesh"},
|
||||
|
@ -81,15 +82,15 @@ namespace german // language namespace
|
|||
{"supported", "unterstützt"},
|
||||
{"Routers", "Router"},
|
||||
{"Floodfills", "Floodfills"},
|
||||
{"Client Tunnels", "Klienttunnel"},
|
||||
{"Client Tunnels", "Clienttunnel"},
|
||||
{"Services", "Services"},
|
||||
{"Enabled", "Aktiviert"},
|
||||
{"Disabled", "Deaktiviert"},
|
||||
{"Encrypted B33 address", "Verschlüsselte B33 Adresse"},
|
||||
{"Address registration line", "Adresseregistrierungszeile"},
|
||||
{"Encrypted B33 address", "Verschlüsselte B33-Adresse"},
|
||||
{"Address registration line", "Adressregistrierungszeile"},
|
||||
{"Domain", "Domain"},
|
||||
{"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"},
|
||||
{"Type", "Typ"},
|
||||
{"EncType", "Verschlüsselungstyp"},
|
||||
|
@ -99,15 +100,15 @@ namespace german // language namespace
|
|||
{"Tags", "Tags"},
|
||||
{"Incoming", "Eingehend"},
|
||||
{"Outgoing", "Ausgehend"},
|
||||
{"Destination", "Destination"},
|
||||
{"Destination", "Ziel"},
|
||||
{"Amount", "Anzahl"},
|
||||
{"Incoming Tags", "Eingehende Tags"},
|
||||
{"Tags sessions", "Tags Sitzungen"},
|
||||
{"Tags sessions", "Tags-Sitzungen"},
|
||||
{"Status", "Status"},
|
||||
{"Local Destination", "Lokale Destination"},
|
||||
{"Local Destination", "Lokales Ziel"},
|
||||
{"Streams", "Streams"},
|
||||
{"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"},
|
||||
{"Invalid", "Ungültig"},
|
||||
{"Store type", "Speichertyp"},
|
||||
|
@ -117,67 +118,67 @@ namespace german // language namespace
|
|||
{"TunnelID", "TunnelID"},
|
||||
{"EndDate", "Enddatum"},
|
||||
{"not floodfill", "kein Floodfill"},
|
||||
{"Queue size", "Warteschlangengröße"},
|
||||
{"Run peer test", "Peer-Test ausführen"},
|
||||
{"Queue size", "Größe der Warteschlange"},
|
||||
{"Run peer test", "Peer-Test durchführen"},
|
||||
{"Decline transit tunnels", "Transittunnel ablehnen"},
|
||||
{"Accept transit tunnels", "Transittunnel akzeptieren"},
|
||||
{"Cancel graceful shutdown", "Beende das kontrollierte herunterfahren"},
|
||||
{"Cancel graceful shutdown", "Beende das kontrollierte Herunterfahren"},
|
||||
{"Start graceful shutdown", "Starte das kontrollierte Herunterfahren"},
|
||||
{"Force shutdown", "Herunterfahren erzwingen"},
|
||||
{"Reload external CSS styles", "Lade externe CSS-Styles neu"},
|
||||
{"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."},
|
||||
{"Logging level", "Protokollierungslevel"},
|
||||
{"Transit tunnels limit", "Limit für Transittunnel"},
|
||||
{"Change", "Verändern"},
|
||||
{"Change", "Ändern"},
|
||||
{"Change language", "Sprache ändern"},
|
||||
{"no transit tunnels currently built", "derzeit keine Transittunnel aufgebaut"},
|
||||
{"SAM disabled", "SAM deaktiviert"},
|
||||
{"no sessions currently running", "Derzeit keine laufenden Sitzungen"},
|
||||
{"SAM session not found", "SAM Sitzung nicht gefunden"},
|
||||
{"SAM Session", "SAM Sitzung"},
|
||||
{"SAM session not found", "SAM-Sitzung nicht gefunden"},
|
||||
{"SAM Session", "SAM-Sitzung"},
|
||||
{"Server Tunnels", "Servertunnel"},
|
||||
{"Client Forwards", "Klient-Weiterleitungen"},
|
||||
{"Client Forwards", "Client-Weiterleitungen"},
|
||||
{"Server Forwards", "Server-Weiterleitungen"},
|
||||
{"Unknown page", "Unbekannte Seite"},
|
||||
{"Invalid token", "Ungültiger Token"},
|
||||
{"SUCCESS", "ERFOLGREICH"},
|
||||
{"Stream closed", "Stream geschlossen"},
|
||||
{"Stream not found or already was closed", "Stream nicht gefunden oder bereits geschlossen"},
|
||||
{"Destination not found", "Destination nicht gefunden"},
|
||||
{"Destination not found", "Ziel nicht gefunden"},
|
||||
{"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"},
|
||||
{"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"},
|
||||
{"Description", "Beschreibung"},
|
||||
{"A bit information about service on domain", "Ein bisschen Informationen über den Service auf der Domain"},
|
||||
{"Submit", "Einreichen"},
|
||||
{"Domain can't end with .b32.i2p", "Domain kann nicht mit .b32.i2p enden"},
|
||||
{"Domain must end with .i2p", "Domain muss mit .i2p enden"},
|
||||
{"Such destination is not found", "Eine solche Destination konnte nicht gefunden werden"},
|
||||
{"A bit information about service on domain", "Ein paar Informationen über den Service auf der Domain"},
|
||||
{"Submit", "Absenden"},
|
||||
{"Domain can't end with .b32.i2p", "Domain kann nicht auf .b32.i2p enden"},
|
||||
{"Domain must end with .i2p", "Domain muss auf .i2p enden"},
|
||||
{"Such destination is not found", "Ein solches Ziel konnte nicht gefunden werden"},
|
||||
{"Unknown command", "Unbekannter Befehl"},
|
||||
{"Command accepted", "Befehl akzeptiert"},
|
||||
{"Proxy error", "Proxy-Fehler"},
|
||||
{"Proxy info", "Proxy-Info"},
|
||||
{"Proxy error: Host not found", "Proxy-Fehler: Host nicht gefunden"},
|
||||
{"Remote host not found in router's addressbook", "Remote-Host nicht im Router Adressbuch gefunden"},
|
||||
{"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einen der Jump-Services unten finden"},
|
||||
{"Remote host not found in router's addressbook", "Remote-Host nicht im Router-Adressbuch gefunden"},
|
||||
{"You may try to find this host on jump services below", "Vielleicht kannst du diesen Host auf einem der nachfolgenden Jump-Services finden"},
|
||||
{"Invalid request", "Ungültige Anfrage"},
|
||||
{"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht interpretieren"},
|
||||
{"addresshelper is not supported", "addresshelper wird nicht unterstützt"},
|
||||
{"Proxy unable to parse your request", "Proxy konnte die Anfrage nicht verarbeiten"},
|
||||
{"addresshelper is not supported", "Addresshelfer wird nicht unterstützt"},
|
||||
{"Host", "Host"},
|
||||
{"added to router's addressbook from helper", "vom Helfer zum Router Adressbuch hinzugefügt"},
|
||||
{"added to router's addressbook from helper", "vom Helfer zum Router-Adressbuch hinzugefügt"},
|
||||
{"Click here to proceed:", "Klicke hier um fortzufahren:"},
|
||||
{"Continue", "Fortsetzen"},
|
||||
{"Addresshelper found", "Adresshelfer gefunden"},
|
||||
{"already in router's addressbook", "bereits im Adressbuch des Routers"},
|
||||
{"Click here to update record:", "Klicke hier, um den Eintrag zu aktualisieren:"},
|
||||
{"invalid request uri", "ungültige Anfrage-URI"},
|
||||
{"Can't detect destination host from request", "Kann Anhand der Anfrage den Destination-Host nicht erkennen"},
|
||||
{"Can't detect destination host from request", "Kann den Ziel-Host von der Anfrage nicht erkennen"},
|
||||
{"Outproxy failure", "Outproxy-Fehler"},
|
||||
{"bad outproxy settings", "ungültige Outproxy-Einstellungen"},
|
||||
{"not inside I2P network, but outproxy is not enabled", "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"},
|
||||
{"cannot resolve upstream proxy", "kann den Upstream-Proxy nicht auflösen"},
|
||||
{"hostname too long", "Hostname zu lang"},
|
||||
|
@ -192,7 +193,7 @@ namespace german // language namespace
|
|||
{"http out proxy not implemented", "HTTP-Outproxy nicht implementiert"},
|
||||
{"cannot connect to upstream http proxy", "Kann nicht zu Upstream-HTTP-Proxy verbinden"},
|
||||
{"Host is down", "Host ist offline"},
|
||||
{"Can't create connection to requested host, it may be down. Please try again later.", "Konnte keine Verbindung zum angefragten Host aufbaunen, vielleicht ist es offline. Versuche es später noch einmal."},
|
||||
{"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."},
|
||||
{"", ""},
|
||||
};
|
||||
|
||||
|
|
|
@ -73,10 +73,13 @@ namespace i18n
|
|||
// Add localization here with language name as namespace
|
||||
namespace afrikaans { 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 french { 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 spanish { 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 uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||
|
@ -87,13 +90,16 @@ namespace i18n
|
|||
static std::map<std::string, langData> languages
|
||||
{
|
||||
{ "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} },
|
||||
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
|
||||
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
|
||||
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} },
|
||||
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
|
||||
{ "ukrainian", {"украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
|
||||
{ "italian", {"Italiano", "it", i2p::i18n::italian::GetLocale} },
|
||||
{ "russian", {"Русский язык", "ru", i2p::i18n::russian::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} },
|
||||
};
|
||||
|
||||
|
|
216
i18n/Italian.cpp
Normal file
216
i18n/Italian.cpp
Normal 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
216
i18n/Spanish.cpp
Normal 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
|
|
@ -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)")
|
||||
("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")
|
||||
("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")
|
||||
#ifdef _WIN32
|
||||
("svcctl", value<std::string>()->default_value(""), "Ignored")
|
||||
|
@ -274,9 +274,12 @@ namespace config {
|
|||
|
||||
options_description ssu2("SSU2 Options");
|
||||
ssu2.add_options()
|
||||
("ssu2.enabled", value<bool>()->default_value(false), "Enable SSU2 (default: disabled)")
|
||||
("ssu2.published", value<bool>()->default_value(false), "Publish SSU2 (default: disabled)")
|
||||
("ssu2.enabled", value<bool>()->default_value(true), "Enable SSU2 (default: enabled)")
|
||||
("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.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");
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -15,6 +15,7 @@
|
|||
#include <map>
|
||||
#include <vector>
|
||||
#include "Base.h"
|
||||
#include "Gzip.h"
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "I2NPProtocol.h"
|
||||
|
|
|
@ -1096,7 +1096,13 @@ namespace client
|
|||
}
|
||||
auto leaseSet = FindLeaseSet (dest);
|
||||
if (leaseSet)
|
||||
streamRequestComplete(CreateStream (leaseSet, port));
|
||||
{
|
||||
auto stream = CreateStream (leaseSet, port);
|
||||
GetService ().post ([streamRequestComplete, stream]()
|
||||
{
|
||||
streamRequestComplete(stream);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (m_StreamingDestination)
|
||||
|
|
|
@ -247,6 +247,8 @@ namespace client
|
|||
// following methods operate with default streaming destination
|
||||
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);
|
||||
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);
|
||||
void SendPing (const i2p::data::IdentHash& to);
|
||||
void SendPing (std::shared_ptr<const i2p::data::BlindedPublicKey> to);
|
||||
|
@ -282,6 +284,9 @@ namespace client
|
|||
void PersistTemporaryKeys (EncryptionKey * keys, bool isSingleKey);
|
||||
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:
|
||||
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -135,6 +135,7 @@ namespace http
|
|||
? url.substr(pos_p, std::string::npos)
|
||||
: url.substr(pos_p, pos_c - pos_p);
|
||||
/* stoi throws exception on failure, we don't need it */
|
||||
port = 0;
|
||||
for (char c : port_str) {
|
||||
if (c < '0' || c > '9')
|
||||
return false;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -49,13 +49,30 @@ namespace data
|
|||
|
||||
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)
|
||||
{
|
||||
/*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);
|
||||
RAND_bytes (m_StandardIdentity.publicKey + 32, 224);
|
||||
}
|
||||
else
|
||||
RAND_bytes (m_StandardIdentity.publicKey, 256);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (publicKey)
|
||||
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)
|
||||
{
|
||||
size_t excessLen = 0;
|
||||
|
@ -93,7 +110,9 @@ namespace data
|
|||
case SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519:
|
||||
{
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
@ -695,7 +714,7 @@ namespace data
|
|||
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)
|
||||
{
|
||||
|
@ -705,9 +724,12 @@ namespace data
|
|||
GenerateSigningKeyPair (type, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
// encryption
|
||||
uint8_t publicKey[256];
|
||||
if (isDestination)
|
||||
RAND_bytes (keys.m_PrivateKey, 256);
|
||||
else
|
||||
GenerateCryptoKeyPair (cryptoType, keys.m_PrivateKey, publicKey);
|
||||
// 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 ();
|
||||
return keys;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -171,7 +171,7 @@ namespace data
|
|||
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 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 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);
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -37,14 +37,7 @@ namespace data
|
|||
|
||||
void LeaseSet::Update (const uint8_t * buf, size_t len, bool verifySignature)
|
||||
{
|
||||
if (len > m_BufferLen)
|
||||
{
|
||||
auto oldBuffer = m_Buffer;
|
||||
m_Buffer = new uint8_t[len];
|
||||
delete[] oldBuffer;
|
||||
}
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
SetBuffer (buf, len);
|
||||
ReadFromBuffer (false, verifySignature);
|
||||
}
|
||||
|
||||
|
@ -59,9 +52,9 @@ namespace data
|
|||
if (readIdentity || !m_Identity)
|
||||
m_Identity = std::make_shared<IdentityEx>(m_Buffer, m_BufferLen);
|
||||
size_t size = m_Identity->GetFullLen ();
|
||||
if (size > m_BufferLen)
|
||||
if (size + 256 > m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: Identity length ", size, " exceeds buffer size ", m_BufferLen);
|
||||
LogPrint (eLogError, "LeaseSet: Identity length ", int(size), " exceeds buffer size ", int(m_BufferLen));
|
||||
m_IsValid = false;
|
||||
return;
|
||||
}
|
||||
|
@ -74,7 +67,7 @@ namespace data
|
|||
size += m_Identity->GetSigningPublicKeyLen (); // unused signing key
|
||||
if (size + 1 > m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: ", size, " exceeds buffer size ", m_BufferLen);
|
||||
LogPrint (eLogError, "LeaseSet: ", int(size), " exceeds buffer size ", int(m_BufferLen));
|
||||
m_IsValid = false;
|
||||
return;
|
||||
}
|
||||
|
@ -89,7 +82,7 @@ namespace data
|
|||
}
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
@ -125,7 +118,7 @@ namespace data
|
|||
auto signedSize = leases - m_Buffer;
|
||||
if (signedSize + m_Identity->GetSignatureLen () > m_BufferLen)
|
||||
{
|
||||
LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", m_BufferLen);
|
||||
LogPrint (eLogError, "LeaseSet: Signature exceeds buffer size ", int(m_BufferLen));
|
||||
m_IsValid = false;
|
||||
}
|
||||
else if (!m_Identity->Verify (m_Buffer, signedSize, leases))
|
||||
|
@ -172,7 +165,7 @@ namespace data
|
|||
m_ExpirationTime = lease.endDate;
|
||||
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
|
||||
(*ret.first)->isUpdated = true;
|
||||
}
|
||||
|
@ -264,7 +257,17 @@ namespace data
|
|||
|
||||
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_BufferLen = len;
|
||||
memcpy (m_Buffer, buf, len);
|
||||
|
@ -274,7 +277,7 @@ namespace data
|
|||
{
|
||||
if (len <= m_BufferLen) m_BufferLen = len;
|
||||
else
|
||||
LogPrint (eLogError, "LeaseSet2: Actual buffer size ", len , " exceeds full buffer size ", m_BufferLen);
|
||||
LogPrint (eLogError, "LeaseSet2: Actual buffer size ", int(len) , " exceeds full buffer size ", int(m_BufferLen));
|
||||
}
|
||||
|
||||
LeaseSet2::LeaseSet2 (uint8_t storeType, const uint8_t * buf, size_t len, bool storeLeases, CryptoKeyType preferredCrypto):
|
||||
|
@ -320,7 +323,7 @@ namespace data
|
|||
else
|
||||
identity = GetIdentity ();
|
||||
size_t offset = identity->GetFullLen ();
|
||||
if (offset + 8 >= len) return;
|
||||
if (offset + 8 > len) return;
|
||||
m_PublishedTimestamp = bufbe32toh (buf + offset); offset += 4; // published timestamp (seconds)
|
||||
uint16_t expires = bufbe16toh (buf + offset); offset += 2; // expires (seconds)
|
||||
SetExpirationTime ((m_PublishedTimestamp + expires)*1000LL); // in milliseconds
|
||||
|
@ -364,6 +367,10 @@ namespace data
|
|||
SetIsValid (verified);
|
||||
}
|
||||
offset += m_TransientVerifier ? m_TransientVerifier->GetSignatureLen () : identity->GetSignatureLen ();
|
||||
if (offset > len) {
|
||||
LogPrint (eLogWarning, "LeaseSet2: short buffer: wanted ", int(offset), "bytes, have ", int(len));
|
||||
return;
|
||||
}
|
||||
SetBufferLen (offset);
|
||||
}
|
||||
|
||||
|
@ -388,17 +395,17 @@ namespace data
|
|||
// properties
|
||||
uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2;
|
||||
offset += propertiesLen; // skip for now. TODO: implement properties
|
||||
if (offset + 1 >= len) return 0;
|
||||
// key sections
|
||||
CryptoKeyType preferredKeyType = m_EncryptionType;
|
||||
bool preferredKeyFound = false;
|
||||
if (offset + 1 > len) return 0;
|
||||
int numKeySections = buf[offset]; offset++;
|
||||
for (int i = 0; i < numKeySections; i++)
|
||||
{
|
||||
if (offset + 4 > len) return 0;
|
||||
uint16_t keyType = bufbe16toh (buf + offset); offset += 2; // encryption key type
|
||||
if (offset + 2 >= len) return 0;
|
||||
uint16_t encryptionKeyLen = bufbe16toh (buf + offset); offset += 2;
|
||||
if (offset + encryptionKeyLen >= len) return 0;
|
||||
if (offset + encryptionKeyLen > len) return 0;
|
||||
if (IsStoreLeases () && !preferredKeyFound) // create encryptor with leases only
|
||||
{
|
||||
// we pick first valid key if preferred not found
|
||||
|
@ -413,7 +420,7 @@ namespace data
|
|||
offset += encryptionKeyLen;
|
||||
}
|
||||
// leases
|
||||
if (offset + 1 >= len) return 0;
|
||||
if (offset + 1 > len) return 0;
|
||||
int numLeases = buf[offset]; offset++;
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (IsStoreLeases ())
|
||||
|
@ -432,7 +439,8 @@ namespace data
|
|||
}
|
||||
else
|
||||
offset += numLeases*LEASE2_SIZE; // 40 bytes per lease
|
||||
return offset;
|
||||
|
||||
return (offset > len ? 0 : offset);
|
||||
}
|
||||
|
||||
size_t LeaseSet2::ReadMetaLS2TypeSpecificPart (const uint8_t * buf, size_t len)
|
||||
|
@ -442,18 +450,18 @@ namespace data
|
|||
uint16_t propertiesLen = bufbe16toh (buf + offset); offset += 2;
|
||||
offset += propertiesLen; // skip for now. TODO: implement properties
|
||||
// entries
|
||||
if (offset + 1 >= len) return 0;
|
||||
if (offset + 1 > len) return 0;
|
||||
int numEntries = buf[offset]; offset++;
|
||||
for (int i = 0; i < numEntries; i++)
|
||||
{
|
||||
if (offset + 40 >= len) return 0;
|
||||
if (offset + LEASE2_SIZE > len) return 0;
|
||||
offset += 32; // hash
|
||||
offset += 3; // flags
|
||||
offset += 1; // cost
|
||||
offset += 4; // expires
|
||||
}
|
||||
// revocations
|
||||
if (offset + 1 >= len) return 0;
|
||||
if (offset + 1 > len) return 0;
|
||||
int numRevocations = buf[offset]; offset++;
|
||||
for (int i = 0; i < numRevocations; i++)
|
||||
{
|
||||
|
|
|
@ -1558,7 +1558,7 @@ namespace transport
|
|||
case eSocksProxy:
|
||||
{
|
||||
// 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(),
|
||||
[] (const boost::system::error_code & ec, std::size_t transferred)
|
||||
{
|
||||
|
@ -1672,21 +1672,21 @@ namespace transport
|
|||
size_t sz = 6; // header + port
|
||||
auto buff = std::make_shared<std::vector<int8_t> >(256);
|
||||
auto readbuff = std::make_shared<std::vector<int8_t> >(256);
|
||||
(*buff)[0] = 0x05;
|
||||
(*buff)[1] = 0x01;
|
||||
(*buff)[0] = SOCKS5_VER;
|
||||
(*buff)[1] = SOCKS5_CMD_CONNECT;
|
||||
(*buff)[2] = 0x00;
|
||||
|
||||
auto& ep = conn->GetRemoteEndpoint ();
|
||||
if(ep.address ().is_v4 ())
|
||||
{
|
||||
(*buff)[3] = 0x01;
|
||||
(*buff)[3] = SOCKS5_ATYP_IPV4;
|
||||
auto addrbytes = ep.address ().to_v4().to_bytes();
|
||||
sz += 4;
|
||||
memcpy(buff->data () + 4, addrbytes.data(), 4);
|
||||
}
|
||||
else if (ep.address ().is_v6 ())
|
||||
{
|
||||
(*buff)[3] = 0x04;
|
||||
(*buff)[3] = SOCKS5_ATYP_IPV6;
|
||||
auto addrbytes = ep.address ().to_v6().to_bytes();
|
||||
sz += 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)
|
||||
{
|
||||
if (e)
|
||||
{
|
||||
LogPrint(eLogError, "NTCP2: SOCKS proxy read error ", e.message());
|
||||
}
|
||||
else if(transferred == sz)
|
||||
{
|
||||
if((*readbuff)[1] == 0x00)
|
||||
else if (!(*readbuff)[1]) // succeeded
|
||||
{
|
||||
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();
|
||||
conn->ClientLogin();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "NTCP2: Proxy reply error ", (int)(*readbuff)[1]);
|
||||
timer->cancel();
|
||||
conn->Terminate();
|
||||
});
|
||||
|
|
|
@ -240,11 +240,10 @@ namespace data
|
|||
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;
|
||||
AddRouterInfo (buf, len, updated);
|
||||
return updated;
|
||||
return AddRouterInfo (buf, len, 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))
|
||||
{
|
||||
bool wasFloodfill = r->IsFloodfill ();
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
r->Update (buf, len);
|
||||
}
|
||||
LogPrint (eLogInfo, "NetDb: RouterInfo updated: ", ident.ToBase64());
|
||||
if (wasFloodfill != r->IsFloodfill ()) // if floodfill status updated
|
||||
{
|
||||
|
@ -436,12 +438,15 @@ namespace data
|
|||
|
||||
// try reseeding from floodfill first if specified
|
||||
std::string riPath;
|
||||
if(i2p::config::GetOption("reseed.floodfill", riPath)) {
|
||||
if(i2p::config::GetOption("reseed.floodfill", riPath))
|
||||
{
|
||||
auto ri = std::make_shared<RouterInfo>(riPath);
|
||||
if (ri->IsFloodfill()) {
|
||||
if (ri->IsFloodfill())
|
||||
{
|
||||
const uint8_t * riData = ri->GetBuffer();
|
||||
int riLen = ri->GetBufferLen();
|
||||
if(!i2p::data::netdb.AddRouterInfo(riData, riLen)) {
|
||||
if (!i2p::data::netdb.AddRouterInfo(riData, riLen))
|
||||
{
|
||||
// bad router info
|
||||
LogPrint(eLogError, "NetDb: Bad router info");
|
||||
return;
|
||||
|
@ -623,7 +628,8 @@ namespace data
|
|||
(it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
||||
it.second->SetUnreachable (false);
|
||||
// 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)
|
||||
// RouterInfo expires after 1 hour if uses introducer
|
||||
|
@ -743,6 +749,11 @@ namespace data
|
|||
{
|
||||
const uint8_t * buf = m->GetPayload ();
|
||||
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);
|
||||
if (ident.IsZero ())
|
||||
{
|
||||
|
@ -753,6 +764,11 @@ namespace data
|
|||
size_t offset = DATABASE_STORE_HEADER_SIZE;
|
||||
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);
|
||||
uint32_t tunnelID = bufbe32toh (buf + offset);
|
||||
offset += 4;
|
||||
|
@ -940,9 +956,9 @@ namespace data
|
|||
}
|
||||
uint16_t numExcluded = bufbe16toh (excluded);
|
||||
excluded += 2;
|
||||
if (numExcluded > 512)
|
||||
if (numExcluded > 512 || (excluded - buf) + numExcluded*32 > (int)msg->GetPayloadLength ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " exceeds 512");
|
||||
LogPrint (eLogWarning, "NetDb: Number of excluded peers", numExcluded, " is too much");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -951,10 +967,11 @@ namespace data
|
|||
{
|
||||
LogPrint (eLogInfo, "NetDb: Exploratory close to ", key, " ", numExcluded, " excluded");
|
||||
std::set<IdentHash> excludedRouters;
|
||||
const uint8_t * excluded_ident = excluded;
|
||||
for (int i = 0; i < numExcluded; i++)
|
||||
{
|
||||
excludedRouters.insert (excluded);
|
||||
excluded += 32;
|
||||
excludedRouters.insert (excluded_ident);
|
||||
excluded_ident += 32;
|
||||
}
|
||||
std::vector<IdentHash> routers;
|
||||
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
|
||||
{
|
||||
return GetRandomRouter (
|
||||
|
@ -1431,6 +1458,7 @@ namespace data
|
|||
else
|
||||
++it;
|
||||
}
|
||||
m_LeasesPool.CleanUpMt ();
|
||||
}
|
||||
|
||||
void NetDb::PopulateRouterInfoBuffer (std::shared_ptr<RouterInfo> r)
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace data
|
|||
void Start ();
|
||||
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 AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len);
|
||||
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> 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> 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::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
|
||||
std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
||||
|
@ -125,6 +126,7 @@ namespace data
|
|||
void ClearRouterInfos () { m_RouterInfos.clear (); };
|
||||
std::shared_ptr<RouterInfo::Buffer> NewRouterInfoBuffer () { return m_RouterInfoBuffersPool.AcquireSharedMt (); };
|
||||
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; };
|
||||
|
||||
|
@ -181,6 +183,7 @@ namespace data
|
|||
uint32_t m_PublishReplyToken = 0;
|
||||
|
||||
i2p::util::MemoryPoolMt<RouterInfo::Buffer> m_RouterInfoBuffersPool;
|
||||
i2p::util::MemoryPoolMt<Lease> m_LeasesPool;
|
||||
};
|
||||
|
||||
extern NetDb netdb;
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace i2p
|
|||
RouterContext::RouterContext ():
|
||||
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
|
||||
m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown),
|
||||
m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID)
|
||||
m_Error (eRouterErrorNone), m_ErrorV6 (eRouterErrorNone), m_NetID (I2PD_NET_ID)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -60,11 +60,7 @@ namespace i2p
|
|||
i2p::data::LocalRouterInfo routerInfo;
|
||||
routerInfo.SetRouterIdentity (GetIdentity ());
|
||||
uint16_t port; i2p::config::GetOption("port", port);
|
||||
if (!port)
|
||||
{
|
||||
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
|
||||
if (port == 9150) port = 9151; // Tor browser
|
||||
}
|
||||
if (!port) port = SelectRandomPort ();
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
|
@ -121,7 +117,11 @@ namespace i2p
|
|||
if (ssu2)
|
||||
{
|
||||
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
|
||||
{
|
||||
addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4;
|
||||
|
@ -166,7 +166,11 @@ namespace i2p
|
|||
if (ssu2)
|
||||
{
|
||||
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
|
||||
{
|
||||
if (!ipv4) // no other ssu2 addresses yet
|
||||
|
@ -192,6 +196,13 @@ namespace i2p
|
|||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
||||
}
|
||||
|
||||
uint16_t RouterContext::SelectRandomPort () const
|
||||
{
|
||||
uint16_t port = rand () % (30777 - 9111) + 9111; // I2P network ports range
|
||||
if (port == 9150) port = 9151; // Tor browser
|
||||
return port;
|
||||
}
|
||||
|
||||
void RouterContext::UpdateRouterInfo ()
|
||||
{
|
||||
m_RouterInfo.CreateBuffer (m_Keys);
|
||||
|
@ -225,6 +236,13 @@ namespace i2p
|
|||
fk.write ((char *)m_SSU2Keys.get (), sizeof (SSU2PrivateKeys));
|
||||
}
|
||||
|
||||
bool RouterContext::IsSSU2Only () const
|
||||
{
|
||||
auto transports = m_RouterInfo.GetCompatibleTransports (false);
|
||||
return (transports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6)) &&
|
||||
!(transports & (i2p::data::RouterInfo::eSSUV4 | i2p::data::RouterInfo::eSSUV6));
|
||||
}
|
||||
|
||||
void RouterContext::SetStatus (RouterStatus status)
|
||||
{
|
||||
if (status != m_Status)
|
||||
|
@ -245,11 +263,18 @@ namespace i2p
|
|||
}
|
||||
}
|
||||
|
||||
void RouterContext::SetStatusSSU2 (RouterStatus status)
|
||||
{
|
||||
if (IsSSU2Only ())
|
||||
SetStatus (status);
|
||||
}
|
||||
|
||||
void RouterContext::SetStatusV6 (RouterStatus status)
|
||||
{
|
||||
if (status != m_StatusV6)
|
||||
{
|
||||
m_StatusV6 = status;
|
||||
m_ErrorV6 = eRouterErrorNone;
|
||||
switch (m_StatusV6)
|
||||
{
|
||||
case eRouterStatusOK:
|
||||
|
@ -264,12 +289,18 @@ namespace i2p
|
|||
}
|
||||
}
|
||||
|
||||
void RouterContext::SetStatusV6SSU2 (RouterStatus status)
|
||||
{
|
||||
if (IsSSU2Only ())
|
||||
SetStatusV6 (status);
|
||||
}
|
||||
|
||||
void RouterContext::UpdatePort (int port)
|
||||
{
|
||||
bool updated = false;
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
if (!address->IsNTCP2 () && !address->IsSSU2 () && address->port != port)
|
||||
if (address->port != port && (address->transportStyle == i2p::data::RouterInfo::eTransportSSU || IsSSU2Only ()))
|
||||
{
|
||||
address->port = port;
|
||||
updated = true;
|
||||
|
@ -297,12 +328,7 @@ namespace i2p
|
|||
}
|
||||
if (isAddr)
|
||||
{
|
||||
if (!port && !address->port)
|
||||
{
|
||||
// select random port only if address's port is not set
|
||||
port = rand () % (30777 - 9111) + 9111; // I2P network ports range
|
||||
if (port == 9150) port = 9151; // Tor browser
|
||||
}
|
||||
if (!port && !address->port) port = SelectRandomPort ();
|
||||
if (port) address->port = port;
|
||||
address->published = publish;
|
||||
memcpy (address->i, m_NTCP2Keys->iv, 16);
|
||||
|
@ -318,18 +344,23 @@ namespace i2p
|
|||
{
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
bool found = false, updated = false;
|
||||
for (auto it = addresses.begin (); it != addresses.end (); ++it)
|
||||
for (auto it = addresses.begin (); it != addresses.end ();)
|
||||
{
|
||||
if ((*it)->IsNTCP2 ())
|
||||
{
|
||||
found = true;
|
||||
if (!enable)
|
||||
if (enable)
|
||||
{
|
||||
addresses.erase (it);
|
||||
(*it)->s = m_NTCP2Keys->staticPublicKey;
|
||||
memcpy ((*it)->i, m_NTCP2Keys->iv, 16);
|
||||
it++;
|
||||
}
|
||||
else
|
||||
it = addresses.erase (it);
|
||||
updated = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
if (enable && !found)
|
||||
{
|
||||
|
@ -342,14 +373,26 @@ namespace i2p
|
|||
|
||||
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;
|
||||
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 ())))
|
||||
{
|
||||
address->port = port;
|
||||
if (port) address->port = port;
|
||||
else if (!address->port) address->port = newPort;
|
||||
address->published = publish;
|
||||
if (publish)
|
||||
address->caps |= (i2p::data::RouterInfo::eSSUIntroducer | i2p::data::RouterInfo::eSSUTesting);
|
||||
|
@ -366,27 +409,41 @@ namespace i2p
|
|||
{
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
bool found = false, updated = false;
|
||||
for (auto it = addresses.begin (); it != addresses.end (); ++it)
|
||||
for (auto it = addresses.begin (); it != addresses.end ();)
|
||||
{
|
||||
if ((*it)->IsSSU2 ())
|
||||
{
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
if (enable && !found)
|
||||
{
|
||||
uint8_t addressCaps = 0;
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool published; i2p::config::GetOption("ntcp2.published", published);
|
||||
if (published)
|
||||
{
|
||||
if (ipv4) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV4);
|
||||
if (ipv6) m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::AddressCaps::eV6);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t addressCaps = 0;
|
||||
if (ipv4) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV4;
|
||||
if (ipv6) addressCaps |= i2p::data::RouterInfo::AddressCaps::eV6;
|
||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, addressCaps);
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
if (updated)
|
||||
|
@ -401,24 +458,31 @@ namespace i2p
|
|||
if (address->host != host && address->IsCompatible (host) &&
|
||||
!i2p::util::net::IsYggdrasilAddress (address->host))
|
||||
{
|
||||
// update host
|
||||
address->host = host;
|
||||
if (host.is_v6 () && address->transportStyle == i2p::data::RouterInfo::eTransportSSU)
|
||||
updated = true;
|
||||
}
|
||||
if (host.is_v6 () && address->IsV6 () && address->ssu &&
|
||||
(!address->ssu->mtu || updated) && m_StatusV6 != eRouterStatusProxy)
|
||||
{
|
||||
// update MTU
|
||||
auto mtu = i2p::util::net::GetMTU (host);
|
||||
if (mtu)
|
||||
{
|
||||
LogPrint (eLogDebug, "Router: Our v6 MTU=", mtu);
|
||||
if (mtu > 1472) { // TODO: magic constant
|
||||
mtu = 1472;
|
||||
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of 1472 bytes");
|
||||
}
|
||||
if (address->ssu) address->ssu->mtu = mtu;
|
||||
}
|
||||
int maxMTU = i2p::util::net::GetMaxMTU (host.to_v6 ());
|
||||
if (mtu > maxMTU)
|
||||
{
|
||||
mtu = maxMTU;
|
||||
LogPrint(eLogWarning, "Router: MTU dropped to upper limit of ", maxMTU, " bytes");
|
||||
}
|
||||
if (mtu && !address->IsSSU2 ()) // SSU1
|
||||
mtu = (mtu >> 4) << 4; // round to multiple of 16
|
||||
address->ssu->mtu = mtu;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL)
|
||||
UpdateRouterInfo ();
|
||||
|
@ -438,6 +502,37 @@ namespace i2p
|
|||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
bool RouterContext::AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4)
|
||||
{
|
||||
if (!IsSSU2Only ()) return false;
|
||||
bool ret = m_RouterInfo.AddSSU2Introducer (introducer, v4);
|
||||
if (ret)
|
||||
UpdateRouterInfo ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RouterContext::RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4)
|
||||
{
|
||||
if (!IsSSU2Only ()) return;
|
||||
if (m_RouterInfo.RemoveSSU2Introducer (h, v4))
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::ClearSSU2Introducers (bool v4)
|
||||
{
|
||||
bool updated = false;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr : addresses)
|
||||
if (addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())) &&
|
||||
addr->ssu && !addr->ssu->introducers.empty ())
|
||||
{
|
||||
addr->ssu->introducers.clear ();
|
||||
updated = true;
|
||||
}
|
||||
if (updated)
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::SetFloodfill (bool floodfill)
|
||||
{
|
||||
m_IsFloodfill = floodfill;
|
||||
|
@ -538,6 +633,7 @@ namespace i2p
|
|||
|
||||
void RouterContext::RemoveNTCPAddress (bool v4only)
|
||||
{
|
||||
bool updated = false;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto it = addresses.begin (); it != addresses.end ();)
|
||||
{
|
||||
|
@ -545,11 +641,38 @@ namespace i2p
|
|||
(!v4only || (*it)->host.is_v4 ()))
|
||||
{
|
||||
it = addresses.erase (it);
|
||||
updated = true;
|
||||
if (v4only) break; // otherwise might be more than one address
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (updated)
|
||||
m_RouterInfo.UpdateSupportedTransports ();
|
||||
}
|
||||
|
||||
void RouterContext::RemoveSSUAddress ()
|
||||
{
|
||||
bool updated = false;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto it = addresses.begin (); it != addresses.end ();)
|
||||
{
|
||||
if ((*it)->transportStyle == i2p::data::RouterInfo::eTransportSSU)
|
||||
{
|
||||
it = addresses.erase (it);
|
||||
updated = true;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (updated)
|
||||
m_RouterInfo.UpdateSupportedTransports ();
|
||||
}
|
||||
|
||||
void RouterContext::SetUnreachableSSU2 (bool v4, bool v6)
|
||||
{
|
||||
if (IsSSU2Only ())
|
||||
SetUnreachable (v4, v6);
|
||||
}
|
||||
|
||||
void RouterContext::SetUnreachable (bool v4, bool v6)
|
||||
|
@ -568,7 +691,8 @@ namespace i2p
|
|||
// delete previous introducers
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
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->caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
|
||||
|
@ -598,13 +722,18 @@ namespace i2p
|
|||
}
|
||||
uint16_t port = 0;
|
||||
// delete previous introducers
|
||||
bool isSSU2Published = IsSSU2Only (); // TODO
|
||||
if (isSSU2Published)
|
||||
i2p::config::GetOption ("ssu2.published", isSSU2Published);
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr : addresses)
|
||||
if (addr->ssu && !addr->IsSSU2 () && ((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
if (addr->ssu && (!addr->IsSSU2 () || isSSU2Published) &&
|
||||
((v4 && addr->IsV4 ()) || (v6 && addr->IsV6 ())))
|
||||
{
|
||||
addr->published = true;
|
||||
addr->caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
||||
addr->ssu->introducers.clear ();
|
||||
if (addr->port && (!addr->IsSSU2 () || IsSSU2Only ()))
|
||||
port = addr->port;
|
||||
}
|
||||
// publish NTCP2
|
||||
|
@ -652,17 +781,18 @@ namespace i2p
|
|||
}
|
||||
port = addr->port;
|
||||
}
|
||||
if (!port) i2p::config::GetOption("port", port);
|
||||
// SSU
|
||||
if (!foundSSU)
|
||||
if (!port)
|
||||
{
|
||||
i2p::config::GetOption("port", port);
|
||||
if (!port) port = SelectRandomPort ();
|
||||
}
|
||||
// SSU
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
if (ssu)
|
||||
if (!foundSSU && ssu)
|
||||
{
|
||||
std::string host = "::1"; // TODO: read host
|
||||
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
|
||||
}
|
||||
}
|
||||
// NTCP2
|
||||
if (!foundNTCP2)
|
||||
{
|
||||
|
@ -695,6 +825,7 @@ namespace i2p
|
|||
if (ssu2Published)
|
||||
{
|
||||
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
|
||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("::1"), ssu2Port);
|
||||
}
|
||||
else
|
||||
|
@ -740,14 +871,16 @@ namespace i2p
|
|||
}
|
||||
if (addr->port) port = addr->port;
|
||||
}
|
||||
if (!port) i2p::config::GetOption("port", port);
|
||||
// SSU
|
||||
if (!foundSSU)
|
||||
if (!port)
|
||||
{
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
if (ssu)
|
||||
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
|
||||
i2p::config::GetOption("port", port);
|
||||
if (!port) port = SelectRandomPort ();
|
||||
}
|
||||
// SSU
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
if (!foundSSU && ssu)
|
||||
m_RouterInfo.AddSSUAddress (host.c_str (), port, nullptr);
|
||||
|
||||
// NTCP2
|
||||
if (!foundNTCP2)
|
||||
{
|
||||
|
@ -775,10 +908,11 @@ namespace i2p
|
|||
if (ssu2Published)
|
||||
{
|
||||
uint16_t ssu2Port; i2p::config::GetOption ("ssu2.port", ssu2Port);
|
||||
if (!ssu2Port) ssu2Port = ssu ? (port + 1) : port;
|
||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, boost::asio::ip::address::from_string ("127.0.0.1"), ssu2Port);
|
||||
}
|
||||
else
|
||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV6);
|
||||
m_RouterInfo.AddSSU2Address (m_SSU2Keys->staticPublicKey, m_SSU2Keys->intro, i2p::data::RouterInfo::eV4);
|
||||
}
|
||||
}
|
||||
m_RouterInfo.EnableV4 ();
|
||||
|
@ -815,6 +949,43 @@ namespace i2p
|
|||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::SetMTU (int mtu, bool v4)
|
||||
{
|
||||
if (mtu < 1280 || mtu > 1500) return;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr: addresses)
|
||||
{
|
||||
if (addr->ssu && ((v4 && addr->IsV4 ()) || (!v4 && addr->IsV6 ())))
|
||||
{
|
||||
if (!addr->IsSSU2 ()) // SSU1
|
||||
{
|
||||
// round to multiple of 16
|
||||
if (v4)
|
||||
{
|
||||
if (mtu > 1484) mtu = 1484;
|
||||
else
|
||||
{
|
||||
mtu -= 12;
|
||||
mtu = (mtu >> 4) << 4;
|
||||
mtu += 12;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mtu > 1488) mtu = 1488;
|
||||
else
|
||||
mtu = (mtu >> 4) << 4;
|
||||
}
|
||||
}
|
||||
if (mtu)
|
||||
{
|
||||
addr->ssu->mtu = mtu;
|
||||
LogPrint (eLogDebug, "Router: MTU for ", v4 ? "ipv4" : "ipv6", " address ", addr->host.to_string(), " is set to ", mtu);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RouterContext::UpdateNTCP2V6Address (const boost::asio::ip::address& host)
|
||||
{
|
||||
bool isYgg = i2p::util::net::IsYggdrasilAddress (host);
|
||||
|
|
|
@ -37,10 +37,9 @@ namespace garlic
|
|||
eRouterStatusOK = 0,
|
||||
eRouterStatusTesting = 1,
|
||||
eRouterStatusFirewalled = 2,
|
||||
eRouterStatusError = 3,
|
||||
eRouterStatusUnknown = 4,
|
||||
eRouterStatusProxy = 5,
|
||||
eRouterStatusMesh = 6
|
||||
eRouterStatusUnknown = 3,
|
||||
eRouterStatusProxy = 4,
|
||||
eRouterStatusMesh = 5
|
||||
};
|
||||
|
||||
enum RouterError
|
||||
|
@ -103,10 +102,14 @@ namespace garlic
|
|||
uint64_t GetTransitBandwidthLimit () const { return (m_BandwidthLimit*m_ShareRatio)/100LL; };
|
||||
RouterStatus GetStatus () const { return m_Status; };
|
||||
void SetStatus (RouterStatus status);
|
||||
void SetStatusSSU2 (RouterStatus status);
|
||||
RouterError GetError () const { return m_Error; };
|
||||
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
|
||||
void SetError (RouterError error) { m_Error = error; };
|
||||
RouterStatus GetStatusV6 () const { return m_StatusV6; };
|
||||
void SetStatusV6 (RouterStatus status);
|
||||
void SetStatusV6SSU2 (RouterStatus status);
|
||||
RouterError GetErrorV6 () const { return m_ErrorV6; };
|
||||
void SetErrorV6 (RouterError error) { m_ErrorV6 = error; };
|
||||
int GetNetID () const { return m_NetID; };
|
||||
void SetNetID (int netID) { m_NetID = netID; };
|
||||
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 UpdateSSU2Address (bool enable);
|
||||
void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later
|
||||
void RemoveSSUAddress (); // delete SSU address for older routers
|
||||
bool AddIntroducer (const i2p::data::RouterInfo::Introducer& introducer);
|
||||
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
bool AddSSU2Introducer (const i2p::data::RouterInfo::Introducer& introducer, bool v4);
|
||||
void RemoveSSU2Introducer (const i2p::data::IdentHash& h, bool v4);
|
||||
void ClearSSU2Introducers (bool v4);
|
||||
bool IsUnreachable () const;
|
||||
void SetUnreachable (bool v4, bool v6);
|
||||
void SetUnreachableSSU2 (bool v4, bool v6);
|
||||
void SetReachable (bool v4, bool v6);
|
||||
bool IsFloodfill () const { return m_IsFloodfill; };
|
||||
void SetFloodfill (bool floodfill);
|
||||
|
@ -139,6 +147,7 @@ namespace garlic
|
|||
void SetSupportsV6 (bool supportsV6);
|
||||
void SetSupportsV4 (bool supportsV4);
|
||||
void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host);
|
||||
void SetMTU (int mtu, bool v4);
|
||||
i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; };
|
||||
|
||||
void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove
|
||||
|
@ -173,8 +182,10 @@ namespace garlic
|
|||
void UpdateRouterInfo ();
|
||||
void NewNTCP2Keys ();
|
||||
void NewSSU2Keys ();
|
||||
bool IsSSU2Only () const; // SSU2 and no SSU
|
||||
bool Load ();
|
||||
void SaveKeys ();
|
||||
uint16_t SelectRandomPort () const;
|
||||
|
||||
bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize);
|
||||
|
||||
|
@ -190,7 +201,7 @@ namespace garlic
|
|||
uint64_t m_BandwidthLimit; // allowed bandwidth
|
||||
int m_ShareRatio;
|
||||
RouterStatus m_Status, m_StatusV6;
|
||||
RouterError m_Error;
|
||||
RouterError m_Error, m_ErrorV6;
|
||||
int m_NetID;
|
||||
std::mutex m_GarlicMutex;
|
||||
std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;
|
||||
|
|
|
@ -257,20 +257,38 @@ namespace data
|
|||
if (!ecode && !address->host.is_unspecified ()) isHost = true;
|
||||
}
|
||||
else if (!strcmp (key, "port"))
|
||||
{
|
||||
try
|
||||
{
|
||||
address->port = boost::lexical_cast<int>(value);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: 'port' exception ", ex.what ());
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "mtu"))
|
||||
{
|
||||
if (address->ssu)
|
||||
{
|
||||
try
|
||||
{
|
||||
address->ssu->mtu = boost::lexical_cast<int>(value);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: 'mtu' exception ", ex.what ());
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP");
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
|
||||
}
|
||||
else if (!strcmp (key, "key"))
|
||||
{
|
||||
if (address->ssu)
|
||||
isIntroKey = (Base64ToByteStream (value, strlen (value), address->i, 32) == 32);
|
||||
else
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP");
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP2");
|
||||
}
|
||||
else if (!strcmp (key, "caps"))
|
||||
address->caps = ExtractAddressCaps (value);
|
||||
|
@ -327,14 +345,41 @@ namespace data
|
|||
introducer.iHost = boost::asio::ip::address::from_string (value, ecode);
|
||||
}
|
||||
else if (!strcmp (key, "iport"))
|
||||
{
|
||||
try
|
||||
{
|
||||
introducer.iPort = boost::lexical_cast<int>(value);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: 'iport' exception ", ex.what ());
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "itag"))
|
||||
{
|
||||
try
|
||||
{
|
||||
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"))
|
||||
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
|
||||
else if (!strcmp (key, "iexp"))
|
||||
{
|
||||
try
|
||||
{
|
||||
introducer.iExp = boost::lexical_cast<uint32_t>(value);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: 'iexp' exception ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!s) return;
|
||||
}
|
||||
if (address->transportStyle == eTransportNTCP)
|
||||
|
@ -708,6 +753,7 @@ namespace data
|
|||
{
|
||||
auto addr = std::make_shared<Address>();
|
||||
addr->transportStyle = eTransportSSU2;
|
||||
addr->port = 0;
|
||||
addr->caps = caps;
|
||||
addr->date = 0;
|
||||
addr->ssu.reset (new SSUExt ());
|
||||
|
@ -727,7 +773,7 @@ namespace data
|
|||
addr->host = host;
|
||||
addr->port = port;
|
||||
addr->published = true;
|
||||
addr->caps = 0;
|
||||
addr->caps = i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer; // BC;
|
||||
addr->date = 0;
|
||||
addr->ssu.reset (new SSUExt ());
|
||||
addr->ssu->mtu = 0;
|
||||
|
@ -979,7 +1025,8 @@ namespace data
|
|||
return GetAddress (
|
||||
[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)
|
||||
{
|
||||
for (auto& addr: *m_Addresses)
|
||||
|
@ -1296,7 +1354,7 @@ namespace data
|
|||
else
|
||||
WriteString ("", s);
|
||||
|
||||
if (isPublished)
|
||||
if (isPublished && !address.host.is_unspecified ())
|
||||
{
|
||||
WriteString ("host", properties);
|
||||
properties << '=';
|
||||
|
@ -1399,7 +1457,7 @@ namespace data
|
|||
properties << ';';
|
||||
}
|
||||
}
|
||||
if (isPublished || (address.ssu && !address.IsSSU2 ()))
|
||||
if ((isPublished || (address.ssu && !address.IsSSU2 ())) && address.port)
|
||||
{
|
||||
WriteString ("port", properties);
|
||||
properties << '=';
|
||||
|
@ -1467,5 +1525,40 @@ namespace data
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -235,6 +235,7 @@ namespace data
|
|||
bool IsPeerTesting (bool v4) const;
|
||||
bool IsSSU2PeerTesting (bool v4) const;
|
||||
bool IsIntroducer (bool v4) const;
|
||||
bool IsSSU2Introducer (bool v4) const;
|
||||
|
||||
uint8_t GetCaps () const { return m_Caps; };
|
||||
void SetCaps (uint8_t caps) { m_Caps = caps; };
|
||||
|
@ -274,6 +275,8 @@ namespace data
|
|||
void SetBufferLen (size_t len) { m_BufferLen = len; };
|
||||
void RefreshTimestamp ();
|
||||
const Addresses& GetAddresses () const { return *m_Addresses; };
|
||||
CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; };
|
||||
void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; };
|
||||
|
||||
private:
|
||||
|
||||
|
@ -316,6 +319,9 @@ namespace data
|
|||
std::string GetProperty (const std::string& key) const;
|
||||
void ClearProperties () override { m_Properties.clear (); };
|
||||
|
||||
bool AddSSU2Introducer (const Introducer& introducer, bool v4);
|
||||
bool RemoveSSU2Introducer (const IdentHash& h, bool v4);
|
||||
|
||||
private:
|
||||
|
||||
void WriteToStream (std::ostream& s) const;
|
||||
|
|
732
libi2pd/SSU2.cpp
732
libi2pd/SSU2.cpp
|
@ -6,6 +6,7 @@
|
|||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include <random>
|
||||
#include "Log.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Transports.h"
|
||||
|
@ -21,7 +22,9 @@ namespace transport
|
|||
RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"),
|
||||
m_SocketV4 (m_ReceiveService.GetService ()), m_SocketV6 (m_ReceiveService.GetService ()),
|
||||
m_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()),
|
||||
m_TerminationTimer (GetService ()), m_ResendTimer (GetService ())
|
||||
m_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 ())
|
||||
{
|
||||
StartIOService ();
|
||||
i2p::config::GetOption ("ssu2.published", m_IsPublished);
|
||||
i2p::config::GetOption("nettime.frompeers", m_IsSyncClockFromPeers);
|
||||
bool found = false;
|
||||
auto& addresses = i2p::context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address: addresses)
|
||||
|
@ -37,6 +42,25 @@ namespace transport
|
|||
if (!address) continue;
|
||||
if (address->transportStyle == i2p::data::RouterInfo::eTransportSSU2)
|
||||
{
|
||||
if (m_IsThroughProxy)
|
||||
{
|
||||
found = true;
|
||||
if (address->IsV6 ())
|
||||
{
|
||||
uint16_t mtu; i2p::config::GetOption ("ssu2.mtu6", mtu);
|
||||
if (!mtu || mtu > SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE)
|
||||
mtu = SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV6_REQUEST_HEADER_SIZE;
|
||||
i2p::context.SetMTU (mtu, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t mtu; i2p::config::GetOption ("ssu2.mtu4", mtu);
|
||||
if (!mtu || mtu > SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE)
|
||||
mtu = SSU2_MAX_PACKET_SIZE - SOCKS5_UDP_IPV4_REQUEST_HEADER_SIZE;
|
||||
i2p::context.SetMTU (mtu, true);
|
||||
}
|
||||
continue; // we don't need port for proxy
|
||||
}
|
||||
auto port = address->port;
|
||||
if (!port)
|
||||
{
|
||||
|
@ -44,8 +68,9 @@ namespace transport
|
|||
if (ssu2Port) port = ssu2Port;
|
||||
else
|
||||
{
|
||||
bool ssu; i2p::config::GetOption("ssu", ssu);
|
||||
uint16_t p; i2p::config::GetOption ("port", p);
|
||||
if (p) port = p;
|
||||
if (p) port = ssu ? (p + 1) : p;
|
||||
}
|
||||
}
|
||||
if (port)
|
||||
|
@ -59,6 +84,7 @@ namespace transport
|
|||
{
|
||||
Receive (m_SocketV4);
|
||||
});
|
||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
if (address->IsV6 ())
|
||||
{
|
||||
|
@ -69,6 +95,7 @@ namespace transport
|
|||
{
|
||||
Receive (m_SocketV6);
|
||||
});
|
||||
ScheduleIntroducersUpdateTimerV6 (); // wait for 30 seconds and decide if we need introducers
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -76,41 +103,87 @@ namespace transport
|
|||
}
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
if (m_IsThroughProxy)
|
||||
ConnectToProxy ();
|
||||
m_ReceiveService.Start ();
|
||||
}
|
||||
ScheduleTermination ();
|
||||
ScheduleResend (false);
|
||||
}
|
||||
}
|
||||
|
||||
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 ();
|
||||
m_Sessions.clear ();
|
||||
m_SessionsByRouterHash.clear ();
|
||||
m_PendingOutgoingSessions.clear ();
|
||||
}
|
||||
|
||||
if (context.SupportsV4 () || context.SupportsV6 ())
|
||||
m_ReceiveService.Stop ();
|
||||
|
||||
m_SocketV4.close ();
|
||||
m_SocketV6.close ();
|
||||
if (IsRunning ())
|
||||
m_TerminationTimer.cancel ();
|
||||
|
||||
if (m_UDPAssociateSocket)
|
||||
{
|
||||
m_UDPAssociateSocket->close ();
|
||||
m_UDPAssociateSocket.reset (nullptr);
|
||||
}
|
||||
|
||||
StopIOService ();
|
||||
|
||||
m_Sessions.clear ();
|
||||
m_SessionsByRouterHash.clear ();
|
||||
m_PendingOutgoingSessions.clear ();
|
||||
m_Relays.clear ();
|
||||
m_Introducers.clear ();
|
||||
m_IntroducersV6.clear ();
|
||||
}
|
||||
|
||||
void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress)
|
||||
{
|
||||
if (localAddress.is_unspecified ()) return;
|
||||
if (localAddress.is_v4 ())
|
||||
{
|
||||
m_AddressV4 = localAddress;
|
||||
uint16_t mtu; i2p::config::GetOption ("ssu2.mtu4", mtu);
|
||||
if (!mtu) mtu = i2p::util::net::GetMTU (localAddress);
|
||||
if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE;
|
||||
if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE;
|
||||
i2p::context.SetMTU (mtu, true);
|
||||
}
|
||||
else if (localAddress.is_v6 ())
|
||||
{
|
||||
m_AddressV6 = localAddress;
|
||||
uint16_t mtu; i2p::config::GetOption ("ssu2.mtu6", mtu);
|
||||
if (!mtu)
|
||||
{
|
||||
int maxMTU = i2p::util::net::GetMaxMTU (localAddress.to_v6 ());
|
||||
mtu = i2p::util::net::GetMTU (localAddress);
|
||||
if (mtu > maxMTU) mtu = maxMTU;
|
||||
}
|
||||
else
|
||||
if (mtu > (int)SSU2_MAX_PACKET_SIZE) mtu = SSU2_MAX_PACKET_SIZE;
|
||||
if (mtu < (int)SSU2_MIN_PACKET_SIZE) mtu = SSU2_MIN_PACKET_SIZE;
|
||||
i2p::context.SetMTU (mtu, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool SSU2Server::IsSupported (const boost::asio::ip::address& addr) const
|
||||
{
|
||||
if (m_IsThroughProxy)
|
||||
return m_SocketV4.is_open ();
|
||||
if (addr.is_v4 ())
|
||||
{
|
||||
if (m_SocketV4.is_open ())
|
||||
|
@ -124,6 +197,14 @@ namespace transport
|
|||
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& socket = localEndpoint.address ().is_v6 () ? m_SocketV6 : m_SocketV4;
|
||||
|
@ -148,7 +229,7 @@ namespace transport
|
|||
void SSU2Server::Receive (boost::asio::ip::udp::socket& socket)
|
||||
{
|
||||
Packet * packet = m_PacketsPool.AcquireMt ();
|
||||
socket.async_receive_from (boost::asio::buffer (packet->buf, SSU2_MTU), packet->from,
|
||||
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)));
|
||||
}
|
||||
|
||||
|
@ -169,7 +250,7 @@ namespace transport
|
|||
while (moreBytes && packets.size () < 32)
|
||||
{
|
||||
packet = m_PacketsPool.AcquireMt ();
|
||||
packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MTU), packet->from, 0, ec);
|
||||
packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec);
|
||||
if (!ec)
|
||||
{
|
||||
i2p::transport::transports.UpdateReceivedBytes (packet->len);
|
||||
|
@ -196,6 +277,15 @@ namespace transport
|
|||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "SSU2: Receive error: code ", ecode.value(), ": ", ecode.message ());
|
||||
if (m_IsThroughProxy)
|
||||
{
|
||||
m_UDPAssociateSocket.reset (nullptr);
|
||||
m_ProxyRelayEndpoint.reset (nullptr);
|
||||
m_SocketV4.close ();
|
||||
ConnectToProxy ();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ep = socket.local_endpoint ();
|
||||
socket.close ();
|
||||
OpenSocket (ep);
|
||||
|
@ -203,23 +293,33 @@ namespace transport
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSU2Server::HandleReceivedPacket (Packet * packet)
|
||||
{
|
||||
if (packet)
|
||||
{
|
||||
if (m_IsThroughProxy)
|
||||
ProcessNextPacketFromProxy (packet->buf, packet->len);
|
||||
else
|
||||
ProcessNextPacket (packet->buf, packet->len, packet->from);
|
||||
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)
|
||||
{
|
||||
if (m_IsThroughProxy)
|
||||
for (auto& packet: packets)
|
||||
ProcessNextPacketFromProxy (packet->buf, packet->len);
|
||||
else
|
||||
for (auto& packet: packets)
|
||||
ProcessNextPacket (packet->buf, packet->len, packet->from);
|
||||
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)
|
||||
|
@ -239,6 +339,8 @@ namespace transport
|
|||
auto ident = it->second->GetRemoteIdentity ();
|
||||
if (ident)
|
||||
m_SessionsByRouterHash.erase (ident->GetIdentHash ());
|
||||
if (m_LastSession == it->second)
|
||||
m_LastSession = nullptr;
|
||||
m_Sessions.erase (it);
|
||||
}
|
||||
}
|
||||
|
@ -254,9 +356,9 @@ namespace transport
|
|||
if (!ret.second)
|
||||
{
|
||||
// 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
|
||||
GetService ().post (std::bind (&SSU2Session::Terminate, ret.first->second));
|
||||
GetService ().post (std::bind (&SSU2Session::RequestTermination, ret.first->second, eSSU2TerminationReasonReplacedByNewSession));
|
||||
// update session
|
||||
ret.first->second = session;
|
||||
}
|
||||
|
@ -363,21 +465,25 @@ namespace transport
|
|||
{
|
||||
case eSSU2SessionStateEstablished:
|
||||
case eSSU2SessionStateSessionConfirmedSent:
|
||||
m_LastSession->ProcessData (buf, len);
|
||||
m_LastSession->ProcessData (buf, len, senderEndpoint);
|
||||
break;
|
||||
case eSSU2SessionStateSessionCreatedSent:
|
||||
m_LastSession->ProcessSessionConfirmed (buf, len);
|
||||
if (!m_LastSession->ProcessSessionConfirmed (buf, len))
|
||||
{
|
||||
m_LastSession->Done ();
|
||||
m_LastSession = nullptr;
|
||||
}
|
||||
break;
|
||||
case eSSU2SessionStateIntroduced:
|
||||
if (m_LastSession->GetRemoteEndpoint ().address ().is_unspecified ())
|
||||
m_LastSession->SetRemoteEndpoint (senderEndpoint);
|
||||
if (m_LastSession->GetRemoteEndpoint () == senderEndpoint)
|
||||
if (m_LastSession->GetRemoteEndpoint ().address () == senderEndpoint.address ()) // port might be different
|
||||
m_LastSession->ProcessHolePunch (buf, len);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: HolePunch endpoint ", senderEndpoint,
|
||||
" doesn't match RelayResponse ", m_LastSession->GetRemoteEndpoint ());
|
||||
m_LastSession->Terminate ();
|
||||
LogPrint (eLogWarning, "SSU2: HolePunch address ", senderEndpoint.address (),
|
||||
" doesn't match RelayResponse ", m_LastSession->GetRemoteEndpoint ().address ());
|
||||
m_LastSession->Done ();
|
||||
m_LastSession = nullptr;
|
||||
}
|
||||
break;
|
||||
|
@ -385,6 +491,11 @@ namespace transport
|
|||
m_LastSession->SetRemoteEndpoint (senderEndpoint);
|
||||
m_LastSession->ProcessPeerTest (buf, len);
|
||||
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:
|
||||
m_LastSession = nullptr;
|
||||
break;
|
||||
|
@ -417,6 +528,11 @@ namespace transport
|
|||
void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * payload, size_t payloadLen,
|
||||
const boost::asio::ip::udp::endpoint& to)
|
||||
{
|
||||
if (m_IsThroughProxy)
|
||||
{
|
||||
SendThroughProxy (header, headerLen, nullptr, 0, payload, payloadLen, to);
|
||||
return;
|
||||
}
|
||||
std::vector<boost::asio::const_buffer> bufs
|
||||
{
|
||||
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,
|
||||
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
|
||||
{
|
||||
boost::asio::buffer (header, headerLen),
|
||||
|
@ -459,10 +580,23 @@ namespace transport
|
|||
{
|
||||
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;
|
||||
if (isValidEndpoint)
|
||||
{
|
||||
if (i2p::util::net::IsInReservedRange(address->host)) return false;
|
||||
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
||||
if (s)
|
||||
{
|
||||
|
@ -519,18 +653,27 @@ namespace transport
|
|||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::shared_ptr<i2p::data::RouterInfo> r;
|
||||
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 ()))
|
||||
{
|
||||
relayTag = it.iTag;
|
||||
relayTag = introducer.iTag;
|
||||
if (relayTag) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (r)
|
||||
{
|
||||
if (relayTag)
|
||||
|
@ -539,7 +682,8 @@ namespace transport
|
|||
auto addr = address->IsV6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address ();
|
||||
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)
|
||||
{
|
||||
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (addr->host, addr->port));
|
||||
|
@ -589,6 +733,7 @@ namespace transport
|
|||
s->SetOnEstablished ([s]() { s->SendPeerTest (); });
|
||||
return true;
|
||||
}
|
||||
else
|
||||
CreateSession (router, addr, true);
|
||||
return true;
|
||||
}
|
||||
|
@ -616,22 +761,36 @@ namespace transport
|
|||
it++;
|
||||
}
|
||||
|
||||
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
|
||||
for (auto it: m_Sessions)
|
||||
{
|
||||
if (it->second->GetState () == eSSU2SessionStateTerminated ||
|
||||
it->second->IsTerminationTimeoutExpired (ts))
|
||||
auto state = it.second->GetState ();
|
||||
if (state == eSSU2SessionStateTerminated || state == eSSU2SessionStateClosing)
|
||||
it.second->Done ();
|
||||
else if (it.second->IsTerminationTimeoutExpired (ts))
|
||||
{
|
||||
if (it->second->IsEstablished ())
|
||||
it->second->TerminateByTimeout ();
|
||||
if (it->second == m_LastSession)
|
||||
m_LastSession = nullptr;
|
||||
it = m_Sessions.erase (it);
|
||||
if (it.second->IsEstablished ())
|
||||
it.second->RequestTermination (eSSU2TerminationReasonIdleTimeout);
|
||||
else
|
||||
it.second->Done ();
|
||||
}
|
||||
else
|
||||
it.second->CleanUp (ts);
|
||||
}
|
||||
|
||||
for (auto it = m_SessionsByRouterHash.begin (); it != m_SessionsByRouterHash.begin ();)
|
||||
{
|
||||
it->second->CleanUp (ts);
|
||||
if (it->second && it->second->GetState () == eSSU2SessionStateTerminated)
|
||||
it = m_SessionsByRouterHash.erase (it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
|
||||
for (auto it = m_Relays.begin (); it != m_Relays.begin ();)
|
||||
{
|
||||
if (it->second && it->second->GetState () == eSSU2SessionStateTerminated)
|
||||
it = m_Relays.erase (it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
|
||||
for (auto it = m_IncomingTokens.begin (); it != m_IncomingTokens.end (); )
|
||||
|
@ -650,13 +809,16 @@ namespace transport
|
|||
it++;
|
||||
}
|
||||
|
||||
m_PacketsPool.CleanUpMt ();
|
||||
m_SentPacketsPool.CleanUp ();
|
||||
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,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
@ -665,12 +827,16 @@ namespace transport
|
|||
{
|
||||
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)
|
||||
it.second->Resend (ts);
|
||||
{
|
||||
resentPacketsNum += it.second->Resend (ts);
|
||||
if (resentPacketsNum > SSU2_MAX_RESEND_PACKETS) break;
|
||||
}
|
||||
for (auto it: m_PendingOutgoingSessions)
|
||||
it.second->Resend (ts);
|
||||
ScheduleResend ();
|
||||
ScheduleResend (resentPacketsNum > SSU2_MAX_RESEND_PACKETS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -711,5 +877,489 @@ namespace transport
|
|||
m_IncomingTokens.emplace (ep, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,15 +17,24 @@ namespace i2p
|
|||
{
|
||||
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_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
|
||||
{
|
||||
struct Packet
|
||||
{
|
||||
uint8_t buf[SSU2_MTU];
|
||||
uint8_t buf[SSU2_MAX_PACKET_SIZE];
|
||||
size_t len;
|
||||
boost::asio::ip::udp::endpoint from;
|
||||
};
|
||||
|
@ -49,7 +58,11 @@ namespace transport
|
|||
void Stop ();
|
||||
boost::asio::io_service& GetService () { return GetIOService (); };
|
||||
void SetLocalAddress (const boost::asio::ip::address& localAddress);
|
||||
bool SetProxy (const std::string& address, uint16_t port);
|
||||
bool UsesProxy () const { return m_IsThroughProxy; };
|
||||
bool IsSupported (const boost::asio::ip::address& addr) const;
|
||||
uint16_t GetPort (bool v4) const;
|
||||
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
|
||||
|
||||
void AddSession (std::shared_ptr<SSU2Session> session);
|
||||
void RemoveSession (uint64_t connID);
|
||||
|
@ -79,6 +92,10 @@ namespace transport
|
|||
uint64_t GetIncomingToken (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:
|
||||
|
||||
|
@ -93,10 +110,27 @@ namespace transport
|
|||
void ScheduleTermination ();
|
||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||
|
||||
void ScheduleResend ();
|
||||
void ScheduleResend (bool more);
|
||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||
|
||||
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:
|
||||
|
||||
|
@ -104,13 +138,26 @@ namespace transport
|
|||
boost::asio::ip::udp::socket m_SocketV4, m_SocketV6;
|
||||
boost::asio::ip::address m_AddressV4, m_AddressV6;
|
||||
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::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::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to
|
||||
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;
|
||||
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:
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -17,6 +17,7 @@
|
|||
#include <boost/asio.hpp>
|
||||
#include "Crypto.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
#include "TransportSession.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -25,19 +26,30 @@ namespace transport
|
|||
{
|
||||
const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||
const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
|
||||
const int SSU2_CLOCK_SKEW = 60; // in seconds
|
||||
const int SSU2_CLOCK_THRESHOLD = 15; // in seconds, if more we should adjust
|
||||
const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // for Retry message, in seconds
|
||||
const int SSU2_NEXT_TOKEN_EXPIRATION_TIMEOUT = 52*60; // for next token block, in seconds
|
||||
const int SSU2_TOKEN_EXPIRATION_THRESHOLD = 2; // in seconds
|
||||
const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds
|
||||
const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds
|
||||
const size_t SSU2_MTU = 1440; // TODO: should be 1456 for ipv4
|
||||
const size_t SSU2_MAX_PAYLOAD_SIZE = SSU2_MTU - 32;
|
||||
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1; // in seconds
|
||||
const int SSU2_RESEND_INTERVAL = 3; // in seconds
|
||||
const size_t SSU2_MAX_PACKET_SIZE = 1500;
|
||||
const size_t SSU2_MIN_PACKET_SIZE = 1280;
|
||||
const int SSU2_HANDSHAKE_RESEND_INTERVAL = 1000; // in millseconds
|
||||
const int SSU2_RESEND_INTERVAL = 300; // in milliseconds
|
||||
const int SSU2_MAX_NUM_RESENDS = 5;
|
||||
const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
|
||||
const size_t SSU2_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 uint8_t SSU2_MAX_NUM_FRAGMENTS = 64;
|
||||
|
||||
// flags
|
||||
const uint8_t SSU2_FLAG_IMMEDIATE_ACK_REQUESTED = 0x01;
|
||||
|
||||
enum SSU2MessageType
|
||||
{
|
||||
|
@ -82,14 +94,18 @@ namespace transport
|
|||
eSSU2SessionStateUnknown,
|
||||
eSSU2SessionStateTokenReceived,
|
||||
eSSU2SessionStateSessionRequestSent,
|
||||
eSSU2SessionStateSessionRequestReceived,
|
||||
eSSU2SessionStateSessionCreatedSent,
|
||||
eSSU2SessionStateSessionCreatedReceived,
|
||||
eSSU2SessionStateSessionConfirmedSent,
|
||||
eSSU2SessionStateEstablished,
|
||||
eSSU2SessionStateClosing,
|
||||
eSSU2SessionStateTerminated,
|
||||
eSSU2SessionStateFailed,
|
||||
eSSU2SessionStateIntroduced,
|
||||
eSSU2SessionStatePeerTest,
|
||||
eSSU2SessionStatePeerTestReceived // 5 before 4
|
||||
eSSU2SessionStatePeerTestReceived, // 5 before 4
|
||||
eSSU2SessionStateTokenRequestReceived
|
||||
};
|
||||
|
||||
enum SSU2PeerTestCode
|
||||
|
@ -118,11 +134,38 @@ namespace transport
|
|||
eSSU2RelayResponseCodeCharlieAliceIsUnknown = 70
|
||||
};
|
||||
|
||||
enum SSU2TerminationReason
|
||||
{
|
||||
eSSU2TerminationReasonNormalClose = 0,
|
||||
eSSU2TerminationReasonTerminationReceived = 1,
|
||||
eSSU2TerminationReasonIdleTimeout = 2,
|
||||
eSSU2TerminationReasonRouterShutdown = 3,
|
||||
eSSU2TerminationReasonDataPhaseAEADFailure= 4,
|
||||
eSSU2TerminationReasonIncompatibleOptions = 5,
|
||||
eSSU2TerminationReasonTncompatibleSignatureType = 6,
|
||||
eSSU2TerminationReasonClockSkew = 7,
|
||||
eSSU2TerminationPaddingViolation = 8,
|
||||
eSSU2TerminationReasonAEADFramingError = 9,
|
||||
eSSU2TerminationReasonPayloadFormatError = 10,
|
||||
eSSU2TerminationReasonSessionRequestError = 11,
|
||||
eSSU2TerminationReasonSessionCreatedError = 12,
|
||||
eSSU2TerminationReasonSessionConfirmedError = 13,
|
||||
eSSU2TerminationReasonTimeout = 14,
|
||||
eSSU2TerminationReasonRouterInfoSignatureVerificationFail = 15,
|
||||
eSSU2TerminationReasonInvalidS = 16,
|
||||
eSSU2TerminationReasonBanned = 17,
|
||||
eSSU2TerminationReasonBadToken = 18,
|
||||
eSSU2TerminationReasonConnectionLimits = 19,
|
||||
eSSU2TerminationReasonIncompatibleVersion = 20,
|
||||
eSSU2TerminationReasonWrongNetID = 21,
|
||||
eSSU2TerminationReasonReplacedByNewSession = 22
|
||||
};
|
||||
|
||||
struct SSU2IncompleteMessage
|
||||
{
|
||||
struct Fragment
|
||||
{
|
||||
uint8_t buf[SSU2_MTU];
|
||||
uint8_t buf[SSU2_MAX_PACKET_SIZE];
|
||||
size_t len;
|
||||
bool isLast;
|
||||
};
|
||||
|
@ -131,6 +174,16 @@ namespace transport
|
|||
int nextFragmentNum;
|
||||
uint32_t lastFragmentInsertTime; // in seconds
|
||||
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
|
||||
|
@ -153,18 +206,14 @@ namespace transport
|
|||
} h;
|
||||
};
|
||||
|
||||
struct SentPacket
|
||||
{
|
||||
uint8_t payload[SSU2_MAX_PAYLOAD_SIZE];
|
||||
size_t payloadSize = 0;
|
||||
uint32_t nextResendTime; // in seconds
|
||||
int numResends = 0;
|
||||
};
|
||||
|
||||
struct HandshakePacket: public SentPacket
|
||||
struct HandshakePacket
|
||||
{
|
||||
Header header;
|
||||
uint8_t headerX[48]; // part1 for SessionConfirmed
|
||||
uint8_t payload[SSU2_MAX_PACKET_SIZE*2];
|
||||
size_t payloadSize = 0;
|
||||
uint64_t sendTime = 0; // in milliseconds
|
||||
bool isSecondFragment = false; // for SessionConfirmed
|
||||
};
|
||||
|
||||
typedef std::function<void ()> OnEstablished;
|
||||
|
@ -186,15 +235,15 @@ namespace transport
|
|||
bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag);
|
||||
void WaitForIntroduction ();
|
||||
void SendPeerTest (); // Alice, Data message
|
||||
void Terminate ();
|
||||
void TerminateByTimeout ();
|
||||
void SendKeepAlive ();
|
||||
void RequestTermination (SSU2TerminationReason reason);
|
||||
void CleanUp (uint64_t ts);
|
||||
void FlushData ();
|
||||
void Done () override;
|
||||
void SendLocalRouterInfo (bool update) override;
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
|
||||
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; };
|
||||
uint64_t GetConnID () const { return m_SourceConnID; };
|
||||
SSU2SessionState GetState () const { return m_State; };
|
||||
|
@ -206,16 +255,19 @@ namespace transport
|
|||
bool ProcessRetry (uint8_t * buf, size_t len);
|
||||
bool ProcessHolePunch (uint8_t * buf, size_t len);
|
||||
bool ProcessPeerTest (uint8_t * buf, size_t len);
|
||||
void ProcessData (uint8_t * buf, size_t len);
|
||||
void ProcessData (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
|
||||
|
||||
private:
|
||||
|
||||
void Terminate ();
|
||||
void Established ();
|
||||
void ScheduleConnectTimer ();
|
||||
void HandleConnectTimer (const boost::system::error_code& ecode);
|
||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
||||
bool SendQueue ();
|
||||
void SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
bool SendQueue (); // returns true if ack block was sent
|
||||
bool SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ResendHandshakePacket ();
|
||||
void ConnectAfterIntroduction ();
|
||||
|
||||
void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len);
|
||||
void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len);
|
||||
|
@ -226,18 +278,25 @@ namespace transport
|
|||
void KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba);
|
||||
void SendTokenRequest ();
|
||||
void SendRetry ();
|
||||
uint32_t SendData (const uint8_t * buf, size_t len); // returns packet num
|
||||
uint32_t SendData (const uint8_t * buf, size_t len, uint8_t flags = 0); // returns packet num
|
||||
void SendQuickAck ();
|
||||
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 SendPathResponse (const uint8_t * data, size_t len);
|
||||
void SendPathChallenge ();
|
||||
|
||||
void HandlePayload (const uint8_t * buf, size_t len);
|
||||
void HandleDateTime (const uint8_t * buf, size_t len);
|
||||
void HandleAck (const uint8_t * buf, size_t len);
|
||||
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum);
|
||||
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts);
|
||||
void HandleAddress (const uint8_t * buf, size_t len);
|
||||
bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep);
|
||||
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
|
||||
std::shared_ptr<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);
|
||||
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
||||
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 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 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, uint32_t nonce); // Alice
|
||||
size_t CreateTerminationBlock (uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
SSU2Server& m_Server;
|
||||
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
||||
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
|
||||
std::unique_ptr<HandshakePacket> m_SessionConfirmedFragment1; // for Bob if applicable
|
||||
std::unique_ptr<HandshakePacket> m_SentHandshakePacket; // SessionRequest or SessionCreated
|
||||
std::unique_ptr<HandshakePacket> m_SessionConfirmedFragment; // for Bob if applicable or second fragment for Alice
|
||||
std::unique_ptr<HandshakePacket> m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
|
||||
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
||||
i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests
|
||||
|
@ -276,17 +336,20 @@ namespace transport
|
|||
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
|
||||
uint32_t m_SendPacketNum, m_ReceivePacketNum;
|
||||
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::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::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||
i2p::I2NPMessagesHandler m_Handler;
|
||||
bool m_IsDataReceived;
|
||||
size_t m_WindowSize;
|
||||
size_t m_WindowSize, m_RTT, m_RTO;
|
||||
uint32_t m_RelayTag; // between Bob and Charlie
|
||||
OnEstablished m_OnEstablished; // callback from Established
|
||||
boost::asio::deadline_timer m_ConnectTimer;
|
||||
SSU2TerminationReason m_TerminationReason;
|
||||
size_t m_MaxPayloadSize;
|
||||
std::unique_ptr<i2p::data::IdentHash> m_PathChallenge;
|
||||
};
|
||||
|
||||
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)
|
||||
|
|
|
@ -19,17 +19,14 @@
|
|||
#include "I2NPProtocol.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "TransportSession.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
|
||||
const size_t SSU_MTU_V4 = 1484;
|
||||
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_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440
|
||||
const int RESEND_INTERVAL = 3; // in seconds
|
||||
|
|
|
@ -41,7 +41,6 @@ namespace transport
|
|||
i2p::context.GetRouterInfo ().GetSSUAddress (true);
|
||||
if (address) m_IntroKey = address->i;
|
||||
}
|
||||
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
SSUSession::~SSUSession ()
|
||||
|
@ -720,8 +719,8 @@ namespace transport
|
|||
if (i2p::context.GetStatus () == eRouterStatusTesting)
|
||||
i2p::context.SetError (eRouterErrorSymmetricNAT);
|
||||
}
|
||||
else if (i2p::context.GetStatus () == eRouterStatusError && i2p::context.GetError () == eRouterErrorSymmetricNAT)
|
||||
i2p::context.SetStatus (eRouterStatusTesting);
|
||||
else if (i2p::context.GetError () == eRouterErrorSymmetricNAT)
|
||||
i2p::context.SetError (eRouterErrorNone);
|
||||
}
|
||||
uint32_t nonce = bufbe32toh (buf);
|
||||
buf += 4; // nonce
|
||||
|
|
|
@ -103,8 +103,6 @@ namespace transport
|
|||
void SendKeepAlive ();
|
||||
uint32_t GetRelayTag () const { return m_RelayTag; };
|
||||
const i2p::data::RouterInfo::IntroKey& GetIntroKey () const { return m_IntroKey; };
|
||||
uint32_t GetCreationTime () const { return m_CreationTime; };
|
||||
void SetCreationTime (uint32_t ts) { m_CreationTime = ts; }; // for introducers
|
||||
|
||||
void FlushData ();
|
||||
void CleanUp (uint64_t ts);
|
||||
|
@ -167,7 +165,6 @@ namespace transport
|
|||
i2p::crypto::AESKey m_SessionKey;
|
||||
i2p::crypto::MACKey m_MacKey;
|
||||
i2p::data::RouterInfo::IntroKey m_IntroKey;
|
||||
uint32_t m_CreationTime; // seconds since epoch
|
||||
SSUData m_Data;
|
||||
bool m_IsDataReceived;
|
||||
std::unique_ptr<SignedData> m_SignedData; // we need it for SessionConfirmed only
|
||||
|
|
|
@ -474,6 +474,29 @@ namespace stream
|
|||
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)
|
||||
{
|
||||
AsyncSend (buf, len, nullptr);
|
||||
|
@ -729,7 +752,7 @@ namespace stream
|
|||
Terminate ();
|
||||
break;
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeTunnel,
|
||||
|
@ -1085,8 +1108,6 @@ namespace stream
|
|||
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
|
||||
m_PendingIncomingTimer (m_Owner->GetService ())
|
||||
{
|
||||
if (m_Gzip)
|
||||
m_Deflator.reset (new i2p::data::GzipDeflator);
|
||||
}
|
||||
|
||||
StreamingDestination::~StreamingDestination ()
|
||||
|
@ -1341,6 +1362,26 @@ namespace 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)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
|
@ -1365,7 +1406,7 @@ namespace stream
|
|||
}
|
||||
|
||||
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;
|
||||
auto msg = m_I2NPMsgsPool.AcquireShared ();
|
||||
|
@ -1373,8 +1414,8 @@ namespace stream
|
|||
buf += 4; // reserve for lengthlength
|
||||
msg->len += 4;
|
||||
|
||||
if (m_Gzip && m_Deflator)
|
||||
size = m_Deflator->Deflate (payload, len, buf, msg->maxLen - msg->len);
|
||||
if (m_Gzip || gzip)
|
||||
size = m_Deflator.Deflate (payload, len, buf, msg->maxLen - msg->len);
|
||||
else
|
||||
size = i2p::data::GzipNoCompression (payload, len, buf, msg->maxLen - msg->len);
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -185,6 +185,7 @@ namespace stream
|
|||
template<typename Buffer, typename ReceiveHandler>
|
||||
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0);
|
||||
size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); };
|
||||
size_t Receive (uint8_t * buf, size_t len, int timeout);
|
||||
|
||||
void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); };
|
||||
|
||||
|
@ -278,13 +279,14 @@ namespace stream
|
|||
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
|
||||
void AcceptOnce (const Acceptor& acceptor);
|
||||
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; };
|
||||
void SetOwner (std::shared_ptr<i2p::client::ClientDestination> owner) { m_Owner = owner; };
|
||||
uint16_t GetLocalPort () const { return m_LocalPort; };
|
||||
|
||||
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(); }
|
||||
void DeletePacket (Packet * p) { return m_PacketsPool.Release(p); }
|
||||
|
@ -315,7 +317,7 @@ namespace stream
|
|||
public:
|
||||
|
||||
i2p::data::GzipInflator m_Inflator;
|
||||
std::unique_ptr<i2p::data::GzipDeflator> m_Deflator;
|
||||
i2p::data::GzipDeflator m_Deflator;
|
||||
|
||||
// for HTTP only
|
||||
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;
|
||||
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t));
|
||||
int left = timeout - t;
|
||||
auto self = s->shared_from_this();
|
||||
self->m_ReceiveTimer.async_wait (
|
||||
[self, buffer, handler, left](const boost::system::error_code & ec)
|
||||
s->m_ReceiveTimer.async_wait (
|
||||
[s, buffer, handler, left](const boost::system::error_code & ec)
|
||||
{
|
||||
self->HandleReceiveTimer(ec, buffer, handler, left);
|
||||
s->HandleReceiveTimer(ec, buffer, handler, left);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -24,6 +24,10 @@ namespace i2p
|
|||
{
|
||||
namespace transport
|
||||
{
|
||||
const size_t IPV4_HEADER_SIZE = 20;
|
||||
const size_t IPV6_HEADER_SIZE = 40;
|
||||
const size_t UDP_HEADER_SIZE = 8;
|
||||
|
||||
class SignedData
|
||||
{
|
||||
public:
|
||||
|
@ -75,6 +79,7 @@ namespace transport
|
|||
{
|
||||
if (router)
|
||||
m_RemoteIdentity = router->GetRouterIdentity ();
|
||||
m_CreationTime = m_LastActivityTimestamp;
|
||||
}
|
||||
|
||||
virtual ~TransportSession () {};
|
||||
|
@ -102,6 +107,9 @@ namespace transport
|
|||
bool IsTerminationTimeoutExpired (uint64_t ts) const
|
||||
{ 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 void SendLocalRouterInfo (bool update = false) { SendI2NPMessages ({ CreateDatabaseStoreMsg () }); };
|
||||
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
|
||||
|
@ -114,7 +122,17 @@ namespace transport
|
|||
bool m_IsOutgoing;
|
||||
int m_TerminationTimeout;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -167,6 +167,8 @@ namespace transport
|
|||
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);
|
||||
m_X25519KeysPairSupplier.Start ();
|
||||
m_IsRunning = true;
|
||||
|
@ -190,6 +192,8 @@ namespace transport
|
|||
|
||||
m_NTCP2Server->UseProxy(proxytype, proxyurl.host, proxyurl.port, proxyurl.user, proxyurl.pass);
|
||||
i2p::context.SetStatus (eRouterStatusProxy);
|
||||
if (ipv6)
|
||||
i2p::context.SetStatusV6 (eRouterStatusProxy);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Transports: Unsupported NTCP2 proxy URL ", ntcp2proxy);
|
||||
|
@ -218,10 +222,29 @@ namespace transport
|
|||
}
|
||||
}
|
||||
// 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
|
||||
bool ipv4; i2p::config::GetOption("ipv4", ipv4);
|
||||
if (ipv4)
|
||||
{
|
||||
std::string address; i2p::config::GetOption("address4", address);
|
||||
|
@ -236,9 +259,19 @@ namespace transport
|
|||
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)
|
||||
{
|
||||
std::string address; i2p::config::GetOption("address6", address);
|
||||
|
@ -253,6 +286,17 @@ namespace transport
|
|||
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);
|
||||
|
@ -285,8 +329,8 @@ namespace transport
|
|||
delete m_SSUServer;
|
||||
m_SSUServer = nullptr;
|
||||
}
|
||||
if (m_SSUServer) DetectExternalIP ();
|
||||
}
|
||||
if (m_SSUServer || m_SSU2Server) DetectExternalIP ();
|
||||
|
||||
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
||||
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
|
||||
|
@ -417,8 +461,7 @@ namespace transport
|
|||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
|
||||
ts, ts + PEER_ROUTER_INFO_UPDATE_INTERVAL, {} })).first;
|
||||
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, {r, ts})).first;
|
||||
}
|
||||
connected = ConnectToPeer (ident, it->second);
|
||||
}
|
||||
|
@ -453,31 +496,22 @@ namespace transport
|
|||
peer.router = netdb.FindRouter (ident); // try to get new one from netdb
|
||||
if (peer.router) // we have RI already
|
||||
{
|
||||
if (peer.numAttempts < 2) // NTCP2, 0 - ipv6, 1- ipv4
|
||||
if (peer.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;
|
||||
if (!peer.numAttempts) // NTCP2 ipv6
|
||||
case i2p::data::RouterInfo::eNTCP2V4:
|
||||
case i2p::data::RouterInfo::eNTCP2V6:
|
||||
{
|
||||
if (context.GetRouterInfo ().IsNTCP2V6 () && peer.router->IsReachableBy (RouterInfo::eNTCP2V6))
|
||||
{
|
||||
address = peer.router->GetPublishedNTCP2V6Address ();
|
||||
if (!m_NTCP2Server) continue;
|
||||
std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eNTCP2V6) ?
|
||||
peer.router->GetPublishedNTCP2V6Address () : peer.router->GetPublishedNTCP2V4Address ();
|
||||
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
|
||||
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)
|
||||
{
|
||||
auto s = std::make_shared<NTCP2Session> (*m_NTCP2Server, peer.router, address);
|
||||
|
@ -487,49 +521,41 @@ namespace transport
|
|||
m_NTCP2Server->Connect (s);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
peer.numAttempts = 2; // switch to SSU
|
||||
}
|
||||
if (peer.numAttempts == 2 || peer.numAttempts == 3) // SSU
|
||||
case i2p::data::RouterInfo::eSSU2V4:
|
||||
case i2p::data::RouterInfo::eSSU2V6:
|
||||
{
|
||||
if (m_SSUServer)
|
||||
{
|
||||
std::shared_ptr<const RouterInfo::Address> address;
|
||||
if (peer.numAttempts == 2) // SSU ipv6
|
||||
{
|
||||
if (context.GetRouterInfo ().IsSSUV6 () && peer.router->IsReachableBy (RouterInfo::eSSUV6))
|
||||
{
|
||||
address = peer.router->GetSSUV6Address ();
|
||||
if (!m_SSU2Server) continue;
|
||||
std::shared_ptr<const RouterInfo::Address> address = (tr == i2p::data::RouterInfo::eSSU2V6) ?
|
||||
peer.router->GetSSU2V6Address () : peer.router->GetSSU2V4Address ();
|
||||
if (address && m_CheckReserved && i2p::util::net::IsInReservedRange(address->host))
|
||||
address = nullptr;
|
||||
}
|
||||
peer.numAttempts++;
|
||||
}
|
||||
if (!address && peer.numAttempts == 3) // SSU ipv4
|
||||
if (address && address->IsReachableSSU ())
|
||||
{
|
||||
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))
|
||||
address = nullptr;
|
||||
}
|
||||
peer.numAttempts++;
|
||||
}
|
||||
if (address && address->IsReachableSSU ())
|
||||
{
|
||||
if (m_SSUServer->CreateSession (peer.router, address))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
peer.numAttempts += 2; // switch to Mesh
|
||||
}
|
||||
if (peer.numAttempts == 4) // Mesh
|
||||
{
|
||||
peer.numAttempts++;
|
||||
if (m_NTCP2Server && context.GetRouterInfo ().IsMesh () && peer.router->IsMesh ())
|
||||
case i2p::data::RouterInfo::eNTCP2V6Mesh:
|
||||
{
|
||||
if (!m_NTCP2Server) continue;
|
||||
auto address = peer.router->GetYggdrasilAddress ();
|
||||
if (address)
|
||||
{
|
||||
|
@ -537,43 +563,14 @@ namespace transport
|
|||
m_NTCP2Server->Connect (s);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint (eLogError, "Transports: Unknown transport ", (int)tr);
|
||||
}
|
||||
}
|
||||
if (peer.numAttempts == 5 || peer.numAttempts == 6) // SSU2
|
||||
{
|
||||
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");
|
||||
|
||||
LogPrint (eLogInfo, "Transports: No compatible addresses available");
|
||||
i2p::data::netdb.SetUnreachable (ident, true); // we are here because all connection attempts failed
|
||||
peer.Done ();
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
|
@ -589,6 +586,41 @@ namespace transport
|
|||
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)
|
||||
{
|
||||
m_Service->post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
|
||||
|
@ -622,20 +654,22 @@ namespace transport
|
|||
i2p::context.SetStatus (eRouterStatusOK);
|
||||
return;
|
||||
}
|
||||
if (m_SSUServer)
|
||||
if (m_SSUServer || m_SSU2Server)
|
||||
PeerTest ();
|
||||
else
|
||||
LogPrint (eLogError, "Transports: Can't detect external IP. SSU is not available");
|
||||
LogPrint (eLogWarning, "Transports: Can't detect external IP. SSU or SSU2 is not available");
|
||||
}
|
||||
|
||||
void Transports::PeerTest (bool ipv4, bool ipv6)
|
||||
{
|
||||
if (RoutesRestricted() || !m_SSUServer) return;
|
||||
if (RoutesRestricted() || (!m_SSUServer && !m_SSU2Server)) return;
|
||||
if (ipv4 && i2p::context.SupportsV4 ())
|
||||
{
|
||||
LogPrint (eLogInfo, "Transports: Started peer test IPv4");
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
|
||||
if (m_SSUServer)
|
||||
{
|
||||
bool statusChanged = false;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
|
@ -657,15 +691,24 @@ namespace transport
|
|||
}
|
||||
if (!statusChanged)
|
||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
|
||||
|
||||
}
|
||||
// SSU2
|
||||
if (m_SSU2Server)
|
||||
if (m_SSU2Server && !m_SSU2Server->UsesProxy ())
|
||||
{
|
||||
excluded.clear ();
|
||||
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
|
||||
if (router)
|
||||
{
|
||||
if (i2p::context.GetStatus () != eRouterStatusTesting)
|
||||
i2p::context.SetStatusSSU2 (eRouterStatusTesting);
|
||||
m_SSU2Server->StartPeerTest (router, true);
|
||||
excluded.insert (router->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ipv6 && i2p::context.SupportsV6 ())
|
||||
|
@ -673,6 +716,8 @@ namespace transport
|
|||
LogPrint (eLogInfo, "Transports: Started peer test IPv6");
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
excluded.insert (i2p::context.GetIdentHash ()); // don't pick own router
|
||||
if (m_SSUServer)
|
||||
{
|
||||
bool statusChanged = false;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
|
@ -694,15 +739,25 @@ namespace transport
|
|||
}
|
||||
if (!statusChanged)
|
||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
|
||||
}
|
||||
|
||||
// SSU2
|
||||
if (m_SSU2Server)
|
||||
if (m_SSU2Server && !m_SSU2Server->UsesProxy ())
|
||||
{
|
||||
excluded.clear ();
|
||||
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
|
||||
if (router)
|
||||
{
|
||||
if (i2p::context.GetStatusV6 () != eRouterStatusTesting)
|
||||
i2p::context.SetStatusV6SSU2 (eRouterStatusTesting);
|
||||
m_SSU2Server->StartPeerTest (router, false);
|
||||
excluded.insert (router->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -756,8 +811,7 @@ namespace transport
|
|||
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session },
|
||||
ts, ts + PEER_ROUTER_INFO_UPDATE_INTERVAL, {} }));
|
||||
m_Peers.insert (std::make_pair (ident, Peer{ nullptr, ts }));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -954,5 +1008,122 @@ namespace transport
|
|||
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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,13 @@ namespace transport
|
|||
std::list<std::shared_ptr<TransportSession> > sessions;
|
||||
uint64_t creationTime, nextRouterInfoUpdateTime;
|
||||
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 ()
|
||||
{
|
||||
|
@ -93,6 +100,7 @@ namespace transport
|
|||
void Stop ();
|
||||
|
||||
bool IsBoundSSU() const { return m_SSUServer != nullptr; }
|
||||
bool IsBoundSSU2() const { return m_SSU2Server != nullptr; }
|
||||
bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; }
|
||||
|
||||
bool IsOnline() const { return m_IsOnline; };
|
||||
|
@ -146,6 +154,7 @@ namespace transport
|
|||
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);
|
||||
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
|
||||
void SetPriority (Peer& peer) const;
|
||||
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
|
||||
void HandlePeerTestTimer (const boost::system::error_code& ecode);
|
||||
|
||||
|
@ -194,6 +203,9 @@ namespace transport
|
|||
};
|
||||
|
||||
extern Transports transports;
|
||||
|
||||
void InitAddressFromIface ();
|
||||
void InitTransports ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -60,6 +60,7 @@ namespace api
|
|||
else
|
||||
i2p::log::Logger().SendTo (i2p::fs::DataDirPath (i2p::fs::GetAppName () + ".log"));
|
||||
i2p::log::Logger().Start ();
|
||||
i2p::transport::InitTransports ();
|
||||
LogPrint(eLogInfo, "API: Starting NetDB");
|
||||
i2p::data::netdb.Start();
|
||||
LogPrint(eLogInfo, "API: Starting Transports");
|
||||
|
|
113
libi2pd/util.cpp
113
libi2pd/util.cpp
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "util.h"
|
||||
#include "Log.h"
|
||||
#include "I2PEndian.h"
|
||||
|
||||
#if not defined (__FreeBSD__)
|
||||
#include <pthread.h>
|
||||
|
@ -21,6 +22,9 @@
|
|||
#include <pthread_np.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <stdlib.h>
|
||||
|
@ -35,7 +39,7 @@
|
|||
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
|
||||
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
|
||||
|
||||
// inet_pton exists Windows since Vista, but XP doesn't have that function!
|
||||
// inet_pton and inet_ntop have been in Windows since Vista, but XP doesn't have these functions!
|
||||
// This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found
|
||||
int inet_pton_xp (int af, const char *src, void *dst)
|
||||
{
|
||||
|
@ -61,6 +65,29 @@ int inet_pton_xp (int af, const char *src, void *dst)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *inet_ntop_xp(int af, const void *src, char *dst, socklen_t size)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
unsigned long s = size;
|
||||
|
||||
ZeroMemory(&ss, sizeof(ss));
|
||||
ss.ss_family = af;
|
||||
|
||||
switch(af) {
|
||||
case AF_INET:
|
||||
((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
|
||||
break;
|
||||
case AF_INET6:
|
||||
((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
/* cannot direclty use &size because of strict aliasing rules */
|
||||
return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)? dst : NULL;
|
||||
}
|
||||
|
||||
#else /* !_WIN32 => UNIX */
|
||||
#include <sys/types.h>
|
||||
#ifdef ANDROID
|
||||
|
@ -119,8 +146,15 @@ namespace util
|
|||
}
|
||||
|
||||
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);
|
||||
# endif
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
pthread_set_name_np(pthread_self(), name);
|
||||
#elif defined(__NetBSD__)
|
||||
|
@ -133,27 +167,12 @@ namespace util
|
|||
namespace net
|
||||
{
|
||||
#ifdef _WIN32
|
||||
bool IsWindowsXPorLater ()
|
||||
{
|
||||
static bool isRequested = false;
|
||||
static bool isXP = false;
|
||||
if (!isRequested)
|
||||
{
|
||||
// request
|
||||
OSVERSIONINFO osvi;
|
||||
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
GetVersionEx(&osvi);
|
||||
|
||||
isXP = osvi.dwMajorVersion <= 5;
|
||||
isRequested = true;
|
||||
}
|
||||
return isXP;
|
||||
}
|
||||
|
||||
int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback)
|
||||
{
|
||||
typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size);
|
||||
IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop");
|
||||
if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found
|
||||
|
||||
ULONG outBufLen = 0;
|
||||
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
|
||||
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
|
||||
|
@ -172,7 +191,7 @@ namespace net
|
|||
|
||||
if(dwRetVal != NO_ERROR)
|
||||
{
|
||||
LogPrint(eLogError, "NetIface: GetMTU(): Enclosed GetAdaptersAddresses() call has failed");
|
||||
LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed");
|
||||
FREE(pAddresses);
|
||||
return fallback;
|
||||
}
|
||||
|
@ -184,7 +203,7 @@ namespace net
|
|||
|
||||
pUnicast = pCurrAddresses->FirstUnicastAddress;
|
||||
if(pUnicast == nullptr)
|
||||
LogPrint(eLogError, "NetIface: GetMTU(): Not a unicast IPv4 address, this is not supported");
|
||||
LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv4 address, this is not supported");
|
||||
|
||||
for(int i = 0; pUnicast != nullptr; ++i)
|
||||
{
|
||||
|
@ -192,8 +211,13 @@ namespace net
|
|||
sockaddr_in* localInterfaceAddress = (sockaddr_in*) lpAddr;
|
||||
if(localInterfaceAddress->sin_addr.S_un.S_addr == inputAddress.sin_addr.S_un.S_addr)
|
||||
{
|
||||
auto result = pAddresses->Mtu;
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
inetntop(AF_INET, &(((struct sockaddr_in *)localInterfaceAddress)->sin_addr), addr, INET_ADDRSTRLEN);
|
||||
|
||||
auto result = pCurrAddresses->Mtu;
|
||||
FREE(pAddresses);
|
||||
pAddresses = nullptr;
|
||||
LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv4 address ", addr);
|
||||
return result;
|
||||
}
|
||||
pUnicast = pUnicast->Next;
|
||||
|
@ -201,13 +225,17 @@ namespace net
|
|||
pCurrAddresses = pCurrAddresses->Next;
|
||||
}
|
||||
|
||||
LogPrint(eLogError, "NetIface: GetMTU(): No usable unicast IPv4 addresses found");
|
||||
LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv4 addresses found");
|
||||
FREE(pAddresses);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback)
|
||||
{
|
||||
typedef const char *(* IPN)(int af, const void *src, char *dst, socklen_t size);
|
||||
IPN inetntop = (IPN)GetProcAddress (GetModuleHandle ("ws2_32.dll"), "InetNtop");
|
||||
if (!inetntop) inetntop = inet_ntop_xp; // use own implementation if not found
|
||||
|
||||
ULONG outBufLen = 0;
|
||||
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
|
||||
PIP_ADAPTER_ADDRESSES pCurrAddresses = nullptr;
|
||||
|
@ -226,7 +254,7 @@ namespace net
|
|||
|
||||
if (dwRetVal != NO_ERROR)
|
||||
{
|
||||
LogPrint(eLogError, "NetIface: GetMTU(): Enclosed GetAdaptersAddresses() call has failed");
|
||||
LogPrint(eLogError, "NetIface: GetMTU: Enclosed GetAdaptersAddresses() call has failed");
|
||||
FREE(pAddresses);
|
||||
return fallback;
|
||||
}
|
||||
|
@ -238,7 +266,7 @@ namespace net
|
|||
PIP_ADAPTER_UNICAST_ADDRESS firstUnicastAddress = pCurrAddresses->FirstUnicastAddress;
|
||||
pUnicast = pCurrAddresses->FirstUnicastAddress;
|
||||
if (pUnicast == nullptr)
|
||||
LogPrint(eLogError, "NetIface: GetMTU(): Not a unicast IPv6 address, this is not supported");
|
||||
LogPrint(eLogError, "NetIface: GetMTU: Not a unicast IPv6 address, this is not supported");
|
||||
|
||||
for (int i = 0; pUnicast != nullptr; ++i)
|
||||
{
|
||||
|
@ -255,9 +283,13 @@ namespace net
|
|||
|
||||
if (found_address)
|
||||
{
|
||||
auto result = pAddresses->Mtu;
|
||||
char addr[INET6_ADDRSTRLEN];
|
||||
inetntop(AF_INET6, &(((struct sockaddr_in6 *)localInterfaceAddress)->sin6_addr), addr, INET6_ADDRSTRLEN);
|
||||
|
||||
auto result = pCurrAddresses->Mtu;
|
||||
FREE(pAddresses);
|
||||
pAddresses = nullptr;
|
||||
LogPrint(eLogInfo, "NetIface: GetMTU: Using ", result, " bytes for IPv6 address ", addr);
|
||||
return result;
|
||||
}
|
||||
pUnicast = pUnicast->Next;
|
||||
|
@ -266,7 +298,7 @@ namespace net
|
|||
pCurrAddresses = pCurrAddresses->Next;
|
||||
}
|
||||
|
||||
LogPrint(eLogError, "NetIface: GetMTU(): No usable unicast IPv6 addresses found");
|
||||
LogPrint(eLogError, "NetIface: GetMTU: No usable unicast IPv6 addresses found");
|
||||
FREE(pAddresses);
|
||||
return fallback;
|
||||
}
|
||||
|
@ -298,7 +330,7 @@ namespace net
|
|||
}
|
||||
else
|
||||
{
|
||||
LogPrint(eLogError, "NetIface: GetMTU(): Address family is not supported");
|
||||
LogPrint(eLogError, "NetIface: GetMTU: Address family is not supported");
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
@ -423,6 +455,27 @@ namespace net
|
|||
#endif
|
||||
}
|
||||
|
||||
int GetMaxMTU (const boost::asio::ip::address_v6& localAddress)
|
||||
{
|
||||
uint32_t prefix = bufbe32toh (localAddress.to_bytes ().data ());
|
||||
switch (prefix)
|
||||
{
|
||||
case 0x20010470:
|
||||
case 0x260070ff:
|
||||
// Hurricane Electric
|
||||
return 1480;
|
||||
break;
|
||||
case 0x2a06a003:
|
||||
case 0x2a06a004:
|
||||
case 0x2a06a005:
|
||||
// route48
|
||||
return 1420;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
return 1500;
|
||||
}
|
||||
|
||||
static bool IsYggdrasilAddress (const uint8_t addr[16])
|
||||
{
|
||||
return addr[0] == 0x02 || addr[0] == 0x03;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -218,6 +218,7 @@ namespace util
|
|||
namespace net
|
||||
{
|
||||
int GetMTU (const boost::asio::ip::address& localAddress);
|
||||
int GetMaxMTU (const boost::asio::ip::address_v6& localAddress); // check tunnel broker for ipv6 address
|
||||
const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6=false);
|
||||
boost::asio::ip::address_v6 GetYggdrasilAddress ();
|
||||
bool IsLocalAddress (const boost::asio::ip::address& addr);
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
|
||||
|
||||
#define I2PD_VERSION_MAJOR 2
|
||||
#define I2PD_VERSION_MINOR 42
|
||||
#define I2PD_VERSION_MICRO 1
|
||||
#define I2PD_VERSION_MINOR 44
|
||||
#define I2PD_VERSION_MICRO 0
|
||||
#define I2PD_VERSION_PATCH 0
|
||||
#ifdef GITVER
|
||||
#define I2PD_VERSION GITVER
|
||||
|
@ -31,7 +31,7 @@
|
|||
|
||||
#define I2P_VERSION_MAJOR 0
|
||||
#define I2P_VERSION_MINOR 9
|
||||
#define I2P_VERSION_MICRO 54
|
||||
#define I2P_VERSION_MICRO 56
|
||||
#define I2P_VERSION_PATCH 0
|
||||
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
||||
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
||||
|
|
|
@ -397,6 +397,19 @@ namespace client
|
|||
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)
|
||||
{
|
||||
auto pos = jump.find(".b32.i2p");
|
||||
|
@ -567,6 +580,7 @@ namespace client
|
|||
|
||||
void AddressBook::LoadLocal ()
|
||||
{
|
||||
if (!m_Storage) return;
|
||||
std::map<std::string, std::shared_ptr<Address>> localAddresses;
|
||||
m_Storage->LoadLocal (localAddresses);
|
||||
for (const auto& it: localAddresses)
|
||||
|
@ -819,40 +833,22 @@ namespace client
|
|||
}
|
||||
else
|
||||
m_Ident = addr->identHash;
|
||||
/* this code block still needs some love */
|
||||
std::condition_variable newDataReceived;
|
||||
std::mutex newDataReceivedMutex;
|
||||
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (m_Ident);
|
||||
if (!leaseSet)
|
||||
// save url parts for later use
|
||||
std::string dest_host = url.host;
|
||||
int dest_port = url.port ? url.port : 80;
|
||||
// try to create stream to addressbook site
|
||||
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (m_Ident, dest_port);
|
||||
if (!stream)
|
||||
{
|
||||
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");
|
||||
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);
|
||||
LogPrint (eLogDebug, "Addressbook: Loaded for ", url.host, ": ETag: ", m_Etag, ", Last-Modified: ", m_LastModified);
|
||||
}
|
||||
/* save url parts for later use */
|
||||
std::string dest_host = url.host;
|
||||
int dest_port = url.port ? url.port : 80;
|
||||
/* create http request & send it */
|
||||
// create http request & send it
|
||||
i2p::http::HTTPReq req;
|
||||
req.AddHeader("Host", dest_host);
|
||||
req.AddHeader("User-Agent", "Wget/1.11.4");
|
||||
|
@ -863,34 +859,29 @@ namespace client
|
|||
req.AddHeader("If-None-Match", m_Etag);
|
||||
if (!m_LastModified.empty())
|
||||
req.AddHeader("If-Modified-Since", m_LastModified);
|
||||
/* convert url to relative */
|
||||
// convert url to relative
|
||||
url.schema = "";
|
||||
url.host = "";
|
||||
req.uri = url.to_string();
|
||||
req.version = "HTTP/1.1";
|
||||
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, dest_port);
|
||||
std::string request = req.to_string();
|
||||
stream->Send ((const uint8_t *) request.data(), request.length());
|
||||
/* read response */
|
||||
// read response
|
||||
std::string response;
|
||||
uint8_t recv_buf[4096];
|
||||
bool end = false;
|
||||
int numAttempts = 0;
|
||||
while (!end)
|
||||
{
|
||||
stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096),
|
||||
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
size_t received = stream->Receive (recv_buf, 4096, SUBSCRIPTION_REQUEST_TIMEOUT);
|
||||
if (received)
|
||||
{
|
||||
if (bytes_transferred)
|
||||
response.append ((char *)recv_buf, bytes_transferred);
|
||||
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
|
||||
response.append ((char *)recv_buf, received);
|
||||
if (!stream->IsOpen ()) end = true;
|
||||
}
|
||||
else if (!stream->IsOpen ())
|
||||
end = true;
|
||||
newDataReceived.notify_all ();
|
||||
},
|
||||
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)
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Addressbook: Subscriptions request timeout expired");
|
||||
numAttempts++;
|
||||
|
@ -900,7 +891,7 @@ namespace client
|
|||
// process remaining buffer
|
||||
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf)))
|
||||
response.append ((char *)recv_buf, len);
|
||||
/* parse response */
|
||||
// parse response
|
||||
i2p::http::HTTPRes res;
|
||||
int res_head_len = res.parse(response);
|
||||
if (res_head_len < 0)
|
||||
|
@ -913,7 +904,7 @@ namespace client
|
|||
LogPrint(eLogError, "Addressbook: Incomplete http response from ", dest_host, ", interrupted by timeout");
|
||||
return false;
|
||||
}
|
||||
/* assert: res_head_len > 0 */
|
||||
// assert: res_head_len > 0
|
||||
response.erase(0, res_head_len);
|
||||
if (res.code == 304)
|
||||
{
|
||||
|
@ -936,7 +927,7 @@ namespace client
|
|||
LogPrint(eLogError, "Addressbook: Response size mismatch, expected: ", len, ", got: ", response.length(), "bytes");
|
||||
return false;
|
||||
}
|
||||
/* assert: res.code == 200 */
|
||||
// assert: res.code == 200
|
||||
auto it = res.headers.find("ETag");
|
||||
if (it != res.headers.end()) m_Etag = it->second;
|
||||
it = res.headers.find("Last-Modified");
|
||||
|
|
|
@ -90,6 +90,8 @@ namespace client
|
|||
void InsertAddress (const std::string& address, const std::string& jump); // for jump links
|
||||
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);
|
||||
void DownloadComplete (bool success, const i2p::data::IdentHash& subscription, const std::string& etag, const std::string& lastModified);
|
||||
//This method returns the ".b32.i2p" address
|
||||
|
|
|
@ -156,7 +156,7 @@ namespace client
|
|||
{
|
||||
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);
|
||||
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 ());
|
||||
}
|
||||
|
||||
|
|
|
@ -261,7 +261,7 @@ namespace client
|
|||
static const std::string transient("transient");
|
||||
if (!filename.compare (0, transient.length (), transient)) // starts with transient
|
||||
{
|
||||
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType);
|
||||
keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType, cryptoType, true);
|
||||
LogPrint (eLogInfo, "Clients: New transient keys address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
|
||||
return true;
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ namespace client
|
|||
else
|
||||
{
|
||||
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);
|
||||
size_t len = keys.GetFullLen ();
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
|
@ -328,7 +328,7 @@ namespace client
|
|||
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType,
|
||||
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);
|
||||
AddLocalDestination (localDestination);
|
||||
return localDestination;
|
||||
|
@ -339,7 +339,7 @@ namespace client
|
|||
i2p::data::SigningKeyType sigType, i2p::data::CryptoKeyType cryptoType,
|
||||
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);
|
||||
AddLocalDestination (localDestination);
|
||||
return localDestination;
|
||||
|
@ -726,6 +726,7 @@ namespace client
|
|||
|
||||
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 ssl = section.second.get(I2P_SERVER_TUNNEL_SSL, false);
|
||||
|
||||
// I2CP
|
||||
std::map<std::string, std::string> options;
|
||||
|
@ -804,6 +805,8 @@ namespace client
|
|||
LogPrint(eLogInfo, "Clients: Disabling loopback address mapping");
|
||||
serverTunnel->SetUniqueLocal(isUniqueLocal);
|
||||
}
|
||||
if (ssl)
|
||||
serverTunnel->SetSSL (true);
|
||||
if (accessList.length () > 0)
|
||||
{
|
||||
std::set<i2p::data::IdentHash> idents;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include "HTTPProxy.h"
|
||||
#include "SOCKS.h"
|
||||
#include "I2PTunnel.h"
|
||||
#include "UDPTunnel.h"
|
||||
#include "SAM.h"
|
||||
#include "BOB.h"
|
||||
#include "I2CP.h"
|
||||
|
@ -61,7 +62,7 @@ namespace client
|
|||
const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword";
|
||||
const char I2P_SERVER_TUNNEL_ADDRESS[] = "address";
|
||||
const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal";
|
||||
|
||||
const char I2P_SERVER_TUNNEL_SSL[] = "ssl";
|
||||
|
||||
class ClientContext
|
||||
{
|
||||
|
|
|
@ -36,12 +36,14 @@ namespace proxy {
|
|||
"reg.i2p",
|
||||
"stats.i2p",
|
||||
"identiguy.i2p",
|
||||
"notbob.i2p"
|
||||
};
|
||||
|
||||
static const std::map<std::string, std::string> jumpservices = {
|
||||
{ "reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/" },
|
||||
{ "identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname=" },
|
||||
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" },
|
||||
{ "notbob.i2p", "http://nytzrhrjjfsutowojvxi7hphesskpqqr65wpistz6wa7cpajhp7a.b32.i2p/cgi-bin/jump.cgi?q=" }
|
||||
};
|
||||
|
||||
static const char *pageHead =
|
||||
|
@ -82,6 +84,7 @@ namespace proxy {
|
|||
void GenericProxyInfo(const std::string& title, const std::string& description);
|
||||
void HostNotFound(std::string& host);
|
||||
void SendProxyError(std::string& content);
|
||||
void SendRedirect(std::string& address);
|
||||
|
||||
void ForwardToUpstreamProxy();
|
||||
void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec);
|
||||
|
@ -207,6 +210,17 @@ namespace proxy {
|
|||
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)
|
||||
{
|
||||
confirm = false;
|
||||
|
@ -237,6 +251,7 @@ namespace proxy {
|
|||
req.RemoveHeader("Via");
|
||||
req.RemoveHeader("From");
|
||||
req.RemoveHeader("Forwarded");
|
||||
req.RemoveHeader("DNT"); // Useless DoNotTrack flag
|
||||
req.RemoveHeader("Accept", "Accept-Encoding"); // Accept*, but Accept-Encoding
|
||||
/* drop proxy-disclosing headers */
|
||||
req.RemoveHeader("X-Forwarded");
|
||||
|
@ -297,7 +312,14 @@ namespace proxy {
|
|||
GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported"));
|
||||
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);
|
||||
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::stringstream ss;
|
||||
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>.";
|
||||
GenericProxyInfo(tr("Addresshelper found"), ss.str());
|
||||
return true; /* request processed */
|
||||
|
@ -422,8 +445,8 @@ namespace proxy {
|
|||
void HTTPReqHandler::ForwardToUpstreamProxy()
|
||||
{
|
||||
LogPrint(eLogDebug, "HTTPProxy: Forwarded to upstream");
|
||||
// build http request
|
||||
|
||||
/* build http request */
|
||||
m_ClientRequestURL = m_RequestURL;
|
||||
LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host);
|
||||
m_ClientRequestURL.schema = "";
|
||||
|
@ -431,17 +454,17 @@ namespace proxy {
|
|||
std::string origURI = m_ClientRequest.uri; // TODO: what do we need to change uri for?
|
||||
m_ClientRequest.uri = m_ClientRequestURL.to_string();
|
||||
|
||||
// update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections
|
||||
/* update User-Agent to ESR version of Firefox, same as Tor Browser below version 8, for non-HTTPS connections */
|
||||
if(m_ClientRequest.method != "CONNECT")
|
||||
m_ClientRequest.UpdateHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0");
|
||||
|
||||
m_ClientRequest.write(m_ClientRequestBuffer);
|
||||
m_ClientRequestBuffer << m_recv_buf.substr(m_req_len);
|
||||
|
||||
// assume http if empty schema
|
||||
/* assume http if empty schema */
|
||||
if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http")
|
||||
{
|
||||
// handle upstream http proxy
|
||||
/* handle upstream http proxy */
|
||||
if (!m_ProxyURL.port) m_ProxyURL.port = 80;
|
||||
if (m_ProxyURL.is_i2p())
|
||||
{
|
||||
|
@ -449,9 +472,9 @@ namespace proxy {
|
|||
auto auth = i2p::http::CreateBasicAuthorizationString (m_ProxyURL.user, m_ProxyURL.pass);
|
||||
if (!auth.empty ())
|
||||
{
|
||||
// remove existing authorization if any
|
||||
/* remove existing authorization if any */
|
||||
m_ClientRequest.RemoveHeader("Proxy-");
|
||||
// add own http proxy authorization
|
||||
/* add own http proxy authorization */
|
||||
m_ClientRequest.AddHeader("Proxy-Authorization", auth);
|
||||
}
|
||||
m_send_buf = m_ClientRequest.to_string();
|
||||
|
@ -470,7 +493,7 @@ namespace proxy {
|
|||
}
|
||||
else if (m_ProxyURL.schema == "socks")
|
||||
{
|
||||
// handle upstream socks proxy
|
||||
/* handle upstream socks proxy */
|
||||
if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified
|
||||
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
|
||||
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
|
||||
|
@ -479,7 +502,7 @@ namespace proxy {
|
|||
}
|
||||
else
|
||||
{
|
||||
// unknown type, complain
|
||||
/* unknown type, complain */
|
||||
GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,10 +46,13 @@ namespace client
|
|||
}
|
||||
|
||||
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):
|
||||
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
|
||||
m_RemoteEndpoint (target), m_IsQuiet (quiet)
|
||||
const boost::asio::ip::tcp::endpoint& target, bool quiet,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx):
|
||||
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 ()
|
||||
|
@ -69,7 +72,7 @@ namespace client
|
|||
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;
|
||||
const uint8_t * ident = addr;
|
||||
|
@ -80,7 +83,9 @@ namespace client
|
|||
}
|
||||
|
||||
#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
|
||||
// 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);
|
||||
if (ec)
|
||||
LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ());
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void I2PTunnelConnection::Connect (bool isUniqueLocal)
|
||||
{
|
||||
I2PTunnelSetSocketOptions(m_Socket);
|
||||
if (m_Socket)
|
||||
{
|
||||
I2PTunnelSetSocketOptions (m_Socket);
|
||||
#ifdef __linux__
|
||||
if (isUniqueLocal && m_RemoteEndpoint.address ().is_v4 () &&
|
||||
m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127)
|
||||
|
@ -131,6 +136,7 @@ namespace client
|
|||
void I2PTunnelConnection::Terminate ()
|
||||
{
|
||||
if (Kill()) return;
|
||||
if (m_SSL) m_SSL = nullptr;
|
||||
if (m_Stream)
|
||||
{
|
||||
m_Stream->Close ();
|
||||
|
@ -145,12 +151,17 @@ namespace client
|
|||
|
||||
void I2PTunnelConnection::Receive ()
|
||||
{
|
||||
if (m_SSL)
|
||||
m_SSL->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
|
||||
std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
else
|
||||
m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
|
||||
std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (),
|
||||
std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (),
|
||||
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)
|
||||
{
|
||||
|
@ -239,6 +250,10 @@ namespace client
|
|||
|
||||
void I2PTunnelConnection::Write (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (m_SSL)
|
||||
boost::asio::async_write (*m_SSL, boost::asio::buffer (buf, len), boost::asio::transfer_all (),
|
||||
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
|
||||
else
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (),
|
||||
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
@ -253,6 +268,30 @@ namespace client
|
|||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PTunnel: Connected");
|
||||
if (m_SSL)
|
||||
m_SSL->async_handshake (boost::asio::ssl::stream_base::client,
|
||||
std::bind (&I2PTunnelConnection::HandleHandshake, shared_from_this (), std::placeholders::_1));
|
||||
else
|
||||
Established ();
|
||||
}
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::HandleHandshake (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "I2PTunnel: Handshake error: ", ecode.message ());
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PTunnel: SSL connected");
|
||||
Established ();
|
||||
}
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Established ()
|
||||
{
|
||||
if (m_IsQuiet)
|
||||
StreamReceive ();
|
||||
else
|
||||
|
@ -267,7 +306,6 @@ namespace client
|
|||
}
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
void I2PClientTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
|
||||
{
|
||||
|
@ -321,15 +359,24 @@ namespace client
|
|||
m_HeaderSent = true;
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
|
||||
}
|
||||
else if (m_OutHeader.tellp () < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE)
|
||||
StreamReceive (); // read more header
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE);
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host):
|
||||
I2PTunnelConnection (owner, stream, socket, target), m_Host (host),
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx):
|
||||
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_Host (host),
|
||||
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)
|
||||
|
@ -404,6 +451,13 @@ namespace client
|
|||
m_HeaderSent = true;
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
|
||||
}
|
||||
else if (m_OutHeader.tellp () < I2P_TUNNEL_HTTP_MAX_HEADER_SIZE)
|
||||
StreamReceive (); // read more header
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "I2PTunnel: HTTP header exceeds max size ", I2P_TUNNEL_HTTP_MAX_HEADER_SIZE);
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -460,9 +514,9 @@ namespace client
|
|||
}
|
||||
|
||||
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):
|
||||
I2PTunnelConnection (owner, stream, socket, target), m_From (stream->GetRemoteIdentity ()),
|
||||
const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx):
|
||||
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_From (stream->GetRemoteIdentity ()),
|
||||
m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass)
|
||||
{
|
||||
}
|
||||
|
@ -473,7 +527,8 @@ namespace client
|
|||
if (m_NeedsWebIrc)
|
||||
{
|
||||
m_NeedsWebIrc = false;
|
||||
m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " " << GetSocket ()->local_endpoint ().address () << std::endl;
|
||||
m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ())
|
||||
<< " " << GetSocket ()->local_endpoint ().address () << std::endl;
|
||||
}
|
||||
|
||||
m_InPacket.clear ();
|
||||
|
@ -660,6 +715,12 @@ namespace client
|
|||
|
||||
void I2PServerTunnel::Stop ()
|
||||
{
|
||||
if (m_PortDestination)
|
||||
m_PortDestination->ResetAcceptor ();
|
||||
auto localDestination = GetLocalDestination ();
|
||||
if (localDestination)
|
||||
localDestination->StopAcceptingStreams ();
|
||||
|
||||
ClearHandlers ();
|
||||
}
|
||||
|
||||
|
@ -733,6 +794,17 @@ namespace client
|
|||
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 ()
|
||||
{
|
||||
if (m_PortDestination)
|
||||
|
@ -773,7 +845,7 @@ namespace client
|
|||
|
||||
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)
|
||||
{
|
||||
return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream,
|
||||
std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), m_Host);
|
||||
return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream, GetEndpoint (), m_Host, GetSSLCtx ());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return std::make_shared<I2PTunnelConnectionIRC> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), this->m_WebircPass);
|
||||
}
|
||||
|
||||
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);
|
||||
return std::make_shared<I2PTunnelConnectionIRC> (this, stream, GetEndpoint (), m_WebircPass, GetSSLCtx ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include "Identity.h"
|
||||
#include "Destination.h"
|
||||
#include "Datagram.h"
|
||||
#include "Streaming.h"
|
||||
#include "I2PService.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_B64[] = "X-I2P-DestB64"; // full address in base64
|
||||
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>
|
||||
{
|
||||
|
@ -43,8 +44,9 @@ namespace client
|
|||
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,
|
||||
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,
|
||||
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
|
||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
const boost::asio::ip::tcp::endpoint& target, bool quiet = true,
|
||||
std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr); // from I2P
|
||||
~I2PTunnelConnection ();
|
||||
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
|
||||
void Connect (bool isUniqueLocal = true);
|
||||
|
@ -55,21 +57,27 @@ namespace client
|
|||
void Terminate ();
|
||||
|
||||
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
|
||||
void HandleWrite (const boost::system::error_code& ecode);
|
||||
virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded
|
||||
|
||||
void StreamReceive ();
|
||||
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleConnect (const boost::system::error_code& ecode);
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> GetSocket () const { return m_Socket; };
|
||||
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > GetSSL () const { return m_SSL; };
|
||||
|
||||
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:
|
||||
|
||||
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::ssl::stream<boost::asio::ip::tcp::socket&> > m_SSL;
|
||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
||||
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
|
||||
bool m_IsQuiet; // don't send destination
|
||||
|
@ -99,8 +107,8 @@ namespace client
|
|||
public:
|
||||
|
||||
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:
|
||||
|
||||
|
@ -120,8 +128,8 @@ namespace client
|
|||
public:
|
||||
|
||||
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:
|
||||
|
||||
|
@ -171,162 +179,6 @@ namespace client
|
|||
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
|
||||
{
|
||||
public:
|
||||
|
@ -342,6 +194,9 @@ namespace client
|
|||
void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = 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);
|
||||
|
||||
const std::string& GetAddress() const { return m_Address; }
|
||||
|
@ -370,6 +225,7 @@ namespace client
|
|||
std::set<i2p::data::IdentHash> m_AccessList;
|
||||
bool m_IsAccessList;
|
||||
std::unique_ptr<boost::asio::ip::address> m_LocalAddress;
|
||||
std::shared_ptr<boost::asio::ssl::context> m_SSLCtx;
|
||||
};
|
||||
|
||||
class I2PServerTunnelHTTP: public I2PServerTunnel
|
||||
|
@ -405,6 +261,8 @@ namespace client
|
|||
|
||||
std::string m_WebircPass;
|
||||
};
|
||||
|
||||
boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -709,7 +709,7 @@ namespace client
|
|||
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
|
||||
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
|
||||
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
|
||||
|
@ -1477,15 +1477,22 @@ namespace client
|
|||
*destination = 0; destination++;
|
||||
auto session = FindSession (sessionID);
|
||||
if (session)
|
||||
{
|
||||
auto localDest = session->GetLocalDestination ();
|
||||
auto datagramDest = localDest ? localDest->GetDatagramDestination () : nullptr;
|
||||
if (datagramDest)
|
||||
{
|
||||
i2p::data::IdentityEx dest;
|
||||
dest.FromBase64 (destination);
|
||||
if (session->Type == eSAMSessionTypeDatagram)
|
||||
session->GetLocalDestination ()->GetDatagramDestination ()->
|
||||
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
|
||||
else // raw
|
||||
session->GetLocalDestination ()->GetDatagramDestination ()->
|
||||
SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
|
||||
datagramDest->SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
|
||||
else if (session->Type == eSAMSessionTypeRaw)
|
||||
datagramDest->SendRawDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
|
||||
else
|
||||
LogPrint (eLogError, "SAM: Unexpected session type ", (int)session->Type, "for session ", sessionID);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "SAM: Datagram destination is not set for session ", sessionID);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "SAM: Session ", sessionID, " not found");
|
||||
|
|
377
libi2pd_client/UDPTunnel.cpp
Normal file
377
libi2pd_client/UDPTunnel.cpp
Normal 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
187
libi2pd_client/UDPTunnel.h
Normal 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
|
Loading…
Reference in a new issue